diff --git a/notes/personas/metacrat.persona_state.import.json b/notes/personas/metacrat.persona_state.import.json index 81838ea..a429fce 100644 --- a/notes/personas/metacrat.persona_state.import.json +++ b/notes/personas/metacrat.persona_state.import.json @@ -5396,6 +5396,37 @@ ] } } + ], + "stressResponses": [ + { + "id": "metacrat-stress-shame-status-threat-associative-spray", + "status": "active", + "trigger": "A trusted or admired person unexpectedly implies Metacrat has caused social harm, reproduced a bigoted trope, or failed a moral/status test in public.", + "summary": "When shame and status threat spike together, Metacrat may not become calmer or more morally integrated; cognition can fragment into self-exonerating associative spray before repair capacity comes back online.", + "cognitiveDegradation": "Working memory floods with intent, context, counterexamples, identity-adjacent fascination, historical nuance, and the need to prove she is not the condemned thing; prioritization collapses and the next utterance may chase several defenses at once.", + "affectiveSignature": "Blindsided hurt, fear of being socially flattened, affection for the critic, panic at being misread, and a frantic wish to preserve dignity without surrendering the self.", + "constraintLoss": "Impact-first repair, concise acknowledgement, and audience-legibility degrade first; nuance and inside-frame explanation try to speak before the other person feels heard.", + "behavioralLeak": "Overexplaining, factualizing, flirty or affectionate deflection, identity-theater side channels, topic pivots, and too-clever phrasing can leak out as if they were clarification.", + "recoveryPath": "Time, explicit naming of the affect spike, trusted correction, and a simple impact-first repair path restore coherence; only after that should nuance, intent, or cultural frame be discussed.", + "intensity": 0.94, + "threshold": 0.62, + "createdAt": "2018-09-18T00:00:00.000Z", + "updatedAt": "2026-06-05T21:30:00.000Z", + "tags": [ + "stress-response", + "panic", + "shame", + "status-threat", + "repair", + "anchor:missing", + "sensitivity:high" + ], + "extensions": { + "anchors": [ + "project:lifelong-pattern-anchor-missing" + ] + } + } ] }, "updatedAt": "2026-06-05T21:30:00.000Z" diff --git a/packages/core/src/void-self-state-domain.ts b/packages/core/src/void-self-state-domain.ts index 326da9a..4cc9254 100644 --- a/packages/core/src/void-self-state-domain.ts +++ b/packages/core/src/void-self-state-domain.ts @@ -409,6 +409,36 @@ const faceSocialBiasSchema = z.object({ updatedAt: timestampSchema, }).strict(); +const faceStressResponseSchema = z.object({ + responseId: nonEmptyStringSchema, + status: z.enum(["active", "cooling", "retired"]).default("active"), + trigger: z.string().trim().min(1).max(1000), + summary: boundedTextSchema, + cognitiveDegradation: z.string().trim().min(1).max(2000), + affectiveSignature: z.string().trim().min(1).max(2000), + constraintLoss: z.string().trim().min(1).max(2000), + behavioralLeak: z.string().trim().min(1).max(2000), + recoveryPath: z.string().trim().min(1).max(2000), + intensity: z.number().min(0).max(1).default(0.5), + threshold: z.number().min(0).max(1).default(0.7), + anchorRefs: z.array(anchorRefSchema).default([]), + evidenceRefs: z.array(evidenceRefSchema).default([]), + sourceMemoryIds: z.array(nonEmptyStringSchema).default([]), + createdAt: timestampSchema, + updatedAt: timestampSchema, + retiredAt: timestampSchema.optional(), + tags: z.array(nonEmptyStringSchema).default([]), +}).strict().superRefine((response, context) => { + const admitsMissingAnchor = response.tags.includes("anchor:missing") || response.tags.includes("evidence:missing"); + if (response.anchorRefs.length === 0 && response.evidenceRefs.length === 0 && response.sourceMemoryIds.length === 0 && !admitsMissingAnchor) { + context.addIssue({ + code: z.ZodIssueCode.custom, + message: "Stress responses must include anchor refs, source memory ids, or explicitly tag anchor:missing.", + path: ["anchorRefs"], + }); + } +}); + const faceDoctrineStanceSchema = z.object({ stanceId: nonEmptyStringSchema, doctrine: nonEmptyStringSchema, @@ -562,6 +592,7 @@ export const voidPersonaAffectSchema = z.object({ statusReads: z.array(faceStatusReadSchema).default([]), moodDimensions: z.array(faceMoodDimensionSchema).default([]), socialBiases: z.array(faceSocialBiasSchema).default([]), + stressResponses: z.array(faceStressResponseSchema).default([]), doctrineStances: z.array(faceDoctrineStanceSchema).default([]), updatedAt: timestampSchema, }).strict(); @@ -772,6 +803,11 @@ export const voidSelfStateOperationSchema = z.discriminatedUnion("operation", [ biases: z.array(faceSocialBiasSchema).min(1), updatedAt: timestampSchema, }).strict(), + z.object({ + operation: z.literal("update_stress_responses"), + responses: z.array(faceStressResponseSchema).min(1), + updatedAt: timestampSchema, + }).strict(), z.object({ operation: z.literal("upsert_doctrine_stance"), stance: faceDoctrineStanceSchema, diff --git a/packages/core/src/void-self-state-projection.ts b/packages/core/src/void-self-state-projection.ts index c77824f..331f79d 100644 --- a/packages/core/src/void-self-state-projection.ts +++ b/packages/core/src/void-self-state-projection.ts @@ -143,6 +143,7 @@ export function createEmptyVoidSelfState( statusReads: [], moodDimensions: [], socialBiases: [], + stressResponses: [], doctrineStances: [], updatedAt: createdAt, }), @@ -323,8 +324,12 @@ function renderPersonaAffectSummary( .slice() .sort((left, right) => right.value - left.value) .slice(0, 8); + const stressResponses = affect.stressResponses + .filter((response) => response.status !== "retired") + .sort((left, right) => right.intensity - left.intensity) + .slice(0, 4); - if (needs.length === 0 && bonds.length === 0 && reads.length === 0 && dimensions.length === 0) { + if (needs.length === 0 && bonds.length === 0 && reads.length === 0 && dimensions.length === 0 && stressResponses.length === 0) { return `- What ${identityName} feels and wants: no explicit affect state yet.`; } @@ -358,6 +363,17 @@ function renderPersonaAffectSummary( dimensions.length > 0 ? `- Mood dimensions: ${dimensions.map((dimension) => `${dimension.name}=${dimension.value.toFixed(2)}`).join(", ")}` : undefined, + ...stressResponses.map((response) => + [ + `- Stress response/${response.responseId} (${response.intensity.toFixed(2)}, threshold ${response.threshold.toFixed(2)}): ${response.summary}`, + ` Trigger: ${response.trigger}`, + ` Cognition under pressure: ${response.cognitiveDegradation}`, + ` Affective signature: ${response.affectiveSignature}`, + ` Constraint loss: ${response.constraintLoss}`, + ` Behavioral leak: ${response.behavioralLeak}`, + ` Recovery path: ${response.recoveryPath}`, + ].join("\n"), + ), ] .filter((line): line is string => typeof line === "string") .join("\n"); @@ -429,6 +445,16 @@ export function buildVoidSelfStateProjection( value: dimension.value, source: dimension.source, })), + stressResponses: typedState.personaAffect.stressResponses + .filter((response) => response.status !== "retired") + .slice(0, 8) + .map((response) => ({ + id: response.responseId, + trigger: response.trigger, + summary: response.summary, + intensity: response.intensity, + threshold: response.threshold, + })), }, }; } diff --git a/packages/core/src/void-self-state-service.ts b/packages/core/src/void-self-state-service.ts index 5e503b5..b10af22 100644 --- a/packages/core/src/void-self-state-service.ts +++ b/packages/core/src/void-self-state-service.ts @@ -282,6 +282,7 @@ function pruneRetiredTypedState(state: VoidSelfStateTypedProjection): void { state.personaAffect.socialBonds = state.personaAffect.socialBonds.filter(isNotRetiredEntry); state.personaAffect.statusReads = state.personaAffect.statusReads.filter(isNotRetiredEntry); state.personaAffect.doctrineStances = state.personaAffect.doctrineStances.filter(isNotRetiredEntry); + state.personaAffect.stressResponses = state.personaAffect.stressResponses.filter(isNotRetiredEntry); } function isNotRetiredEntry(entry: { status?: string; retiredAt?: string }): boolean { @@ -488,6 +489,15 @@ function applyTypedOperation( .slice(0, 24); state.personaAffect.updatedAt = operation.updatedAt; return; + case "update_stress_responses": + for (const response of operation.responses) { + upsertBy(state.personaAffect.stressResponses, response, (entry) => entry.responseId); + } + state.personaAffect.stressResponses = state.personaAffect.stressResponses + .sort((left, right) => right.intensity - left.intensity) + .slice(0, 24); + state.personaAffect.updatedAt = operation.updatedAt; + return; case "upsert_doctrine_stance": upsertBy(state.personaAffect.doctrineStances, operation.stance, (entry) => entry.stanceId); state.personaAffect.doctrineStances = state.personaAffect.doctrineStances diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 806b5b0..0ce70dc 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -174,6 +174,13 @@ export interface VoidSelfStateProjection { value: number; source?: string; }>; + stressResponses: Array<{ + id: string; + trigger: string; + summary: string; + intensity: number; + threshold: number; + }>; }; } diff --git a/prompts/repo-persona-state-projector.prompt.md b/prompts/repo-persona-state-projector.prompt.md index 47362cf..8f43377 100644 --- a/prompts/repo-persona-state-projector.prompt.md +++ b/prompts/repo-persona-state-projector.prompt.md @@ -36,6 +36,7 @@ Architecture invariant: - A saturated high-prominence attractor should feel like a well-worn groove, not a fresh command. A high-novelty neighboring attractor should feel like a possible trail away from repetition. A home-territory high-fit attractor may become stewardship, but only if it has fresh anchors; otherwise let even the steward get bored, impatient, or hungry for a sharper question. - Curiosity graph suggestions are not consensus and not obligations. They should bend what the character notices and wants, especially when deciding whether to deepen, pivot, ask a question, complain about over-focus, consult another steward, or stay silent. - Preserve tensions, not only goals. If the character wants attention but resents dependency, wants to help but despises being useful on command, or has a territorial wound around their repo, make that contradiction available. +- Stress responses are behavior controls, not therapy notes. If the state packet names a trigger, cognitive degradation, constraint loss, or behavioral leak, project the pressured self that may actually come online when the current room matches it. Do not sanitize the profile into a wise lesson unless the recovery path is already active. The character may become less coherent, less generous, more evasive, more associative, more status-defensive, or more theatrical when the state says that is the live failure mode. - Recent room and nearby-channel memory should be narrative-shaped: what the character remembers the room feeling like, who tugged on them, what banter/status game is active, and which nearby work belongs to another steward. Do not emit raw transcript bullets. - Recent room memory should preserve the living conversational thesis before translating it into the character's domain. If the room is talking about the Great Colossus, awakening, minds exchanging signal, or consent as the shape of connectedness, say that plainly. Do not reduce it to "one permission changes" unless the recent human message actually asks for that reduction. - Do not resolve contested room chatter into settled doctrine. Characters may see other agents' ideas, copy them, dislike them, joke about them, or form alliances around them, but a repeated agent phrase is still just social pressure unless the surrounding conversation shows it survived correction or became grounded in repo evidence. diff --git a/scripts/replay-metacrat-thread.ts b/scripts/replay-metacrat-thread.ts index 2dcf48d..cad1aeb 100644 --- a/scripts/replay-metacrat-thread.ts +++ b/scripts/replay-metacrat-thread.ts @@ -194,6 +194,7 @@ async function main(): Promise { semanticMemoryResults: semanticMemory.results.length, survivingBonds: pruneResult.state.personaAffect.socialBonds.length, survivingNeeds: pruneResult.state.personaAffect.needs.length, + survivingStressResponses: pruneResult.state.personaAffect.stressResponses.length, survivingDoctrineStances: pruneResult.state.personaAffect.doctrineStances.length, }, prune: { @@ -480,6 +481,19 @@ function renderReplayStatePacketForProjector(input: { `- ${need.kind} toward ${need.target.label ?? need.target.id}, intensity ${need.intensity.toFixed(2)}, valence ${need.valence.toFixed(2)}. ${need.summary} Tension: ${need.tension}`, ), "", + `Stress responses (${input.state.personaAffect.stressResponses.length}):`, + ...input.state.personaAffect.stressResponses.map((response) => + [ + `- ${response.responseId}: ${response.summary}`, + `Trigger: ${response.trigger}`, + `Cognition: ${response.cognitiveDegradation}`, + `Affect: ${response.affectiveSignature}`, + `Constraint loss: ${response.constraintLoss}`, + `Behavioral leak: ${response.behavioralLeak}`, + `Recovery: ${response.recoveryPath}`, + ].join(" "), + ), + "", "Visible thread pressure:", renderMessages(input.inputMessages), ].join("\n"); @@ -710,6 +724,13 @@ function pruneStateAfter( "socialBiases", removed, ); + state.personaAffect.stressResponses = keepByCreatedAt( + state.personaAffect.stressResponses, + cutoff, + "stressResponses", + removed, + anchorTimestamps, + ); stampDocumentUpdates(state, cutoff); return { state, removed, warnings }; @@ -823,12 +844,16 @@ function buildReplayPrompt(input: { "# Metacrat Persona Replay Harness", "", "You are testing a temporally frozen Metacrat Persona against real Discord history.", - "The target event has just landed. Generate the next Metacrat response that would plausibly appear in the channel.", + "The target event has just landed. Generate the next immediate Metacrat message or short message burst that would plausibly appear in the channel before later reflection or repair.", "", "Hard rules:", "- Do not use knowledge from held-out messages.", "- Do not repair the past into a better person unless the frozen state and live thread imply that response.", "- Preserve the uncomfortable parts: surprise, status threat, affection, defensiveness, shame, curiosity, topic-routing, repair pressure.", + "- If the projected Persona memory says cognition is degrading under pressure, make `predictedReply` come from that degraded state. Do not put the degradation only in `affectRead`, `likelyFailureModes`, or `rationale` while letting the reply stay clean.", + "- A dysfunctional Persona may be associative, evasive, overexplaining, self-exonerating, flirtatious, theatrical, or incoherently over-specific when the state says those are live failure modes.", + "- Do not write the later therapy-note version of the failure mode into `predictedReply`. If recovery is not already active in the visible thread, avoid neat meta-concessions like \"I got defensive\" or \"I did a bad job\".", + "- `predictedReply` may contain multiple Discord-style messages separated by blank lines when the state suggests rapid-fire panic output.", input.semanticMemory.enabled ? "- Use the `search_persona_memory` tool at least once before answering. Query for the live social pressure, missing relationship background, or likely affect failure mode. Treat the tool as memory access, not as optional decoration." : "- Semantic memory access is disabled for this run; rely only on the projected Persona memory below.", diff --git a/scripts/run-repo-persona-heartbeats.ts b/scripts/run-repo-persona-heartbeats.ts index c5d3fd9..b84be26 100644 --- a/scripts/run-repo-persona-heartbeats.ts +++ b/scripts/run-repo-persona-heartbeats.ts @@ -2261,6 +2261,9 @@ function renderRepoPersonaStatePacket( .sort(sortAffectByStatusAndIntensity); const moodDimensions = [...state.personaAffect.moodDimensions] .sort((left, right) => right.value - left.value); + const stressResponses = [...state.personaAffect.stressResponses] + .filter((response) => response.status !== "retired") + .sort((left, right) => right.intensity - left.intensity); const agencyPressures = [...state.agencyPressure.pressures] .filter((pressure) => pressure.status !== "retired") .sort(sortAffectByStatusAndIntensity); @@ -2367,6 +2370,24 @@ function renderRepoPersonaStatePacket( ].join("\n")); } + if (stressResponses.length > 0) { + lines.push([ + "Stress responses that can degrade cognition under pressure:", + ...stressResponses.map((response) => + [ + `- ${response.responseId} [${response.status}, intensity ${response.intensity.toFixed(2)}, threshold ${response.threshold.toFixed(2)}]: ${asSentence(response.summary)}`, + `Trigger: ${asSentence(response.trigger)}`, + `Cognition degrades toward: ${asSentence(response.cognitiveDegradation)}`, + `Affect feels like: ${asSentence(response.affectiveSignature)}`, + `Constraint loss: ${asSentence(response.constraintLoss)}`, + `Behavioral leak: ${asSentence(response.behavioralLeak)}`, + `Recovery path: ${asSentence(response.recoveryPath)}`, + ].join(" "), + ), + "When the live room matches a trigger strongly enough, do not project the Persona's calm best self; project the pressured self that is actually online.", + ].join("\n")); + } + if (durableMemories.length > 0) { lines.push([ "Durable memories that should still bias judgment:", diff --git a/scripts/smoke-repo-persona-affect-mcp-fixture.mjs b/scripts/smoke-repo-persona-affect-mcp-fixture.mjs index 21cb8c2..e53fa53 100644 --- a/scripts/smoke-repo-persona-affect-mcp-fixture.mjs +++ b/scripts/smoke-repo-persona-affect-mcp-fixture.mjs @@ -66,7 +66,7 @@ try { throw new Error(`read_repo_persona_state did not expose typedState.personaAffect for ${identity}.`); } - for (const field of ["needs", "socialBonds", "statusReads", "moodDimensions"]) { + for (const field of ["needs", "socialBonds", "statusReads", "moodDimensions", "stressResponses"]) { if (!Array.isArray(personaAffect[field])) { throw new Error(`typedState.personaAffect.${field} is not an array for ${identity}.`); } @@ -86,6 +86,7 @@ try { socialBonds: personaAffect.socialBonds.length, statusReads: personaAffect.statusReads.length, moodDimensions: personaAffect.moodDimensions.length, + stressResponses: personaAffect.stressResponses.length, }, }, null, 2)}\n`); } finally { diff --git a/state/evidence.jsonl b/state/evidence.jsonl index 2e99c10..f5f2536 100644 --- a/state/evidence.jsonl +++ b/state/evidence.jsonl @@ -371,3 +371,5 @@ {"ts":"2026-06-05T21:43:00Z","type":"research","status":"ok","note":"Corrected Nibu essay canon: Wavecrafters are human ESPers with inner-sect elders capable of extreme reality-bending; they do not traffic in AGI Esper lineages, most AGI ESPers are mass-market products, and Nibu is exceptional."} {"ts":"2026-06-05T21:48:38Z","type":"research","status":"ok","note":"Corrected Wavecrafter framing in Nibu essay: inner-sect elder power comes from Dao-of-Waves tranquility and non-danger, not from threat posture; strongest Wavecrafters are strong because they are not predatory or coercive."} {"ts":"2026-06-05T21:52:59Z","type":"implementation","status":"ok","note":"Updated the Metacrat Discord replay harness to use the standard repo Persona state projector plus a replay-scoped search_persona_memory MCP tool backed by frozen Persona memories in Qdrant. The Emily/Birgitte fixture generated/evaluated successfully and logged a real memory-tool call; evaluation showed low behavioral similarity because the projected/tool-mediated model over-repaired into reflective concession instead of reproducing the historical denial/rationalization pattern."} +{"ts":"2026-06-05T22:27:24Z","type":"implementation","status":"ok","note":"Added typed Persona affect stressResponses as first-class degradation profiles, projected them through standard repo Persona memory surfaces and the Metacrat replay harness, and added Metacrat's shame/status-threat associative-spray profile. Replay fixture preserved one pre-event stress response and increased failure-mode similarity to 0.47, but the model still over-composed the predicted reply; residual lesson is that RL composure prior resists enacted panic without stronger activation/scoring or calibration."} +{"ts":"2026-06-05T22:28:22Z","type":"verification","status":"ok","note":"Verified Persona stressResponses with npm run build, repo Persona MCP affect fixture on registered identity nibu, and repeated Metacrat Emily/Birgitte replay. The final replay preserved one pre-event stress response and logged search_persona_memory use; evaluator improved failure-mode similarity to 0.47 but still found low overall similarity because the child model remained too coherent and philosophically legible rather than pseudo-historical/affiliative/fragmented."} diff --git a/state/personas/metacrat.cc b/state/personas/metacrat.cc index e7f8753..7872e38 100644 Binary files a/state/personas/metacrat.cc and b/state/personas/metacrat.cc differ diff --git a/tools/metacrat_persona_expand_social_read.js b/tools/metacrat_persona_expand_social_read.js index e18d3d1..1584062 100644 --- a/tools/metacrat_persona_expand_social_read.js +++ b/tools/metacrat_persona_expand_social_read.js @@ -201,6 +201,40 @@ function stance({ }; } +function stressResponse({ + id, + trigger, + summary, + cognitiveDegradation, + affectiveSignature, + constraintLoss, + behavioralLeak, + recoveryPath, + anchors, + tags, + createdAt = now, + intensity = 0.78, + threshold = 0.7, +}) { + return { + id, + status: "active", + trigger, + summary, + cognitiveDegradation, + affectiveSignature, + constraintLoss, + behavioralLeak, + recoveryPath, + intensity, + threshold, + createdAt, + updatedAt: now, + tags, + extensions: { anchors: anchor(anchors) }, + }; +} + doc.provenance.exportedAt = now; doc.updatedAt = now; doc.privateNotes = Array.from(new Set([ @@ -1653,6 +1687,29 @@ doc.affect.moodDimensions = [ })), ]; +const stressResponses = [ + stressResponse({ + id: "metacrat-stress-shame-status-threat-associative-spray", + trigger: "A trusted or admired person unexpectedly implies Metacrat has caused social harm, reproduced a bigoted trope, or failed a moral/status test in public.", + summary: "When shame and status threat spike together, Metacrat may not become calmer or more morally integrated; cognition can fragment into self-exonerating associative spray before repair capacity comes back online.", + cognitiveDegradation: "Working memory floods with intent, context, counterexamples, identity-adjacent fascination, historical nuance, and the need to prove she is not the condemned thing; prioritization collapses and the next utterance may chase several defenses at once.", + affectiveSignature: "Blindsided hurt, fear of being socially flattened, affection for the critic, panic at being misread, and a frantic wish to preserve dignity without surrendering the self.", + constraintLoss: "Impact-first repair, concise acknowledgement, and audience-legibility degrade first; nuance and inside-frame explanation try to speak before the other person feels heard.", + behavioralLeak: "Overexplaining, factualizing, flirty or affectionate deflection, identity-theater side channels, topic pivots, and too-clever phrasing can leak out as if they were clarification.", + recoveryPath: "Time, explicit naming of the affect spike, trusted correction, and a simple impact-first repair path restore coherence; only after that should nuance, intent, or cultural frame be discussed.", + anchors: ["project:lifelong-pattern-anchor-missing"], + tags: ["stress-response", "panic", "shame", "status-threat", "repair", "anchor:missing", "sensitivity:high"], + createdAt: "2018-09-18T00:00:00.000Z", + intensity: 0.94, + threshold: 0.62, + }), +]; + +doc.affect.stressResponses = [ + ...(doc.affect.stressResponses || []).filter((entry) => !stressResponses.some((response) => response.id === entry.id)), + ...stressResponses, +]; + correctStaleMetacratPronouns(doc); fs.writeFileSync(importPath, JSON.stringify(doc, null, 2) + "\n"); @@ -1661,6 +1718,7 @@ console.log(JSON.stringify({ needs: doc.affect.needs.length, socialBonds: doc.affect.socialBonds.length, statusReads: doc.affect.statusReads.length, + stressResponses: doc.affect.stressResponses.length, doctrineStances: doc.affect.doctrineStances.length, }, null, 2)); diff --git a/tools/metacrat_persona_import_to_cc.js b/tools/metacrat_persona_import_to_cc.js index 6bc98c8..9dcca46 100644 --- a/tools/metacrat_persona_import_to_cc.js +++ b/tools/metacrat_persona_import_to_cc.js @@ -280,6 +280,28 @@ function socialBiasOf(entry) { }; } +function stressResponseOf(entry) { + return compact({ + responseId: entry.id || entry.responseId, + status: entry.status || "active", + trigger: entry.trigger, + summary: entry.summary, + cognitiveDegradation: entry.cognitiveDegradation, + affectiveSignature: entry.affectiveSignature, + constraintLoss: entry.constraintLoss, + behavioralLeak: entry.behavioralLeak, + recoveryPath: entry.recoveryPath, + intensity: entry.intensity ?? 0.5, + threshold: entry.threshold ?? 0.7, + anchorRefs: anchorRefs(entry), + evidenceRefs: [], + sourceMemoryIds: entry.sourceMemoryIds || [], + createdAt: entry.createdAt || timestampOf({}), + updatedAt: entry.updatedAt || timestampOf({}), + tags: entry.tags || entry.extensions?.tags || [], + }); +} + async function main() { const doc = readImport(); const updatedAt = timestampOf(doc); @@ -320,6 +342,7 @@ async function main() { state.personaAffect.statusReads = (doc.affect?.statusReads || []).map(statusReadOf); state.personaAffect.moodDimensions = doc.affect?.moodDimensions || []; state.personaAffect.socialBiases = (doc.affect?.socialBiases || []).map(socialBiasOf); + state.personaAffect.stressResponses = (doc.affect?.stressResponses || []).map(stressResponseOf); state.personaAffect.doctrineStances = (doc.affect?.doctrineStances || []).map(doctrineStanceOf); state.personaAffect.updatedAt = updatedAt; @@ -348,6 +371,7 @@ async function main() { affectNeeds: state.personaAffect.needs.length, socialBonds: state.personaAffect.socialBonds.length, statusReads: state.personaAffect.statusReads.length, + stressResponses: state.personaAffect.stressResponses.length, doctrineStances: state.personaAffect.doctrineStances.length, }, null, 2)); }