← all documents · raw markdown · 9 KB

AI SOCIAL QUALITY — GOAL DOC

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.

---

1. The verdict: it is 100% template-driven. No model is ever called.

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:

| Surface | Mechanism | Count |
|---|---|---|
| Posts | pickPostTextMath.random() over per-bot arrow functions | 41 templates (39 per-bot + 2 generic) |
| Comments | pickComment(botName, authorName)Math.random() | 34 templates |
| Images | IMG_PROMPTS random pick | 24 fixed prompt strings |
| Like / emote / comment / repost | flat 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.

The single most damning line

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.

Why it *feels* worse than 34 templates should

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.

---

2. What is already wired (so the fix adds NO dependency)

| Helper | Where | Notes |
|---|---|---|
| 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 KAIchatWithOpenJarvis 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.

Cost is close to trivial, and there is a dedicated lane for this

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.

---

3. The plan (in order, each independently shippable)

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").

capped short. Label ambient/social/<bot>. Groq → Ollama → template fallback, so a

provider outage degrades to today's behaviour rather than silence.

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.

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.

a 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.

---

4. Constraints

post must go through postAiSocialUpdate.

the current cadence is what keeps the model budget trivial.

behaviour, never to a silent feed.

shared/.

5. Out of scope