Restore repo face prompt assembly inspection
This commit is contained in:
parent
7f90ab82e4
commit
78a4c735de
6 changed files with 151 additions and 3 deletions
|
|
@ -170,7 +170,7 @@ This note is the source-grounded description of how the live VoidBot stack is sh
|
|||
- Role mentions, display-name-prefix mentions, and human replies to a Face's webhook/persona message append pending Face obligations to `pendingMentions` in `REPO_FACE_HEARTBEAT_STATE_PATH`; ordinary bot mentions and DMs append pending obligations for the built-in `void` participant. They no longer create direct provider jobs from `MessageCreate`. The CTB scheduler temporarily prioritizes agents with pending mentions, attaches those prompts plus reply targets to the next `repo-face-rumination` or Void moderation pass, and consumes the pending items only after a turn is actually queued. Rumination jobs remain private unless they explicitly emit a parent-owned Face action block or a typed candidate intervention; the worker must never auto-post their summaries as the base bot. Heartbeat prompts now include the channel permission plan, recent context from non-primary permitted channels, social embodiment, jurisdiction respect, comedy/improv doctrine, and anti-template sampling guidance so a Face can jump into domain chatter, form durable social reads, consult the right steward, and answer people without copying nearby note formulas.
|
||||
- Repo Face proposal/article PRs, PR comments, and registered-identity Discord posts now cross through Bifrost bridge commands. The child may emit `VOIDBOT_REPO_IDENTITY_PROPOSAL_PR` for consensus-needed repo/lore/design proposals, `VOIDBOT_REPO_IDENTITY_ARTICLE` for bylined essays, or `VOIDBOT_REPO_IDENTITY_PR_COMMENT` for signed arguments on existing proposal PRs. The worker validates the registered identity and records Face-local receipts, but `BIFROST_ROOT/tools/bifrost-bridge.mjs` owns draft PR creation, PR comments, and Discord persona posting. This is not merely helper placement: Bifrost's original governance/labor platform role makes it the owner of governed public crossings for GameCult work, including GitHub, Discord-native work/member/swarm interactions, dispatch receipts, CultNet/CultCache intake, and future collaboration interfaces. Heimdall is the OAuth/account/capability gate; Bifrost is the bridge/request/routing/receipt authority. VoidBot still owns room cognition, moderation judgment, archive/source retrieval, and Face personality; Bifrost owns Discord when Discord acts as the Labor Platform interface or swarm transport. If Nibu or another Face frames something as the next cut/pass and enough source or room material exists, the expected action is to draft the artifact, submit the PR, and announce it rather than continuing to nag the room.
|
||||
- Repo Face turns now receive a Bifrost governance digest from `BIFROST_ROOT/tools/governance-threads.mjs digest --repo <repo> --agent <identity>` alongside recent Discord context. Bifrost typed CultCache topics/comments are the canonical home for feature requests, Face opinions, missing questions, approvals, and dispatch receipts; Discord is a mirror/evidence source. When `BIFROST_DISCORD_CHANNEL_ID` is configured for `#bifrost`, repo Face Discord context ignores mirrored bot chatter there because the authoritative activity arrives through the Bifrost digest. Human `#bifrost` messages should only become governance comments after Bifrost/Heimdall can link the Discord id to a registered actor. Repo-local asks that would send a human or Codex to add/fix/name/scaffold/document/test/investigate/implement belong on Bifrost with enough room/repo context for a claimed Codex turn; a Face may still speak characterfully in Aquarium, but not as the only dispatch surface. Legacy `UPDATE REQUEST` packets are parsed only as compatibility input and reconciled into Bifrost topics instead of immediate transport enqueue. Local `#bifrost` is channel `1506760343836430376`; work proposals, governance mirrors, and Bifrost dispatch receipts should go there, while Aquarium remains the casual/social swarm room.
|
||||
- Repo character turns now have a two-Interpreter target shape. The intended state-in boundary is `prompts/repo-face-state-projector.prompt.md`: typed state, recent room memory, governance memory, and identity material should be translated into lived character prose before the child sees it. The old inspection path has been cut, so no renderer can masquerade as the live shape. Until the first Spark/Codex Interpreter call is wired, the child turn prompt intentionally receives no state-projected memory surface. The child character should eventually receive projected memories, mood, obligations, allowed rooms, governance memory, and recent chat as prose rather than structured state. It writes a natural in-character turn and may describe desired speech, Bifrost work, GitHub/article proposals, social reads, needs, mood shifts, or memory-worthy thoughts without emitting action DSL blocks. The second Spark/Codex Interpreter decides route/retry/drop and translates warranted effects into structured `STATE NOTE`, `SAY`, and `BIFROST TOPIC` blocks for the worker. `STATE NOTE` blocks become typed short-term memory, affect needs, social bonds, status reads, mood dimensions, or agency pressure. The worker still accepts legacy compact JSON sentinels and legacy `UPDATE REQUEST` blocks for compatibility, but the live prompt path keeps transport and state syntax out of the character's roleplay surface.
|
||||
- Repo character turns now have a two-Interpreter target shape. The state-in boundary is `prompts/repo-face-state-projector.prompt.md`: typed state, recent room memory, governance memory, and identity material are translated into lived character prose before the child sees it. Deterministic code still owns full prompt assembly and inspection through `scripts/run-repo-face-heartbeats.ts --assemble-prompt <identity>`; that path composes stable doctrine, channel permissions, Bifrost digest, recent chat, and a supplied Interpreter memory surface into the exact child prompt. It must not invent personality, mood, or affect from typed state. Until the first Spark/Codex Interpreter call is wired into the live heartbeat, repo Face turns intentionally lack state-projected memory prose rather than using a fake substitute. The child character should receive projected memories, mood, obligations, allowed rooms, governance memory, and recent chat as prose rather than structured state. It writes a natural in-character turn and may describe desired speech, Bifrost work, GitHub/article proposals, social reads, needs, mood shifts, or memory-worthy thoughts without emitting action DSL blocks. The second Spark/Codex Interpreter decides route/retry/drop and translates warranted effects into structured `STATE NOTE`, `SAY`, and `BIFROST TOPIC` blocks for the worker. `STATE NOTE` blocks become typed short-term memory, affect needs, social bonds, status reads, mood dimensions, or agency pressure. The worker still accepts legacy compact JSON sentinels and legacy `UPDATE REQUEST` blocks for compatibility, but the live prompt path keeps transport and state syntax out of the character's roleplay surface.
|
||||
- `scripts/run-repo-face-heartbeats.ts` now acts as the broader agent CTB/Final-Fantasy-X-style initiative scheduler. The wall-clock task is only a pulse; virtual initiative time chooses who gets the next turn. The scheduler stores per-agent speed, reaction bias, load, groups, heat, effective speed, next-turn time, active turn markers, and recent queue history at `REPO_FACE_HEARTBEAT_STATE_PATH`. Repo Faces queue `repo-face-rumination` owner-Codex jobs; built-in Void currently participates as the `void` system agent and starts the existing moderation rumination runner as its turn. Initiative freezes while an owner-Codex job or Void moderation lock is active; recovery is scheduled only after that active turn completes, and completion cannot grant another turn in the same scheduler pulse. The active-job scan recovers stale Face jobs by marking ancient approved/running rows failed through the job queue before building the active-turn map. Above-baseline heat bypasses repo Face nap gating, while active-turn freeze still prevents overlapping thoughts.
|
||||
- The live wall-clock pulse for this scheduler is `scripts/run-gamecult-orchestrator.ps1`, installed as `GameCult Local Orchestrator`. `scripts/install-repo-face-heartbeat-task.ps1` remains available for isolated testing, but its per-organ scheduled task should stay disabled in normal operation. Turn frequency is controlled by `REPO_FACE_HEARTBEAT_BASE_RECOVERY_MINUTES`, `REPO_FACE_HEARTBEAT_GLOBAL_HEAT`, `REPO_FACE_HEARTBEAT_SPEED_OVERRIDES`, and `REPO_FACE_HEARTBEAT_HEAT_OVERRIDES`. Heat keys can target `all`, `kind:<participantKind>`, `turn:<turnKind>`, `identity:<id>`, `repo:<repo>`, `display:<name>`, `channel:<id>`, or the bare identity/repo/display key.
|
||||
- The local ignored registry currently includes Nibu for `AetheriaLore`, with Face state in `E:/Projects/AetheriaLore/.voidbot/state/nibu.cc`, Discord role id `1505918221453103247`, and an Aquarium channel allow-list. Nibu's Face state now treats AetheriaLore-first map-building, abrasive character voice, consensus-backed change agency, her own `Aetheria/Lore/Nibu.md` character-note stewardship, Nibu-authored lore essays, and tangential setting fascination as separate active doctrines. That tangential fascination points at embodied ship minds, junkyard abandonment, murderous autonomy, salvage, and save-scumming survival. The repo Face heartbeat organ is now pulsed by `GameCult Local Orchestrator`; after restarting the stack onto the new worker code, forced heartbeat job `76020f1e-0c55-424b-9e35-89ba7f77fcc3` completed without base-bot auto-posting.
|
||||
|
|
|
|||
|
|
@ -191,7 +191,7 @@ The runner can hand these to a CLI/MCP tool. The store decides what survives.
|
|||
- recent event timing in that bounded context now crosses a projection boundary first, so the child sees "5 minutes ago" language while the parent keeps exact timestamps for bookkeeping
|
||||
- public artifact sharing now crosses the same boundary: repo activity exposes content hints, rumination verifies authorship through retrieval, and a configured `publicSpeechTarget` lets Void queue a normal typed candidate intervention instead of needing a blog-specific announcement path. Void also has explicit herald jurisdiction over the GameCult website/blog; essay-shaped pressures should queue article-proposal candidates for Void-authored site pieces.
|
||||
- Discord mention participation now uses role/display-name/bot addressing as CTB queue input and parent-owned speech rails as the output boundary. A Face mention appends a pending obligation for that Face; an ordinary Void mention or DM appends a pending obligation for the built-in `void` participant. Ambient direct mentions no longer create direct provider jobs.
|
||||
- repo-scoped character turns reuse the typed self-state machinery per identity, but the child character must not see that machinery directly. Addressed chats are consumed by the heartbeat scheduler, which prioritizes the identity, and the intended boundary has two Interpreter prompts: `repo-face-state-projector.prompt.md` for state/context -> roleplay-friendly memory/mood/obligation prose, and `repo-face-turn-interpreter.prompt.md` for natural character reflection -> typed memory, affect, social-read, mood, agency, speech, and Bifrost operations. The old preview path is gone; until the first Interpreter call is wired, the live turn prompt intentionally lacks state-projected memory prose rather than pretending an inspection renderer is the real architecture.
|
||||
- repo-scoped character turns reuse the typed self-state machinery per identity, but the child character must not see that machinery directly. Addressed chats are consumed by the heartbeat scheduler, which prioritizes the identity, and the intended boundary has two Interpreter prompts: `repo-face-state-projector.prompt.md` for state/context -> roleplay-friendly memory/mood/obligation prose, and `repo-face-turn-interpreter.prompt.md` for natural character reflection -> typed memory, affect, social-read, mood, agency, speech, and Bifrost operations. Deterministic prompt assembly remains valid: `scripts/run-repo-face-heartbeats.ts --assemble-prompt <identity>` composes the final child prompt from templates, channel policy, recent chat, Bifrost digest, and a supplied Interpreter memory surface. It must not turn typed state into character prose itself. Until the first Interpreter call is wired, the live turn prompt intentionally lacks state-projected memory prose rather than pretending an inspection renderer is the real architecture.
|
||||
- repo-scoped Faces now have per-topic channel permissions instead of one work-shaped speech lane. `allowedChannelIds` remains the hard gate, while `channelPermissions` gives each Face labeled topics, speech thresholds, and speed multipliers. The current social invariant is that Aquarium is intentionally cheap for casual in-character chatter, while domain channels receive matching work/lore/music/realtime/agent-state thoughts when the recent cross-channel context lines up. Heat is the live-room valve: above-baseline heat now relaxes nap gating, but active-turn freeze still prevents an agent from being queued again while it is thinking.
|
||||
- repo-scoped character prompts now explicitly treat social embodiment as durable state territory without exposing schemas to the child. A character may write naturally about humans, other agents, or room dynamics; the Interpreter turns meaning-bearing friendship, rivalry, irritation, trust, recurring bits, bonds, needs, and status reads into typed state when they carry claim/question, tension, action implication, and anchors. Public posts should speak to someone directly and must not begin with note-title formulas or identity labels such as "bright bridge note" or "tiny fish sorting note."
|
||||
- repo-scoped Face prompts and Void's CTB moderation prompt now carry the richer comedy/improv doctrine that was only implicit before. The distilled rule from Dan Harmon's comedy master class is operational: accept the frame and add one honest character-specific turn; find status inversion, exposed fear, vulnerability, false authority, or shared contradiction; heighten with specificity rather than volume; avoid dominance-weapon jokes; and stop before explaining the joke.
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
"state:install-mood-task": "powershell -ExecutionPolicy Bypass -File scripts/install-void-mood-drift-task.ps1",
|
||||
"state:install-repo-face-heartbeat-task": "powershell -ExecutionPolicy Bypass -File scripts/install-repo-face-heartbeat-task.ps1",
|
||||
"agents:inspect-requests": "tsx scripts/inspect-agent-state-requests.ts",
|
||||
"agents:assemble-character-prompt": "tsx scripts/run-repo-face-heartbeats.ts --assemble-prompt",
|
||||
"agents:heartbeat-model-smoke": "node scripts/smoke-repo-face-heartbeat-model-scenarios.mjs",
|
||||
"postgres:up": "docker compose -f infra/postgres/docker-compose.yml up -d",
|
||||
"codex:mcp:install": "tsx scripts/install-codex-mcp.ts",
|
||||
|
|
|
|||
|
|
@ -130,6 +130,17 @@ interface ChannelSnapshot {
|
|||
async function main(): Promise<void> {
|
||||
const config = loadConfig();
|
||||
const dryRun = process.argv.includes("--dry-run");
|
||||
const assemblePromptIdentity = readArgValue("--assemble-prompt");
|
||||
if (assemblePromptIdentity) {
|
||||
const result = await assembleRepoFaceTurnPrompt({
|
||||
config,
|
||||
identityId: assemblePromptIdentity,
|
||||
outPath: readArgValue("--out"),
|
||||
memorySurfacePath: readArgValue("--memory-surface"),
|
||||
});
|
||||
process.stdout.write(`${JSON.stringify(result)}\n`);
|
||||
return;
|
||||
}
|
||||
|
||||
const pause = await readAgentSwarmPause();
|
||||
if (pause.paused) {
|
||||
|
|
@ -1425,6 +1436,131 @@ function buildHeartbeatPrompt(input: {
|
|||
});
|
||||
}
|
||||
|
||||
async function assembleRepoFaceTurnPrompt(input: {
|
||||
config: ReturnType<typeof loadConfig>;
|
||||
identityId: string;
|
||||
outPath?: string;
|
||||
memorySurfacePath?: string;
|
||||
}): Promise<{
|
||||
ok: true;
|
||||
identityId: string;
|
||||
promptLength: number;
|
||||
outPath?: string;
|
||||
memorySurfacePath?: string;
|
||||
}> {
|
||||
const faceRegistry = await loadFaceIdentityRegistry(input.config.repoDiscordIdentitiesPath);
|
||||
const registry = faceRegistryAsRepoDiscordRegistry(faceRegistry);
|
||||
const identity = registry.identities.find(
|
||||
(entry) => entry.id.toLowerCase() === input.identityId.toLowerCase(),
|
||||
);
|
||||
|
||||
if (!identity) {
|
||||
throw new Error(`Unknown repo Face identity: ${input.identityId}`);
|
||||
}
|
||||
|
||||
const channelPlan = buildChannelPlan(identity, input.config.repoFaceHeartbeats.defaultChannelId);
|
||||
const channelId = channelPlan.primaryChannelId;
|
||||
if (!channelId) {
|
||||
throw new Error(`No prompt assembly channel is configured for ${identity.id}.`);
|
||||
}
|
||||
|
||||
const [recentMessages, channelSnapshots, bifrostDigest, memorySurface] = await Promise.all([
|
||||
fetchRecentDiscordMessages({
|
||||
botToken: input.config.botToken,
|
||||
channelId,
|
||||
limit: 15,
|
||||
ignoreBotMessages: channelId === input.config.bifrostDiscordChannelId,
|
||||
}),
|
||||
fetchChannelSnapshots({
|
||||
botToken: input.config.botToken,
|
||||
channelIds: channelPlan.snapshotChannelIds,
|
||||
primaryChannelId: channelId,
|
||||
limit: 6,
|
||||
bifrostDiscordChannelId: input.config.bifrostDiscordChannelId,
|
||||
}),
|
||||
fetchBifrostGovernanceDigest({
|
||||
bifrostRoot: input.config.bifrostRoot,
|
||||
repoName: identity.repoName,
|
||||
agentIdentity: identity.id,
|
||||
}),
|
||||
readOptionalMemorySurface(input.memorySurfacePath),
|
||||
]);
|
||||
const participant = buildInspectionParticipant(
|
||||
identity,
|
||||
input.config.repoFaceHeartbeats.baseRecoveryMinutes,
|
||||
);
|
||||
const prompt = buildHeartbeatPrompt({
|
||||
identity,
|
||||
channelId,
|
||||
channelPlan,
|
||||
channelSnapshots,
|
||||
recentMessages,
|
||||
memorySurface,
|
||||
bifrostDigest,
|
||||
participant,
|
||||
pendingMentions: [],
|
||||
jurisdictionDive: buildJurisdictionDiveDirective(identity, participant),
|
||||
githubActionsEnabled: input.config.repoFaceGithubActionsEnabled,
|
||||
});
|
||||
|
||||
if (input.outPath) {
|
||||
const outPath = resolve(input.outPath);
|
||||
await mkdir(dirname(outPath), { recursive: true });
|
||||
await writeFile(outPath, prompt, "utf8");
|
||||
}
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
identityId: identity.id,
|
||||
promptLength: prompt.length,
|
||||
outPath: input.outPath ? resolve(input.outPath) : undefined,
|
||||
memorySurfacePath: input.memorySurfacePath ? resolve(input.memorySurfacePath) : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
async function readOptionalMemorySurface(path: string | undefined): Promise<string | undefined> {
|
||||
if (!path) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const content = (await readFile(resolve(path), "utf8")).trim();
|
||||
return content.length > 0 ? content : undefined;
|
||||
}
|
||||
|
||||
function buildInspectionParticipant(
|
||||
identity: RepoDiscordIdentity,
|
||||
baseRecoveryMinutes: number,
|
||||
): FaceHeartbeatParticipant {
|
||||
return {
|
||||
identityId: identity.id,
|
||||
participantKind: "repo_face",
|
||||
turnKind: "repo_face_rumination",
|
||||
repoName: identity.repoName,
|
||||
displayName: identity.displayName,
|
||||
initiativeSpeed: 1,
|
||||
reactionBias: 0.5,
|
||||
interruptThreshold: 0.6,
|
||||
currentLoad: 0,
|
||||
status: "active",
|
||||
groups: [
|
||||
"all",
|
||||
"kind:repo_face",
|
||||
"turn:repo_face_rumination",
|
||||
`identity:${normalizeKey(identity.id)}`,
|
||||
`repo:${normalizeKey(identity.repoName)}`,
|
||||
],
|
||||
heat: 1,
|
||||
effectiveSpeed: 1,
|
||||
baseRecoveryMinutes,
|
||||
nextTurnAt: 0,
|
||||
queuedCount: 0,
|
||||
constraints: [
|
||||
"Prompt assembly is deterministic inspection only.",
|
||||
"Character memory and affect prose must come from the Interpreter memory surface.",
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
function buildChannelPlan(
|
||||
identity: RepoDiscordIdentity,
|
||||
defaultChannelId?: string,
|
||||
|
|
@ -1952,6 +2088,16 @@ function stripLeadingBom(input: string): string {
|
|||
return input.charCodeAt(0) === 0xfeff ? input.slice(1) : input;
|
||||
}
|
||||
|
||||
function readArgValue(name: string): string | undefined {
|
||||
const index = process.argv.indexOf(name);
|
||||
if (index < 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const value = process.argv[index + 1];
|
||||
return value && !value.startsWith("--") ? value : undefined;
|
||||
}
|
||||
|
||||
void main().catch((error) => {
|
||||
console.error(error);
|
||||
process.exitCode = 1;
|
||||
|
|
|
|||
|
|
@ -229,3 +229,4 @@
|
|||
{"ts":"2026-05-21T11:05:20Z","type":"implementation","status":"ok","note":"Added the inbound repo character state-projector prompt and a read-only render path for inspecting full projected character context; replaced the child identity surface and state summary projection with character-facing prose that strips raw state paths, grants, scheduler details, numeric scores, and old state provenance from Nibu's prompt preview."}
|
||||
{"ts":"2026-05-21T11:33:37Z","type":"implementation","status":"ok","note":"Cut the repo Face state-to-character preview path: removed the render CLI, package script, core character-surface renderer, and heartbeat fallback wiring so only the planned Interpreter projection can reintroduce state-projected character prose."}
|
||||
{"ts":"2026-05-21T12:00:37Z","type":"implementation","status":"ok","note":"Tightened repo-face-state-projector prompt: the first Interpreter translates typed state and event logs into compact narrative memory only, not a full character-card rewrite; stable identity remains in the child prompt surface."}
|
||||
{"ts":"2026-05-21T12:05:56Z","type":"implementation","status":"ok","note":"Restored deterministic repo Face prompt assembly as an inspection boundary: --assemble-prompt composes stable templates, channel context, Bifrost digest, and a supplied Interpreter memory surface without reintroducing deterministic typed-state-to-character prose."}
|
||||
|
|
|
|||
|
|
@ -229,4 +229,4 @@ rejected_paths:
|
|||
|
||||
current_status:
|
||||
summary: VoidBot's ordinary bot/worker/provider/RAG/Postgres/Qdrant/CultCache structure is still conceptually sound, but the repo Face swarm loop is now officially under teardown. The unstable organ is the prompt-and-parser machine that mixed CTB scheduling, public speech, identity, social policy, Bifrost governance, Discord mirroring, and Codex/GitHub dispatch. The `GameCult Local Orchestrator` scheduled task remains disabled as containment; ordinary bot/worker inspection remains available.
|
||||
next_action: Use `notes/agent-swarm-teardown-plan-2026-05-21.md` as the active rebuild map and `notes/agent-swarm-shutdown-audit-2026-05-21.md` as incident evidence. The first teardown cut landed, including repo pause gate, active turn framing, Spark/Codex parent Interpreter with one retry, sanitizer deletion, legacy UPDATE REQUEST reconciliation into Bifrost topics, prompt-surface extraction into `prompts/`, and the slim character protocol where children should receive roleplay-friendly memory/mood/obligation projection and write natural turns while the parent Interpreter emits structured state, speech, and Bifrost intents. The inbound state-projector prompt exists, but the old preview path has been cut because it was not the live shape. The next cut is wiring `repo-face-state-projector.prompt.md` as an actual Spark/Codex Interpreter call; until then, repo Face turns intentionally lack state-projected prose rather than using a fake substitute. Identity collapse is in progress. Active bot, worker, MCP repo-identity tools, and Face CTB scheduler paths now load through the canonical Face registry and treat repo Discord identities as compatibility projections. Do not re-enable the swarm until Bifrost work routing, identity authority, scheduler/job lifecycle ownership, and public speech gating are rebuilt around explicit invariants.
|
||||
next_action: Use `notes/agent-swarm-teardown-plan-2026-05-21.md` as the active rebuild map and `notes/agent-swarm-shutdown-audit-2026-05-21.md` as incident evidence. The first teardown cut landed, including repo pause gate, active turn framing, Spark/Codex parent Interpreter with one retry, sanitizer deletion, legacy UPDATE REQUEST reconciliation into Bifrost topics, prompt-surface extraction into `prompts/`, and the slim character protocol where children should receive roleplay-friendly memory/mood/obligation projection and write natural turns while the parent Interpreter emits structured state, speech, and Bifrost intents. The inbound state-projector prompt exists, and deterministic prompt assembly/inspection is restored as `scripts/run-repo-face-heartbeats.ts --assemble-prompt <identity>`: code assembles stable templates, channel policy, recent chat, Bifrost digest, and a supplied Interpreter memory surface, while Spark/Codex alone translates typed state into character-facing memory prose. The next cut is wiring `repo-face-state-projector.prompt.md` as an actual Spark/Codex Interpreter call; until then, repo Face turns intentionally lack state-projected prose rather than using a fake substitute. Identity collapse is in progress. Active bot, worker, MCP repo-identity tools, and Face CTB scheduler paths now load through the canonical Face registry and treat repo Discord identities as compatibility projections. Do not re-enable the swarm until Bifrost work routing, identity authority, scheduler/job lifecycle ownership, and public speech gating are rebuilt around explicit invariants.
|
||||
|
|
|
|||
Loading…
Reference in a new issue