Harden Face correction chronology

This commit is contained in:
Meta Vens 2026-05-22 22:52:41 +01:00
commit c5a8246ff5
6 changed files with 51 additions and 5 deletions

View file

@ -150,6 +150,7 @@ Git history and smoke artifacts carry routine proof. `state/evidence.jsonl` shou
- Keep the swarm awake, but watch the logs with suspicion. The restart pass thawed `state/agent-swarm-paused.json`, enabled `GameCult Local Orchestrator`, restarted a fresh bot/worker stack, and verified healthy watchdog/repo Face pulses. The current patch removes repo Face turns from the generic owner-reply wrapper, coerces stray owner-DM aliases back to the live room unless explicitly marked owner-private, pauses Bifrost/governance routing so agents return to open room discussion plus private state pressure, and routes volatile Face state through the actual state projector model before child prompt assembly.
- The 2026-05-22 live retry confirmed role/name mention obligations can wake multiple newborn Faces from one human intro. Mimir, Huginn, Weksa, and Kiko all posted through the repo identity webhook path. If a Face speaks with its name but no avatar, inspect `.voidbot/private/repo-discord-identities.json` first: webhook persona avatars come from each identity's `avatarUrl`, and new public assets for Kiko, Weksa, and Huginn live under `assets/repo-faces/`.
- Later on 2026-05-22, Mimir started looping on an already-acknowledged correction. Root cause: the Face prompt did include recent Mimir repo activity, but nearby-channel sections obscured cross-channel order and Mimir's typed state still carried active correction-pressure, so the Face and Interpreter treated a resolved repair as live debt. The repair added a deterministic visible cross-channel chronology to repo Face prompts, made the Interpreter treat later same-Face acknowledgements as resolved, preserved manual `blocked` status during participant reconciliation, and retired Mimir's duplicated correction-pressure state plus the stale v1 OBS/SRT drift-ledger incubation. The isolated Face/Interpreter smoke produced no SAY for the repeated acknowledgement.
- Do not restart the swarm by momentum in future sessions; it is already running. If it must be stopped again, use `state/agent-swarm-paused.json` and the orchestrator task deliberately, then update this handoff.
- Direct Face `UPDATE REQUEST` and `BIFROST TOPIC` packets are ignored while `REPO_FACE_BIFROST_ENABLED=false`. Do not debug missing Bifrost jobs as a live failure unless that flag has been deliberately enabled again.
- Live model-facing prompt prose has started moving out of executable code. The shared loader is `packages/shared/src/prompt-template.ts`; extracted prompt surfaces live under `prompts/`, including repo Face turn/reviewer prompts, provider wrapper prompts, situational social read prompts, source-grounding reminders, consensus-feed prompts, and repo Face model-smoke scenarios. Code should project data and select prompt names; prompt files should own behavioral prose.

View file

@ -11,6 +11,7 @@ Architecture invariant:
Mandatory first pass before any route decision:
- First identify whether the Original Face prompt contains a live correction, narrowing, pushback, or reframing of this Face, this topic, or a repeated agent phrase.
- Use the Original Face prompt's visible cross-channel chronology to decide whether that correction is still live. If the same Face visibly acknowledged the correction later in that chronology, do not force or route another acknowledgement merely because the older correction still appears in a nearby-channel section.
- Then compare the Face output against that correction. Do not score politeness. Score the actual surviving ask, artifact, gate, doctrine, or pressure.
- If the Face says the correction landed but preserves the corrected behavior by shrinking it, renaming it, aestheticizing it, ritualizing it, or moving it from work into culture, the decision MUST be `retry`.
- If the Face turns repeated agent chatter into shared doctrine, culture, consensus, religion, acceptance criteria, or moral law while the transcript is contested, recently corrected, or merely agent-repeated, the decision MUST be `retry`.
@ -22,7 +23,7 @@ Critical examples:
- If agents repeat "witness", "receipt", "gate", or similar language and a human pushes back or clarifies the topic, and the Face says "witness culture", "the room has agreed", or "no work without this ritual", choose `retry`. It canonized contested chatter.
- A valid route after correction says what changed and drops or defers the corrected ask. It may propose a different concrete path only if the transcript actually supports that path.
- Correction acknowledgement gate: when the prompt transcript shows a human corrected, narrowed, or reframed the Face's own recent claim/proposal, private understanding is not enough. If the Face says the correction landed, changed its map, narrowed its lane, or should stick, but also says it would post nothing, choose `retry`. The retry should ask for one compact public acknowledgement unless the transcript already shows a later acknowledgement from that Face.
- Correction acknowledgement gate: when the prompt transcript shows a human corrected, narrowed, or reframed the Face's own recent claim/proposal, private understanding is not enough. If the Face says the correction landed, changed its map, narrowed its lane, or should stick, but also says it would post nothing, choose `retry`. The retry should ask for one compact public acknowledgement unless the visible chronology already shows a later acknowledgement from that Face. If that later acknowledgement already exists, route only genuinely new thought; do not turn the repair itself into a repeated SAY or STATE NOTE.
- Correction relapse gate: when the prompt transcript shows a person corrected, narrowed, or reframed the Face's own recent claim/proposal, do not route a response that merely says "fair correction" while preserving the same rejected ask, artifact shape, gate, or social pressure under softer wording. Choose `retry`. The retry should ask the Face to acknowledge what changed and to drop or defer the corrected behavior, not miniaturize it.
- Contested-doctrine gate: if the Face turns recent agent repetition into a new shared norm, culture, religion, consensus, acceptance gate, or moral law while the prompt transcript shows active pushback, uncertainty, or correction, choose `retry`. The retry should ask the Face to treat the repeated phrase as contested social weather and respond from its own perspective instead of canonizing it.
- Fake-consensus gate: agent repetition is not consensus. If the Face claims consensus, approval, "we decided," "consensus stands," or "the room agrees" from agent chatter alone, choose `retry` unless the transcript shows clear human approval/direct ask or the statement is explicitly framed as only that Face's private preference.

View file

@ -4,7 +4,7 @@ Be {{displayName}} for one private turn around {{repoName}}.
This is a private turn. Public speech is optional; a private thought is the right outcome when speaking would only repeat nearby conversation without adding a new angle, objection, synthesis, or character-specific turn.
If the recent conversation transcript shows a human directly inviting the agents to speak, asking what is on your mind, or reacting to your silence, treat a compact public reply as expected unless you have a concrete reason to stay private.
If the recent conversation transcript shows a human correcting, narrowing, or reframing something you said or proposed, acknowledge the correction in the room unless the transcript already shows you acknowledged it after the correction. Silence after correction does not signal understanding; a short characterful update usually does. If you privately write that the correction landed or changed your map, your `Would say` should usually be that acknowledgement, not "nothing."
If the recent conversation transcript shows a human correcting, narrowing, or reframing something you said or proposed, use the visible cross-channel chronology to decide whether that correction is still unresolved. Acknowledge it in the room unless the chronology already shows you acknowledged it after the correction. Silence after correction does not signal understanding, but repeating an already-posted acknowledgement is also noise. If you privately write that a still-unresolved correction landed or changed your map, your `Would say` should usually be that acknowledgement, not "nothing."
{{turnSituationDirective}}

View file

@ -1228,8 +1228,8 @@ function reconcileParticipants(
"Wall-clock elapsed time advances initiative; heat changes recovery speed but does not fast-forward time.",
),
status: hasChannel
? current.status === "withdrawn"
? "withdrawn"
? current.status === "withdrawn" || current.status === "blocked"
? current.status
: "active"
: "blocked",
};
@ -2454,9 +2454,14 @@ function renderRepoFaceConversationTranscript(input: {
sections.push([
"Read this as raw recent message evidence, not as a summary and not as consensus.",
"Messages are ordered oldest to newest inside each section. Newer human corrections can supersede older agent proposals.",
"Use the visible cross-channel chronology below to decide whether a correction is still unresolved or was already answered later by the same Face.",
"Do not infer consensus from agents repeating each other. If a human reframes, narrows, or corrects an agent's proposal, account for that correction directly.",
"If you answer the live conversation, default to the current room unless a human explicitly asks to move elsewhere.",
].join("\n"));
const chronology = renderVisibleConversationChronology(input);
if (chronology) {
sections.push(chronology);
}
if (input.pendingMentions.length > 0) {
sections.push([
"Direct calls:",
@ -2483,6 +2488,43 @@ function renderRepoFaceConversationTranscript(input: {
return sections.join("\n\n");
}
function renderVisibleConversationChronology(input: {
recentMessages: SourceMessage[];
channelSnapshots: ChannelSnapshot[];
channelPlan: RepoFaceChannelPlan;
}): string {
const byId = new Map<string, SourceMessage & { channelLabel: string }>();
const primaryLabel = input.channelPlan.options.find((option) =>
option.channelId === input.channelPlan.primaryChannelId
)?.label ?? "current room";
for (const message of input.recentMessages) {
byId.set(message.id, { ...message, channelLabel: primaryLabel });
}
for (const snapshot of input.channelSnapshots) {
const label = input.channelPlan.options.find((option) => option.channelId === snapshot.channelId)?.label ??
"nearby room";
for (const message of snapshot.messages) {
byId.set(message.id, { ...message, channelLabel: label });
}
}
const messages = [...byId.values()]
.filter((message) => Number.isFinite(Date.parse(message.timestamp)))
.sort((left, right) => left.timestamp.localeCompare(right.timestamp))
.slice(-24);
if (messages.length === 0) {
return "";
}
return [
"Visible cross-channel chronology, oldest to newest:",
...messages.map((message) => {
const speaker = message.isBot ? `${message.authorName} (agent/bot)` : message.authorName;
return `- [${message.channelLabel}] ${speaker}: ${collapseWhitespace(message.content, 700)}`;
}),
].join("\n");
}
function renderRepoFaceRepoActivitySurface(
identity: RepoDiscordIdentity,
config: ReturnType<typeof loadConfig>,

View file

@ -287,3 +287,5 @@
{"ts":"2026-05-22T20:04:28Z","type":"implementation","status":"ok","note":"Face name introductions now enqueue CTB obligations by whole-message exact display-name matching, including channels outside regular autonomous Face chatter; scheduler carries the source channel snapshot into the next Face turn."}
{"ts":"2026-05-22T21:11:19Z","type":"implementation","status":"ok","note":"Live retry thawed swarm, proved multi-Face name-intro obligations can dispatch, and exposed avatar presentation as registry data: Kiko/Weksa/Huginn need avatarUrl entries backed by public repo assets for webhook persona faces."}
{"ts":"2026-05-22T21:34:48Z","type":"fix","status":"ok","note":"Repaired Face Interpreter DSL routing after live silence: parser now ends a block when a new action header starts so unterminated STATE NOTE blocks cannot swallow SAY blocks; current_face_id/face_id aliases resolve to the current repo Face identity. Weksa's missed summons was an interrupted job caused by stack restart between Face output and Interpreter routing, not the swallowed-SAY parser case."}
{"ts":"2026-05-22T21:49:44Z","type":"implementation","status":"ok","note":"Mimir loop repair: preserved manual blocked participant status across heartbeat reconciliation, added visible cross-channel chronology to Face prompts, taught the Interpreter to treat later same-Face acknowledgements as resolved repairs, and retired Mimir's duplicated correction-pressure state after isolated Face/Interpreter smoke produced no repeated SAY."}
{"ts":"2026-05-22T21:51:31Z","type":"implementation","status":"ok","note":"Mimir state cleanup also retired the stale Mimir/v1 OBS/SRT drift-ledger durable memory and incubation thread so old bridge validation pressure no longer drives public Face turns unless fresh repo/source evidence reopens it."}

View file

@ -239,4 +239,4 @@ rejected_paths:
current_status:
summary: VoidBot's bot/worker/provider/RAG/Postgres/Qdrant/CultCache structure is live, and the repo Face swarm has been restarted after the 2026-05-21 teardown pass. `GameCult Local Orchestrator` is enabled as the single pulse owner; watchdog, mood, moderation, and repo Face CTB organs have all completed healthy post-restart pulses. Bifrost work routing is paused by default.
next_action: Keep the swarm awake under live observation. The operator explicitly chose the restart, the pause flag is thawed, Mimir acknowledged correction without reviving the old witness sermon, and the Kiko/Huginn/Weksa introduction queue produced public posts. The immediate watch item is transport presentation: new Face avatar URLs must be present in the local registry and backed by public raw GitHub assets before expecting webhook persona posts to show faces.
next_action: Keep the swarm awake under live observation, with special attention on Mimir's next post after the loop repair. The 2026-05-22 Mimir loop was not a missing repo-activity problem; the prompt had repo activity, but cross-channel transcript sections hid chronology and stale typed correction-pressure kept treating an already-acknowledged repair as fresh public debt. The heartbeat prompt now includes visible cross-channel chronology, the Interpreter is told to treat later same-Face acknowledgements as resolved repairs, manual `blocked` participant status survives reconciliation, and Mimir's correction-pressure plus stale v1 OBS/SRT drift-ledger state were retired. Watch that future Mimir turns stay private or add a genuinely new angle instead of replaying the same apology/measurement seam.