Type the Persona chat artifact contract
This commit is contained in:
parent
59fd800702
commit
7e4564a34f
7 changed files with 169 additions and 44 deletions
|
|
@ -85,7 +85,7 @@ fn main() -> Result<()> {
|
|||
&artifact_dir,
|
||||
&cultmesh_store,
|
||||
&runtime_id,
|
||||
"draft",
|
||||
PersonaChatStatus::Draft,
|
||||
"drafted without posting",
|
||||
)?
|
||||
}
|
||||
|
|
@ -137,7 +137,7 @@ fn run_draft(
|
|||
artifact_dir: &Path,
|
||||
cultmesh_store: &Path,
|
||||
runtime_id: &str,
|
||||
status: &str,
|
||||
status: PersonaChatStatus,
|
||||
reason: &str,
|
||||
) -> Result<Value> {
|
||||
ensure_content(content, "Persona chat")?;
|
||||
|
|
@ -152,7 +152,7 @@ fn run_draft(
|
|||
)?;
|
||||
let path = write_draft(content, config, artifact_dir, status, reason, Some(&audit))?;
|
||||
Ok(serde_json::json!({
|
||||
"ok": status != "blocked",
|
||||
"ok": status != PersonaChatStatus::Blocked,
|
||||
"posted": false,
|
||||
"draftPath": path,
|
||||
"speechAudit": audit,
|
||||
|
|
@ -221,7 +221,7 @@ fn run_post(
|
|||
content,
|
||||
config,
|
||||
artifact_dir,
|
||||
"blocked",
|
||||
PersonaChatStatus::Blocked,
|
||||
&format!("speech audit blocked: {}", audit.reasons.join("; ")),
|
||||
Some(&audit),
|
||||
)?;
|
||||
|
|
@ -238,7 +238,7 @@ fn run_post(
|
|||
content,
|
||||
config,
|
||||
artifact_dir,
|
||||
"blocked",
|
||||
PersonaChatStatus::Blocked,
|
||||
"missing #aquarium channel id",
|
||||
Some(&audit),
|
||||
)?;
|
||||
|
|
@ -255,7 +255,7 @@ fn run_post(
|
|||
content,
|
||||
config,
|
||||
artifact_dir,
|
||||
"blocked",
|
||||
PersonaChatStatus::Blocked,
|
||||
"requested channel does not match configured #aquarium channel id",
|
||||
Some(&audit),
|
||||
)?;
|
||||
|
|
@ -272,7 +272,7 @@ fn run_post(
|
|||
content,
|
||||
config,
|
||||
artifact_dir,
|
||||
"blocked",
|
||||
PersonaChatStatus::Blocked,
|
||||
"missing Bifrost bridge CLI path",
|
||||
Some(&audit),
|
||||
)?;
|
||||
|
|
@ -289,7 +289,7 @@ fn run_post(
|
|||
content,
|
||||
config,
|
||||
artifact_dir,
|
||||
"blocked",
|
||||
PersonaChatStatus::Blocked,
|
||||
"missing Bifrost identity for public Persona crossing",
|
||||
Some(&audit),
|
||||
)?;
|
||||
|
|
@ -306,7 +306,7 @@ fn run_post(
|
|||
content,
|
||||
config,
|
||||
artifact_dir,
|
||||
"blocked",
|
||||
PersonaChatStatus::Blocked,
|
||||
"missing Heimdall-backed Discord capability/account reference for public Persona crossing; register the Bifrost identity, link it to Heimdall, and provide a heimdall:discord:* reference",
|
||||
Some(&audit),
|
||||
)?;
|
||||
|
|
@ -323,7 +323,7 @@ fn run_post(
|
|||
content,
|
||||
config,
|
||||
artifact_dir,
|
||||
"blocked",
|
||||
PersonaChatStatus::Blocked,
|
||||
"Heimdall reference must be shaped for Discord public Persona crossings: heimdall:discord:*",
|
||||
Some(&audit),
|
||||
)?;
|
||||
|
|
@ -351,7 +351,7 @@ fn run_post(
|
|||
content,
|
||||
config,
|
||||
artifact_dir,
|
||||
"posted",
|
||||
PersonaChatStatus::Posted,
|
||||
&format!(
|
||||
"posted message {} through {}",
|
||||
posted.message_id, posted.transport
|
||||
|
|
@ -430,6 +430,31 @@ struct PersonaSpeechAudit {
|
|||
audit_path: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
enum PersonaChatStatus {
|
||||
Draft,
|
||||
Blocked,
|
||||
Posted,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct PersonaChatArtifact {
|
||||
schema_version: String,
|
||||
created_at: String,
|
||||
status: PersonaChatStatus,
|
||||
reason: String,
|
||||
allowed_channel_name: String,
|
||||
allowed_channel_id: Option<String>,
|
||||
persona_name: Option<String>,
|
||||
persona_avatar_url: Option<String>,
|
||||
content: String,
|
||||
speech_audit: Option<PersonaSpeechAudit>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
bifrost_bridge_receipt: Option<Value>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct RecentPersonaSpeech {
|
||||
opening_key: String,
|
||||
|
|
@ -837,7 +862,7 @@ console.log(JSON.stringify({
|
|||
&temp_dir,
|
||||
&cultmesh_store,
|
||||
runtime_id,
|
||||
"draft",
|
||||
PersonaChatStatus::Draft,
|
||||
"drafted without posting",
|
||||
)?;
|
||||
let bubble = run_bubble(
|
||||
|
|
@ -942,7 +967,7 @@ console.log(JSON.stringify({
|
|||
"Rite noted: Modeling and Soul keep circling the same evidence seam.",
|
||||
&config,
|
||||
&temp_dir,
|
||||
"posted",
|
||||
PersonaChatStatus::Posted,
|
||||
"seed prior posted Persona output for speech-audit smoke",
|
||||
Some(&repeated_audit_seed),
|
||||
)?;
|
||||
|
|
@ -954,7 +979,7 @@ console.log(JSON.stringify({
|
|||
"Rite noted: Modeling and Soul keep circling the same evidence seam.",
|
||||
&config,
|
||||
&temp_dir,
|
||||
"posted",
|
||||
PersonaChatStatus::Posted,
|
||||
"seed second prior posted Persona output for speech-audit smoke",
|
||||
Some(&repeated_audit_seed),
|
||||
)?;
|
||||
|
|
@ -972,7 +997,19 @@ console.log(JSON.stringify({
|
|||
let latest_cultmesh_audit =
|
||||
load_latest_epiphany_cultmesh_persona_speech_audit(&cultmesh_store, runtime_id)?
|
||||
.context("Persona speech audit smoke expected latest CultMesh audit")?;
|
||||
let draft_artifact: Value = serde_json::from_str(&fs::read_to_string(
|
||||
draft["draftPath"]
|
||||
.as_str()
|
||||
.context("draft smoke result missing artifact path")?,
|
||||
)?)?;
|
||||
let posted_artifact: Value = serde_json::from_str(&fs::read_to_string(
|
||||
bridged["draftPath"]
|
||||
.as_str()
|
||||
.context("posted smoke result missing artifact path")?,
|
||||
)?)?;
|
||||
let ok = draft["ok"] == true
|
||||
&& chat_artifact_has_contract(&draft_artifact, false)
|
||||
&& chat_artifact_has_contract(&posted_artifact, true)
|
||||
&& bubble["ok"] == true
|
||||
&& bubble["bubble"]["schemaVersion"] == BUBBLE_SCHEMA_VERSION
|
||||
&& bubble["bubble"].as_object().is_some_and(|payload| {
|
||||
|
|
@ -1044,36 +1081,44 @@ fn write_draft(
|
|||
content: &str,
|
||||
config: &PersonaConfig,
|
||||
artifact_dir: &Path,
|
||||
status: &str,
|
||||
status: PersonaChatStatus,
|
||||
reason: &str,
|
||||
speech_audit: Option<&PersonaSpeechAudit>,
|
||||
) -> Result<PathBuf> {
|
||||
let payload = serde_json::json!({
|
||||
"schema_version": CHAT_SCHEMA_VERSION,
|
||||
"created_at": now_iso(),
|
||||
"status": status,
|
||||
"reason": reason,
|
||||
"allowed_channel_name": if config.allowed_channel_name.is_empty() { "#aquarium" } else { &config.allowed_channel_name },
|
||||
"allowed_channel_id": allowed_channel_id(config),
|
||||
"persona_name": config.persona_name,
|
||||
"persona_avatar_url": config.persona_avatar_url,
|
||||
"content": content.trim(),
|
||||
"speechAudit": speech_audit,
|
||||
});
|
||||
let payload = PersonaChatArtifact {
|
||||
schema_version: CHAT_SCHEMA_VERSION.to_string(),
|
||||
created_at: now_iso(),
|
||||
status,
|
||||
reason: reason.to_string(),
|
||||
allowed_channel_name: if config.allowed_channel_name.is_empty() {
|
||||
"#aquarium".to_string()
|
||||
} else {
|
||||
config.allowed_channel_name.clone()
|
||||
},
|
||||
allowed_channel_id: allowed_channel_id(config),
|
||||
persona_name: config.persona_name.clone(),
|
||||
persona_avatar_url: config.persona_avatar_url.clone(),
|
||||
content: content.trim().to_string(),
|
||||
speech_audit: speech_audit.cloned(),
|
||||
bifrost_bridge_receipt: None,
|
||||
};
|
||||
let path = artifact_dir.join(format!("Persona-chat-{}-{}.json", now_stamp(), short_id()));
|
||||
write_json(&path, &payload)?;
|
||||
write_json(&path, &serde_json::to_value(payload)?)?;
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
fn bind_publication_receipt(path: &Path, receipt: &Value) -> Result<()> {
|
||||
let mut payload: Value = serde_json::from_str(&fs::read_to_string(path)?)?;
|
||||
payload["bifrostBridgeReceipt"] = receipt.clone();
|
||||
write_json(path, &payload)
|
||||
let mut payload: PersonaChatArtifact = serde_json::from_str(&fs::read_to_string(path)?)?;
|
||||
payload.bifrost_bridge_receipt = Some(receipt.clone());
|
||||
write_json(path, &serde_json::to_value(payload)?)
|
||||
}
|
||||
|
||||
fn has_bound_discord_publication(payload: &Value) -> bool {
|
||||
let receipt = &payload["bifrostBridgeReceipt"];
|
||||
let channel_id = payload["allowed_channel_id"].as_str().unwrap_or_default();
|
||||
let channel_id = payload["allowedChannelId"]
|
||||
.as_str()
|
||||
.or_else(|| payload["allowed_channel_id"].as_str())
|
||||
.unwrap_or_default();
|
||||
let audit_id = payload["speechAudit"]["auditId"]
|
||||
.as_str()
|
||||
.or_else(|| payload["speechAudit"]["audit_id"].as_str())
|
||||
|
|
@ -1086,6 +1131,31 @@ fn has_bound_discord_publication(payload: &Value) -> bool {
|
|||
&& receipt["provenance"]["sourceId"].as_str() == Some(audit_id)
|
||||
}
|
||||
|
||||
fn chat_artifact_has_contract(payload: &Value, expects_receipt: bool) -> bool {
|
||||
const REQUIRED: [&str; 10] = [
|
||||
"allowedChannelId",
|
||||
"allowedChannelName",
|
||||
"content",
|
||||
"createdAt",
|
||||
"personaAvatarUrl",
|
||||
"personaName",
|
||||
"reason",
|
||||
"schemaVersion",
|
||||
"speechAudit",
|
||||
"status",
|
||||
];
|
||||
payload.as_object().is_some_and(|object| {
|
||||
let expected_len = REQUIRED.len() + usize::from(expects_receipt);
|
||||
object.len() == expected_len
|
||||
&& REQUIRED.iter().all(|key| object.contains_key(*key))
|
||||
&& object.contains_key("bifrostBridgeReceipt") == expects_receipt
|
||||
&& matches!(
|
||||
payload["status"].as_str(),
|
||||
Some("draft" | "blocked" | "posted")
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn bubble_payload(content: &str, source: &str, mood: &str) -> Value {
|
||||
serde_json::json!({
|
||||
"schemaVersion": BUBBLE_SCHEMA_VERSION,
|
||||
|
|
@ -1133,9 +1203,7 @@ fn latest_persona_artifacts(artifact_dir: &Path, limit: usize) -> Vec<Value> {
|
|||
"path": path,
|
||||
"name": path.file_name().and_then(|name| name.to_str()),
|
||||
"modifiedAt": modified_at.to_rfc3339_opts(SecondsFormat::Secs, true),
|
||||
"schemaVersion": payload
|
||||
.get("schemaVersion")
|
||||
.or_else(|| payload.get("schema_version")),
|
||||
"schemaVersion": payload.get("schemaVersion").or_else(|| payload.get("schema_version")),
|
||||
"status": payload.get("status"),
|
||||
"reason": payload.get("reason"),
|
||||
"content": payload.get("content"),
|
||||
|
|
@ -1185,7 +1253,10 @@ fn recent_persona_speech(artifact_dir: &Path, limit: usize) -> Vec<RecentPersona
|
|||
Some(RecentPersonaSpeech {
|
||||
opening_key: opening_key(&content),
|
||||
topic_key: topic_key(&content),
|
||||
channel_id: payload["allowed_channel_id"].as_str().map(str::to_string),
|
||||
channel_id: payload["allowedChannelId"]
|
||||
.as_str()
|
||||
.or_else(|| payload["allowed_channel_id"].as_str())
|
||||
.map(str::to_string),
|
||||
action_kind,
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1619,3 +1619,36 @@ snake_case remains read-only compatibility input. Bubble and character packet
|
|||
schemas reject extra root fields, character intent rejects undeclared cargo,
|
||||
and native smokes assert the exact emitted top-level sets. A registered schema
|
||||
that the producer does not actually speak is catalog theater, not a contract.
|
||||
|
||||
The adjacent Persona chat/post artifact had the same wound with more authority
|
||||
attached. Its live writer emitted snake_case fields plus speech-audit and
|
||||
Bifrost-receipt evidence while the registered schema required camelCase, omitted
|
||||
the evidence, accepted arbitrary lifecycle strings, and hid every disagreement
|
||||
behind `additionalProperties=true`.
|
||||
|
||||
Authority map for the cut:
|
||||
|
||||
- Owner: the Persona Discord artifact writer owns local draft/post artifact
|
||||
shape; Bifrost owns the publication receipt embedded after validated crossing.
|
||||
- Inputs: audited content, fixed channel/Persona configuration, internally
|
||||
derived draft/blocked/posted outcome, and optional validated Bifrost receipt.
|
||||
- Outputs: one typed `epiphany.persona_chat.v0` artifact.
|
||||
- Derived state: latest-artifact and repetition views are read-only projections.
|
||||
- Forbidden writers: callers, generic surface schemas, MVP aggregation, and
|
||||
audit telemetry cannot author lifecycle or publication.
|
||||
- Cut line: delete the snake_case writer vocabulary and arbitrary status string;
|
||||
retain snake_case only in legacy readers.
|
||||
|
||||
The writer is now a typed Rust `PersonaChatArtifact` with a closed
|
||||
`PersonaChatStatus` enum. The registered schema names every emitted root field,
|
||||
requires the speech audit, admits the Bifrost receipt only as optional provider
|
||||
evidence, rejects extra fields, and uses the same lowercase schema identity and
|
||||
catalog path as the file. The native smoke loads both draft and posted artifacts
|
||||
and checks their exact top-level field sets.
|
||||
|
||||
Proprioception found the next Modeling frontier: `epiphany.surface.persona.v0`
|
||||
is effectively catalog fiction. Its schema requires only `ok`, accepts untyped
|
||||
artifact arrays, and no producer writes a document with that schema version.
|
||||
Real Reddit and Other artifacts exist but are not registered as output
|
||||
contracts. Rebuild that surface around typed discriminated artifact references,
|
||||
register the real outputs, or stop advertising them as typed surface outputs.
|
||||
|
|
|
|||
|
|
@ -27,6 +27,16 @@ and be projected into later Hands prompts. Otherwise an excellent coordinator
|
|||
can perform the structure transiently, but the next awakened Self inherits only
|
||||
the applause.
|
||||
|
||||
The next artifact-contract audit then cut `epiphany.persona_chat.v0`: its live
|
||||
writer and catalog had disagreed on field vocabulary, evidence fields, and
|
||||
lifecycle closure. The writer is now typed Rust with closed
|
||||
draft/blocked/posted status, the schema is strict and complete, and legacy
|
||||
snake_case survives only on read. Proprioception identified the next larger
|
||||
frontier: `epiphany.surface.persona.v0` has no real schema-versioned producer and
|
||||
types almost nothing, while Reddit and Other emit real artifacts absent from
|
||||
the catalog. Rebuild that surface around discriminated typed artifact refs or
|
||||
delete the false advertisement.
|
||||
|
||||
Epiphany is in an authority-provenance purification pass. The live question is not whether a command can produce a plausible document; it is whether the subsystem writing that document owns the fact it asserts.
|
||||
|
||||
The confirmed conceptual substitutions have been cut:
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"$id": "https://gamecult.dev/epiphany/cultnet/epiphany.Persona-chat.schema.json",
|
||||
"$id": "https://gamecult.dev/epiphany/cultnet/epiphany.persona-chat.schema.json",
|
||||
"title": "Epiphany Persona Chat Draft",
|
||||
"description": "Persona chat draft/post artifact.",
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"schemaVersion": {
|
||||
"type": "string"
|
||||
|
|
@ -13,7 +13,8 @@
|
|||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"enum": ["draft", "blocked", "posted"]
|
||||
},
|
||||
"reason": {
|
||||
"type": "string"
|
||||
|
|
@ -41,6 +42,12 @@
|
|||
},
|
||||
"content": {
|
||||
"type": "string"
|
||||
},
|
||||
"speechAudit": {
|
||||
"type": ["object", "null"]
|
||||
},
|
||||
"bifrostBridgeReceipt": {
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
|
@ -49,6 +56,10 @@
|
|||
"status",
|
||||
"reason",
|
||||
"allowedChannelName",
|
||||
"content"
|
||||
"allowedChannelId",
|
||||
"personaName",
|
||||
"personaAvatarUrl",
|
||||
"content",
|
||||
"speechAudit"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -835,10 +835,10 @@
|
|||
"schemaVersion": "epiphany.persona_bubble.v0",
|
||||
"documentType": "epiphany.persona_bubble.v0",
|
||||
"title": "Epiphany Persona Bubble",
|
||||
"path": "epiphany.Persona-bubble.schema.json"
|
||||
"path": "epiphany.persona-bubble.schema.json"
|
||||
},
|
||||
{
|
||||
"schemaId": "https://gamecult.dev/epiphany/cultnet/epiphany.Persona-chat.schema.json",
|
||||
"schemaId": "https://gamecult.dev/epiphany/cultnet/epiphany.persona-chat.schema.json",
|
||||
"kind": "document_payload",
|
||||
"wireContracts": [
|
||||
"cultnet.schema.v0"
|
||||
|
|
@ -846,7 +846,7 @@
|
|||
"schemaVersion": "epiphany.persona_chat.v0",
|
||||
"documentType": "epiphany.persona_chat.v0",
|
||||
"title": "Epiphany Persona Chat Draft",
|
||||
"path": "epiphany.Persona-chat.schema.json"
|
||||
"path": "epiphany.persona-chat.schema.json"
|
||||
},
|
||||
{
|
||||
"schemaId": "https://gamecult.dev/epiphany/cultnet/epiphany.character-turn-packet.schema.json",
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -661,4 +661,4 @@ current_status:
|
|||
- Service lifecycle receipt history is now event-only: `load_epiphany_cultmesh_daemon_service_lifecycle_receipts` filters out the `epiphany-local/daemon-service-lifecycle-receipt/latest` mirror, while `load_latest_epiphany_cultmesh_daemon_service_lifecycle_receipt` remains the latest-state reader. Focused unit test `service_lifecycle_receipt_history_excludes_latest_mirror` proves two per-receipt writes return exactly two history records and latest points at the second. Fresh smoke `verse-query-lifecycle-history-filter-smoke` and live overview/triage `local-20260618-145632-497-efac6f0d` / `local-20260618-145632-321-867947af` prove compact swarm sights still expose service lifecycle attention plus the present elevated runbook artifact without daemon pokes or service mutation. Do not use a broad unscoped cargo test filter for this check on Windows; it fans out across bin harnesses and hit a broken pipe during this pass.
|
||||
- `epiphany-verse-query seed-compact` is the compact seed receipt for wrappers and read-only rites. The older `seed` command still prints the full local Verse context for explicit debugging, but compact wrapper modes now avoid full `seed`/final `query` readback after the combined overview proof exposed Windows debug stack overflow from full-context serialization pressure.
|
||||
last_completed_repo_swarm_cut: Huginn live-fire has crossed the first real repo Body from Persona pressure to branch-local Hands commit and Soul/Mind closure, and now has repeatable closure and redacted-public-proof mouths. On `E:\Projects\Huginn` branch `epiphany/huginn/first-awakening`, `tools/epiphany_local_run.ps1 -Mode repo-persona-intake` accepted item `huginn-inspection-readback-livefire`, Weksa/Persona intake remained sealed, and `epiphany-work derive-plan` lowered it to model-authored safe family `repo.status_section` for `README.md` only. `tools/epiphany_local_run.ps1 -Mode repo-swarm-run` advanced four bounded pulses: run-from-plan, adopt-from-plan, execute-from-plan, and close-from-execute. Huginn commit `e9b07c38fceabbdc89ea35c2156326c80c0393a2` updated only `README.md`; closure receipt `.epiphany\work\work-close-huginn-inspection-readback-livefire.json` records status `closed`, Soul verdict `passed`, Mind state commit `repo-work-close-huginn-inspection-readback-livefire-mind-commit`, safe family `repo.status_section`, path scope `README.md`, Bifrost publication gate, and `privateStateExposed=false`. Verification also ran Huginn's TypeScript compiler through concrete Node path: `C:\Program Files\nodejs\node.exe E:\Projects\Huginn\node_modules\typescript\bin\tsc -p tsconfig.json` passed. Native verifier `epiphany-repo-livefire-closure-smoke --workspace E:\Projects\Huginn --item huginn-inspection-readback-livefire --expected-path README.md --expected-family repo.status_section --expected-gate awaiting-publication --expected-blocker bifrost-publication-missing` checks closure receipt, Mind map entry, overview gate/blocker, authority seals, and `git show` changed paths, returning `status=ok`, `soulVerdict=passed`, `publicationGate=Bifrost`, and `privateStateExposed=false`. Wrapper `tools/epiphany_local_run.ps1 -Mode repo-livefire-closure -Workspace E:\Projects\Huginn -RepoWorkItem huginn-inspection-readback-livefire -SkipBuild` exposes the same read-only proof through the local operator mouth; artifact `local-20260621-190156-803-2f85ba15` printed commit, path, gate, blocker, Soul verdict, Mind commit receipt, and `privateStateExposed=False` without push, merge, publication, or target mutation. New wrapper `tools/epiphany_local_run.ps1 -Mode repo-work-public-proof -Workspace E:\Projects\Huginn -RepoWorkItem huginn-inspection-readback-livefire -SkipBuild` exports the redacted proof bundle and repo-local Verse index without publishing; artifact `local-20260621-190645-345-4555b1dc` wrote `.epiphany\public\proof-bundles\repo-work-public-proof-huginn-inspection-readback-livefire.json`, proof id `repo-work-public-proof-huginn-inspection-readback-livefire`, SHA-256 `857545b199f3e37eb06c2e757e5cfc201958e46667bb1f8403fd333a22620f63`, nine artifact rows, zero publication rows, and `privateStateExposed=False`. swarm overview readback `local-20260621-190701-140-b9519ec6` sees the `REPO-WORK-PUBLIC-PROOF` row and action row from Huginn's repo-local Verse with the same SHA-256 while Bifrost publication remains missing. Launcher compact modes are now named compact operator-context modes instead of read-only modes, and `bifrost-public-proof` summaries print item, target public Verse, room, proof SHA-256, review receipts, and credit receipts so publication receipts are auditable without claiming upstream sync.
|
||||
next_action: Continue the observation-to-consequence audit using live organ-shaped prompt probes. Preserve the Aetheria dogfood lesson: Modeling's typed persistent anatomy/freshness/frontier/index knowledgebase is the targeting organ for Hands, while Eyes, Soul, Imagination, and Self retain distinct evidence, verification, future-shape, and routing authority. Bifrost command cardinality is now source-grounded as an upstream wound: ambient command-id strings are not typed one-shot crossing commands, and Epiphany must not invent local authority. Bubble and character artifacts now match their strict registered root contracts. Next audit the remaining artifact/schema pairs and prompt projections for catalog theater, caller-authored consequence, or transient coordinator knowledge masquerading as persistent Modeling state. Keep closure's zero-substring invariant.
|
||||
next_action: Continue the observation-to-consequence audit using live organ-shaped prompt probes. Preserve the Aetheria dogfood lesson: Modeling's typed persistent anatomy/freshness/frontier/index knowledgebase is the targeting organ for Hands. Bubble, character, and Persona chat artifacts now match strict registered contracts; chat lifecycle is a closed typed producer decision and legacy snake_case is read-only. Next rebuild or delete the fictional `epiphany.surface.persona.v0` advertisement: it has no schema-versioned producer and untyped artifact arrays, while real Reddit and Other outputs lack registered contracts. The coherent surface should carry discriminated typed artifact references without gaining mouth eligibility, Mind admission, Bifrost publication, or provider-delivery authority. Keep closure's zero-substring invariant.
|
||||
|
|
|
|||
Reference in a new issue