Require bound Persona publication receipts
This commit is contained in:
parent
ef26c0753a
commit
09e562e34b
6 changed files with 313 additions and 12 deletions
|
|
@ -358,6 +358,7 @@ fn run_post(
|
|||
),
|
||||
Some(&audit),
|
||||
)?;
|
||||
bind_publication_receipt(&path, &posted.bridge_receipt)?;
|
||||
Ok(serde_json::json!({
|
||||
"ok": true,
|
||||
"posted": true,
|
||||
|
|
@ -647,10 +648,17 @@ fn post_bifrost_discord_message(
|
|||
let stdout = String::from_utf8(output.stdout).context("Bifrost bridge stdout was not UTF-8")?;
|
||||
let receipt: Value =
|
||||
serde_json::from_str(strip_bom(&stdout)).context("Bifrost bridge stdout was not JSON")?;
|
||||
validate_discord_publication_receipt(
|
||||
&receipt,
|
||||
config,
|
||||
bifrost_identity,
|
||||
heimdall_capability_ref,
|
||||
channel_id,
|
||||
audit,
|
||||
)?;
|
||||
let message_id = receipt["messageId"]
|
||||
.as_str()
|
||||
.or_else(|| receipt["externalReceiptId"].as_str())
|
||||
.unwrap_or("bifrost-discord-post")
|
||||
.expect("validated Discord receipt has messageId")
|
||||
.to_string();
|
||||
Ok(PostedDiscordMessage {
|
||||
message_id,
|
||||
|
|
@ -661,6 +669,87 @@ fn post_bifrost_discord_message(
|
|||
})
|
||||
}
|
||||
|
||||
fn validate_discord_publication_receipt(
|
||||
receipt: &Value,
|
||||
config: &PersonaConfig,
|
||||
bifrost_identity: &str,
|
||||
heimdall_capability_ref: &str,
|
||||
channel_id: &str,
|
||||
audit: &PersonaSpeechAudit,
|
||||
) -> Result<()> {
|
||||
let expected_source_kind = config
|
||||
.bifrost_source_kind
|
||||
.as_deref()
|
||||
.unwrap_or("epiphany_persona_speech");
|
||||
let expected_authority = config
|
||||
.bifrost_authority_ref
|
||||
.as_deref()
|
||||
.unwrap_or("epiphany.persona_speech_audit");
|
||||
let checks = [
|
||||
(receipt["action"].as_str() == Some("discord-post"), "action"),
|
||||
(receipt["ok"].as_bool() == Some(true), "successful outcome"),
|
||||
(
|
||||
receipt["channelId"].as_str() == Some(channel_id),
|
||||
"channel binding",
|
||||
),
|
||||
(non_empty(&receipt["messageId"]), "provider message id"),
|
||||
(non_empty(&receipt["transport"]), "provider transport"),
|
||||
(non_empty(&receipt["url"]), "receipt URL"),
|
||||
(
|
||||
non_empty(&receipt["crossingReceiptId"]),
|
||||
"canonical crossing receipt id",
|
||||
),
|
||||
(
|
||||
receipt["provenance"]["bifrostIdentity"].as_str() == Some(bifrost_identity),
|
||||
"Bifrost identity",
|
||||
),
|
||||
(
|
||||
receipt["provenance"]["sourceKind"].as_str() == Some(expected_source_kind),
|
||||
"source kind",
|
||||
),
|
||||
(
|
||||
receipt["provenance"]["sourceId"].as_str() == Some(audit.audit_id.as_str()),
|
||||
"speech audit binding",
|
||||
),
|
||||
(
|
||||
receipt["provenance"]["authorityReference"].as_str() == Some(expected_authority),
|
||||
"authority reference",
|
||||
),
|
||||
(
|
||||
receipt["provenance"]["epiphanyLaneId"].as_str() == Some("Persona"),
|
||||
"lane identity",
|
||||
),
|
||||
(
|
||||
receipt["provenance"]["epiphanyAgentIdentity"].as_str() == Some("epiphany.Persona"),
|
||||
"agent identity",
|
||||
),
|
||||
(
|
||||
receipt["provenance"]["heimdallCapabilityRef"].as_str()
|
||||
== Some(heimdall_capability_ref),
|
||||
"Heimdall capability",
|
||||
),
|
||||
];
|
||||
for (valid, label) in checks {
|
||||
if !valid {
|
||||
return Err(anyhow!(
|
||||
"Bifrost Discord publication receipt missing or mismatched {label}"
|
||||
));
|
||||
}
|
||||
}
|
||||
let message_id = receipt["messageId"].as_str().expect("validated message id");
|
||||
let url = receipt["url"].as_str().expect("validated receipt URL");
|
||||
if !url.contains(&format!("/{channel_id}/{message_id}")) {
|
||||
return Err(anyhow!(
|
||||
"Bifrost Discord publication receipt URL is not bound to channel and message"
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn non_empty(value: &Value) -> bool {
|
||||
value.as_str().is_some_and(|value| !value.trim().is_empty())
|
||||
}
|
||||
|
||||
fn bifrost_node_executable() -> String {
|
||||
env::var("EPIPHANY_BIFROST_NODE")
|
||||
.ok()
|
||||
|
|
@ -696,8 +785,12 @@ function opt(name) {
|
|||
}
|
||||
console.log(JSON.stringify({
|
||||
action: args[0],
|
||||
ok: true,
|
||||
channelId: opt("--channel-id"),
|
||||
messageId: "bridge-message-123",
|
||||
externalReceiptId: "bridge-message-123",
|
||||
transport: "discord-webhook",
|
||||
crossingReceiptId: "crossing_smoke-discord-post",
|
||||
url: `https://discord.com/channels/test/${opt("--channel-id")}/bridge-message-123`,
|
||||
provenance: {
|
||||
bifrostIdentity: opt("--identity"),
|
||||
|
|
@ -715,6 +808,10 @@ console.log(JSON.stringify({
|
|||
.with_context(|| format!("failed to write {}", fake_bridge.display()))?;
|
||||
let mut bridge_config = config.clone();
|
||||
bridge_config.bifrost_bridge_cli_path = Some(fake_bridge);
|
||||
let invalid_bridge = temp_dir.join("invalid-bifrost-bridge.mjs");
|
||||
fs::write(&invalid_bridge, "console.log('{}');\n")?;
|
||||
let mut invalid_receipt_config = bridge_config.clone();
|
||||
invalid_receipt_config.bifrost_bridge_cli_path = Some(invalid_bridge);
|
||||
let mut missing_capability_config = bridge_config.clone();
|
||||
missing_capability_config.heimdall_capability_ref_env =
|
||||
Some("HEIMDALL_CAPABILITY_REF_MISSING_FOR_SMOKE".to_string());
|
||||
|
|
@ -809,6 +906,18 @@ console.log(JSON.stringify({
|
|||
Some("https://example.invalid/Persona.png".to_string()),
|
||||
None,
|
||||
)?;
|
||||
let invalid_receipt_rejected = run_post(
|
||||
"Persona must reject empty successful bridge JSON.",
|
||||
&invalid_receipt_config,
|
||||
&temp_dir,
|
||||
&cultmesh_store,
|
||||
runtime_id,
|
||||
Some("123".to_string()),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.is_err();
|
||||
let wrong = run_post(
|
||||
"Persona should not post outside #aquarium.",
|
||||
&bridge_config,
|
||||
|
|
@ -837,6 +946,10 @@ console.log(JSON.stringify({
|
|||
"seed prior posted Persona output for speech-audit smoke",
|
||||
Some(&repeated_audit_seed),
|
||||
)?;
|
||||
let verified_post_count = recent_persona_speech(&temp_dir, 12)
|
||||
.iter()
|
||||
.filter(|speech| speech.action_kind == PersonaSpeechActionKind::Post)
|
||||
.count();
|
||||
write_draft(
|
||||
"Rite noted: Modeling and Soul keep circling the same evidence seam.",
|
||||
&config,
|
||||
|
|
@ -872,6 +985,8 @@ console.log(JSON.stringify({
|
|||
&& wrong_surface_capability["ok"] == false
|
||||
&& wrong_surface_capability["blocked"] == "wrong-heimdall-capability-surface"
|
||||
&& bridged["ok"] == true
|
||||
&& invalid_receipt_rejected
|
||||
&& verified_post_count == 1
|
||||
&& bridged["transport"] == "bifrost.discord-post"
|
||||
&& bridged["bifrostBridgeReceipt"]["provenance"]["bifrostIdentity"] == "epiphany.Persona"
|
||||
&& bridged["bifrostBridgeReceipt"]["provenance"]["heimdallCapabilityRef"]
|
||||
|
|
@ -899,6 +1014,8 @@ console.log(JSON.stringify({
|
|||
"missingCapability": missing_capability,
|
||||
"wrongSurfaceCapability": wrong_surface_capability,
|
||||
"bridged": bridged,
|
||||
"invalidReceiptRejected": invalid_receipt_rejected,
|
||||
"verifiedPostCount": verified_post_count,
|
||||
"wrongChannel": wrong,
|
||||
"repeatedSpeech": repeated,
|
||||
"latestCultMeshSpeechAudit": latest_cultmesh_audit,
|
||||
|
|
@ -932,6 +1049,27 @@ fn write_draft(
|
|||
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)
|
||||
}
|
||||
|
||||
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 audit_id = payload["speechAudit"]["auditId"]
|
||||
.as_str()
|
||||
.or_else(|| payload["speechAudit"]["audit_id"].as_str())
|
||||
.unwrap_or_default();
|
||||
receipt["action"] == "discord-post"
|
||||
&& receipt["ok"] == true
|
||||
&& receipt["channelId"].as_str() == Some(channel_id)
|
||||
&& non_empty(&receipt["messageId"])
|
||||
&& non_empty(&receipt["crossingReceiptId"])
|
||||
&& receipt["provenance"]["sourceId"].as_str() == Some(audit_id)
|
||||
}
|
||||
|
||||
fn bubble_payload(content: &str, source: &str, mood: &str) -> Value {
|
||||
serde_json::json!({
|
||||
"schema_version": BUBBLE_SCHEMA_VERSION,
|
||||
|
|
@ -1019,7 +1157,7 @@ fn recent_persona_speech(artifact_dir: &Path, limit: usize) -> Vec<RecentPersona
|
|||
let status = payload["status"].as_str().unwrap_or_default();
|
||||
let action_kind = if payload["schema_version"] == BUBBLE_SCHEMA_VERSION {
|
||||
PersonaSpeechActionKind::Bubble
|
||||
} else if status == "posted" {
|
||||
} else if status == "posted" && has_bound_discord_publication(&payload) {
|
||||
PersonaSpeechActionKind::Post
|
||||
} else {
|
||||
PersonaSpeechActionKind::Draft
|
||||
|
|
|
|||
|
|
@ -443,7 +443,92 @@ fn post_bifrost_reddit_thread(
|
|||
));
|
||||
}
|
||||
let stdout = String::from_utf8(output.stdout).context("Bifrost bridge stdout was not UTF-8")?;
|
||||
serde_json::from_str(strip_bom(&stdout)).context("Bifrost bridge stdout was not JSON")
|
||||
let receipt: Value =
|
||||
serde_json::from_str(strip_bom(&stdout)).context("Bifrost bridge stdout was not JSON")?;
|
||||
validate_reddit_publication_receipt(
|
||||
&receipt,
|
||||
config,
|
||||
bifrost_identity,
|
||||
heimdall_capability_ref,
|
||||
subreddit,
|
||||
audit,
|
||||
)?;
|
||||
Ok(receipt)
|
||||
}
|
||||
|
||||
fn validate_reddit_publication_receipt(
|
||||
receipt: &Value,
|
||||
config: &PersonaRedditConfig,
|
||||
bifrost_identity: &str,
|
||||
heimdall_capability_ref: &str,
|
||||
subreddit: &str,
|
||||
audit: &PersonaSpeechAudit,
|
||||
) -> Result<()> {
|
||||
let expected_source_kind = config
|
||||
.bifrost_source_kind
|
||||
.as_deref()
|
||||
.unwrap_or("epiphany_persona_reddit");
|
||||
let expected_authority = config
|
||||
.bifrost_authority_ref
|
||||
.as_deref()
|
||||
.unwrap_or("epiphany.persona_speech_audit");
|
||||
let checks = [
|
||||
(receipt["action"].as_str() == Some("reddit-post"), "action"),
|
||||
(receipt["ok"].as_bool() == Some(true), "successful outcome"),
|
||||
(
|
||||
receipt["subreddit"].as_str() == Some(subreddit),
|
||||
"subreddit binding",
|
||||
),
|
||||
(
|
||||
non_empty(&receipt["thingId"]) || non_empty(&receipt["url"]),
|
||||
"provider thing id or receipt URL",
|
||||
),
|
||||
(
|
||||
non_empty(&receipt["crossingReceiptId"]),
|
||||
"canonical crossing receipt id",
|
||||
),
|
||||
(
|
||||
receipt["provenance"]["bifrostIdentity"].as_str() == Some(bifrost_identity),
|
||||
"Bifrost identity",
|
||||
),
|
||||
(
|
||||
receipt["provenance"]["sourceKind"].as_str() == Some(expected_source_kind),
|
||||
"source kind",
|
||||
),
|
||||
(
|
||||
receipt["provenance"]["sourceId"].as_str() == Some(audit.audit_id.as_str()),
|
||||
"speech audit binding",
|
||||
),
|
||||
(
|
||||
receipt["provenance"]["authorityReference"].as_str() == Some(expected_authority),
|
||||
"authority reference",
|
||||
),
|
||||
(
|
||||
receipt["provenance"]["epiphanyLaneId"].as_str() == Some("Persona"),
|
||||
"lane identity",
|
||||
),
|
||||
(
|
||||
receipt["provenance"]["epiphanyAgentIdentity"].as_str() == Some("epiphany.Persona"),
|
||||
"agent identity",
|
||||
),
|
||||
(
|
||||
receipt["provenance"]["heimdallCapabilityRef"].as_str()
|
||||
== Some(heimdall_capability_ref),
|
||||
"Heimdall capability",
|
||||
),
|
||||
];
|
||||
for (valid, label) in checks {
|
||||
if !valid {
|
||||
return Err(anyhow!(
|
||||
"Bifrost Reddit publication receipt missing or mismatched {label}"
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn non_empty(value: &Value) -> bool {
|
||||
value.as_str().is_some_and(|value| !value.trim().is_empty())
|
||||
}
|
||||
|
||||
fn audit_persona_speech(
|
||||
|
|
@ -574,6 +659,7 @@ console.log(JSON.stringify({
|
|||
personaFlairText: opt("--persona-flair-text"),
|
||||
thingId: "t3_epiphanysmoke",
|
||||
url: `https://reddit.com/r/${opt("--subreddit")}/comments/t3_epiphanysmoke/epiphany_smoke/`,
|
||||
crossingReceiptId: "crossing_smoke-reddit-post",
|
||||
provenance: {
|
||||
bifrostIdentity: opt("--identity"),
|
||||
sourceKind: opt("--source-kind"),
|
||||
|
|
@ -599,6 +685,12 @@ console.log(JSON.stringify({
|
|||
bifrost_authority_ref: Some("epiphany.persona_speech_audit".to_string()),
|
||||
heimdall_capability_ref_env: Some("HEIMDALL_CAPABILITY_REF_TEST".to_string()),
|
||||
};
|
||||
let invalid_bridge = temp_dir.join("invalid-bifrost-bridge.mjs");
|
||||
fs::write(&invalid_bridge, "console.log('{}');\n")?;
|
||||
let invalid_receipt_config = PersonaRedditConfig {
|
||||
bifrost_bridge_cli_path: Some(invalid_bridge),
|
||||
..config.clone()
|
||||
};
|
||||
let no_bridge_config = PersonaRedditConfig {
|
||||
bifrost_bridge_cli_path: None,
|
||||
..config.clone()
|
||||
|
|
@ -683,6 +775,19 @@ console.log(JSON.stringify({
|
|||
None,
|
||||
None,
|
||||
)?;
|
||||
let invalid_receipt_rejected = run_post(
|
||||
"Epiphany rejects empty bridge JSON",
|
||||
"Persona must not call arbitrary JSON a Reddit publication receipt.",
|
||||
&invalid_receipt_config,
|
||||
&temp_dir,
|
||||
&cultmesh_store,
|
||||
runtime_id,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.is_err();
|
||||
let seed = audit_persona_speech(
|
||||
"Repeated Reddit rite",
|
||||
"Modeling and Soul keep circling the same public proof seam.",
|
||||
|
|
@ -702,6 +807,10 @@ console.log(JSON.stringify({
|
|||
Some(&seed),
|
||||
None,
|
||||
)?;
|
||||
let verified_post_count = recent_persona_speech(&temp_dir, 12)
|
||||
.iter()
|
||||
.filter(|speech| speech.posted)
|
||||
.count();
|
||||
write_post_artifact(
|
||||
"Repeated Reddit rite",
|
||||
"Modeling and Soul keep circling the same public proof seam.",
|
||||
|
|
@ -735,6 +844,8 @@ console.log(JSON.stringify({
|
|||
&& wrong_surface_capability["ok"] == false
|
||||
&& wrong_surface_capability["blocked"] == "wrong-heimdall-capability-surface"
|
||||
&& bridged["ok"] == true
|
||||
&& invalid_receipt_rejected
|
||||
&& verified_post_count == 1
|
||||
&& bridged["transport"] == "bifrost.reddit-post"
|
||||
&& bridged["bifrostBridgeReceipt"]["provenance"]["bifrostIdentity"] == "epiphany.Persona"
|
||||
&& bridged["bifrostBridgeReceipt"]["provenance"]["heimdallCapabilityRef"]
|
||||
|
|
@ -753,6 +864,8 @@ console.log(JSON.stringify({
|
|||
"missingCapability": missing_capability,
|
||||
"wrongSurfaceCapability": wrong_surface_capability,
|
||||
"bridged": bridged,
|
||||
"invalidReceiptRejected": invalid_receipt_rejected,
|
||||
"verifiedPostCount": verified_post_count,
|
||||
"repeatedSpeech": repeated,
|
||||
"latestCultMeshSpeechAudit": latest_cultmesh_audit,
|
||||
});
|
||||
|
|
@ -854,12 +967,28 @@ fn recent_persona_speech(artifact_dir: &Path, limit: usize) -> Vec<RecentPersona
|
|||
opening_key: opening_key(&title),
|
||||
topic_key: topic_key(&format!("{title}\n\n{content}")),
|
||||
subreddit: payload["subreddit"].as_str().map(str::to_string),
|
||||
posted: payload["status"].as_str() == Some("posted"),
|
||||
posted: payload["status"].as_str() == Some("posted")
|
||||
&& has_bound_reddit_publication(&payload),
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn has_bound_reddit_publication(payload: &Value) -> bool {
|
||||
let receipt = &payload["bifrostBridgeReceipt"];
|
||||
let subreddit = payload["subreddit"].as_str().unwrap_or_default();
|
||||
let audit_id = payload["speechAudit"]["auditId"]
|
||||
.as_str()
|
||||
.or_else(|| payload["speechAudit"]["audit_id"].as_str())
|
||||
.unwrap_or_default();
|
||||
receipt["action"] == "reddit-post"
|
||||
&& receipt["ok"] == true
|
||||
&& receipt["subreddit"].as_str() == Some(subreddit)
|
||||
&& (non_empty(&receipt["thingId"]) || non_empty(&receipt["url"]))
|
||||
&& non_empty(&receipt["crossingReceiptId"])
|
||||
&& receipt["provenance"]["sourceId"].as_str() == Some(audit_id)
|
||||
}
|
||||
|
||||
fn ensure_post(title: &str, content: &str) -> Result<()> {
|
||||
if title.trim().is_empty() {
|
||||
return Err(anyhow!("Persona Reddit title is empty"));
|
||||
|
|
|
|||
|
|
@ -1549,7 +1549,27 @@ artifact reads. The stale caller is deleted and aggregate Persona state is
|
|||
candidate speech, local bubble, eligibility, Mind admission, and Bifrost/provider
|
||||
delivery are distinct consequences.
|
||||
|
||||
Prompt signal: Modeling currently requires a durable `statePatch` even when no
|
||||
model change is warranted. That can manufacture persistence merely to satisfy
|
||||
the output contract. The next prompt-contract pass should add a source-grounded
|
||||
`no-state-change` result rather than encouraging ceremonial scratch churn.
|
||||
Prompt signal, corrected after operator review: Modeling must always maintain
|
||||
the live persistent body map. A source pass that confirms existing anatomy
|
||||
still owes a typed freshness/checkpoint witness so Hands can trust the map
|
||||
instead of rediscovering the repo. The prompt improvement is not a
|
||||
`no-state-change` escape hatch; it is sharper language requiring every patch to
|
||||
name changed, confirmed, or invalidated anatomy plus freshness/frontier and
|
||||
semantic-index consequences, so mandatory persistence cannot decay into
|
||||
ceremonial scratch churn.
|
||||
|
||||
## Successful bridge process is not publication
|
||||
|
||||
Eyes inspected Bifrost's actual bridge contract. Discord and Reddit stdout must
|
||||
carry exact action, `ok=true`, destination binding, provider identity/URL,
|
||||
canonical crossing receipt id, and provenance bound to the Persona speech audit,
|
||||
lane, agent, authority, Bifrost identity, and Heimdall capability. Epiphany had
|
||||
accepted exit zero plus arbitrary JSON; Discord even invented a fallback message
|
||||
id. Both mouths now validate the existing Bifrost contract before writing
|
||||
`posted`. Hostile exit-zero `{}` bridges are rejected by native smokes.
|
||||
|
||||
Publication history no longer trusts local artifact `status=posted`. Discord
|
||||
binds the validated receipt into its artifact; both history readers require a
|
||||
receipt whose action, target, provider evidence, crossing id, and audit source id
|
||||
match the artifact. Forged status-only artifacts remain readable local files but
|
||||
cannot increase verified post counts or steer same-target publication pressure.
|
||||
|
|
|
|||
|
|
@ -604,5 +604,19 @@ repo-intake consensus status argument and demoted aggregate Persona status from
|
|||
`ready` to `loaded`. Persona prompts now explicitly separate candidate, bubble,
|
||||
eligibility, Mind admission, and Bifrost/provider delivery ownership. Native
|
||||
negative/smoke checks, 259 library tests, 32 work tests, and all-bin compilation
|
||||
pass. Next repair Modeling's forced-patch prompt pressure and the P0 Discord/
|
||||
Reddit arbitrary-JSON publication receipts found by Eyes and Soul.
|
||||
pass. Operator correction: Modeling's mandatory typed patch is intentional
|
||||
because the live searchable body map is its product; confirmation passes must
|
||||
bank freshness and source-grounded map confidence. Improve the patch vocabulary,
|
||||
do not add a no-state-change escape. Continue the P0 Discord/Reddit arbitrary-
|
||||
JSON publication receipt cut found by Eyes and Soul.
|
||||
|
||||
That P0 cut is complete. Epiphany now validates Bifrost's real Discord/Reddit
|
||||
publication output: exact action/outcome, target, provider id or URL, canonical
|
||||
crossing receipt id, and returned provenance bound to the speech audit and
|
||||
authority inputs. Discord fallback IDs are deleted. Native hostile smokes prove
|
||||
exit-zero `{}` cannot publish. Posted artifacts count as history only when their
|
||||
embedded receipt binds back to the artifact target and audit; forged
|
||||
`status=posted` files do not count. The remaining Bifrost seam is command
|
||||
cardinality: Epiphany inherits a CultMesh command id, so the coordinator must
|
||||
ensure one typed command id per crossing rather than letting multiple posts share
|
||||
one canonical `crossing_<command>` receipt.
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -660,4 +660,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. Character-loop caller status and synthetic heartbeat physiology are cut; Persona prompts name candidate/bubble/eligibility/admission/delivery ownership. Next add a truthful Modeling `no-state-change` result so prompts do not force ceremonial persistence, then cut the P0 Discord/Reddit arbitrary-JSON publication receipts and forged local `status=posted` history identified independently by Eyes and Soul. Keep closure's zero-substring invariant.
|
||||
next_action: Continue the observation-to-consequence audit using live organ-shaped prompt probes. Modeling must keep producing typed persistent body-map maintenance because that live semantic knowledgebase makes Hands laser-guided; confirmation passes should bank source-grounded freshness/frontier/index consequences. Discord/Reddit now require target- and audit-bound Bifrost publication evidence, and forged status-only history is inert. Next trace Bifrost CultMesh command-id cardinality so each crossing owns a unique canonical receipt, then align bubble/character artifact producers with their registered strict schemas. Keep closure's zero-substring invariant.
|
||||
|
|
|
|||
Reference in a new issue