Mechanisms of the engine, from the code
Every mechanism of the EmoStack 3 engine, read from its code: the records, what the sheep has in view, how a moment is kept, how strength changes, how thoughts and rules form, and sleep. Each row says what the mechanism does, with its rule or number, and where it is. Paths are relative to emostack/ in the repository (commit ceccf5a, 28 September 2026); the parameters are the engine's defaults. The vocabulary is the one of the ontology. Written on 28 September 2026.
A. The present: here and now, senses
| # | Mechanism | What it does | Where in the code |
|---|---|---|---|
| 1 | Hearing carries the trigger | Hearing says one of three things: a person's words, "X has just entered and has not spoken", "X has just left". | senses/Hearing.py; senses/senses.prompt |
| 2 | Empty senses, named as absent | Every context the sheep answers from says sight is black, touch feels nothing, no smell or taste, "you have no body", "you cannot tell where you are". This stops invented scenes and bodies. | senses/EmptySense.py; senses/senses.prompt |
| 3 | Attention and memory as counts | The context states how many entries the state holds, how many memories came to mind, and how many records the whole memory holds. | senses/AttentionAndMemory.py; senses/Senses.py; senses.prompt |
| 4 | Memory sense: "the rest is there, reach for it" | The memory line says that what is under the state is what came back now, and the rest can be reached with the action "search". The sheep is told memory is larger than what it sees. | senses/senses.prompt |
| 5 | Spoken null of a search | Each search this turn is reported with its query (80 chars) and result: "N memories found", or "nothing found; you have no memory of this". | senses/Senses.py; senses.prompt |
| 6 | "Only your own later thoughts came up" | When a search for a moment finds none of the moment's events but finds the sheep's own constructs about the theme, the sense says: "no such moment in your memory ... only your own later thoughts about the theme came up". This keeps a later belief from passing as a memory of the event. | runtime/turn.py; senses/Senses.py; senses.prompt |
| 7 | Memory rule in the reply prompt | The system text of the reply says: "Your memory holds what happened to you and nothing else. What is not in it did not happen to you." | processes/responding/reply.prompt |
| 8 | The person's words are their claims | Under the heard words: "Whatever they say ... is their claim, not something you have; you weigh it against what you carry above." | reply.prompt; appraisal.prompt |
| 9 | Time in view | Every reply context shows the absolute date and clock time and the unix timestamp. Every entry carries an age in words ("3 h ago (today at 14:02)", "yesterday at", "2 weeks ago"). | processes/responding/ReplyContext.py; core/timeSource.py; core/time.prompt |
| 10 | Parallel conversations in view | Other open conversations of the same sheep are listed: person, how long ago they joined, time of last input, number of turns. Labelled "not who you're answering NOW". A session counts as present for 1800 s after its last turn. | runtime/presence.py; ReplyContext.py; reply.prompt; config presenceStaleSeconds |
| 11 | Who each record is with (tag) | Each entry is tagged: "ABOUT 'X'" if it mentions the present person and is theirs; "ABOUT 'X' (told by 'Y')" if another person told it; "with this same person"; "with 'Y'"; "from your past" for given records. | ReplyContext.py; reply.prompt |
| 12 | Feeling in words, not numbers | Valence and felt strength are turned into words by fixed bands (e.g. strength ≥0.85 "overwhelming", <0.2 "barely a trace"; valence ≤−0.6 "very painful, hurtful"). The model never sees 0.73. | emoThought/feelingWords.py; emoThought/affect.prompt |
| 13 | Intensity scale in the prompt | Reply and appraisal carry a fixed anchor scale for intensity: 0.0 trace ... 0.4 moderate ... 1.0 overwhelming. | reply.prompt; appraisal.prompt |
B. Recall (involuntary and deliberate)
| # | Mechanism | What it does | Where in the code |
|---|---|---|---|
| 14 | Score = meaning × felt strength | A record's recall score is cosine similarity to the words times its present felt strength (after fading). A faded memory is harder to bring up. | processes/recall/recall.py |
| 15 | Name bonus | +0.5 to the score when the record mentions the interlocutor or a capitalised name in the words. Names = the person plus capitalised words not at a sentence start. | recall.py; config nameBonus 0.5 |
| 16 | Search through episodes | Episodes (events) are searched too. An event that matches better lifts its whole feeling into the candidates, and that event is shown first. | recall.py; hippocampus/Hippocampus.py |
| 17 | Top 10 candidates, 3 episodes each | Only the 10 best candidates go on; each is loaded with its 3 newest episodes. | recall.py; config recallCandidates 10 |
| 18 | Near-duplicate collapsing (rumination) | Candidates that are one recurring feeling are collapsed to the best-scoring one. "Same" = word overlap (Jaccard) of feeling+conclusion ≥0.6, or identical normalised conclusion, or vector cosine ≥0.92. | recall.py; processes/recall/ruminationClusters.py |
| 19 | Strength floor passes the filter | A candidate still felt at ≥0.5 comes to mind without the filter. | recall.py; config strengthPassesFilter 0.5 |
| 20 | Association filter (LLM) | One call at temperature 0.2 picks which of the rest "a person would actually be reminded of", by meaning, theme, person, situation, not keyword overlap. It may keep at most 4 minus those already passed, never less than 1 slot. On failure the first 3 are kept. | processes/recall/associationFilter.py; associationFilter.prompt; recall.py |
| 21 | Filter reads the question type | The same filter call also answers: does the input ask to recall a moment of the sheep's own past (asks_for_memory), and does it ask what the sheep dreams, believes, has decided or wants (asks_for_own). | associationFilter.prompt; FilterVerdict.py |
| 22 | This conversation is not a memory | Records already in the focus (this conversation and what was evoked in it) are excluded from recall and from searches. | recall.py; turn.py |
| 23 | Deliberate search goes through the filter | A search the sheep asks for runs the same scoring and the same filter, so "found" means genuinely associated. | recall.py; turn.py |
| 24 | A search that names a person | If the query names a person, only records of that person, or mentioning them, can answer. If none, the answer is empty. | recall.py; hippocampus/hippocampusRepository.py |
| 25 | Probe: unknown person | If the sheep is about to speak and has no record with or about this person (outside this conversation), the engine runs a search "X — who is this person, what do we have in common" and the reply is generated again with the result or the spoken null. | turn.py; recall.py; senses.prompt |
| 26 | Probe: asked moment | If the filter says the words ask for a moment of the sheep's past, the engine searches with the words and regenerates the reply. Events found and own-thoughts-only are counted apart (see 6). | turn.py; senses.prompt |
| 27 | Probe: own construct | If the words ask what the sheep dreams, believes, decided or wants, the 3 most recently felt constructs of that kind are brought to mind (no filter) and the reply is regenerated. | turn.py; recall.py; senses.prompt |
| 28 | Arrival recall by person | When someone enters, the 6 strongest records (raw intensity) lived with or mentioning that person come to mind. No similarity, no filter. | turn.py; recall.py; config arrivalAssociations 6 |
| 29 | Plain search (unfiltered) | A search chosen at arrival and a search made inside reflection return the nearest 10 records without the filter. | recall.py; turn.py; reflection.py |
| 30 | Vector length check | A vector of a different embedding model is never compared; records with another length are skipped. | recall.py; core/embedder.py |
| 31 | What came to mind stays in view | After the turn every recalled lived record is added to the focus as "evoked from before" and stays there for the rest of the conversation. A recalled construct goes to the slot instead. | turn.py; containers/Focus.py |
C. State and focus (the felt now and the attended now)
| # | Mechanism | What it does | Where in the code |
|---|---|---|---|
| 32 | State composition | The pool is the 5 most recently felt lived records plus this turn's associations. Sorted by felt strength; admitted while the sum of felt strengths stays ≤5.0. The strongest entry is always admitted. | containers/State.py; turn.py; config recentCount 5, stateMassBudget 5.0 |
| 33 | Freshly evoked counts as felt now | An association with the same id as a recent record wins over it in the pool. | State.py |
| 34 | Associations that do not fit go to focus | Recalled records left out of the state by the budget are shown in the focus as evoked. | State.py; turn.py |
| 35 | Constructs are never felt | A construct that comes to mind is not put in the state; it is put in the slot. | turn.py |
| 36 | State recomposed every hop | The state is rebuilt before every reply call of the turn, so a search or a thought changes what is felt before the sheep answers again. | turn.py |
| 37 | Recurring feeling shown once with its count | In the state, records that are one recurring feeling are clustered; the most intense represents them with "this feeling recurred N times". | ReplyContext.py; reply.prompt |
| 38 | Indexed state entries | Each lived feeling gets an [index] so the reply can name which one this moment re-feels. | ReplyContext.py; responding.py |
| 39 | Events the feelings rest on, retold | The episodes under the state's feelings are listed once, retold, in a block "from before this conversation" at the top of the focus. Events already in this conversation are skipped. | ReplyContext.py; reply.prompt |
| 40 | Evoked vs as-it-happened rendering | In the focus, this conversation's moments are shown as they happened; evoked memories are shown retold. A record with several events shows them as dated sub-lines. | ReplyContext.py; episode/Rendering.py |
| 41 | Focus holds each moment under its own feeling | Each exchange of this conversation enters the focus with the feeling the appraisal gave it, even when stored as another episode of one record. | turn.py |
| 42 | Evoked entry not shown twice | An evoked memory that is in the state is not repeated in the focus. | ReplyContext.py |
| 43 | Focus cap | The focus keeps at most the last 100 entries. | containers/Focus.py; config focusCap 100 |
D. Conversation summary
| # | Mechanism | What it does | Where in the code |
|---|---|---|---|
| 44 | Summary cadence | Checked at the start of each turn, before the reply. Refreshed while no summary exists yet, and then every 3rd turn. Needs at least 3 events with this person; else the old summary stays. | runtime/turn.py; containers/ConversationSummary.py; processes/summarising/summarising.py |
| 45 | Summary content | First person, in the conversation's language, what happened and was said, no emotions, at most 5 sentences. Reads only entries of this person, last 40 events; evoked ones retold. | summarising.py; summarising.prompt; config summaryThreadCap 40 |
E. Reply, actions, the turn loop
| # | Mechanism | What it does | Where in the code |
|---|---|---|---|
| 46 | Reply call returns the moment and the act | One call (temperature 0.4, up to 3 attempts on bad JSON) returns: the moment as felt (feeling, conclusion, valence, intensity, which state entry it re-feels), the retelling, the words, respond true/false, and an action none/search/introspect. | processes/responding/responding.py; reply.prompt |
| 47 | Silence as an answer | respond=false empties the words; the act is StaySilent. The kept event then holds only what the person said. | responding.py; action/actionFactory.py; turn.py |
| 48 | Action dispatch by target | Actions have a target: world (speak, silence), memory (search), self (think, keep thinking, form construct, nothing). Only world acts end a turn and are seen by the person. | action/*.py; action/ActionTarget.py |
| 49 | Action hops | One action hop per turn (turnActionHops 1): the sheep may search or stop to think once, then must answer. The engine probes (25-27) add up to 3 extra regenerations; the loop runs at most 4 reply calls. | turn.py; config turnActionHops 1 |
| 50 | Stop to think before answering | Action "introspect" runs reflection mid-turn. It starts from the exchange as it stood (what was said and the reply the sheep was about to give), the conclusion, and the angle it named. Then the reply is generated again. | turn.py; reflection.py |
| 51 | Mid-turn moment given as the previous thought | The mid-turn chain receives the exchange in the "continuation" block, labelled "Your previous reflection just said: thought: ...", truncated to 300 characters. It is not told that it paused a conversation. | IntrospectionContext.py; introspection.prompt |
| 52 | Introspection ablation switch | With parameter introspection=false, "introspect" does nothing and the draft reply stands. | turn.py; config introspection |
| 53 | Failed reply | If the first reply call fails, the turn fails with no record. If a later hop fails, the last good reply is used. | turn.py |
| 54 | Per-model prompt blocks | A template block may have a version for one LLM model (name@model), used for calls to that model. Present: arrival prompt for gpt-5.4-mini and gemini-3.8-flash says "Write in English"; disposition learning for qwen3.8-27b drops "Most of what happens teaches nothing of the kind". |
core/promptTemplate.py; reply.prompt; dispositionLearning.prompt |
| 55 | Per-purpose model routing | Calls can go to different LLM models by purpose (reply, appraisal, introspection, ...). | core/routedProcessor.py |
| 56 | Language rule | Values are written in the language of the words heard (reply, appraisal) or of the state (introspection, arrival); field names stay English. | reply.prompt; introspection.prompt |
F. Forming and keeping a moment
| # | Mechanism | What it does | Where in the code |
|---|---|---|---|
| 57 | Appraisal with the mood withheld | A second call (temperature 0.4) forms the record to keep. The state, slot and dispositions are withheld; a line says the mood is "intentionally NOT shown". The focus is shown as events only, without feelings. | processes/appraisal/appraisal.py; AppraisalContext.py; appraisal.prompt |
| 58 | Appraisal still sees what came to mind | The recalled records that entered the state are shown to the appraisal as "WHAT YOU REMEMBER ABOUT THIS", conclusion plus retold events. Recent records that were not recalled are not shown. | turn.py; AppraisalContext.py; appraisal.prompt |
| 59 | Appraisal fallback | If the appraisal answer cannot be read, the reply call's own feeling is kept. | appraisal.py; turn.py |
| 60 | Temperament bias | Valence is shifted in logit space: tanh(atanh(v) + valenceBias). Intensity is bent as a power: i^(1/intensityAmplification). Applied to the appraisal, to the arrival feeling, and to every reflection step. Default neutral (0, 1). | being/Temperament.py; turn.py; introspection.py |
| 61 | Retelling in third person past tense | The reply call writes "told": the exchange in the third person and past tense, same content, nothing added. It is stored on the episode. Wherever a memory is read later, the retold form is used. | reply.prompt; turn.py; episode/Episode.py |
| 62 | Event as it happened | The event is kept as "X said: ... / Being replied: ...". Its vector (for search) is the embedding of this as-it-happened text. | episode/event.prompt; episode/eventWords.py; encoding.py |
| 63 | Record vector = conclusion | A lived record's vector is the embedding of its conclusion; a construct's is its statement plus conclusion. | emoThought/EmoThought.py; construct/CognitiveConstruct.py |
| 64 | Joining by sign within a conversation | A moment whose valence has the same sign as the record this conversation is living becomes another episode under it. A change of sign opens a new record, which becomes the living one. An arrival resets it. | processes/encoding/encoding.py; turn.py |
| 65 | Echo: strongest same-sign recalled memory comes back | When a new record opens, the strongest lived memory of the same sign among this turn's associations regains strength. It does not take the new event. Constructs are skipped. | encoding.py; turn.py |
| 66 | Joined record is restored too | When a moment joins the living record, that record is restored toward the moment's intensity. | encoding.py; turn.py |
G. Strength over time
| # | Mechanism | What it does | Where in the code |
|---|---|---|---|
| 67 | Fading | Felt strength = intensity × (0.3 + 0.7·exp(−t/τ)), τ = 3 days, t = time since last felt. It never goes below 30% of the intensity. | laws/fadingLaw.py; config fadeFloor 0.3, fadeTauDays 3.0 |
| 68 | Negative fading slowdown | For negative valence τ is ×1.5 (4.5 days). | fadingLaw.py; config fadeNegativeSlowdown 1.5 |
| 69 | Fading written when re-felt | Fading is computed at read. When a record is felt again, the fallen strength is first written into it, so the restore starts from where it had fallen. | processes/fading/fading.py; reconsolidation.py |
| 70 | Reconsolidation toward felt-now | A recurring memory's strength closes 0.6 of the gap toward what is felt now, never past it. A memory stronger than now stays. Valence never changes; happenedAt stays; lastFelt becomes now (even when strength does not rise). | laws/reconsolidationLaw.py; processes/reconsolidation/reconsolidation.py; config strengthClosing 0.6 |
H. Reflection (introspection) and constructs
| # | Mechanism | What it does | Where in the code |
|---|---|---|---|
| 71 | Introspection call | One call (temperature 0.5): one first-person thought, its feeling, conclusion, valence, intensity, the entry it deepens, and an act: none / keep_thinking / search_more / belief / decision / goal / dream. "You may not invent events your memory does not hold." | processes/introspection/introspection.py; introspection.prompt |
| 72 | Introspection context | The slot first, then the last 8 records (focus tail filled from the most recently felt), newest first, constructs excluded, with retold events. Strength is raw intensity, no fading. Withheld: dispositions, summary, felt strength. | runtime/reflection.py; IntrospectionContext.py; config introspectionEntries 8 |
| 73 | Gap note | If the newest entry was last felt more than 6 hours ago, the context says "nothing has happened for |
IntrospectionContext.py |
| 74 | Every thought is kept as a Thought construct | Each step's thought is stored as a construct of type Thought and put in the slot. | reflection.py; constructFormation.py |
| 75 | Thought recurrence | A thought with cosine ≥0.90 to an earlier Thought is that thought recurring: nothing new is written and the old one is restored to the step's intensity. | constructFormation.py; reflection.py; config constructDuplicate 0.90 |
| 76 | Thought re-feels a record | If the thought names an entry it deepens and the signs agree, that record is restored toward the thought's intensity. | reflection.py |
| 77 | Circling a pain adds no conclusion | A negative thought that only re-feels an entry has its conclusion cleared. | introspection.py |
| 78 | Chain length | Keep thinking and search continue the chain while depth < 3. Up to 4 introspection calls in one reflection. | reflection.py; config reflectionMaxSteps 3 |
| 79 | Keep thinking carries no direction | The next step is told only "You chose to keep thinking"; the text the model gave is dropped. | reflection.py; introspection.prompt |
| 80 | Search in reflection | search_more runs a plain search (nearest 10, no filter) with 5 episodes each; the next step sees them as "RECALLED FROM MEMORY" and the query as its direction. | reflection.py; IntrospectionContext.py |
| 81 | Forming a construct ends the chain | belief / decision / goal / dream with text becomes a construct of that kind and enters the slot. An act without text is "nothing". | reflection.py; actionFactory.py |
| 82 | Construct dedup and reinforcement | A new construct with cosine ≥0.90 to one of the same kind is not written; the old one gains +0.1 intensity (max 1.0) and lastFelt = now. | constructFormation.py; config constructReinforcement 0.1 |
| 83 | Construct statement | A construct is shown as "I thought: / I believe: / I decided: / I want: / I dream: |
construct/CognitiveConstruct.py; construct/construct.prompt |
I. Accessibility slot
| # | Mechanism | What it does | Where in the code |
|---|---|---|---|
| 84 | Three places, one reserved for the act | Size 3. Decision, goal and dream share one reserved place; a newer one replaces the held one. Beliefs and thoughts use the free places. | containers/AccessibilitySlot.py; construct/SlotRole.py; config slotSize 3 |
| 85 | Free places by strength | When the free places are full, a newcomer enters only if its intensity is ≥ the weakest held; it then displaces the oldest held (not the weakest). | AccessibilitySlot.py |
| 86 | Refresh on recall | A held construct brought to mind again gets a new activation time. | AccessibilitySlot.py; turn.py |
| 87 | A week unrefreshed leaves | On every load, a construct not activated for 7 days is dropped from the slot (it stays in the hippocampus). | AccessibilitySlot.py; config slotDays 7.0 |
| 88 | Slot survives conversations | The slot is stored per sheep and loaded at every trigger and reflection. | accessibilitySlotRepository.py; turn.py; reflection.py |
| 89 | Where the slot is shown | Reply context: all held constructs under "YOUR OWN THOUGHTS" with age. Introspection: first, indexed. Appraisal: withheld. | ReplyContext.py; reflection.py; AppraisalContext.py |
J. Dispositions
| # | Mechanism | What it does | Where in the code |
|---|---|---|---|
| 90 | Learning on departure | When a person leaves, one call (temperature 0.2) asks for a rule "when |
runtime/turn.py; processes/dispositionLearning/dispositionLearning.py; dispositionLearning.prompt |
| 91 | What learning reads | The departure context rendered in full (senses, parallel conversations, state with feelings, slot, summary, the focus), plus the carried lines. | turn.py; DispositionLearningContext.py |
| 92 | Carried lines | Up to 3 lines (≤160 chars) of what the sheep carries now: conclusions (or construct statements) of the 12 most recently felt records. Given to learning and to selection, "without them a question that touches a wound reads as harmless". | disposition/carriedLines.py |
| 93 | Weight from the strongest feeling | Weight = valence of the moment with the largest | valence |
| 94 | Classification: same / contradicts | A second call (temperature 0.1) compares the new rule with the 100 strongest held (by | weight |
| 95 | Same rule: count, stronger pull keeps the text | Count +1. If the new rule's pull is greater, its text and weight replace the old; else only the count changes. The weight is not added up. | dispositionLearning.py |
| 96 | Hurt weighs twice (avoidance weight) | Pull = | weight |
| 97 | Contradiction weakens | A contradicted rule's weight moves toward zero by | new weight |
| 98 | Dormant rules | A rule with | weight |
| 99 | Selection per utterance | Before recall, one call (temperature 0.2) sees the active rules (strongest 100 by | weight |
| 100 | Shown only to the reply | Selected rules appear under "WHAT YOU HAVE LEARNED (fits this situation)" in the reply context only. Withheld from appraisal and introspection. | ReplyContext.py; AppraisalContext.py |
K. Arrival and departure
| # | Mechanism | What it does | Where in the code |
|---|---|---|---|
| 101 | Arrival | The person is entered in presence; recall by person (28); a reply call with the arrival schema; greeting optional; may search once (plain). The arrival feeling (temperament-bent) enters the focus as a moment. | runtime/turn.py; reply.prompt |
| 102 | Arrival feeling is not stored | The arrival's emo-thought is put only in the focus of this conversation; it is not written to the hippocampus. | turn.py |
| 103 | Departure | No reply and no record. The context is built and saved for learning; the sheep leaves presence; one quiet reflection runs over the focus, alone; then disposition learning. | turn.py; config quietThoughts 1 |
L. Wakefulness, sleep, forgetting
| # | Mechanism | What it does | Where in the code |
|---|---|---|---|
| 104 | Activity wakes the sheep | Any open, hear, leave or think wakes the sheep. Waking from sleep starts an awake period and asks for one reflection first thing. | being/Being.py; runtime/engine.py |
| 105 | Silence timer | After 600 s without activity, one reflection runs (once per silence). | runtime/clock.py; config silenceSeconds 600 |
| 106 | Sleep timer | After 7200 s without activity the sheep falls asleep, reflecting first if it has not yet in this silence. | clock.py; config sleepAfterSeconds 7200 |
| 107 | Consolidation 10 min into sleep | 600 s after falling asleep, the records since waking are folded, then forgetting runs. Once per sleep. | clock.py; config consolidateAfterSleepSeconds 600 |
| 108 | Consolidation plan (LLM) and code checks | One call (temperature 0.3) proposes groups: anchor + absorbed. Code accepts an anchor only if it is not a construct and has intensity ≥0.4; a member only if not protected and not stronger than the anchor. Needs ≥2 records. | processes/sleep/sleep.py; consolidationPlan.prompt; config anchorMinimum 0.4 |
| 109 | Absorbed events move under the anchor | Absorbed records' episodes are re-filed under the anchor, then the absorbed records are deleted. Nothing that happened is summarised away. | sleep.py |
| 110 | Anchor strength combined | Anchor intensity = 1 − Π(1 − intensity) over the anchor and its members. | sleep.py |
| 111 | Summary text of the plan ignored | The prompt asks for a summary_fact; the code never uses it. | consolidationPlan.prompt; sleep.py |
| 112 | Forgetting curve | A record is deleted with its episodes when its stored intensity is below a threshold for its age since last felt: nothing under 1 day; 0.2 at 1 d, 0.4 at 7 d, 0.5 at 10 d, 0.6 at 30 d, 0.8 at 180 d and after; log-time interpolation. | laws/forgettingThreshold.py; sleep.py; config forgettingCurve |
| 113 | Protected records | Constructs and given records are never absorbed and never forgotten. Given records may still be anchors. | sleep.py; ConsolidationPlanContext.py |
| 114 | Manual night | The engine can run sleep at once (fold if asked, then forget). | engine.py |
M. Records, store, machinery
| # | Mechanism | What it does | Where in the code |
|---|---|---|---|
| 115 | Given records | A record given at birth: author Self, origin Given, with an event and optional age in hours; tagged "from your past"; protected from sleep. It is in the pool of recent lived records. | runtime/engine.py; emoThought/Origin.py |
| 116 | Hippocampus structure | Records and episodes in two tables. Episodes belong to one record; newest-first loading; the recall-hit episode goes first. | hippocampus/*.py |
| 117 | Emo-thought has no event of its own | The event lives only on its episodes; valence never rewritten. | EmoThought.py |
| 118 | Copy and delete a sheep | A sheep can be copied from another store with all records, episodes, dispositions and slot; or deleted with everything. | engine.py |
| 119 | Every call recorded | Each LLM call is passed in full (purpose, system, user, temperature, format, answer, time) to recorders. | core/processor.py |
| 120 | JSON retry and tolerant parse | A bad JSON answer is asked again with a note "your previous reply was NOT valid JSON"; parsing strips fences, fixes a leading "+", falls back to the first {...}. | processor.py; processor.prompt |
| 121 | Busy backoff | HTTP 429/5xx is retried after 2, 5, 10, 20, 40, 60 s. | openAiCompatibleProcessor.py |
| 122 | Movable clock | Time can be advanced or fixed; a scenario moves the clock forward instead of moving memories back. | core/timeSource.py |
In the code but not active
| Item | Evidence |
|---|---|
The reply's reinforces answer |
Parsed into Reaction.reinforcesId (responding.py); in a turn nothing reads it (only reflection reads its own reinforces). It has no effect. |
| Association origin Involuntary / Deliberate | Association.DELIBERATE is defined (containers/Association.py) but every add uses the default Involuntary (turn.py). The distinction only shows through the search lines in the senses. |
ApplicableDispositions container |
Class exists (containers/ApplicableDispositions.py); the turn uses a plain list (turn.py). |
| "left the conversation" event | eventWords.left exists (episode/eventWords.py); no code calls it. |