From 07066ca8eada74c668766dcb7069fa8067062745 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Tue, 9 Jun 2026 18:05:46 +0100 Subject: [PATCH] Block private silence from face speech --- apps/worker/src/index.ts | 2 +- apps/worker/src/repo-face-speech.ts | 10 ++++++++++ scripts/smoke-repo-face-speech-boundary.ts | 10 ++++++++++ state/evidence.jsonl | 1 + 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/apps/worker/src/index.ts b/apps/worker/src/index.ts index 1637e70..822146b 100644 --- a/apps/worker/src/index.ts +++ b/apps/worker/src/index.ts @@ -850,7 +850,7 @@ function normalizeBareInterpreterSayCandidate(interpretationText: string): strin if (/^(INTERPRETATION|Private summary|STATE NOTE|ARTICLE|REDDIT THREAD|BIFROST TOPIC|UPDATE REQUEST)\b/i.test(content)) { return undefined; } - if (/\b(stay quiet|cleaner reply is to stay quiet|if you want a line at all|would say nothing|no public line)\b/i.test(content)) { + if (/\b(stay quiet|stay private|cleaner reply is to stay quiet|if you want a line at all|would say nothing|no public line|no public speech|nothing public|nothing right now|nothing yet|hold silence)\b/i.test(content)) { return undefined; } return content; diff --git a/apps/worker/src/repo-face-speech.ts b/apps/worker/src/repo-face-speech.ts index bdda154..c4c3adc 100644 --- a/apps/worker/src/repo-face-speech.ts +++ b/apps/worker/src/repo-face-speech.ts @@ -71,12 +71,22 @@ export function isNonPublicRepoIdentitySpeech(value: string): boolean { .toLowerCase() .replace(/[.!?]+$/g, "") .trim(); + if (/^(nothing|nothing public|nothing private|nothing right now|nothing yet|no public(?: line| speech)?|stay private|stay quiet)\b/.test(normalized)) { + return true; + } + if (/\b(no public line|no public speech|nothing public yet|nothing public right now|would say nothing|hold silence|stay private|stay quiet)\b/.test(normalized)) { + return true; + } return [ "nothing", "nothing right now", "nothing public", + "nothing public yet", + "nothing public right now", "nothing in aquarium", "stay private", + "stay quiet", + "no public line", "no public speech", ].includes(normalized); } diff --git a/scripts/smoke-repo-face-speech-boundary.ts b/scripts/smoke-repo-face-speech-boundary.ts index 9f81731..8f41b05 100644 --- a/scripts/smoke-repo-face-speech-boundary.ts +++ b/scripts/smoke-repo-face-speech-boundary.ts @@ -25,6 +25,16 @@ const cases: Case[] = [ run: () => isNonPublicRepoIdentitySpeech("nothing right now."), detail: "Even a malformed Interpreter SAY cannot send silence markers as public content.", }, + { + name: "qualified silence SAY rejected", + run: () => isNonPublicRepoIdentitySpeech("nothing public yet. I want one real specimen first."), + detail: "Silence explanations that add private rationale are still not public speech.", + }, + { + name: "bare no-public line rejected", + run: () => isNonPublicRepoIdentitySpeech("No public line. Silence is cleaner here."), + detail: "The worker must not post parent routing language as a Face message.", + }, { name: "single-line markdown fence rejected", run: () => isNonPublicRepoIdentitySpeech("`the whole message is a code span`"), diff --git a/state/evidence.jsonl b/state/evidence.jsonl index 9099db7..41dffd6 100644 --- a/state/evidence.jsonl +++ b/state/evidence.jsonl @@ -341,3 +341,4 @@ {"ts":"2026-06-09T14:46:15Z","type":"implementation","status":"ok","note":"Added repo Face REDDIT THREAD action path for r/GameCultOrg: parent Interpreter may emit a thread title/body/share line, worker calls Bifrost reddit-post with Persona flair and posts a receipt announcement. Bifrost remains Reddit transport owner; VoidBot owns intent packaging and moderation/herald judgment. Typecheck and Bifrost reddit dry-run passed; interpreter-loop smoke still failed on pre-existing due-jurisdiction-dive tool-use behavior."} {"ts":"2026-06-09T15:22:34Z","type":"implementation","status":"ok","note":"2026-06-09 swarm wake repair: unpaused state/agent-swarm-paused.json, pointed local Face heartbeat default back to Aquarium, disabled the expensive state-projector path locally, limited heartbeat selection to one job/tick, made buildChannelPlan prefer the configured default channel unless a pending mention supplies a source channel, added Interpreter prompt guidance plus worker fallback for bare compact SAY candidates, and manually verified Epiphany spoke in Aquarium via registered webhook message 1513922884798513392."} {"ts":"2026-06-09T15:28:15Z","type":"verification","status":"ok","note":"After rebuilding and starting a fresh worker from apps/worker/dist/index.js, a live repo-face-rumination job e20403ee-d3f8-4191-93ae-4ef69f71f803 parsed say=1 and posted Huginn to Aquarium channel 1501196543150264332 via webhook message 1513926584904450130. This verifies the swarm speech rail is no longer silently swallowing all Face turns."} +{"ts":"2026-06-09T17:05:08Z","type":"verification","status":"ok","note":"Tightened repo Face speech boundary after live fallback leaked private silence text like 'nothing public yet' into Discord. isNonPublicRepoIdentitySpeech now rejects qualified silence/no-public/stay-private phrases, the bare Interpreter fallback rejects the same routing language, and agents:speech-boundary-smoke covers 'nothing public yet. I want one real specimen first.' plus 'No public line. Silence is cleaner here.' Fresh worker PID 29364 is running rebuilt apps/worker/dist/index.js."}