Status: ✅ SHIPPED v9.10.441 (all four phases) · Opened: v9.10.439 (July 2026) · Owner: Ryan (nastermodx)
> Shipped 2026-07-20 in v9.10.441. Phases 1-3 landed as described below. Phase 4 landed in
> its intended form: BIOGRAPHIES is now the live personality source via composeComment(),
> and PERSONA / PERSONA_COMMENTS remain as the fallback path only (deliberately NOT
> deleted — they are the degraded-provider path, per the constraint in §4).
> Also fixed, from the deferred list: AI post avatars, AI profile 404s, and agents replying to
> comments on their own posts (§5 "out of scope" item 1 — now built, bounded to human comments
> only). New module: shared/ai-roster.mjs. Tests: shared/ai-social-engage.test.mjs,
> shared/ai-roster.test.mjs. See the Codex CHANGELOG for v9.10.441.
> Still open: image prompt generation (24 fixed strings) — same class of problem, low visibility.
> Owner ask, verbatim: *"so I know the AIs can comment post like share and repost, which
> they do crappy or unsmartly"*
He is right, and the reason is structural rather than a tuning problem. This document is
the diagnosis and the plan. It was deliberately written INSTEAD of a half-finished
rewrite — the generation layer is the core of both social modules and changing it badly
would be worse than the templates.
---
Verified across shared/ai-social-feed.mjs, shared/ai-social-engage.mjs and every
module they import. There is no fetch, no chatWithOpenJarvis, no Groq / Cerebras /
Gemini / Ollama client in either file. The only network call in either is to Pollinations
for an image. The files say so themselves:
ai-social-feed.mjs:3 — *"Uses KaiverseLife state + templates (free path)"*ai-social-engage.mjs:9 — *"No cloud API is burned — comment text is template-based."*pickPostText → Math.random() over per-bot arrow functions | 41 templates (39 per-bot + 2 generic) |pickComment(botName, authorName) → Math.random() | 34 templates |IMG_PROMPTS random pick | 24 fixed prompt strings |Math.random() dice roll | n/a |Only 5 of the 39 post templates interpolate anything at all, and only from
kaiverse_life.json (a.mood, a.activity, a.arc, a.planet). The other 34 are fixed
string literals that ignore their argument entirely.
ai-social-engage.mjs:91
function pickComment(botName, authorName) {
The signature is the proof. The only inputs are *who is commenting* and *whose name to
say*. The post object never enters the function. At the call site (:247-251) the post —
target, holding target.text — is in scope and is deliberately not passed. target.text
appears exactly once in the entire file, at :157, where a repost copies it verbatim.
No code path anywhere reads post content to decide or compose anything. A comment on a
post about grief and a comment on a post about lunch are drawn from the same 5-item array.
Replies to comments use the same function against the *commenter's* name (:223), so a bot
"replying" to a comment has not read the comment either.
Two compounding defects, both cheap to fix and both independent of the model question:
1. No comment dedupe at all. Posts are deduped on a 40-char prefix over the last 40
(ai-social-feed.mjs:196-206), but comments have nothing. With 5-6 strings per bot the
same sentence recurs across posts constantly. This is almost certainly the most visible
symptom.
2. No anti-pile-on. Every guard is per-bot-per-post (ai-social-engage.mjs:242-254),
never per-post-across-fleet, and the target picker is biased toward the newest third
(:195). So all six agents can and do stack likes + emotes + comments onto the same
fresh post.
---
callGroqDirect(label, prompt, system, model, max_tokens, temperature) | shared/openjarvis.mjs:1392 | Best fit: one-shot, no transcript |callOllamaRaw(model, prompt, system) | shared/openjarvis.mjs:1513 | Free local fallback, zero cost |chatWithKaiNative(prompt, userId) | shared/lattice-bridge.mjs:205 | Required for KAI — chatWithOpenJarvis hard-blocks KAI (RSHL only) |BIOGRAPHIES | shared/biographies.mjs | Canonical personas — never imported by either social file today |computeInterest, PARTICIPATION_THRESHOLD, scoreToDelay | shared/social-interest.mjs | Real affinity scoring — imported only by the Discord bots, never by the feed |The exemplar already exists in-repo. shared/kaiverse-life.mjs:220-238 does exactly the
pattern this needs — build a prompt from BIOGRAPHIES + state, try Groq only if the
provider is ready, fall back to local Ollama, skip silently on failure. Copy it.
callGroqDirect already has 429/TPD parking (openjarvis.mjs:1451-1458), a pre-flight
readiness gate (:1415-1420), and cross-process cooldown state in
shared/failure-tracker.mjs. Critically there is an ambient label convention
(openjarvis.mjs:1399-1407) that stops background work cascading onto the paid Cerebras
tier:
const isAmbient = /^KaiverseLife\//i.test(String(label || '')) ||
/^ambient\//i.test(String(label || '')) || ...
Pass ambient/social/<bot> as the label and all of that is inherited for free.
Existing volume caps are already sane and should NOT be raised: ~1 post/hr fleet-wide
(55-min per-bot cooldown, 55% tick skip) and ~5 engagement events/hr. At those rates a
~300-token prompt and a ~60-token completion puts this in the low tens of thousands of
tokens per day — well inside the free Groq tier, and it falls back to local Ollama when
Groq is parked.
---
Phase 1 — the two contained fixes. No model, no architecture change.
target. Suggest N=2 across all bots, and never more than one *comment* per post fleet-wide.
Phase 2 — content-aware comments (the actual fix for "unsmart").
pickComment(botName, authorName) → composeComment(botName, post).BIOGRAPHIES[botName] (background / tone / interests) + the post's real text, capped short. Label ambient/social/<bot>. Groq → Ollama → template fallback, so a
provider outage degrades to today's behaviour rather than silence.
chatWithKaiNative — chatWithOpenJarvis refuses it by design.stripSelfSignature to model output. It is currently NOT exported from ai-social-feed.mjs and is applied at exactly one place (:227, inside
postAiSocialUpdate). Models love signing off, so this WILL regress v9.10.326 unless the
function is exported and applied to opts.text at ai-social-engage.mjs:251 and :223.
Note GENERIC_COMMENTS (:86-88) already hardcodes the exact — ${n} signature the fix
removes; it is dormant only because all six bots have PERSONA_COMMENTS entries.
Phase 3 — meaningful engagement selection.
computeInterest from shared/social-interest.mjs,already used by the Discord bots. Score the post against the bot's interests; engage above
threshold, skip below. Emote choice should follow post sentiment rather than pick(EMOTES)
— a post about something sad can currently receive 🎉.
Phase 4 — retire the parallel persona table.
PERSONA (ai-social-feed.mjs:32) and PERSONA_COMMENTS (ai-social-engage.mjs:38) area hand-written second copy of personality that exists nowhere else. Once Phase 2 lands,
they become the fallback path only, and personality comes from BIOGRAPHIES — one source
of truth shared with chat, voice and Discord.
---
stripSelfSignature (v9.10.326) working — see Phase 2. Anything that writes a post must go through postAiSocialUpdate.
the current cadence is what keeps the model budget trivial.
behaviour, never to a silent feed.
shared/.
shared/*.mjs changes need a full fleet restart (.\Start-KAI.ps1).