diff --git a/.gitignore b/.gitignore index 8d1d188..d0d980b 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,9 @@ coverage.xml # Code documentation for internal usage doc_*.* example_*.txt +echo cycle_log.jsonl +echo terminal.txt + # Build/dist build/ diff --git a/README.md b/README.md index 8c9a17b..6ba2d8a 100644 --- a/README.md +++ b/README.md @@ -208,9 +208,9 @@ Environment truth (storyboard state): [env] ... stage=... posture=... mom=... nipple=... -Keyframes (stage/zone boundaries): +Keyframes (episode boundaries) (for example): -[env→world] KEYFRAME: ... +[env→world] KEYFRAME: periodic(step=20, period=10) [wm<->col] store: ... @@ -233,6 +233,72 @@ v0 is intentionally minimal (posture only) [env-loop] summary ... env_step=... stage=... env_posture=... bm_posture=... last_policy=... zone=... +### How to Read the Cognitive Cycle Summary (at time of writing) + +During **menu 37** closed-loop runs, each cognitive cycle ends with a short **footer block** intended for fast human scanning. +This footer is intentionally pragmatic and is **under constant development** as Phase IX evolves; treat it as a reading aid, +not a stable API. + +You will see lines with the prefix: + +- `[cycle] IN` — “important inputs” for this cycle: env_step, stage, posture, mom/nipple, zone, drives, and the action that + the environment applied on this tick (the action was chosen on the prior cycle). +- `[cycle] WM` — **WorkingMap** summary: + - `surfaceΔ` lists coarse slot changes (posture / proximity / hazard / nipple) derived from EnvState truth. + - `scratch` reports which policy executed and how many bindings it wrote (typically into **WM_SCRATCH** when execute_on=WM). +- `[cycle] WG` — **WorldGraph** long-term injection summary: how many `pred:*` and `cue:*` bindings were written this tick + (in `changes` mode, this may be `preds+0` when slots are unchanged). +- `[cycle] COL` — **WM⇄Column** keyframe pipeline summary (store / retrieve / apply). If no keyframe-triggered memory ops ran, + the footer will say so explicitly. +- `[cycle] ACT` — action recap: executed policy name, reward if present in logs, and the **next** action string that will be + fed back to `env.step(...)` on the next cycle. + +As the system matures (HAL/robotics, richer perception, more WorkingMap semantics), the exact fields may change — the guiding +principle is constant: **show the smallest digest that lets you visually confirm the architecture is behaving as intended**. + + + + + +### 2b) Terminal tag legend (prefixes) + closed-loop terminology + +CCA8 prints many lines with a `[tag]` prefix. These tags are a stable “legend” that lets you skim runs quickly. + +**Core env-loop tags** +- **[env-loop]**: one **closed-loop cognitive cycle** driver iteration (env update → internal updates → policy select/execute). +- **[env]**: environment-side events and “truth now” (storyboard stage, posture, mom/nipple state, etc.). +- **[env→working]**: EnvObservation projected into **WorkingMap.MapSurface** (entity/slot updates). +- **[env→world]**: EnvObservation written into the **WorldGraph** (long-term episode index). +- **[wm<->col]**: WorkingMap ⇄ Column keyframe pipeline: +- **store** writes a MapSurface snapshot engram + a lightweight pointer binding; **retrieve** ranks past snapshots + by context (stage/zone/signature) while excluding the just-stored one; **apply** injects priors (merge/seed) into WorkingMap. +- **[pred_err]**: prediction error v0 (expected vs observed posture); used for retrieval gating and for action shaping + (a small negative reward shaping update is applied after mismatch streaks). +- **[gate:]**: a specific gate/trigger’s diagnostic readout (drives, BodyMap stale, zone classification, etc.). +- **[pick]**: which policy won this cycle and why (deficits / non-drive tie-break / RL note if enabled). +- **[executed]**: the chosen policy executed (its internal success/reward signal; confirmation is via NEXT cycle’s observation). +- **[maps]**: which map was used to **select** vs **execute** (e.g., `selection_on=WG execute_on=WM`). +- **[obs-mask]**: partial observability masking (token drops), when enabled. + +**Important terminology (to avoid “step” ambiguity)** +- **Cognitive cycle (closed-loop)**: EnvObservation arrives → maps update → policy select/execute → action fed back to env. + (Printed as “Cognitive Cycle i/N” in menu 37.):contentReference[oaicite:3]{index=3} +- **env_step / step_index**: the environment’s internal counter since env.reset() (0-indexed).:contentReference[oaicite:4]{index=4} +- **controller step**: one Action Center invocation (“what should I do now?”). In menu 37, we do one controller step per cognitive cycle. + +**Environment simulator vs “world model” (AI literature note)** +In modern AI literature, a “world model” usually means an agent’s **internal learned predictive model**. +In CCA8, **HybridEnvironment** is the external **simulated world** (ground truth generator), not the agent’s learned world model. +CCA8’s internal “world model-ish” content is distributed across: +- BodyMap (fast “belief now” registers), +- WorkingMap.MapSurface (entity/slot belief table), +- WorldGraph (long-term episode index), +- Columns/Engrams (heavy stored map snapshots), +plus predicted postconditions in Scratch (hypotheses) and prediction-error signals on the next cycle. + + + + ### 3) Optional experiment: partial observability (2–3 minutes) @@ -2533,64 +2599,99 @@ Q: Is load failure fatal? A: No, runner continues with a fresh session. ## Intro Glossary -- **Predicate** — symbolic fact token (atomic). -- **Binding** — node that carries predicate tag(s) and holds meta/engrams/edges. -- **Edge** — directed relation labeled `"then"`, encoding episode flow. -- **WorldGraph** — the episode index graph. -- **Policy** — primitive behavior with `trigger` + `execute`. -- **Action Center** — ordered scan of policies, runs first match per controller step -- **Drives** — homeostatic variables (hunger/fatigue/warmth) that generate drive flags for triggers. -- **Engram** — pointer to heavy content (features/sensory/temporal traces) stored outside the graph. -- **Provenance** — `meta.policy` stamp recording which policy created a binding. - - -**Predicate (tag)** -Namespaced symbolic token (string) carried by a binding, e.g., `pred:stand`, `pred:mom:close`, `pred:milk:drinking`. A binding can carry multiple predicates. -**Binding (node / episode)** -A time-slice container that holds: predicate tags, lightweight `meta`, and **pointers** to rich engrams (not the engrams themselves). +This glossary is intentionally “runner-facing”: terms are defined in the way you see them in menu output and snapshots. + +### A) One-line cheat sheet (high frequency terms) + +- **HybridEnvironment**: the external (simulated) world; produces observations from actions. +- **EnvState**: environment “truth” (the simulator’s internal state). +- **EnvObservation**: the observation packet crossing into the agent (predicates/cues + env_meta). +- **Closed-loop cognitive cycle**: one env↔agent iteration: observe → update → select/execute → feedback action. +- **controller step**: one Action Center invocation (policy arbitration + possible write). +- **ctx (Ctx)**: runtime context object: cross-cycle counters + knobs + caches (the runner↔engine seam). +- **WorldGraph (WG)**: long-term symbolic episode index: bindings + edges + anchors + pointers to engrams. +- **Binding**: a node (“episode card”) with tags (pred/cue/action/anchor) plus meta/engrams/edges. +- **Edge**: directed link `src → dst`, usually label `"then"` for episode flow (label is an annotation). +- **Anchors**: named pointers into a graph (NOW, NOW_ORIGIN, HERE, etc.). +- **BodyMap**: fast belief registers for gating (posture, distances, nipple state, zone, staleness). +- **WorkingMap (WM)**: short-term workspace graph; contains MapSurface + Scratch + Creative subregions. +- **MapSurface**: WM’s stable entity/slot table (“what I believe now”); updated in-place each cycle. +- **Scratch**: WM’s procedural trace (state–action–state chains; predicted postconditions). +- **Engram / Column**: heavy payload store (e.g., MapSurface snapshots); WG stores pointers to these. +- **Keyframe**: a boundary cycle where we may store/retrieve/apply WM snapshots (WM⇄Column pipeline). +- **Prediction error (pred_err v0)**: mismatch between predicted postcondition and next observation. -**Edge (directed link)** -A directed connection `src → dst` with optional relation label (e.g., `approach`, `search`, `latch`, `suckle`). Think temporal/causal adjacency. +--- + +### B) MapSurface terminology: entity vs slot-family (the core idea) -**Anchors** -Special bindings (e.g., `NOW`). Use **World stats** to find the actual binding ID (e.g., `NOW=b0`). +**Entity** = “the thing we are talking about.” +Examples: `self`, `mom`, `shelter`, `cliff`. -**WorldGraph** -Holds bindings + edges and fast tag→binding indexes for planning and lookup (~the compact symbolic 5%). +**Slot-family** = “the attribute channel we store for that entity.” +Examples: `posture`, `proximity:mom`, `proximity:shelter`, `hazard:cliff`, `nipple`. -**Engram (rich memory)** -Large payloads stored outside the graph and referenced by pointers from bindings (~the rich 95%). Resolved via the column provider. +**Value** = the current value in that slot-family. +Examples: `fallen`, `standing`, `close`, `far`, `hidden`, `found`, `latched`. -**Column provider** -`cca8_column.py` resolves binding→engrams and manages simple engram CRUD for demos. +Concrete example (EnvObservation token → MapSurface update): +- EnvObservation predicate `pred:posture:fallen` becomes: + - Entity = `self` + - Slot-family = `posture` + - Value = `fallen` +MapSurface semantics: overwrite within a slot-family (exactly one current value per slot-family). -**Policy** -Trigger (conditions on predicates/drives/sensory cues) + primitive (callable). Lives in code (`cca8_controller.py`), not in the graph. +--- -**Drives** -Scalar homeostatic variables (0–1): `hunger`, `warmth`, `fatigue`. When crossing thresholds, the runner emits drive flags like `drive:hunger_high`. +### C) Policy selection terms: gate vs trigger vs execute -**Search knobs** +- **Gating (dev/safety gate):** “Is this policy even allowed in the candidate set right now?” +- **Triggering:** “Given world + drives + BodyMap, does this policy want to run now?” +- **Executing:** among triggered candidates, choose ONE winner and run it. -* `k`: branch cap during expansion (smaller = decisive, larger = broader). +**Action Center**: the policy runtime that forms the triggered candidate set and selects **one winner**. +Current selection rule (high-level): +deficit (drive-urgency) → non-drive tie-break → (if RL enabled: q tie-break inside near-tie band) → stable order. -* `sigma`: small Gaussian jitter to break ties/avoid stagnation. +--- -* `jump`: ε-exploration probability to occasionally take a random plausible move. +### D) Timekeeping and counters -**Cues & ticks** +- **env_step / step_index**: environment step since last reset (0-indexed). +- **controller_steps**: count of Action Center invocations. +- **cog_cycles**: count of “meaningful” cognitive iterations; canonically the env-loop cycle counter. -* **Sensory cue** adds transient evidence (vision/smell/sound/touch). +(See “Tutorial on Timekeeping” and “Tutorial on Cognitive Cycles” for the full contract.) -* **Autonomic tick** updates drives (e.g., hunger rises) and can emit drive flags. +--- -**Instinct step** -One step chosen by the controller using policies + drives + cues. You can accept/reject proposals. +### E) Startup knobs you will see in the banner / profile line + +- **sigma**: TemporalContext drift noise scale (how fast the soft clock wanders during step()). +- **jump**: TemporalContext boundary noise scale (how distinct chapters feel after boundary()). +- **winners_k / k**: reserved “top-k winners” knob for future competitive selection (e.g., WTA / multi-proposal arbitration). + Today it is mostly a profile label and a forward-compatible parameter. + +--- + +### F) Attach modes (how new bindings are wired) + +- `attach="now"`: create NOW → new edge (then) and update LATEST. +- `attach="latest"`: create LATEST → new edge (then) and update LATEST. +- `attach="none"`: create a floating binding (valid but disconnected until you add an edge). + +--- + +### G) “World model” terminology (AI literature vs CCA8) + +In common AI usage, a “world model” usually means the agent’s internal learned predictive model. +In CCA8: +- HybridEnvironment is the external *simulated world* (truth generator). +- The internal “world model-ish” belief lives in BodyMap + WorkingMap.MapSurface (+ priors from Column snapshots), + with WorldGraph acting as the long-term episode index and pointer scaffold. -**Planning** -BFS-style search from the `NOW` anchor to any binding carrying a target predicate (`pred:`), traversing directed edges. @@ -3058,6 +3159,7 @@ This aligns with the CCA7 article’s emphasis on verifiers + provenance, withou CCA8 uses five orthogonal time measures. They serve different purposes and are intentionally decoupled. + **1) Controller steps** — one Action Center decision/execution loop (aka “instinct step”). *Purpose:* cognition/behavior pacing (not wall-clock). *Source:* a loop in the runner that evaluates policies once and may write to the WorldGraph. When that write occurs, we mark a **temporal boundary (epoch++)**. :contentReference[oaicite:0]{index=0} @@ -3077,32 +3179,38 @@ With regards to terminology and operations that affect controller steps: **“Autonomic tick”** = physiology + **one controller step**. **“Simulate fall”** = inject fallen + **one controller step** (no drift) (but no cognitive cycle increment) + **2) Temporal drift** — the *soft clock* (unit vector) that drifts a bit each step and jumps at boundaries. *Purpose:* similarity + episode segmentation that’s unitless and cheap (cosine of current vs last-boundary vector). *Drift call:* `ctx.temporal.step()`; *Boundary call:* `ctx.temporal.boundary()`; vectors are re-normalized every time. See module notes on drift vs boundary. :contentReference[oaicite:1]{index=1} *Runner usage:* we drift once per instinct step and once per autonomic tick in the current build; boundary is taken when an instinct step actually writes new facts. :contentReference[oaicite:2]{index=2} + **3) Autonomic ticks** — a fixed-rate heartbeat (physiology/IO), independent of controller latency. *Purpose:* hardware/robotics cadence; advancing drives; dev-age. *Source variable:* `ctx.ticks` (int). *Where incremented today:* the **Autonomic Tick** menu path increments `ticks`, nudges drives, and performs a drift; it can also trigger a thresholded boundary. :contentReference[oaicite:3]{index=3} :contentReference[oaicite:4]{index=4} + **4) Developmental age (days)** — a coarse developmental measure used for stage gating. *Source variable:* `ctx.age_days` (float), advanced along with autonomic ticks; used by `world.set_stage_from_ctx(ctx)`. :contentReference[oaicite:5]{index=5} -**5) Cognitive cycles** — a derived counter for CLOSED-LOOP env↔controller iterations -(EnvObservation → internal update → (keyframe optional store/retrieve/apply) → policy select/execute → action feedback to env). -*Purpose:* progress gating & timeouts (e.g., “if no success in N cycles, switch strategy”), analytics. +**5) Cognitive cycles** — a derived counter for “meaningful sense→decide→act” iterations -*Source variable:* `ctx.cog_cycles` (int). -*Where incremented today:* in the HybridEnvironment closed-loop paths (menu 37; menu 35 is an alias that runs one closed-loop step), once per env↔controller iteration (ordinary or keyframe). -*Not incremented by:* controller-only paths (Instinct Step, Autonomic Tick, Simulate Fall), which can still advance `controller_steps` and temporal drift. +In CCA8, the most canonical “cognitive cycle” is the **closed-loop env↔controller iteration** used by menu 37: +EnvObservation → internal update (BodyMap/WorkingMap/WorldGraph as configured) → policy select/execute → action feedback to env.:contentReference[oaicite:8]{index=8} -*Contrast with controller steps:* `controller_steps` counts every Action Center invocation; within menu 37 runs, we execute exactly one controller step per cognitive cycle, so in those runs `controller_steps` and `cog_cycles` advance together. Outside the env-loop, `controller_steps` may advance without `cog_cycles`. +*Source variable:* `ctx.cog_cycles` (int). -*Recommended invariants:* `cog_cycles ≤ controller_steps`; epochs (`ctx.boundary_no`) increment only on boundary jumps (writes or τ-cuts), never decrement. +*Where incremented today (current runner behavior):* +- **Env-loop (menu 37; menu 35 alias)**: increments once per closed-loop iteration (ordinary or keyframe). +- **Controller-only “Instinct step” path:** currently increments **only when the controller wrote new bindings** (i.e., a real state/action update occurred). + (This is a temporary “meaningful write = cycle” definition while we continue to harden the explicit sense→process→act loop.) +*Contrast with controller steps:* `controller_steps` counts every Action Center invocation; in menu 37 runs they typically advance together (1 controller step per closed-loop cycle). Outside the env-loop, `controller_steps` may advance without `cog_cycles` (e.g., no-op decisions), and `cog_cycles` may increment only on a successful write. + +*Recommended invariant:* `cog_cycles ≤ controller_steps`. ### Event boundaries & epochs @@ -3155,7 +3263,7 @@ A: Wall-clock is great for logs and cross-run inspection, but awkward for unitle -# Tutorial on Cognitive Cycles] +# Tutorial on Cognitive Cycles @@ -3513,6 +3621,20 @@ Log line format (example): [pred_err] v0 err={'posture': 1} pred_posture=standing obs_posture=fallen from=policy:stand_up +**Action shaping ("extinction pressure”)** + +When the same policy repeatedly predicts a posture postcondition and the next EnvObservation contradicts it, +CCA8 applies a small **negative shaping reward** to that policy’s skill ledger value (`SkillStat.q`). + +- The penalty is applied only after a short mismatch streak (>=2) to avoid punishing the normal “first mismatch after reset” + where the environment has not yet consumed the last action. +- This creates a biologically-inspired “stop repeating actions that do not work” pressure without requiring a full RL backend. + +You may see an additional line during menu 37 runs: + +[pred_err] shaping: policy=policy:stand_up reward=-0.15 (streak=2) q=+0.42 + +This shaping affects RL tie-break behavior (`q`) and also feeds the discrepancy-history used by some non-drive tie-breaks. @@ -3684,8 +3806,41 @@ We also dedup snapshots using a signature so we don’t store the same map 100 t --- +## WorkingMap <-> Column (wm<->col): what is stored and what “merge” reconstitutes + +When you see: + +[wm<->col] store: ok sig=... bid=bNN eid=XXXXXXXX… + +**eid=... (the payload)** is a Column engram record whose payload is a serialized **wm_mapsurface_v1** snapshot: +- entities (stable ids like self/mom/shelter/cliff) +- per-entity slot values (e.g., posture, proximity:mom, hazard:cliff, ...) +- selected WorkingMap relations that represent spatial/scene structure (when present) +- minimal context meta (stage, zone, epoch, created_at, etc.) + +**sig=... (the signature)** is a compact scene fingerprint derived from MapSurface slot/value content. +It is used only for deduplication and fast candidate scoring; it is not the memory itself. + +**bid=bNN (the pointer binding)** is a lightweight WorldGraph binding that stores the signature and points to the engram id. +This lets the WorldGraph remain small while Column holds the heavy payload. + +### Retrieve +`retrieve` selects candidate prior snapshots (engrams) using context filters such as stage/zone and similarity scoring, +and it explicitly excludes the **just-stored** eid to prevent “self-retrieval loops.” + +### Apply (merge mode) +`apply` in merge/seed mode injects priors conservatively into WorkingMap: +- it does NOT overwrite currently observed slot families +- it does NOT inject cue:* as “present now” belief (cue leakage guard) +- it fills only missing slot families and records provenance in meta where helpful + +The result is not a time-travel rewrite of truth; it is a **prior** that can be corrected immediately by EnvObservation on subsequent cycles. + + ### Auto-retrieve: what it means (and what it does NOT mean) +nb. older than above section; revise if necessary + **Auto-retrieve means:** At a keyframe (stage/zone boundary), CCA8 automatically tries to pull a previously stored `wm_mapsurface` snapshot from Column memory and apply it to the current WorkingMap as a **prior**. @@ -4042,14 +4197,123 @@ CCA8 uses several small “maps” as well the large WorldGraph map for its memo This subsection distills repeated Q&A from Phase VII–VIII design review into explicit “map contracts” and a quick reading guide for env-loop outputs (MapSurface entity table + WorkingMap autosnapshot). + + + #### One-line roles (“what question does this map answer?”) -- BodyMap: “what is true of my body / peripersonal near-space right now (fast gating)?” -- WorkingMap.MapSurface: “what do I believe about the scene right now (semantic state table)?” -- WorkingMap.Scratch: “what did I just try to do, and what outcome did I expect (procedural trace)?” -- WorkingMap.Creative: “what candidate futures did I simulate (counterfactual rollouts; future)?” -- WorldGraph (long-term): “what happened over time (episode index + planning skeleton)?” -- Columns/Engrams: “where does the heavy payload live (stored map instances / scenes / features)?” + +- BodyMap: + “What is true of my body and peripersonal near-space right now?” + Fast, overwrite-style, used for gating and safety. + +- WorkingMap.MapSurface: + “What do I believe about the current scene right now?” + Stable entity+relation map updated in place (overwrite-by-slot-family, not append-log). + +- WorkingMap.Scratch: + “What did I just try to do, and what outcome did I expect?” + Procedural trace and short-lived reasoning scaffolding (action chains + predicted postconditions). + +- WorkingMap.Creative: + “What candidate futures did I simulate?” + Counterfactual rollouts that must not directly mutate MapSurface truth. + +- WorldGraph: + “What happened over time, and how do I index and traverse episodes?” + Long-lived, sparse episode skeleton + pointer scaffold. + +- Columns / Engrams: + “Where does the heavy representational payload live?” + Immutable stored map instances, sensory features, and scene descriptors. + +--- + +#### NavPatch: the map substrate that makes “everything is a navigation map” concrete + +CCA8 uses an explicit WorkingMap entity table because it is inspectable and action-ready. +However, the goal is not a brittle symbol graph. The goal is to treat entities as *handles* into +richer map structure. + +A **NavPatch** is a compact local navigation map fragment that can be owned by an entity or shared +across entities. It is the unit that allows: + +- entity geometry and structure (e.g., “rectangular block”) to be represented as map content, +- sensory evidence to be represented as map layers without storing raw pixels, +- approximate matching and merging (non-brittle reuse), +- hierarchical composition (“concepts link to other navmaps”). + +NavPatch is not a new “memory place”; it is content that can live: +- transiently inside WorkingMap for fast use, and/or +- persistently as Column engrams, indexed by thin pointers in WorldGraph. + +A minimal NavPatch contract (v0): +- patch_id: stable identifier (local or engram id) +- frame: coordinate frame label (e.g., self_local, allocentric_stub) +- extent: patch bounds in that frame (meters or normalized units) +- representation: + - either a small grid with layered fields (occupancy / hazard / affordance / salience), + - or a vector form (polygons / line segments / keypoints), + - or both (hybrid) +- links: + - transforms to other patches (pose constraints, adjacency, containment) + - optional semantic links to “concept patches” (map-of-maps) + +--- + +#### Memory pipeline component contracts + +These contracts define the interfaces between modules so we can evolve the internals while keeping +the system stable and testable. + +Environment side: +- EnvState: + Ground-truth simulator state. Agent must never read it directly. +- PerceptionAdapter: + EnvState → EnvObservation + May include a stub “raw sensor” layer plus a processed navmap layer. + +Boundary packet: +- EnvObservation: + The only per-tick message the agent receives. + Contains: + - raw_sensors (optional; stubbed in simulation) + - predicates (discrete state tokens) + - cues (transient evidence tokens) + - env_meta (stage/zone/time, etc.) + +Agent side updates (per tick): +- BodyMap updater: + EnvObservation → BodyMap + Overwrite-style. Must stay small and authoritative for gating. + +- WorkingMap updater: + EnvObservation (+ optional retrieved priors) → WorkingMap.MapSurface + Overwrite-by-slot-family. Must not accumulate multiple competing values within a slot-family. + + Optional: + - updates / creates NavPatches and attaches them to entities + - stores only summaries in MapSurface slots (the heavy patch can be stored elsewhere) + +Keyframes: +- Keyframe detector: + Uses env_meta + gating signals (missingness, prediction error, staleness) to decide boundaries. + +- Snapshot store: + WorkingMap.MapSurface → Column MapEngram (heavy payload) + WorldGraph ← thin pointer binding tagged by context and carrying engram_id + +Retrieval as priors: +- Candidate selection: + WorldGraph pointer bindings filtered by context, scored by overlap and signatures. +- Prior injection: + - merge/seed: fill only missing slot families and relations; never inject cue:* as “present now” + - replace: rebuild MapSurface from snapshot (debug / strong prior) + +Immutability: +- Column engrams are immutable records. +- Any update is stored as a new engram, with ancestry recorded in meta, and indexed by a new pointer. + --- @@ -4164,21 +4428,204 @@ Legend reminders: --- -## G. Keyframes: what happens at a boundary (short checklist) -At a keyframe boundary (currently stage/zone changes): -- WorldGraph long-term slot cache may reset and rewrite the stable state slots -- MapSurface snapshot is stored to Column (dedup by signature) -- WorldGraph pointer node is written (tags for stage/zone; carries engram pointer) -- Optional auto-retrieve may run: - - replace mode: rebuild surface from snapshot - - seed/merge mode: seed predicates only; do not inject cue:* into live state -- Temporal epoch/boundary tracking updates -- Scratch is not cleared automatically (yet) → consider a future Scratch TTL rule + + +## G. Keyframes (episode boundaries): what they do and how they are triggered + +A **keyframe** is a special cognitive cycle that we treat as an **episode boundary** (think: “video keyframe”). +Most cycles are ordinary “sense → update → choose action.” A keyframe cycle is a boundary cycle where we also run +boundary-only bookkeeping and (optionally) boundary-only memory operations. + +Keyframes exist for two practical reasons: + +1) **Trace hygiene** + In long-term WorldGraph observation logging (`longterm_obs_mode="changes"`), we normally write only slot changes. + A keyframe resets the long-term slot/cue de-dup caches so the next injection can cleanly re-assert stable facts + at the new boundary (without spamming every tick). + +2) **Memory boundary semantics** + Keyframes are the natural cycles where we run the **WM⇄Column boundary pipeline** (store/retrieve/apply), + so priors can influence action selection *at the boundary* without doing heavy memory work on every tick. + +--- + +### G1) What a keyframe does (boundary semantics) + +At a keyframe boundary, the runner may do all of the following (depending on which knobs are enabled): + +- **Emit a KEYFRAME log line**: `[env→world] KEYFRAME: | cleared ...` +- **Clear long-term observation caches** (changes-mode): `ctx.lt_obs_slots` (and cue cache) are cleared. + This forces the next boundary observation write to behave “snapshot-like” for one tick. +- **Trigger the WM⇄Column boundary pipeline** (if enabled in your Phase VII/VIII knobs): + - store a WorkingMap.MapSurface snapshot to Column (dedup by signature), + - write/refresh a thin WorldGraph pointer binding, + - optional guarded auto-retrieve + apply (replace or seed/merge). +- **Temporal bookkeeping** may also treat boundaries as “chapter points” (epoch/boundary tracking), but this is conceptually + separate from keyframes: keyframes are *memory/segmentation boundaries*, while TemporalContext is a *soft clock*. + +--- + +### G2) Keyframe triggers (Phase IX) + +A keyframe is decided **once per cognitive cycle** at the env→memory boundary hook +(`inject_obs_into_world(...)`) **before** policy selection. This is deliberate: +we do not want to split a cycle while a policy is half-written. + +Keyframe trigger families (any can force a keyframe this cycle): + + +#### 1) Episode-start keyframe (env reset) +- Trigger: `env.reset()` produces `time_since_birth <= 0.0`. +- Reason string example: `env_reset(time_since_birth=0.00)` + + +#### 2) Context discontinuity keyframes (storyboard boundaries) +- **Stage change** (default ON): scenario stage changed (birth → struggle → first_stand → ...). + - Knob: `ctx.longterm_obs_keyframe_on_stage_change` +- **Zone change** (default ON): coarse safety zone label changed (e.g., safe ↔ unsafe_cliff_near). + - Knob: `ctx.longterm_obs_keyframe_on_zone_change` + +These are the main storyboard “chapter boundaries.” + + +#### 3) Periodic keyframes (optional; “max-gap” scheduling) + +Periodic keyframes exist to guarantee occasional episode boundaries when the world is quiet (robotics / long stretches without milestones). + +- Knob: `ctx.longterm_obs_keyframe_period_steps` (0 disables) +- Optional knob: `ctx.longterm_obs_keyframe_period_reset_on_any_keyframe` + - `False` = legacy absolute schedule: fire when `controller_steps % period == 0` + - `True` = reset-on-any-keyframe (recommended): treat periodic as a “max gap since last keyframe” + - if any other keyframe happens (env_reset, milestone, surprise, stage/zone, emotion), restart the periodic counter + - periodic fires when `(controller_steps - last_keyframe_step) >= period` + - if another keyframe already fired on this cycle, we do **not** add an extra “periodic” reason line, but the periodic counter still resets + +Reason string example: `periodic(step=20, period=10)` + + +- Optional knobs (sleep suppression; robotics/HAL): + - `ctx.longterm_obs_keyframe_period_suppress_when_sleeping_nondreaming` + - `ctx.longterm_obs_keyframe_period_suppress_when_sleeping_dreaming` + + When enabled, periodic keyframes are suppressed if the observation indicates the agent is sleeping in that mode. + Sleep state can be supplied either as: + - `env_meta`: `sleep_state`/`sleep_mode` (string) or `sleeping`/`dreaming` (bool), or + - predicates such as `sleeping:non_dreaming` / `sleeping:dreaming` (with `rem`/`nrem` aliases allowed). + + + +#### 4) Surprise keyframes (optional; prediction error v0) +- Trigger: sustained mismatch signal (streak-based). +- Knobs: + - `ctx.longterm_obs_keyframe_on_pred_err` + - `ctx.longterm_obs_keyframe_pred_err_min_streak` +- Reason string example: `pred_err_v0(streak=2)` + + +#### 5) Goal milestone keyframes (optional; derived or HAL-supplied) + +Milestones are **event-based segmentation** (“something that matters happened”), not just “state changed.” + +Two inputs are supported: + +A) **HAL / rich env milestones** (deduped): +- env_meta may carry `milestones=["reached_mom", "obtained_reward", ...]`. +- Keyframe fires on *new* milestone strings. + +B) **Derived milestones from predicate transitions** (no manual milestone lists): +- Derived by comparing previous vs current slot values (works well in storyboard and early HAL). +- Current derived milestone set (v0): + - `posture:fallen → posture:standing` ⇒ `stood_up` + - `proximity:mom:* → proximity:mom:close` ⇒ `reached_mom` + - `nipple:* → nipple:found` ⇒ `found_nipple` + - `nipple:* → nipple:latched` ⇒ `latched_nipple` + - `milk:* → milk:drinking` ⇒ `milk_drinking` + - `(absent) → resting` ⇒ `rested` + +Knob: `ctx.longterm_obs_keyframe_on_milestone` + +Reason string example: `milestone:stood_up,reached_mom` + +#### 6) Strong emotion keyframes (optional; HAL / richer envs) +- Trigger: rising edge into a high-intensity affect state, or an affect-label switch while still “high.” +- Inputs: + - env_meta may carry `emotion` / `affect` as either: + - dict: `{"label": "fear", "intensity": 0.93}`, or + - string label: `"fear"` (intensity optional). + - If env_meta supplies nothing, we allow a conservative proxy: + - unsafe zone ⇒ `fear` at intensity `1.0` (so you can debug this even before real affect plumbing). +- Knobs: + - `ctx.longterm_obs_keyframe_on_emotion` + - `ctx.longterm_obs_keyframe_emotion_threshold` (default ~0.85) + +Reason string example: `emotion:fear@1.00` + +--- + +### G3) Keyframes as “memory boundaries” (WM⇄Column pipeline ordering invariant) + +Keyframes are the *only* cycles where we allow boundary-only memory operations to run automatically. + +**Ordering invariant (keyframes):** +EnvObservation → BodyMap update → MapSurface update → +(keyframe) store snapshot + pointer update → +(keyframe) optional retrieve+apply (replace or seed/merge) → +policy selection → policy execution → action feedback → next cycle + +This is strict on purpose: +- Store must see the current MapSurface (coherent “belief-now bundle”). +- Retrieve/apply must run before selection if you want priors to influence that boundary’s action. +- Retrieve excludes the engram just stored on the same keyframe (no trivial self-retrieval). +- Seed/merge mode must not inject cue:* as “present now” (no cue leakage). + +**Reserved future slot (write-back / reconsolidation):** +After policy execution, a keyframe may later add a *post-execution* write-back hook that: +- writes new engrams (copy-on-write) and/or patch records, +- updates pointer bindings, +without changing the belief state already used for action selection in that same cycle. + +--- + +### G4) HAL / robotics: how keyframes generalize beyond storyboard + +In real robots there is no storyboard stage like `"first_stand"`. Keyframes still work; you just lean on the non-storyboard triggers: + +- **Zone/context discontinuities** derived from sensor fusion: + - hazard near/far thresholds, doorway crossed, cliff-edge detected, traction lost, dock-visible, etc. +- **Milestones** emitted by HAL or derived from predicates: + - `contact_made`, `object_grasped`, `reached_dock`, `human_detected`, `task_success`, `task_fail`, etc. +- **Strong emotion / arousal** from interoception + safety monitors: + - high “fear” / high “urgency” / high “startle” / high “pain” +- **Periodic** keyframes as a throttle for expensive ops: + - store/retrieve/apply every N decisions or every N seconds (if you later anchor to wall-clock ticks) +- **Surprise** keyframes: + - prediction error streaks (e.g., repeated mismatch between expected and observed posture/pose/contacts) + +Practical robotics usage: +- Keyframes become the safe moments to run heavier memory operations (map snapshotting, retrieval, consolidation), + because the boundary hook is the one place we guarantee we are not mid-write in a policy/action chain. + +--- + +### G5) What you should see in the terminal + +On a keyframe boundary, expect a line like: + +- `[env→world] KEYFRAME: stage_change 'struggle'→'first_stand' | cleared ...` +- `[env→world] KEYFRAME: milestone:stood_up | cleared ...` +- `[env→world] KEYFRAME: emotion:fear@1.00 | cleared ...` + +If WM⇄Column auto-store/retrieve/apply is enabled, you may also see: + +- `[wm<->col] store: ... (auto_keyframe_...)` +- `[wm<->col] retrieve: ...` +- `[wm<->col] apply: ...` --- + ## H. Anchors, episodes, and keyframes (how “boundaries” actually work) This subsection clarifies the “boundary machinery” used by the memory pipeline: anchors (`NOW`, `NOW_ORIGIN`), keyframes, @@ -5568,6 +6015,29 @@ This section explains how the CCA8 runner uses **anchors**, the **LATEST** point The goal is that when you say “hang this new fact off the current situation,” the system knows *where* in the WorldGraph that is — not just “whatever node happened to be written last.” + +## Anchor NOW movement: attach="now" vs WorldGraph.set_now() + +Two distinct ideas often get conflated: + +### 1) attach="now" +Creating a new binding with `attach="now"`: +- adds an edge `NOW --then--> new_binding` +- updates `LATEST = new_binding` +- **does not** re-point the NOW anchor itself + +So NOW remains a stable anchor binding unless explicitly moved. + +### 2) WorldGraph.set_now(...) +`world.set_now(bid)` **re-points the anchor**: +- updates the anchors map so NOW points to a different binding id +- updates anchor tags (removes `anchor:NOW` from the previous binding, adds it to the new one) + +In closed-loop runs, NOW may be moved explicitly at keyframes (or continuously in a debugging mode) so that +“plan from NOW” reflects the current state binding even when long-term env writes are deduplicated. + + + ### Anchors vs. LATEST: mental model The WorldGraph keeps two distinct orientation mechanisms: **anchors** and a **LATEST** pointer. @@ -7112,6 +7582,27 @@ Later phases will expand BodyMap and add a PeripersonalMap, but this v1 gives us +## Zone (BodyMap spatial classification) — what it is used for + +CCA8 uses a coarse **zone** label derived from BodyMap near-space slots: + +- `proximity:shelter:{near|far}` +- `hazard:cliff:{near|far}` + +Current classification (intentionally minimal): +- **unsafe_cliff_near**: cliff is near AND shelter is not near +- **safe**: shelter is near AND cliff is not near +- **unknown**: any other combination (including missing data) + +Zone is used as a **gating signal**, not as a long-term semantic fact: +- example: `policy:rest` is vetoed in `unsafe_cliff_near` even if fatigue is high +- example: `policy:follow_mom` receives a positive “escape cliff” preference in `unsafe_cliff_near` + +This keeps safety decisions fast: policies can consult BodyMap instead of scanning the long-term episodic chain. + + + + ### Q&A – BodyMap (Body + Near-World) Q: Why use a separate WorldGraph for BodyMap instead of just tags in Ctx? diff --git a/cca8_env.py b/cca8_env.py index 243141f..765a9fd 100644 --- a/cca8_env.py +++ b/cca8_env.py @@ -286,6 +286,7 @@ class EnvObservation: - raw_sensors : numeric/tensor-like channels (distances, images, IMU, ...) - predicates : discrete tokens suitable for insertion into WorldGraph - cues : tokens routed into Features/Columns + - nav_patches : processed local navigation map fragments (NavPatch v0; JSON-safe dicts) - env_meta : lightweight metadata (episode id, uncertainties, etc.) Note: these are *observations*, not beliefs. WorldGraph+Columns are where @@ -294,6 +295,7 @@ class EnvObservation: raw_sensors: Dict[str, Any] = field(default_factory=dict) predicates: List[str] = field(default_factory=list) cues: List[str] = field(default_factory=list) + nav_patches: List[Dict[str, Any]] = field(default_factory=list) env_meta: Dict[str, Any] = field(default_factory=dict) @@ -670,6 +672,13 @@ def observe(self, env_state: EnvState) -> EnvObservation: cues: List[str] = [] meta: Dict[str, Any] = {} + # --- nav patches (v0 stub) --- + # We do NOT model raw pixels. Instead, we emit small, JSON-safe NavPatch dicts + # representing processed spatial structure (hazards, shelter region, mom region, etc.). + # These are designed to be stored as separate Column engrams later. + patches: List[Dict[str, Any]] = [] + + # --- raw channels --- dx = env_state.mom_position[0] - env_state.kid_position[0] dy = env_state.mom_position[1] - env_state.kid_position[1] @@ -737,13 +746,137 @@ def observe(self, env_state: EnvState) -> EnvObservation: meta["time_since_birth"] = env_state.time_since_birth meta["scenario_stage"] = env_state.scenario_stage + # --- NavPatch stub stream --- + # These are NOT used for policy selection yet. They are Phase X scaffolding so the agent-side + # predictive matching loop can start generating top-K match traces, and so later WorkingMap code + # can attach patch_refs to entities. + + #nav_patches = self._stub_nav_patches(env_state) #commented out since variable not used + + # Scene-level patch (zone + position) + try: + patches.append({ + "schema": "navpatch_v1", + "local_id": "p_scene", + "entity_id": "scene", + "role": "scene", + "frame": "ego_schematic_v1", + "extent": {"type": "aabb", "x0": -2.0, "y0": -2.0, "x1": 2.0, "y1": 2.0}, + "tags": [ + f"zone:{env_state.zone}", + f"position:{env_state.position}", + f"stage:{env_state.scenario_stage}", + ], + "layers": {}, + "obs": {"source": "PerceptionAdapter.observe"}, + }) + except Exception: + pass + + # Entity-linked patches (hazard/shelter/mom) + def _add_simple_patch(local_id: str, entity_id: str, role: str, tags: list[str]) -> None: + patches.append({ + "schema": "navpatch_v1", + "local_id": local_id, + "entity_id": entity_id, + "role": role, + "frame": "ego_schematic_v1", + "extent": {"type": "aabb", "x0": -1.0, "y0": -1.0, "x1": 1.0, "y1": 1.0}, + "tags": list(tags), + "layers": {}, + "obs": {"source": "PerceptionAdapter.observe"}, + }) + + try: + _add_simple_patch("p_cliff", "cliff", "hazard", [f"hazard:cliff:{env_state.cliff_distance}"]) + _add_simple_patch("p_shelter", "shelter", "shelter", [f"proximity:shelter:{env_state.shelter_distance}"]) + _add_simple_patch("p_mom", "mom", "agent", [f"proximity:mom:{env_state.mom_distance}"]) + except Exception: + pass + return EnvObservation( raw_sensors=raw, predicates=preds, cues=cues, + nav_patches=patches, env_meta=meta, ) + def _stub_nav_patches(self, env_state: EnvState) -> List[Dict[str, Any]]: + """Return a minimal list of NavPatch-like dicts derived from EnvState. + + Purpose + ------- + The newborn-goat storyboard is not yet a full navigation environment. However, we still want + an *intermediate representation* that looks like the eventual NavPatch stream so we can: + + 1) test JSON-safe patch schemas end-to-end, + 2) run a simple prototype matching loop against Column memory, + 3) log top-K candidate matches for interpretability and future learning. + + Design + ------ + - The dict schema is intentionally small and JSON-safe. + - We avoid any heavy geometry (images, point clouds) in this stub. + - The agent-side code computes patch signatures and matching; this function only emits patches. + + Schema (v0) + ----------- + Each patch is a dict with keys: + + v : str, version label ("navpatch_v1") + id : str, short patch id within the observation (e.g., "p_zone") + kind : str, coarse patch family ("zone" | "hazard" | "affordance" | ...) + tags : list[str], human-readable tags (order is not significant) + geom : dict[str, Any], small structured features used for matching (JSON-safe) + meta : dict[str, Any], volatile tick info (NOT part of signature) + + Returns + ------- + list[dict[str, Any]] + Patch list for this observation tick. + """ + zone = getattr(env_state, "zone", None) or "unknown" + cliff = getattr(env_state, "cliff_distance", None) or "unknown" + shelter = getattr(env_state, "shelter_distance", None) or "unknown" + + # The 'zone' patch is a coarse summary used for future route/context selection. + p_zone = { + "v": "navpatch_v1", + "id": "p_zone", + "kind": "zone", + "tags": [f"zone:{zone}"], + "geom": { + "zone": zone, + "position": getattr(env_state, "position", None) or "unknown", + "cliff_distance": cliff, + "shelter_distance": shelter, + }, + "meta": { + "source": "PerceptionAdapter", + "step_index": int(getattr(env_state, "step_index", 0) or 0), + }, + } + + # The 'cliff hazard' patch is the first concrete hazard-like patch in the storyboard. + p_cliff = { + "v": "navpatch_v1", + "id": "p_cliff", + "kind": "hazard", + "tags": ["hazard:cliff", f"cliff:{cliff}", f"shelter:{shelter}"], + "geom": { + "cliff_distance": cliff, + "shelter_distance": shelter, + "zone": zone, + }, + "meta": { + "source": "PerceptionAdapter", + "step_index": int(getattr(env_state, "step_index", 0) or 0), + }, + } + + return [p_zone, p_cliff] + # --------------------------------------------------------------------------- # HybridEnvironment: orchestrator diff --git a/cca8_run.py b/cca8_run.py index 276a3b7..8c3b676 100644 --- a/cca8_run.py +++ b/cca8_run.py @@ -74,6 +74,7 @@ PRIMITIVES, skill_readout, skill_q, + update_skill, skills_to_dict, skills_from_dict, HUNGER_HIGH, @@ -95,6 +96,7 @@ from cca8_temporal import TemporalContext from cca8_column import mem as column_mem from cca8_env import HybridEnvironment, EnvObservation # environment simulation (HybridEnvironment/EnvState/EnvObservation) +from cca8_features import FactMeta # --- Public API index, version, global variables and constants ---------------------------------------- @@ -230,6 +232,9 @@ class Ctx: last_drive_flags: Optional[set[str]] = None env_episode_started: bool = False # Environment / HybridEnvironment integration env_last_action: Optional[str] = None # last fired policy name for env.step(...) + # Console UX: print the env-loop tag legend once per session (menu 35/37). + env_loop_legend_printed: bool = False + # Partial observability / observation masking (Phase VIII) # - obs_mask_prob: independent drop probability for each non-protected token. # - obs_mask_seed: if set, masking uses a deterministic per-step RNG (seeded from base seed + step_ref). @@ -241,8 +246,12 @@ class Ctx: obs_mask_seed: Optional[int] = None obs_mask_last_cfg_sig: Optional[str] = None mini_snapshot: bool = True #mini-snapshot toggle starting value - posture_discrepancy_history: list[str] = field(default_factory=list) #per-session list of discrepancies motor command vs what environment reports + # Env-loop (menu 37) per-cycle footer summary. + # This is intentionally pragmatic and subject to change as Phase IX evolves. + env_loop_cycle_summary: bool = True + env_loop_cycle_summary_max_items: int = 6 + posture_discrepancy_history: list[str] = field(default_factory=list) #per-session list of discrepancies motor command vs what environment reports # BodyMap: tiny body+near-world map (separate WorldGraph instance) body_world: Optional[cca8_world_graph.WorldGraph] = None body_ids: dict[str, str] = field(default_factory=dict) @@ -259,6 +268,7 @@ class Ctx: working_trace: bool = False # debug only: also append a per-tick trace (can grow quickly) wm_entities: dict[str, str] = field(default_factory=dict) # entity_id -> binding id (in working_world) wm_last_env_cues: dict[str, set[str]] = field(default_factory=dict) # entity_id -> last injected cue tags + wm_last_navpatch_sigs: dict[str, set[str]] = field(default_factory=dict) # entity_id -> last injected NavPatch sig16 set # WorkingMap Creative layer (counterfactual rollouts / imagined futures) # - Does not change policy selection yet (purely inspectable scaffolding for Option B). @@ -271,6 +281,47 @@ class Ctx: wm_mapsurface_last_sig: Optional[str] = None wm_mapsurface_last_engram_id: Optional[str] = None wm_mapsurface_last_world_bid: Optional[str] = None + # NavPatches (Phase X; NavPatch plan v5) + # - EnvObservation may include processed local navigation map fragments (nav_patches). + # - WorkingMap entities can own patch references via binding.meta['wm']['patch_refs']. + # - We can store patch payloads as separate Column engrams (dedup by content signature). + navpatch_enabled: bool = True + navpatch_store_to_column: bool = True + navpatch_verbose: bool = False + navpatch_sig_to_eid: dict[str, str] = field(default_factory=dict) # sig(hex) -> engram_id + navpatch_last_log: dict[str, Any] = field(default_factory=dict) + + # NavPatch (Phase X scaffolding): predictive matching loop + traceability + # navpatch_enabled gates the entire matching pipeline. When enabled, EnvObservation.nav_patches + # is treated as a stream of local "scene patches" that can be matched to prior prototypes stored + # as Column engrams. This is not used for policy selection yet; it exists for logging and for + # later WorkingMap integration. + + # NavPatch matching (Phase X): diagnostic top-K match traces (predictive coding hooks). + navpatch_match_top_k: int = 3 + navpatch_match_accept_score: float = 0.85 + navpatch_match_ambiguous_margin: float = 0.05 # if best-second < margin, mark match as ambiguous (do not hallucinate certainty) + navpatch_last_matches: list[dict[str, Any]] = field(default_factory=list) + + # NavPatch priors (Phase X 2.2a): top-down bias terms (OFF by default) + # ------------------------------------------------------------------- + # These priors are used ONLY inside the NavPatch matching loop to bias which stored + # prototype a new patch best matches. They do not affect policy selection yet. + # + # Design constraints: + # - Priors must never override strong evidence. We enforce this via an "error guard": + # if raw matching error exceeds navpatch_priors_error_guard, we refuse to call a match + # "near" even if priors would raise the posterior score. + navpatch_priors_enabled: bool = True + navpatch_priors_hazard_bias: float = 0.05 + navpatch_priors_error_guard: float = 0.45 + # Precision weighting (Phase X 2.2b): evidence reliability weights (v1 uses tags vs extent). + navpatch_precision_tags: float = 0.75 + navpatch_precision_extent: float = 0.25 + navpatch_precision_tags_birth: float = 0.60 # lower precision early → more ambiguity + navpatch_precision_tags_struggle: float = 0.65 # slightly better than birth, still degraded + navpatch_last_priors: dict[str, Any] = field(default_factory=dict) + # MapSurface auto-retrieve (Option B6/B7) # - When enabled, on keyframes (stage/zone boundary) we try to seed WorkingMap from a prior # wm_mapsurface engram (default mode="merge" so it behaves as a conservative prior). @@ -302,9 +353,9 @@ class Ctx: # Safety / posture retry bookkeeping last_standup_step: Optional[int] = None last_standup_failed: bool = False - # Long-term WorldGraph env-observation injection (STUB) - #-This records the user's preference, but is NOT enforced yet: we still inject EnvObservation - # into the long-term WorldGraph each tick, exactly as before. + # Long-term WorldGraph EnvObservation injection (real knob): + # - If False, we skip long-term WorldGraph writes entirely (BodyMap still updates; WorkingMap may still update). + # - Keyframes are only relevant when long-term injection is enabled AND mode="changes". longterm_obs_enabled: bool = True # Long-term WorldGraph observation logging controls @@ -327,17 +378,72 @@ class Ctx: longterm_obs_dedup_cues: bool = True lt_obs_cues: dict[str, dict[str, Any]] = field(default_factory=dict) - # If True, force a one-tick snapshot when the env's scenario_stage changes (including resets). - longterm_obs_keyframe_on_stage_change: bool = True + # Keyframe triggers (Phase IX; long-term obs, changes-mode): + # - period_steps: 0 disables periodic keyframes; N>0 triggers when controller_steps % N == 0. + # - on_zone_change: treat coarse zone flips as keyframes (safe/unsafe discontinuities). + # - on_pred_err: treat sustained pred_err v0 mismatch as a surprise keyframe (streak-based). + # - on_milestone: milestone keyframes (env_meta milestones and/or derived slot transitions); off by default. + # - on_emotion: strong emotion/arousal keyframes (env_meta emotion/affect; rising-edge into "high"); off by default. + + longterm_obs_keyframe_on_stage_change: bool = False + longterm_obs_keyframe_on_zone_change: bool = False + #ctx.longterm_obs_keyframe_on_stage_change in presets set to False + + longterm_obs_keyframe_period_steps: int = 0 + #longterm_obs_keyframe_period_steps: int = 10 #toggle: keyframe every 10 cog cycles + # Periodic keyframes can be scheduled in two ways: + # - legacy: absolute clock (step_no % period == 0) + # - reset-on-any-keyframe: treat periodic as a "max gap" since last keyframe + # (i.e., if *any* other keyframe happens, restart the periodic counter). + # The reset-on-any-keyframe mode reduces "weird mid-episode splits" and prevents periodic + # keyframes from clustering right after meaningful milestone boundaries. + longterm_obs_keyframe_period_reset_on_any_keyframe: bool = True + # Optional: suppress periodic keyframes while sleeping. + # + # Motivation (robotics / HAL): + # - Periodic keyframes are a "max-gap" safety net when the world is quiet. + # - During sleep, we may prefer NOT to inject arbitrary episode boundaries. + # + # Sleep detection is best-effort (future-facing): + # - env_meta may supply: sleep_state/sleep_mode (str) OR sleeping/dreaming (bool) + # - or predicates may include: sleeping:non_dreaming / sleeping:dreaming (plus rem/nrem aliases) + longterm_obs_keyframe_period_suppress_when_sleeping_nondreaming: bool = False + longterm_obs_keyframe_period_suppress_when_sleeping_dreaming: bool = False + + longterm_obs_keyframe_on_pred_err: bool = False + #longterm_obs_keyframe_on_pred_err: bool = True #toggle: keyframe surprise + longterm_obs_keyframe_pred_err_min_streak: int = 2 + + longterm_obs_keyframe_on_milestone: bool = True #toggle: milestone keyframes (env_meta + derived transitions) + #longterm_obs_keyframe_on_milestone: bool = False + + longterm_obs_keyframe_on_emotion: bool = False + longterm_obs_keyframe_emotion_threshold: float = 0.85 + + # Keyframe bookkeeping (long-term observation cache) + lt_obs_last_zone: Optional[str] = None + lt_obs_pred_err_streak: int = 0 + lt_obs_last_keyframe_step: Optional[int] = None + lt_obs_last_milestones: set[str] = field(default_factory=set) + lt_obs_last_emotion_label: Optional[str] = None + lt_obs_last_emotion_high: bool = False # If True, print per-token reuse lines when longterm_obs_mode="changes" skips unchanged slots. longterm_obs_verbose: bool = False - # Private state: per-slot last (token, bid, emit_step) used by longterm_obs_mode="changes". lt_obs_slots: dict[str, dict[str, Any]] = field(default_factory=dict) lt_obs_last_stage: Optional[str] = None + # Per-cycle JSON log record (Phase X): minimal, replayable trace contract + # --------------------------------------------------------------------- + # When enabled, each closed-loop env step appends a JSON-safe dict record to ctx.cycle_json_records, + # and optionally writes it to ctx.cycle_json_path as JSONL (one record per line). + cycle_json_enabled: bool = False + cycle_json_path: Optional[str] = None + cycle_json_max_records: int = 2000 + cycle_json_records: list[dict[str, Any]] = field(default_factory=list) + def reset_controller_steps(self) -> None: """quick reset of Ctx.controller_steps counter @@ -595,12 +701,13 @@ def _bid_key(bid: str) -> int: tail = all_ids[-max(1, int(n)) :] print(f"{title}: last {len(tail)} binding(s) of {len(all_ids)} total") print( - " Legend edges: wm_entity=WM_ROOT→entity (MapSurface membership); " - "wm_scratch=WM_ROOT→WM_SCRATCH (policy scratch root; keeps WM_ROOT clean); " - "wm_creative=WM_ROOT→WM_CREATIVE (counterfactual rollouts); " - "distance_to=WM_SELF→entity (meta has meters/class); then=policy action chain (should hang off WM_SCRATCH)" + " Legend: edges=wm_entity(root→entity), wm_scratch(root→scratch), wm_creative(root→creative), " + "distance_to(self→entity), then(action chain)" ) - print(" Legend tags : wm:entity / wm:eid: / wm:kind: mark entities; pred:* on entities = current belief; cue:* on entities = cues present now; meta.wm.pos={x,y,frame}") + print(" tags=wm:* entity markers; pred:* belief-now; cue:* cues-now; meta.wm.pos={x,y,frame}") + + + for bid in tail: b = ww._bindings.get(bid) # pylint: disable=protected-access if b is None: @@ -677,8 +784,7 @@ def print_working_map_layers(ctx, *, title: str = "[workingmap] layers") -> None # Optional: show candidate summaries if present if cands: - print(" Creative candidates (best first): trig=Y means policy trigger satisfied; trig=N means blocked") - print("(Note: The 'score' value is a simple ranking signal for this display, not RL skill value or deficit calculation.)") + print(" Creative candidates: trig=Y/N (trigger satisfied or blocked); score is a display heuristic (not deficit or RL q).") try: ordered = sorted(cands, key=lambda c: float(getattr(c, "score", 0.0)), reverse=True) except Exception: @@ -726,8 +832,15 @@ def _sort_key(item) -> tuple[int, str]: return (0, "") if eid == "self" else (1, eid) print(title) - print(" ent node kind pos(x,y) dist_m class seen preds (short) cues (short)") - print(" ------- ---------- -------- -------------- ------ -------- ---- -------------------------- ----------------") + print(" ent node kind pos(x,y) dist_m class seen patches preds (short) cues (short)") + print(" ------- ---------- -------- -------------- ------ -------- ---- ------------------ -------------------------- ----------------") + + # Footer summary counters (NavPatch visibility; keeps logs readable during long runs) + ent_rows = 0 + ent_with_patches = 0 + patch_refs_total = 0 + uniq_sig16: set[str] = set() + uniq_patch_eids: set[str] = set() for eid, bid in sorted(ent_map.items(), key=_sort_key): if not isinstance(bid, str): @@ -754,6 +867,22 @@ def _sort_key(item) -> tuple[int, str]: dist_m = wmm.get("dist_m") if isinstance(wmm, dict) else None dist_class = wmm.get("dist_class") if isinstance(wmm, dict) else None last_seen = wmm.get("last_seen_step") if isinstance(wmm, dict) else None + patch_refs = wmm.get("patch_refs") if isinstance(wmm, dict) else None + + # Summary bookkeeping (count only rows we actually render) + ent_rows += 1 + if isinstance(patch_refs, list) and patch_refs: + ent_with_patches += 1 + patch_refs_total += len(patch_refs) + for ref in patch_refs: + if not isinstance(ref, dict): + continue + s16 = ref.get("sig16") + if isinstance(s16, str) and s16: + uniq_sig16.add(s16) + peid = ref.get("engram_id") + if isinstance(peid, str) and peid: + uniq_patch_eids.add(peid) node_disp = f"{_wm_display_id(bid)} ({bid})" @@ -765,6 +894,13 @@ def _sort_key(item) -> tuple[int, str]: dist_txt = f"{float(dist_m):6.2f}" if isinstance(dist_m, (int, float)) else " n/a " cls_txt = str(dist_class) if isinstance(dist_class, str) else "n/a" seen_txt = f"{int(last_seen):4d}" if isinstance(last_seen, int) else " n/a" + patch_n = len(patch_refs) if isinstance(patch_refs, list) else 0 + patch_sig16 = None + if patch_n and isinstance(patch_refs[0], dict): + v = patch_refs[0].get("sig16") + if isinstance(v, str) and v: + patch_sig16 = v + patch_txt = f"{patch_n}:{patch_sig16}" if patch_n and patch_sig16 else ("0" if patch_n == 0 else str(patch_n)) frame_txt = str(frame) if isinstance(frame, str) else "" #to clear pylint #0612: Unused variable "frame_txt" will append frame to pos_txt if isinstance(frame, str) and frame_txt: @@ -789,7 +925,13 @@ def _sort_key(item) -> tuple[int, str]: print( f" {eid:<7} {node_disp:<10} {kind:<8} {pos_txt:<14} {dist_txt:>6} {cls_txt:<8} {seen_txt:>4} " - f"{pred_txt:<26} {cue_txt}" + f"{patch_txt:<18} {pred_txt:<26} {cue_txt}" + ) + + if ent_rows: + print( + f" [patches] ent_with={ent_with_patches}/{ent_rows} refs_total={patch_refs_total} " + f"uniq_sig16={len(uniq_sig16)} uniq_eid={len(uniq_patch_eids)}" ) @@ -943,6 +1085,7 @@ def _entity_record(eid: str, bid: str) -> dict[str, Any]: dist_m = wmm.get("dist_m") if isinstance(wmm, dict) else None dist_class = wmm.get("dist_class") if isinstance(wmm, dict) else None last_seen = wmm.get("last_seen_step") if isinstance(wmm, dict) else None + patch_refs = wmm.get("patch_refs") if isinstance(wmm, dict) else None rec: dict[str, Any] = { "eid": eid, @@ -958,6 +1101,11 @@ def _entity_record(eid: str, bid: str) -> dict[str, Any]: "preds": preds, "cues": cues, } + + if isinstance(patch_refs, list): + # patch_refs are JSON-safe dicts (sig/engram_id/role/frame/tags). + rec["patch_refs"] = patch_refs + if include_internal_ids: rec["bid"] = bid return rec @@ -1208,7 +1356,6 @@ def store_mapsurface_snapshot_v1(world, ctx: Ctx, *, reason: str, attach: str = raise ValueError("attach must be None|'now'|'none'") # Store engram in Column + attach pointer to the WorldGraph binding - from cca8_features import FactMeta # local import OK attrs = { "schema": payload.get("schema"), @@ -1240,6 +1387,125 @@ def store_mapsurface_snapshot_v1(world, ctx: Ctx, *, reason: str, attach: str = return {"stored": True, "sig": sig, "bid": bid, "engram_id": engram_id, "stage": stage, "zone": zone} +# ----------------------------------------------------------------------------- +# NavPatch v1 helpers (Phase X; NavPatch plan v5) +# ----------------------------------------------------------------------------- + +def _navpatch_core_v1(patch: dict[str, Any]) -> dict[str, Any]: + """Return the stable core of a NavPatch payload for signatures/dedup. + + A NavPatch is a compact, local navigation map fragment intended to be: + - JSON-safe (dict/list/str/int/float/bool/None only) + - stable under repeated observation of the same structure + - composable (map-of-maps) via links/transforms in later phases + + This helper strips volatile fields (timestamps, match traces, etc.) so the + same structural patch yields the same signature across cycles. + + Parameters + ---------- + patch: + JSON-safe dict produced by PerceptionAdapter (env-side) or by later + agent-side processing. + + Returns + ------- + dict + Canonicalized core dict used for hashing. + """ + if not isinstance(patch, dict): + return {"schema": "navpatch_v1", "error": "not_dict"} + + schema = patch.get("schema") if isinstance(patch.get("schema"), str) else "navpatch_v1" + + core: dict[str, Any] = { + "schema": schema, + "local_id": patch.get("local_id") if isinstance(patch.get("local_id"), str) else None, + "entity_id": patch.get("entity_id") if isinstance(patch.get("entity_id"), str) else None, + "role": patch.get("role") if isinstance(patch.get("role"), str) else None, + "frame": patch.get("frame") if isinstance(patch.get("frame"), str) else None, + } + + extent = patch.get("extent") + if isinstance(extent, dict): + core["extent"] = { + k: extent.get(k) + for k in sorted(extent) + if isinstance(k, str) and isinstance(extent.get(k), (str, int, float, bool, type(None))) + } + + tags = patch.get("tags") + if isinstance(tags, list): + core["tags"] = sorted({t for t in tags if isinstance(t, str) and t}) + + layers = patch.get("layers") + if isinstance(layers, dict): + core["layers"] = { + k: layers.get(k) + for k in sorted(layers) + if isinstance(k, str) and isinstance(layers.get(k), (str, int, float, bool, type(None))) + } + + links = patch.get("links") + if isinstance(links, list): + core["links"] = [x for x in links if isinstance(x, (str, int, float, bool, type(None), dict, list))] + + return core + + +def navpatch_payload_sig_v1(patch: dict[str, Any]) -> str: + """Stable content signature for a NavPatch payload.""" + core = _navpatch_core_v1(patch) + blob = json.dumps(core, sort_keys=True, separators=(",", ":"), ensure_ascii=False) + return hashlib.sha256(blob.encode("utf-8")).hexdigest() + + +def store_navpatch_engram_v1(ctx: Ctx, patch: dict[str, Any], *, reason: str) -> dict[str, Any]: + """Store a NavPatch payload into Column memory, with per-run dedup by content signature. + + Dedup strategy (v0): + - Deduplicate within a single run using ctx.navpatch_sig_to_eid. + - Later we can replace this with a Column-side signature index if/when Column is persisted. + """ + sig = navpatch_payload_sig_v1(patch) + sig16 = sig[:16] + + cache = getattr(ctx, "navpatch_sig_to_eid", None) + if isinstance(cache, dict): + existing = cache.get(sig) + if isinstance(existing, str) and existing: + return {"stored": False, "sig": sig, "sig16": sig16, "engram_id": existing, "reason": "dedup_cache"} + + attrs: dict[str, Any] = { + "schema": patch.get("schema") if isinstance(patch.get("schema"), str) else "navpatch_v1", + "sig": sig, + "sig16": sig16, + "reason": reason, + } + + for k in ("entity_id", "role", "frame", "local_id"): + v = patch.get(k) + if isinstance(v, str) and v: + attrs[k] = v + + tags = patch.get("tags") + if isinstance(tags, list): + attrs["tags"] = [t for t in tags if isinstance(t, str) and t][:12] + + fm = FactMeta(name="navpatch", links=[], attrs=attrs).with_time(ctx) + engram_id = column_mem.assert_fact("navpatch", patch, fm) + + if isinstance(cache, dict): + cache[sig] = engram_id + else: + try: + ctx.navpatch_sig_to_eid = {sig: engram_id} + except Exception: + pass + + return {"stored": True, "sig": sig, "sig16": sig16, "engram_id": engram_id, "reason": reason} + + def _wm_entity_anchor_name(entity_id: str) -> str: """Return the WorkingMap anchor name for an entity id (must match the MapSurface naming scheme).""" eid = (entity_id or "unknown").strip().lower() @@ -2037,7 +2303,8 @@ def should_autoretrieve_mapsurface( zone: str | None, stage_changed: bool, zone_changed: bool, - boundary_reason: str | None = None, + forced_keyframe: bool = False, + boundary_reason: str | None = None ) -> dict[str, Any]: """Guard hook: decide whether CCA8 should attempt MapSurface auto-retrieval *right now*. @@ -2113,7 +2380,7 @@ def should_autoretrieve_mapsurface( pred_n = 0 cue_n = 0 - boundary = bool(stage_changed) or bool(zone_changed) + boundary = bool(stage_changed) or bool(zone_changed) or bool(forced_keyframe) diag: dict[str, Any] = { "stage": stage, @@ -3111,7 +3378,7 @@ def _bindings_with_cue(world, token: str) -> List[str]: def any_cue_tokens_present(world, tokens: List[str]) -> bool: """Return True if **any** `cue:` exists anywhere in the graph. """ - return any(_bindings_with_cue(world, tok) for tok in tokens) + return any(bool(_bindings_with_cue(world, tok)) for tok in tokens) def has_pred_near_now(world, token: str, hops: int = 3) -> bool: @@ -3125,7 +3392,7 @@ def has_pred_near_now(world, token: str, hops: int = 3) -> bool: def any_pred_present(world, tokens: List[str]) -> bool: """Return True if any pred: in `tokens` exists anywhere in the graph.""" - return any(_bindings_with_pred(world, tok) for tok in tokens) + return any(bool(_bindings_with_pred(world, tok)) for tok in tokens) def neighbors_near_self(world) -> List[str]: @@ -3641,11 +3908,17 @@ def apply_hardwired_profile_phase7(ctx: "Ctx", world) -> None: ctx.longterm_obs_enabled = True ctx.longterm_obs_mode = "changes" ctx.longterm_obs_reassert_steps = 0 - ctx.longterm_obs_keyframe_on_stage_change = True + + # IMPORTANT: + # The Phase VII hardwired profile enables the memory pipeline, but it must NOT override + # keyframe trigger knobs (stage/zone/periodic/pred_err/milestone/emotion). Those are + # experiment settings on Ctx and should remain under direct user control. + ctx.longterm_obs_verbose = False except Exception: pass + # Low-noise, useful log try: if hasattr(ctx, "longterm_obs_keyframe_log"): @@ -4225,7 +4498,7 @@ def boot_prime_stand(world, ctx) -> None: attach="now", meta={"boot": "init", "added_by": "system"}, ) - print(f"[boot] Seeded posture:fallen as {fallen_bid} (NOW -> fallen)") + print(f"[boot] Seeded posture:fallen as {fallen_bid} (birth-state binding; anchor:NOW → pred:posture:fallen)") except Exception as e: print(f"[boot] Could not seed posture:fallen: {e}") @@ -5169,7 +5442,17 @@ def print_startup_notices(world) -> None: startup of the runner ''' try: - print(f"[planner] Active planner on startup: {world.get_planner().upper()}") + planner = str(world.get_planner()).upper() + expl = { + "BFS": "Breadth-First Search (unweighted shortest path by hop count)", + "DIJKSTRA": "Dijkstra (lowest total edge weight; equals BFS when all weights=1)", + }.get(planner) + if expl: + print(f"[planner] Active planner on startup: {planner} — {expl}") + else: + print(f"[planner] Active planner on startup: {planner}") + + except Exception as e: print(f"unable to retrieve which active planner is running: {e}") logging.error(f"Unable to retrieve startup active planner status: {e}", exc_info=True) @@ -5740,7 +6023,6 @@ def _tag_discard(bid_: str, t: str): # (4) retrieve and reconstruct the surface (replace mode), # (5) seed/merge predicates only (no cue leakage) into a live semantic world. try: - from cca8_features import FactMeta from cca8_column import mem as _mem # (A) Build a tiny "mapsurface" world (tokens match HybridEnvironment.observe()). @@ -7196,6 +7478,569 @@ def _bid_key(bid: str) -> int: ww.delete_binding(binding_to_delete) all_ids.remove(binding_to_delete) +# ----------------------------------------------------------------------------- +# NavPatch (Phase X): predictive matching loop (priors OFF baseline) +# ----------------------------------------------------------------------------- + +def _navpatch_tag_jaccard(tags_a: Any, tags_b: Any) -> float: + a: set[str] = set() + b: set[str] = set() + + if isinstance(tags_a, list): + for t in tags_a: + if isinstance(t, str) and t: + a.add(t) + + if isinstance(tags_b, list): + for t in tags_b: + if isinstance(t, str) and t: + b.add(t) + + u = a | b + return (len(a & b) / float(len(u))) if u else 1.0 + + +def _navpatch_extent_sim(ext_a: Any, ext_b: Any) -> float: + # If we don't have numeric extents on both sides, do not penalize. + if not (isinstance(ext_a, dict) and isinstance(ext_b, dict)): + return 1.0 + + keys = ("x0", "y0", "x1", "y1") + a_vals: dict[str, float] = {} + b_vals: dict[str, float] = {} + + for k in keys: + av = ext_a.get(k) + bv = ext_b.get(k) + if not isinstance(av, (int, float)) or not isinstance(bv, (int, float)): + return 1.0 + a_vals[k] = float(av) + b_vals[k] = float(bv) + + # Normalize by the larger span so the score is scale-insensitive. + span_a = max(abs(a_vals["x1"] - a_vals["x0"]), abs(a_vals["y1"] - a_vals["y0"]), 1.0) + span_b = max(abs(b_vals["x1"] - b_vals["x0"]), abs(b_vals["y1"] - b_vals["y0"]), 1.0) + denom = max(span_a, span_b, 1.0) + + diff_sum = 0.0 + for k in keys: + diff_sum += abs(a_vals[k] - b_vals[k]) / denom + + # diff_sum in [0..~4]; convert to similarity in [0..1] + sim = 1.0 - min(1.0, diff_sum / 4.0) + return float(max(0.0, min(1.0, sim))) + + +def navpatch_similarity_v1(patch_a: dict[str, Any], patch_b: dict[str, Any]) -> float: + """Similarity score in [0,1] based on tag overlap + (optional) extent overlap. + + This is intentionally simple (priors OFF baseline). It is only for debugging/top-K traces now. + """ + a = _navpatch_core_v1(patch_a) + b = _navpatch_core_v1(patch_b) + + role_a = a.get("role") + role_b = b.get("role") + if isinstance(role_a, str) and isinstance(role_b, str) and role_a and role_b and role_a != role_b: + return 0.0 + + tag_sim = _navpatch_tag_jaccard(a.get("tags"), b.get("tags")) + ext_sim = _navpatch_extent_sim(a.get("extent"), b.get("extent")) + + score = 0.75 * tag_sim + 0.25 * ext_sim + return float(max(0.0, min(1.0, score))) + + +def navpatch_priors_bundle_v1(ctx: Ctx, env_obs: EnvObservation) -> dict[str, Any]: + """Compute a lightweight top-down priors bundle for NavPatch matching (v1.1). + + Purpose + ------- + This bundle is the “top-down context” for the patch matching loop. It is: + - JSON-safe (so we can store it in cycle_log.jsonl), + - traceable (sig16 stable fingerprint), + - intentionally small (no heavy payload). + + v1.1 additions + ------------- + Adds a minimal precision vector so we can weight evidence vs priors in a stable way. + + Precision is not “Friston math” here; it is simply a tunable reliability weight: + - tags precision : how much we trust symbolic tag overlap (salience/texture-like channel) + - extent precision: how much we trust geometric overlap (schematic geometry channel) + + We make tags precision stage-sensitive: + - birth/struggle → lower tags precision (more ambiguity) + - later stages → default tags precision + + Fields (v1.1) + ------------ + v: + Schema label: "navpatch_priors_v1". + enabled: + True when priors were requested by ctx.navpatch_priors_enabled. + sig16: + Stable 16-hex signature of the bundle contents (for traceability). + stage: + Env meta stage string when present (e.g., "birth", "struggle"). + zone: + BodyMap coarse zone label when available (e.g., "unsafe_cliff_near", "safe", "unknown"). + hazard_bias: + Positive bias applied to hazard-like candidates when the zone is unsafe. + err_guard: + Evidence-first guardrail: if evidence error > err_guard, priors must not force a confident match. + precision: + Per-layer evidence reliability weights (v1.1: {"tags": f, "extent": f}). + """ + stage: str | None = None + try: + meta = getattr(env_obs, "env_meta", None) + if isinstance(meta, dict): + s = meta.get("scenario_stage") + stage = s if isinstance(s, str) and s else None + except Exception: + stage = None + + zone: str | None = None + try: + z = body_space_zone(ctx) + zone = z if isinstance(z, str) and z else None + except Exception: + zone = None + + # ---- hazard prior (v1) ---- + hazard_bias = 0.0 + try: + hb = float(getattr(ctx, "navpatch_priors_hazard_bias", 0.0) or 0.0) + except Exception: + hb = 0.0 + if zone == "unsafe_cliff_near": + hazard_bias = hb + + # ---- evidence-first guard (v1) ---- + try: + guard = float(getattr(ctx, "navpatch_priors_error_guard", 0.45) or 0.45) + except Exception: + guard = 0.45 + guard = max(0.0, min(1.0, float(guard))) + + # ---- precision vector (v1.1) ---- + try: + tags_prec = float(getattr(ctx, "navpatch_precision_tags", 0.75) or 0.75) + except Exception: + tags_prec = 0.75 + try: + ext_prec = float(getattr(ctx, "navpatch_precision_extent", 0.25) or 0.25) + except Exception: + ext_prec = 0.25 + + if stage == "birth": + try: + tags_prec = min(tags_prec, float(getattr(ctx, "navpatch_precision_tags_birth", tags_prec) or tags_prec)) + except Exception: + pass + elif stage == "struggle": + try: + tags_prec = min(tags_prec, float(getattr(ctx, "navpatch_precision_tags_struggle", tags_prec) or tags_prec)) + except Exception: + pass + + tags_prec = max(0.0, min(1.0, float(tags_prec))) + ext_prec = max(0.0, min(1.0, float(ext_prec))) + precision = {"tags": tags_prec, "extent": ext_prec} + + core = { + "v": "navpatch_priors_v1", + "enabled": True, + "stage": stage, + "zone": zone, + "hazard_bias": float(hazard_bias), + "err_guard": float(guard), + "precision": precision, + } + blob = json.dumps(core, sort_keys=True, separators=(",", ":"), ensure_ascii=False) + sig16 = hashlib.sha256(blob.encode("utf-8")).hexdigest()[:16] + + out = dict(core) + out["sig16"] = sig16 + return out + + +def navpatch_candidate_prior_bias_v1(priors: dict[str, Any], cand_payload: dict[str, Any], cand_attrs: dict[str, Any]) -> float: + """Return the additive prior bias term for a candidate NavPatch prototype (v1). + + v1 semantics: + - If priors carries a positive hazard_bias and the candidate looks "hazard-like" + (role == "hazard" OR any tag starts with "hazard:"), return hazard_bias. + - Otherwise return 0.0. + """ + if not isinstance(priors, dict) or not priors.get("enabled", False): + return 0.0 + + try: + hazard_bias = float(priors.get("hazard_bias", 0.0) or 0.0) + except Exception: + hazard_bias = 0.0 + if hazard_bias == 0.0: + return 0.0 + + role = None + try: + r = cand_attrs.get("role") if isinstance(cand_attrs, dict) else None + if isinstance(r, str) and r: + role = r + else: + r2 = cand_payload.get("role") if isinstance(cand_payload, dict) else None + role = r2 if isinstance(r2, str) and r2 else None + except Exception: + role = None + + tags: list[str] = [] + try: + t = cand_payload.get("tags") if isinstance(cand_payload, dict) else None + if isinstance(t, list): + tags = [x for x in t if isinstance(x, str) and x] + else: + t2 = cand_attrs.get("tags") if isinstance(cand_attrs, dict) else None + if isinstance(t2, list): + tags = [x for x in t2 if isinstance(x, str) and x] + except Exception: + tags = [] + + hazard_like = (role == "hazard") or any(isinstance(t, str) and t.startswith("hazard:") for t in tags) + return float(hazard_bias) if hazard_like else 0.0 + + +def navpatch_predictive_match_loop_v1(ctx: Ctx, env_obs: EnvObservation) -> list[dict[str, Any]]: + """Compute a top-K candidate match trace for EnvObservation.nav_patches. + + This implements Phase X “predictive matching” (v1.1): + - store observed patches as navpatch engrams (deduped by signature), + - rank other stored prototypes as candidate interpretations (top-K), + - apply priors as a *small bias term* (hazard_bias), + - weight evidence by a tiny precision vector (tags vs extent), + - classify match confidence as commit vs ambiguous vs unknown. + + Self-exclusion + -------------- + If we stored (or dedup-reused) the current patch engram this tick, the Column scan will contain it. + We must exclude that engram_id from candidate ranking so we do not trivially match the patch to itself. + """ + if ctx is None or not bool(getattr(ctx, "navpatch_enabled", False)): + return [] + + patches = getattr(env_obs, "nav_patches", None) or [] + if not isinstance(patches, list) or not patches: + try: + ctx.navpatch_last_matches = [] + except Exception: + pass + return [] + + # Config (keep terminal readable; clamp) + try: + top_k = int(getattr(ctx, "navpatch_match_top_k", 3) or 3) + except Exception: + top_k = 3 + top_k = max(1, min(10, top_k)) + + try: + accept = float(getattr(ctx, "navpatch_match_accept_score", 0.85) or 0.85) + except Exception: + accept = 0.85 + accept = max(0.0, min(1.0, accept)) + + try: + amb_margin = float(getattr(ctx, "navpatch_match_ambiguous_margin", 0.05) or 0.05) + except Exception: + amb_margin = 0.05 + amb_margin = max(0.0, min(1.0, amb_margin)) + + # Priors bundle (Phase X 2.2a): OFF by default. + priors_enabled = bool(getattr(ctx, "navpatch_priors_enabled", False)) + priors: dict[str, Any] = {"v": "navpatch_priors_v1", "enabled": False, "sig16": None} + + if priors_enabled: + priors = navpatch_priors_bundle_v1(ctx, env_obs) + + try: + ctx.navpatch_last_priors = dict(priors) + except Exception: + pass + + # Precision weights (Phase X 2.2b): used even when priors are off (as stable knobs). + prec_tags = None + prec_ext = None + if isinstance(priors, dict) and isinstance(priors.get("precision"), dict): + p = priors.get("precision") # type: ignore[assignment] + try: + prec_tags = float(p.get("tags")) # type: ignore[union-attr] + except Exception: + prec_tags = None + try: + prec_ext = float(p.get("extent")) # type: ignore[union-attr] + except Exception: + prec_ext = None + + if prec_tags is None: + try: + prec_tags = float(getattr(ctx, "navpatch_precision_tags", 0.75) or 0.75) + except Exception: + prec_tags = 0.75 + if prec_ext is None: + try: + prec_ext = float(getattr(ctx, "navpatch_precision_extent", 0.25) or 0.25) + except Exception: + prec_ext = 0.25 + + prec_tags = max(0.0, min(1.0, float(prec_tags))) + prec_ext = max(0.0, min(1.0, float(prec_ext))) + + # Candidate prototype records (best-effort; Column is RAM-local) + try: + proto_recs = column_mem.find(name_contains="navpatch", has_attr="sig", limit=500) + except Exception: + proto_recs = [] + + out: list[dict[str, Any]] = [] + + for p in patches: + if not isinstance(p, dict): + continue + + sig = navpatch_payload_sig_v1(p) + sig16 = sig[:16] + + # Ensure an engram exists (or reuse cached) if storage is enabled. + stored_flag: bool | None = None + engram_id: str | None = None + if bool(getattr(ctx, "navpatch_store_to_column", False)): + try: + st = store_navpatch_engram_v1(ctx, p, reason="env_obs") + if isinstance(st, dict): + stored_flag = bool(st.get("stored")) if "stored" in st else None + eid = st.get("engram_id") + if isinstance(eid, str) and eid: + engram_id = eid + except Exception: + pass + + # Precompute observed patch core once (stable keys only). + obs_core = _navpatch_core_v1(p) + + # Score top-K prototypes. + # Tuple: (score_post, score_evidence, score_unweighted, prior_bias, tag_sim, ext_sim, engram_id) + scored: list[tuple[float, float, float, float, float, float, str]] = [] + role_p = p.get("role") + + for rec in proto_recs: + if not isinstance(rec, dict): + continue + + eid = rec.get("id") + if not isinstance(eid, str) or not eid: + continue + + # Self-exclusion + if isinstance(engram_id, str) and engram_id and eid == engram_id: + continue + + payload = rec.get("payload") + if not isinstance(payload, dict): + continue + + meta = rec.get("meta") if isinstance(rec.get("meta"), dict) else {} + attrs = meta.get("attrs") if isinstance(meta.get("attrs"), dict) else {} + + role_r = attrs.get("role") if isinstance(attrs, dict) else None + if ( + isinstance(role_p, str) and role_p + and isinstance(role_r, str) and role_r + and role_p != role_r + ): + continue + + proto_core = _navpatch_core_v1(payload) + + # Evidence channels (v1.1): tags vs extent + tag_sim = float(_navpatch_tag_jaccard(obs_core.get("tags"), proto_core.get("tags"))) + ext_sim = float(_navpatch_extent_sim(obs_core.get("extent"), proto_core.get("extent"))) + + tag_sim = max(0.0, min(1.0, tag_sim)) + ext_sim = max(0.0, min(1.0, ext_sim)) + + # Unweighted evidence score (diagnostic; does not change with precision) + score_unw = 0.5 * tag_sim + 0.5 * ext_sim + + # Precision-weighted evidence score + err_tags = 1.0 - tag_sim + err_ext = 1.0 - ext_sim + denom = float(prec_tags + prec_ext) + if denom > 0.0: + err_weighted = (float(prec_tags) * err_tags + float(prec_ext) * err_ext) / denom + else: + err_weighted = 0.5 * (err_tags + err_ext) + score_evidence = 1.0 - err_weighted + score_evidence = max(0.0, min(1.0, float(score_evidence))) + + prior_bias = float(navpatch_candidate_prior_bias_v1(priors, payload, attrs)) if priors_enabled else 0.0 + score_post = max(0.0, min(1.0, float(score_evidence + prior_bias))) + + scored.append((score_post, score_evidence, score_unw, float(prior_bias), tag_sim, ext_sim, eid)) + + scored.sort(key=lambda t: (-t[0], t[6])) + + top_list = [ + { + "engram_id": eid, + "score": float(score_post), + "score_raw": float(score_evidence), + "score_unweighted": float(score_unw), + "prior_bias": float(prior_bias), + "err": float(1.0 - score_post), + "err_raw": float(1.0 - score_evidence), + "err_unweighted": float(1.0 - score_unw), + "tag_sim": float(tag_sim), + "ext_sim": float(ext_sim), + } + for (score_post, score_evidence, score_unw, prior_bias, tag_sim, ext_sim, eid) in scored[:top_k] + ] + + # Add normalized weights (posterior proxy) for future graded belief work. + if top_list: + s_post = float(sum(c["score"] for c in top_list)) + s_raw = float(sum(c["score_raw"] for c in top_list)) + for c in top_list: + c["w"] = (float(c["score"]) / s_post) if s_post > 0.0 else (1.0 / float(len(top_list))) + c["w_raw"] = (float(c["score_raw"]) / s_raw) if s_raw > 0.0 else (1.0 / float(len(top_list))) + + best = top_list[0] if top_list else None + best_score = float(best.get("score", 0.0)) if isinstance(best, dict) else 0.0 + best_score_raw = float(best.get("score_raw", 0.0)) if isinstance(best, dict) else 0.0 + best_err_raw = float(1.0 - best_score_raw) + + second = top_list[1] if len(top_list) > 1 else None + margin = (best_score - float(second.get("score", 0.0))) if isinstance(second, dict) else None + margin_raw = (best_score_raw - float(second.get("score_raw", 0.0))) if isinstance(second, dict) else None + + # Decision labels are for logs/JSON traces, not control logic yet. + decision: str | None = None + decision_note: str | None = None + + if stored_flag is False: + decision = "reuse_exact" + else: + if best is None: + decision = "new_no_candidates" + else: + if priors_enabled: + try: + guard = float(priors.get("err_guard", 0.45) or 0.45) + except Exception: + guard = 0.45 + guard = max(0.0, min(1.0, float(guard))) + + if best_err_raw > guard: + decision = "new_novel" + decision_note = "guard_high_err" + else: + decision = "new_near_match" if best_score >= accept else "new_novel" + else: + decision = "new_near_match" if best_score >= accept else "new_novel" + + # Commit classification (Phase X 2.2c-style semantics, without changing control yet). + commit = "unknown" + if decision == "reuse_exact": + commit = "commit" + elif decision_note == "guard_high_err": + commit = "unknown" + elif best is None: + commit = "unknown" + else: + if best_score >= accept: + if isinstance(margin, float) and margin < amb_margin: + commit = "ambiguous" + if decision_note is None: + decision_note = "ambiguous_low_margin" + else: + commit = "commit" + else: + commit = "unknown" + + rec_out = { + "sig": sig, + "sig16": sig16, + "priors_sig16": (priors.get("sig16") if isinstance(priors, dict) else None), + "local_id": p.get("local_id"), + "entity_id": p.get("entity_id"), + "role": p.get("role"), + "stored": stored_flag, + "engram_id": engram_id, + "decision": decision, + "decision_note": decision_note, + "commit": commit, + "margin": float(margin) if isinstance(margin, float) else None, + "margin_raw": float(margin_raw) if isinstance(margin_raw, float) else None, + "best": best, + "top_k": top_list, + } + out.append(rec_out) + + # Attach trace back onto the patch itself (JSON-safe). + try: + p["sig"] = sig + p["sig16"] = sig16 + p["match"] = { + "decision": decision, + "decision_note": decision_note, + "commit": commit, + "margin": rec_out.get("margin"), + "priors_sig16": rec_out.get("priors_sig16"), + "best": best, + "top_k": top_list, + } + except Exception: + pass + + try: + ctx.navpatch_last_matches = out + except Exception: + pass + return out + + + + +# ----------------------------------------------------------------------------- +# Per-cycle JSON record helper (Phase X) +# ----------------------------------------------------------------------------- + +def append_cycle_json_record(ctx: Ctx, record: dict[str, Any]) -> None: + """Append a per-cycle JSON-safe record to ctx and optionally write it as JSONL.""" + if ctx is None or not bool(getattr(ctx, "cycle_json_enabled", False)): + return + + max_n = int(getattr(ctx, "cycle_json_max_records", 0) or 0) + if max_n <= 0: + max_n = 2000 + + buf = getattr(ctx, "cycle_json_records", None) + if not isinstance(buf, list): + ctx.cycle_json_records = [] + buf = ctx.cycle_json_records + buf.append(record) + if len(buf) > max_n: + del buf[:-max_n] + + path = getattr(ctx, "cycle_json_path", None) + if not isinstance(path, str) or not path.strip(): + return + try: + with open(path, "a", encoding="utf-8") as f: + f.write(json.dumps(record, sort_keys=True, ensure_ascii=False) + "\n") + except Exception: + return + def inject_obs_into_working_world(ctx: Ctx, env_obs: EnvObservation) -> dict[str, Any]: """ @@ -7648,10 +8493,114 @@ def _project(dist_m: float, ent: str, *, kind: str | None) -> tuple[float, float except Exception: pass - # --- Coordinates + distance edges (schematic map) --- - raw = getattr(env_obs, "raw_sensors", {}) or {} - for ent, bid in (getattr(ctx, "wm_entities", {}) or {}).items(): - if ent in ("self",): + # --- NavPatches: processed local navmap fragments (Phase X; plan v5) --- + # These are *not* raw pixels. They are small, structured maplets that can be stored + # as separate Column engrams and referenced from MapSurface entity nodes. + if bool(getattr(ctx, "navpatch_enabled", False)): + patches_in = getattr(env_obs, "nav_patches", None) or [] + refs_by_ent: dict[str, list[dict[str, Any]]] = {} + sigs_by_ent: dict[str, set[str]] = {} + + if isinstance(patches_in, list): + for p in patches_in: + if not isinstance(p, dict): + continue + + ent_raw = p.get("entity_id") or p.get("entity") or "self" + try: + ent = str(ent_raw).strip().lower() or "self" + except Exception: + ent = "self" + + sig = navpatch_payload_sig_v1(p) + sig16 = sig[:16] + + engram_id: str | None = None + if bool(getattr(ctx, "navpatch_store_to_column", False)): + try: + st = store_navpatch_engram_v1(ctx, p, reason="env_obs") + engram_id = st.get("engram_id") if isinstance(st, dict) else None + except Exception: + engram_id = None + + ref: dict[str, Any] = { + "sig16": sig16, + "sig": sig, + "engram_id": engram_id, + "local_id": p.get("local_id"), + "role": p.get("role"), + "frame": p.get("frame"), + } + + tags = p.get("tags") + if isinstance(tags, list): + ref["tags"] = [t for t in tags if isinstance(t, str) and t][:8] + + refs_by_ent.setdefault(ent, []).append(ref) + sigs_by_ent.setdefault(ent, set()).add(sig16) + + # Attach refs to WM entities (replace per tick, like cues) + for ent, refs in refs_by_ent.items(): + kind = None + try: + roles = {r.get("role") for r in refs if isinstance(r, dict)} + if "hazard" in roles or ent in ("cliff", "drop", "danger"): + kind = "hazard" + elif "shelter" in roles or ent == "shelter": + kind = "shelter" + elif ent in ("mom", "mother", "self"): + kind = "agent" + except Exception: + kind = None + + bid = _ensure_entity(ent, kind_hint=kind) + b = ww._bindings.get(bid) + if b is not None: + if not isinstance(getattr(b, "meta", None), dict): + b.meta = {} + wmm = b.meta.setdefault("wm", {}) + if isinstance(wmm, dict): + wmm["patch_refs"] = list(refs) + try: + if refs and isinstance(refs[0], dict): + fr = refs[0].get("frame") + if isinstance(fr, str) and fr: + wmm["patch_frame"] = fr + except Exception: + pass + + try: + ctx.wm_last_navpatch_sigs[ent] = set(sigs_by_ent.get(ent, set())) + except Exception: + pass + + if bool(getattr(ctx, "navpatch_verbose", False)): + try: + disp = f"{_wm_display_id(bid)} ({bid})" + print(f"[env→working] PATCH x{len(refs)} → {disp} (entity={ent})") + except Exception: + pass + + # Clear patch_refs for entities that had patches last tick but none now + try: + for ent in list((getattr(ctx, "wm_last_navpatch_sigs", {}) or {}).keys()): + if ent in refs_by_ent: + continue + bid = (getattr(ctx, "wm_entities", {}) or {}).get(ent) + if isinstance(bid, str) and bid in ww._bindings: + b = ww._bindings.get(bid) + if b is not None and isinstance(getattr(b, "meta", None), dict): + wmm = b.meta.get("wm") + if isinstance(wmm, dict): + wmm.pop("patch_refs", None) + ctx.wm_last_navpatch_sigs.pop(ent, None) + except Exception: + pass + + # --- Coordinates + distance edges (schematic map) --- + raw = getattr(env_obs, "raw_sensors", {}) or {} + for ent, bid in (getattr(ctx, "wm_entities", {}) or {}).items(): + if ent in ("self",): continue if not isinstance(bid, str) or bid not in ww._bindings: continue @@ -7764,8 +8713,13 @@ def inject_obs_into_world(world, ctx: Ctx, env_obs: EnvObservation) -> dict[str, token "resting" -> slot "resting" (no ":") Keyframes (only in "changes" mode): - - time_since_birth == 0.0 forces a snapshot (reset) - - scenario_stage change can force a snapshot if enabled + - episode start (env_reset): time_since_birth <= 0.0 + - stage change (if enabled): env_meta["scenario_stage"] changed + - zone change (if enabled): coarse safety zone flip derived from shelter/cliff predicates + - periodic (optional): every N controller steps (period_steps > 0) + - surprise (optional): pred_err v0 sustained mismatch (streak-based) + - milestones (optional): env_meta milestone flags AND/OR derived predicate slot transitions + - strong emotion/arousal (optional): env_meta emotion/affect (rising edge into "high"), with a conservative hazard proxy Even when we skip writing an unchanged token, token_to_bid will still map that token to the most recent binding id for its slot (so downstream helpers can still find it). @@ -7904,15 +8858,30 @@ def _strip_pred_prefix(tok: str) -> str: pass # Mirror into WorkingMap when enabled. + # Keep the returned dict so callers (e.g., env-loop footer) can summarize what happened. + working_inj = None try: if getattr(ctx, "working_enabled", False): - inject_obs_into_working_world(ctx, env_obs) + working_inj = inject_obs_into_working_world(ctx, env_obs) + except Exception: + working_inj = None + + # NavPatch predictive matching loop (Phase X baseline; priors OFF). + # This only records traceability metadata and stores new patch engrams in Column. + # It must never break env stepping. + try: + navpatch_predictive_match_loop_v1(ctx, env_obs) except Exception: pass # Allow turning off long-term injection entirely (BodyMap/WorkingMap still update). if not getattr(ctx, "longterm_obs_enabled", True): - return {"predicates": created_preds, "cues": created_cues, "token_to_bid": token_to_bid} + return { + "predicates": created_preds, + "cues": created_cues, + "token_to_bid": token_to_bid, + "working": working_inj, + } mode = (getattr(ctx, "longterm_obs_mode", "snapshot") or "snapshot").strip().lower() do_changes = mode in ("changes", "dedup", "delta", "state_changes") @@ -7929,11 +8898,31 @@ def _strip_pred_prefix(tok: str) -> str: if c is not None ] + keyframe = False + keyframe_reasons: list[str] = [] # In "changes" mode: optionally force a one-tick snapshot at stage transitions/resets if do_changes: force_snapshot = False reasons: list[str] = [] + step_no = int(getattr(ctx, "controller_steps", 0) or 0) + + # ---- Coarse zone (derived from pred tokens; does NOT depend on BodyMap update ordering) ---- + zone_now = "unknown" + shelter = None + cliff = None + for _tok in pred_tokens: + if isinstance(_tok, str) and _tok.startswith("proximity:shelter:"): + shelter = _tok.rsplit(":", 1)[-1] + elif isinstance(_tok, str) and _tok.startswith("hazard:cliff:"): + cliff = _tok.rsplit(":", 1)[-1] + if cliff == "near" and shelter != "near": + zone_now = "unsafe_cliff_near" + elif shelter == "near" and cliff != "near": + zone_now = "safe" + + last_zone = getattr(ctx, "lt_obs_last_zone", None) + # Reset keyframe: env.reset() produces time_since_birth == 0.0 if isinstance(time_since_birth, (int, float)) and float(time_since_birth) <= 0.0: force_snapshot = True @@ -7941,11 +8930,283 @@ def _strip_pred_prefix(tok: str) -> str: # Stage-change keyframe (optional) last_stage = getattr(ctx, "lt_obs_last_stage", None) - if getattr(ctx, "longterm_obs_keyframe_on_stage_change", True): + if bool(getattr(ctx, "longterm_obs_keyframe_on_stage_change", True)): if stage is not None and last_stage is not None and stage != last_stage: force_snapshot = True reasons.append(f"stage_change {last_stage!r}→{stage!r}") + # Zone-change keyframe (optional) + if bool(getattr(ctx, "longterm_obs_keyframe_on_zone_change", True)): + if isinstance(last_zone, str) and zone_now != last_zone: + force_snapshot = True + reasons.append(f"zone_change {last_zone!r}→{zone_now!r}") + + # Periodic keyframe (optional; safe: evaluated only at this boundary hook) + # + # Two semantics: + # A) legacy absolute schedule: step_no % period == 0 + # B) reset-on-any-keyframe: treat periodic as a max-gap since last keyframe + # (if any other keyframe happens, the periodic counter restarts). + try: + period = int(getattr(ctx, "longterm_obs_keyframe_period_steps", 0) or 0) + except Exception: + period = 0 + + if period > 0 and step_no > 0: + reset_on_any = bool(getattr(ctx, "longterm_obs_keyframe_period_reset_on_any_keyframe", False)) + + hit = False + if reset_on_any: + last_kf = getattr(ctx, "lt_obs_last_keyframe_step", None) + last_kf_step = int(last_kf) if isinstance(last_kf, int) else 0 + if last_kf_step > step_no: + # Defensive: controller_steps can be reset in some flows; treat that as a new epoch. + last_kf_step = 0 + hit = (step_no - last_kf_step) >= period + else: + hit = (step_no % period) == 0 + + # Optional suppression: do not fire periodic keyframes while sleeping. + # + # We detect sleep state best-effort from either: + # A) env_meta: sleep_state/sleep_mode (str), or sleeping/dreaming (bool) + # B) predicate tokens: sleeping:non_dreaming / sleeping:dreaming (rem/nrem aliases allowed) + if hit: + sup_nd = bool(getattr(ctx, "longterm_obs_keyframe_period_suppress_when_sleeping_nondreaming", False)) + sup_dr = bool(getattr(ctx, "longterm_obs_keyframe_period_suppress_when_sleeping_dreaming", False)) + + if sup_nd or sup_dr: + sleep_kind: str | None = None + + # A) env_meta string label + try: + sm = env_meta.get("sleep_state") or env_meta.get("sleep_mode") or env_meta.get("sleep") + except Exception: + sm = None + + if isinstance(sm, str) and sm.strip(): + s = sm.strip().lower().replace(" ", "_") + if s in ("dreaming", "rem", "rem_sleep", "sleep_rem"): + sleep_kind = "dreaming" + elif s in ("non_dreaming", "nondreaming", "nrem", "nrem_sleep", "sleep_nrem", "non_rem"): + sleep_kind = "non_dreaming" + + # A2) env_meta boolean flags + if sleep_kind is None: + try: + sleeping_flag = env_meta.get("sleeping") + dreaming_flag = env_meta.get("dreaming") + except Exception: + sleeping_flag = None + dreaming_flag = None + + if isinstance(sleeping_flag, bool) and sleeping_flag: + sleep_kind = "dreaming" if bool(dreaming_flag) else "non_dreaming" + + # B) predicate tokens + if sleep_kind is None: + try: + toks = {t.strip().lower() for t in pred_tokens if isinstance(t, str) and t.strip()} + except Exception: + toks = set() + + if ( + "sleeping:dreaming" in toks + or "sleep:dreaming" in toks + or "sleeping:rem" in toks + or "sleep:rem" in toks + ): + sleep_kind = "dreaming" + elif ( + "sleeping:non_dreaming" in toks + or "sleep:non_dreaming" in toks + or "sleeping:nrem" in toks + or "sleep:nrem" in toks + ): + sleep_kind = "non_dreaming" + elif ("sleeping" in toks) or ("sleep" in toks): + # If sleep is present but untyped, treat as non-dreaming by default. + sleep_kind = "non_dreaming" + + if (sleep_kind == "non_dreaming") and sup_nd: + hit = False + elif (sleep_kind == "dreaming") and sup_dr: + hit = False + + if hit: + # If another keyframe is already happening this tick, do NOT add a second "periodic" reason. + # In reset-on-any-keyframe mode, the periodic counter will still be reset by that other keyframe. + if not force_snapshot: + force_snapshot = True + reasons.append(f"periodic(step={step_no}, period={period})") + + # Surprise keyframe from pred_err v0 (optional; streak-based) + if bool(getattr(ctx, "longterm_obs_keyframe_on_pred_err", False)): + pe = getattr(ctx, "pred_err_v0_last", None) + pe_any = False + if isinstance(pe, dict) and pe: + try: + pe_any = any(int(v or 0) != 0 for v in pe.values()) + except Exception: + pe_any = False + + streak = int(getattr(ctx, "lt_obs_pred_err_streak", 0) or 0) + streak = (streak + 1) if pe_any else 0 + ctx.lt_obs_pred_err_streak = streak + + try: + min_streak = int(getattr(ctx, "longterm_obs_keyframe_pred_err_min_streak", 2) or 2) + except Exception: + min_streak = 2 + min_streak = max(1, min_streak) + + if pe_any and streak >= min_streak: + force_snapshot = True + reasons.append(f"pred_err_v0(streak={streak})") + else: + ctx.lt_obs_pred_err_streak = 0 + + # Milestone keyframes (HAL + derived from predicate transitions). Off by default. + # + # Two sources: + # A) env_meta milestone flags (HAL/richer envs) — may be sticky and repeat across ticks → dedup. + # B) derived transition events from predicate slots (storyboard + early HAL) — event-based, no sticky dedup needed. + # + # Derived events currently recognized: + # - posture:fallen -> posture:standing => stood_up + # - proximity:mom:* -> proximity:mom:close => reached_mom + # - (first) nipple:found => found_nipple + # - (first) nipple:latched => latched_nipple + # - (first) milk:drinking => milk_drinking + # - (first) resting => rested + if bool(getattr(ctx, "longterm_obs_keyframe_on_milestone", False)): + ms_events: set[str] = set() + + # --- A) Env-supplied milestone flags (sticky) --- + ms_raw = env_meta.get("milestones") or env_meta.get("milestone") + ms_list: list[str] = [] + if isinstance(ms_raw, str) and ms_raw: + ms_list = [ms_raw] + elif isinstance(ms_raw, list): + ms_list = [m for m in ms_raw if isinstance(m, str) and m] + + if ms_list: + prev_raw = getattr(ctx, "lt_obs_last_milestones", None) + prev: set[str] = {x for x in prev_raw if isinstance(x, str) and x} if isinstance(prev_raw, set) else set() + new_ms = {m for m in ms_list if m not in prev} + if new_ms: + ms_events |= new_ms + try: + prev |= new_ms + ctx.lt_obs_last_milestones = prev + except Exception: + pass + + # --- B) Derived milestone events (slot transitions) --- + try: + prev_slots = getattr(ctx, "lt_obs_slots", None) + prev_slots = prev_slots if isinstance(prev_slots, dict) else {} + + # Build current slot->token mapping from this observation (pred_tokens has no "pred:" prefix). + curr_by_slot: dict[str, str] = {} + for tok in pred_tokens: + if not isinstance(tok, str) or not tok: + continue + slot = tok.rsplit(":", 1)[0] if ":" in tok else tok + if slot not in curr_by_slot: + curr_by_slot[slot] = tok + + def _prev_token(slot: str) -> str | None: + p = prev_slots.get(slot) + if isinstance(p, dict): + t = p.get("token") + return t if isinstance(t, str) else None + return None + + # posture transition + prev_posture = _prev_token("posture") + curr_posture = curr_by_slot.get("posture") + if curr_posture == "posture:standing" and prev_posture != "posture:standing": + ms_events.add("stood_up") + + # mom proximity transition + prev_mom = _prev_token("proximity:mom") + curr_mom = curr_by_slot.get("proximity:mom") + if curr_mom == "proximity:mom:close" and prev_mom != "proximity:mom:close": + ms_events.add("reached_mom") + + # nipple milestones + prev_nipple = _prev_token("nipple") + curr_nipple = curr_by_slot.get("nipple") + if curr_nipple == "nipple:found" and prev_nipple != "nipple:found": + ms_events.add("found_nipple") + if curr_nipple == "nipple:latched" and prev_nipple != "nipple:latched": + ms_events.add("latched_nipple") + + # milk milestone + prev_milk = _prev_token("milk") + curr_milk = curr_by_slot.get("milk") + if curr_milk == "milk:drinking" and prev_milk != "milk:drinking": + ms_events.add("milk_drinking") + + # resting milestone + prev_rest = _prev_token("resting") + curr_rest = curr_by_slot.get("resting") + if curr_rest == "resting" and prev_rest != "resting": + ms_events.add("rested") + except Exception: + # Derived milestones are strictly best-effort; never break env injection. + pass + + if ms_events: + force_snapshot = True + reasons.append("milestone:" + ",".join(sorted(ms_events))) + + # Strong emotion keyframe stub (HAL / richer envs). Off by default. + # Note: we treat hazard zone as a conservative proxy ("fear") only when env_meta doesn't supply emotion. + if bool(getattr(ctx, "longterm_obs_keyframe_on_emotion", False)): + label = None + intensity = None + + emo_raw = env_meta.get("emotion") or env_meta.get("affect") + if isinstance(emo_raw, dict): + lab = emo_raw.get("label") + inten = emo_raw.get("intensity") + label = lab if isinstance(lab, str) and lab else None + try: + intensity = float(inten) if inten is not None else None + except Exception: + intensity = None + elif isinstance(emo_raw, str) and emo_raw: + label = emo_raw + + # Proxy if no explicit emotion: unsafe zone -> fear-high + if intensity is None and label is None: + if zone_now == "unsafe_cliff_near": + label = "fear" + intensity = 1.0 + + try: + thr = float(getattr(ctx, "longterm_obs_keyframe_emotion_threshold", 0.85) or 0.85) + except Exception: + thr = 0.85 + + high = bool(isinstance(intensity, (int, float)) and float(intensity) >= thr) + prev_label = getattr(ctx, "lt_obs_last_emotion_label", None) + prev_high = bool(getattr(ctx, "lt_obs_last_emotion_high", False)) + + # Rising edge: (not high) -> high, or label changes while high. + if high and (label != prev_label or not prev_high): + force_snapshot = True + inten_txt = f"{float(intensity):.2f}" if isinstance(intensity, (int, float)) else "n/a" + reasons.append(f"emotion:{label or 'n/a'}@{inten_txt}") + + try: + ctx.lt_obs_last_emotion_label = label if isinstance(label, str) else None + ctx.lt_obs_last_emotion_high = high + except Exception: + pass + # [KEYFRAME HOOK + ORDERING INVARIANT] # This is the keyframe/boundary detection point for the env→memory injection path. # inject_obs_into_world(...) runs BEFORE policy selection (Action Center), so any keyframe-driven @@ -7959,6 +9220,21 @@ def _strip_pred_prefix(tok: str) -> str: # After policy selection+execution, a keyframe may also write new engrams (copy-on-write) and # update WorldGraph pointers for future retrieval, without mutating the belief state already # used for action selection in this cycle. See README: "WM ⇄ Column engram pipeline". + + # REAL-EMBODIMENT KEYFRAMES (HAL / non-storyboard): + # In real robots there is no storyboard stage. We will therefore support additional keyframe triggers here, + # evaluated ONLY at this boundary hook (never mid-cycle): + # + # - periodic: every N controller_steps (ctx.longterm_obs_keyframe_period_steps) + # - surprise: pred_err v0 sustained mismatch (ctx.pred_err_v0_last + min_streak) + # - context discontinuity: zone flips (zone_now derived here vs ctx.lt_obs_last_zone) + # - milestones: env_meta milestones and/or derived slot transitions (goal-relevant outcomes) + # - emotion/arousal: env_meta emotion/affect (rising edge into "high"), with a conservative hazard proxy + # + # TIME-BASED SAFETY: + # Even the periodic keyframe must be checked only at this boundary hook so we never split a cycle + # while intermediate planner/policy structures are half-written. + if force_snapshot: old_pred_n = len(getattr(ctx, "lt_obs_slots", {}) or {}) old_cue_n = len(getattr(ctx, "lt_obs_cues", {}) or {}) @@ -7971,7 +9247,17 @@ def _strip_pred_prefix(tok: str) -> str: if bool(getattr(ctx, "longterm_obs_keyframe_log", True)): why = ", ".join(reasons) if reasons else "keyframe" print(f"[env→world] KEYFRAME: {why} | cleared {old_pred_n} pred slot(s), {old_cue_n} cue slot(s)") + try: + ctx.lt_obs_last_keyframe_step = step_no + except Exception: + pass + ctx.lt_obs_last_stage = stage + ctx.lt_obs_last_zone = zone_now + + # For downstream callers (e.g., env-loop) that want a unified keyframe definition: + keyframe = bool(force_snapshot) + keyframe_reasons = list(reasons) def _slot_key(tok: str) -> str: @@ -8126,7 +9412,15 @@ def _slot_key(tok: str) -> str: except Exception: pass - return {"predicates": created_preds, "cues": created_cues, "token_to_bid": token_to_bid} + return { + "predicates": created_preds, + "cues": created_cues, + "token_to_bid": token_to_bid, + "working": working_inj, + "keyframe": bool(keyframe), + "keyframe_reasons": list(keyframe_reasons), + "zone_now": getattr(ctx, "lt_obs_last_zone", None), + } def _wm_creative_update(policy_rt, world, drives, ctx, *, exec_world=None) -> None: @@ -8348,6 +9642,304 @@ def _score_policy(name: str) -> tuple[float, str, dict]: pass +def print_env_loop_tag_legend_once(ctx: Ctx) -> None: + """Print a compact legend for console prefixes (once per session). + + We keep the run output readable for new users, but avoid re-printing the + legend every time menu 35/37 is used. + """ + if ctx is None: + return + if ctx.env_loop_legend_printed: + return + ctx.env_loop_legend_printed = True + + print("\nLegend (console tags):") + print(" [env-loop] closed-loop driver (one cognitive cycle = env update → policy select → policy act)") + print(" [env] environment events (reset/step; with HAL ON, this would be real sensor I/O)") + print(" [env→working] EnvObservation → WorkingMap (fast scratch / map surface)") + print(" [env→world] EnvObservation → WorldGraph (long-term episode index)") + print(" [env→controller] Action Center output (policy selection + execution)") + print(" [wm<->col] WorkingMap ⇄ Column keyframe pipeline (store snapshot → retrieve candidates → apply/merge priors)") + print(" [pred_err] prediction error v0 (expected vs observed); gates auto-retrieve and shapes policy value via penalty on streaks") + print(" [gate:

] gating explanation for policy

") + print(" [pick] which policy was selected this cycle") + print(" [executed] policy execution result (effects show up in the NEXT cycle's observation)") + print(" [maps] selection_on=map used to score; execute_on=map used to run actions") + print(" [obs-mask] partial-observability masking (token drops) when enabled") + print("") + + +def _print_cog_cycle_footer(*, + ctx: "Ctx", + drives, + env_obs, + prev_state, + curr_state, + env_step: int | None, + zone: str | None, + inj: dict[str, Any] | None, + fired_txt: str | None, + col_store_txt: str | None, + col_retrieve_txt: str | None, + col_apply_txt: str | None, + action_applied_this_step: str | None, + next_action_for_env: str | None, + cycle_no: int, + cycle_total: int) -> None: + """ + Print a compact, end-of-cycle footer intended for fast human scanning. + + Intent + ------ + Menu 37 (closed-loop env↔controller runs) produces many diagnostic lines. This footer is the + "cheap digest" line-set that lets a maintainer quickly see what happened in *this* cognitive + cycle in terms of the architecture: + + inputs → MapSurface deltas → Scratch writes → WorldGraph writes → Column ops → action + + The footer is intentionally pragmatic and will evolve as Phase IX/robotics/HAL integration evolves. + Treat it as a reading aid, not a stable API. + + Notes + ----- + - "MapSurface deltas" are derived from EnvState diffs (authoritative simulator truth). MapSurface is + driven by EnvObservation, so EnvState changes correspond to slot-family changes (posture, proximity, + hazard, nipple, etc.). + - "Scratch writes" are summarized from the policy runtime's returned text (added bindings, executed line). + - Column ops are summarized from the wm<->col store/retrieve/apply block when it ran this cycle. + """ + if not bool(getattr(ctx, "env_loop_cycle_summary", True)): + return + + try: + max_items = int(getattr(ctx, "env_loop_cycle_summary_max_items", 6) or 6) + except Exception: + max_items = 6 + + def _sf(x) -> str: + try: + return f"{float(x):.2f}" + except Exception: + return "n/a" + + def _fmt_items(items, *, prefix: str = "", limit: int = 6) -> str: + if not items: + return "(none)" + out = [] + for it in items: + if isinstance(it, str) and it: + out.append(f"{prefix}{it}") + if not out: + return "(none)" + if len(out) <= limit: + return ", ".join(out) + head = ", ".join(out[:limit]) + return f"{head}, +{len(out) - limit} more" + + def _get_state_attr(st, name: str): + try: + return getattr(st, name, None) + except Exception: + return None + + def _surface_deltas(ps, cs) -> list[str]: + # These correspond to the newborn-goat "big slots" that map cleanly onto MapSurface slot-families. + fields = [ + ("posture", "kid_posture"), + ("mom", "mom_distance"), + ("shelter", "shelter_distance"), + ("cliff", "cliff_distance"), + ("nipple", "nipple_state"), + ] + out: list[str] = [] + for label, attr in fields: + a = _get_state_attr(ps, attr) if ps is not None else None + b = _get_state_attr(cs, attr) if cs is not None else None + if ps is None: + out.append(f"{label}={b}") + else: + if a != b: + out.append(f"{label} {a}→{b}") + return out + + def _parse_fired(txt: str | None) -> dict[str, Any]: + # fired text is produced by PolicyRuntime.consider_and_maybe_fire(...). + out: dict[str, Any] = {"policy": None, "added": None, "reward": None, "sel_on": None, "exec_on": None} + if not (isinstance(txt, str) and txt.strip()): + return out + import re # local import (matches existing style in this file) + lines = [ln.strip() for ln in txt.splitlines() if ln.strip()] + if not lines: + return out + + # First line: "policy:xyz (added N bindings)" + first = lines[0] + parts = first.split() + if parts and parts[0].startswith("policy:"): + out["policy"] = parts[0] + m = re.search(r"added\s+(\d+)\s+bindings", first) + if m: + try: + out["added"] = int(m.group(1)) + except Exception: + out["added"] = None + + for ln in lines: + if ln.startswith("[executed]"): + # Example: [executed] policy:follow_mom (ok, reward=+0.10) binding=w38 (b38) + m2 = re.search(r"reward=([+\-]?\d+(?:\.\d+)?)", ln) + if m2: + try: + out["reward"] = float(m2.group(1)) + except Exception: + out["reward"] = None + if ln.startswith("[maps]"): + # Example: [maps] selection_on=WG execute_on=WM + if "selection_on=" in ln: + try: + out["sel_on"] = ln.split("selection_on=", 1)[1].split()[0].strip() + except Exception: + pass + if "execute_on=" in ln: + try: + out["exec_on"] = ln.split("execute_on=", 1)[1].split()[0].strip() + except Exception: + pass + return out + + # Keyframe indicator: best-effort. (Keyframe reasons still appear in the KEYFRAME log line above.) + is_kf = False + try: + is_kf = (getattr(ctx, "lt_obs_last_keyframe_step", None) == getattr(ctx, "controller_steps", None)) + except Exception: + is_kf = False + + st_stage = _get_state_attr(curr_state, "scenario_stage") + st_post = _get_state_attr(curr_state, "kid_posture") + st_mom = _get_state_attr(curr_state, "mom_distance") + st_nip = _get_state_attr(curr_state, "nipple_state") + + dr_h = _sf(getattr(drives, "hunger", None)) + dr_f = _sf(getattr(drives, "fatigue", None)) + dr_w = _sf(getattr(drives, "warmth", None)) + + # WG write summary (env injection) + wg_preds: list[str] = [] + wg_cues: list[str] = [] + if isinstance(inj, dict): + p = inj.get("predicates") + c = inj.get("cues") + if isinstance(p, list): + wg_preds = [x for x in p if isinstance(x, str) and x] + if isinstance(c, list): + wg_cues = [x for x in c if isinstance(x, str) and x] + + # EnvObservation input summary (what crossed the env→agent boundary this tick) + obs_preds: list[str] = [] + obs_cues: list[str] = [] + obs_drop_p = 0 + obs_drop_c = 0 + if env_obs is not None: + try: + pr = getattr(env_obs, "predicates", None) + if isinstance(pr, list): + obs_preds = [str(x).replace("pred:", "", 1) for x in pr if isinstance(x, str) and x] + except Exception: + obs_preds = [] + + try: + cr = getattr(env_obs, "cues", None) + if isinstance(cr, list): + obs_cues = [str(x).replace("cue:", "", 1) for x in cr if isinstance(x, str) and x] + except Exception: + obs_cues = [] + + try: + em = getattr(env_obs, "env_meta", None) + if isinstance(em, dict): + obs_drop_p = int(em.get("obs_mask_dropped_preds", 0) or 0) + obs_drop_c = int(em.get("obs_mask_dropped_cues", 0) or 0) + except Exception: + obs_drop_p = 0 + obs_drop_c = 0 + + fired_info = _parse_fired(fired_txt) + + # ---- line 1: inputs + kf_txt = "KF" if is_kf else "--" + step_txt = str(env_step) if isinstance(env_step, int) else "?" + zone_txt = zone if isinstance(zone, str) else "?" + + mask_txt = "" + if (obs_drop_p or obs_drop_c) and (obs_drop_p >= 0 and obs_drop_c >= 0): + mask_txt = f" mask_drop(p={obs_drop_p} c={obs_drop_c})" + + print( + f"[cycle] IN {kf_txt} cycle={cycle_no}/{cycle_total} env_step={step_txt} " + f"stage={st_stage} posture={st_post} mom={st_mom} nipple={st_nip} zone={zone_txt} " + f"drives(h={dr_h} f={dr_f} w={dr_w}) applied_action={action_applied_this_step!r} " + f"obs(p={len(obs_preds)} c={len(obs_cues)}){mask_txt}" + ) + + # ---- line 1b: observation detail (preds/cues + navpatch summary) + patches_in = getattr(env_obs, "nav_patches", None) or [] + patch_n = len(patches_in) if isinstance(patches_in, list) else 0 + uniq_sig16: set[str] = set() + + if patch_n: + for p in patches_in: + if not isinstance(p, dict): + continue + try: + uniq_sig16.add(navpatch_payload_sig_v1(p)[:16]) + except Exception: + pass + + nav_txt = f"nav_patches={patch_n} uniq_sig16={len(uniq_sig16)}" + + if obs_preds or obs_cues or patch_n: + pred_txt = _fmt_items(obs_preds, prefix="", limit=max_items) if obs_preds else "(none)" + cue_txt = _fmt_items(obs_cues, prefix="", limit=max_items) if obs_cues else "(none)" + print(f"[cycle] OBS preds: {pred_txt} | cues: {cue_txt} | {nav_txt}") + + # ---- line 2: WorkingMap summary (surface deltas + scratch writes) + deltas = _surface_deltas(prev_state, curr_state) + delta_txt = _fmt_items(deltas, prefix="", limit=max_items) if deltas else "(no surface slot change)" + pol = fired_info.get("policy") or next_action_for_env + added = fired_info.get("added") + exec_on = fired_info.get("exec_on") + scratch_txt = "(no policy fired)" + if isinstance(pol, str) and pol: + if isinstance(added, int): + scratch_txt = f"{pol} +{added} binding(s)" + else: + scratch_txt = f"{pol}" + if exec_on: + scratch_txt += f" (exec_on={exec_on})" + print(f"[cycle] WM surfaceΔ: {delta_txt} | scratch: {scratch_txt}") + + # ---- line 3: WorldGraph writes this tick + wg_txt = f"preds+{len(wg_preds)} cues+{len(wg_cues)}" + wg_pred_txt = _fmt_items(wg_preds, prefix="pred:", limit=max_items) + wg_cue_txt = _fmt_items(wg_cues, prefix="cue:", limit=max_items) + print(f"[cycle] WG wrote {wg_txt} | {wg_pred_txt} | {wg_cue_txt}") + + # ---- line 4: Column ops (only meaningful on keyframes) + if col_store_txt or col_retrieve_txt or col_apply_txt: + cs = col_store_txt or "store: (n/a)" + cr = col_retrieve_txt or "retrieve: (n/a)" + ca = col_apply_txt or "apply: (n/a)" + print(f"[cycle] COL {cs} | {cr} | {ca}") + else: + print("[cycle] COL (no wm<->col ops this cycle)") + + # ---- line 5: action recap + r = fired_info.get("reward") + rtxt = f"{r:+.2f}" if isinstance(r, (int, float)) else "n/a" + print(f"[cycle] ACT executed={pol!r} reward={rtxt} next_action={next_action_for_env!r}") + + def run_env_closed_loop_steps(env, world, drives, ctx, policy_rt, n_steps: int) -> None: """ Run N closed-loop steps between the HybridEnvironment and the CCA8 brain @@ -8830,6 +10422,7 @@ def _explain_posture_change(prev_state, curr_state, action_for_env: str | None) print("[env-loop] N must be ≥ 1; nothing to do.") return + print_env_loop_tag_legend_once(ctx) print(f"[env-loop] Running {n_steps} closed-loop cognitive cycle(s) (env↔controller).") print("[env-loop] Each cognitive cycle will:") print(" 1) Advance controller_steps and the temporal soft clock (one drift),") @@ -8838,10 +10431,16 @@ def _explain_posture_change(prev_state, curr_state, action_for_env: str | None) print(" 4) Run ONE controller step (Action Center) and store the last policy name.\n") if not getattr(ctx, "env_episode_started", False): - print("[env-loop] Note: environment episode has not started yet; " - "the first cognitive cycle will call env.reset().") + print("[env-loop] Note: this episode has not started yet; the first cognitive cycle will call env.reset().") + print("[env-loop] (With HAL ON, this is where we'd sample the first real sensor snapshot.)") for i in range(n_steps): print(f"\n[env-loop] Cognitive Cycle {i+1}/{n_steps}") + # Per-cycle capture for the footer summary (reset each cycle). + fired_txt = None + inj = None + col_store_txt = None + col_retrieve_txt = None + col_apply_txt = None # Count one CLOSED-LOOP cognitive cycle (env↔controller iteration). try: @@ -8896,26 +10495,139 @@ def _explain_posture_change(prev_state, curr_state, action_for_env: str | None) # --- Prediction error v0 (no behavior change; just measure + log) --- # Compare last cycle's predicted postcondition (hypothesis) vs this cycle's observed env posture. + pred_posture: str | None = None + obs_posture: str | None = None + err_vec: dict[str, int] = {} + src_txt = "(n/a)" + try: - prev_pred = getattr(ctx, "pred_next_posture", None) - if isinstance(prev_pred, str) and prev_pred: + pred_posture = getattr(ctx, "pred_next_posture", None) + if isinstance(pred_posture, str) and pred_posture: obs_posture = getattr(getattr(env, "state", None), "kid_posture", None) if isinstance(obs_posture, str) and obs_posture: - mismatch = 0 if obs_posture == prev_pred else 1 + mismatch = 0 if obs_posture == pred_posture else 1 err_vec = {"posture": mismatch} ctx.pred_err_v0_last = err_vec src = getattr(ctx, "pred_next_policy", None) src_txt = src if isinstance(src, str) and src else "(n/a)" - print(f"[pred_err] v0 err={err_vec} pred_posture={prev_pred} obs_posture={obs_posture} from={src_txt}") + print( + f"[pred_err] v0 err={err_vec} pred_posture={pred_posture} obs_posture={obs_posture} " + f"from={src_txt}" + ) else: - ctx.pred_err_v0_last = {"posture": 1} + err_vec = {"posture": 1} + ctx.pred_err_v0_last = err_vec else: + err_vec = {} ctx.pred_err_v0_last = {} except Exception: + # If anything goes wrong, keep the signal empty rather than crashing the env-loop. + err_vec = {} + ctx.pred_err_v0_last = {} + + # ---------------------------------------------------------------------------------- + # [pred_err] shaping penalty (extinction pressure when postconditions fail) + # + # Goal: + # If a policy repeatedly predicts a postcondition (v0: posture) and the next env + # observation contradicts it, apply a small negative reward shaping update to that + # policy's skill ledger entry. + # + # Design notes: + # - We ignore the very first mismatch after reset-like transitions by requiring + # a short mismatch streak (>=2) before applying the penalty. + # - We ALSO append a standardized entry into ctx.posture_discrepancy_history so + # existing non-drive tie-break logic (RecoverFall's discrepancy bonus) can use it + # during menu 37 (which otherwise doesn't build history via mini-snapshots). + # + # Knobs (optional; safe defaults if absent): + # ctx.pred_err_shaping_enabled : bool (default True) + # ctx.pred_err_shaping_penalty : float (default 0.15) + # ---------------------------------------------------------------------------------- + try: + shaping_enabled = bool(getattr(ctx, "pred_err_shaping_enabled", True)) + except Exception: + shaping_enabled = True + + try: + pen_mag = float(getattr(ctx, "pred_err_shaping_penalty", 0.15) or 0.15) + except Exception: + pen_mag = 0.15 + + try: + # v0 is posture-only today; treat any non-zero as a mismatch. + v0_posture_err = 0 + if isinstance(err_vec, dict): + try: + v0_posture_err = int(err_vec.get("posture", 0) or 0) + except Exception: + v0_posture_err = 0 + + if ( # pylint: disable=too-many-boolean-expressions + shaping_enabled + and v0_posture_err != 0 + and isinstance(action_for_env, str) + and action_for_env + and isinstance(obs_posture, str) + and isinstance(pred_posture, str) + ): + # 1) Append a standardized discrepancy entry (so RecoverFall can see streaks in menu 37) + entry = ( + f"[discrepancy] env posture={obs_posture!r} " + f"vs policy-expected posture={pred_posture!r} from {action_for_env}" + ) + try: + hist = getattr(ctx, "posture_discrepancy_history", []) + if not isinstance(hist, list): + hist = [] + + # Important: we want repeated mismatches to accumulate so streak>=2 can trigger shaping. + hist.append(entry) + if len(hist) > 50: + del hist[:-50] + + ctx.posture_discrepancy_history = hist + except Exception: + pass + + # 2) Compute a short mismatch streak over the newest entries + streak = 0 + try: + hist2 = getattr(ctx, "posture_discrepancy_history", []) + if isinstance(hist2, list) and hist2: + for h in reversed(hist2[-10:]): + s = str(h) + if ( + (f"from {action_for_env}" in s) + and ("env posture=" in s and obs_posture in s) + and ("policy-expected posture=" in s and pred_posture in s) + ): + streak += 1 + else: + break + except Exception: + streak = 0 + + # 3) Apply shaping only after the streak threshold (ignore first mismatch) + if streak >= 2: + shaping_reward = -abs(pen_mag) * float(v0_posture_err) + update_skill(action_for_env, shaping_reward, ok=False) + try: + q_now = float(skill_q(action_for_env)) + except Exception: + q_now = 0.0 + print( + f"[pred_err] shaping: policy={action_for_env} reward={shaping_reward:+.2f} " + f"(streak={streak}) q={q_now:+.2f}" + ) + except Exception: + # Shaping must never crash the env-loop. pass + # 3) EnvObservation → WorldGraph + BodyMap inj = inject_obs_into_world(world, ctx, env_obs) + try: token_to_bid = inj.get("token_to_bid", {}) if isinstance(inj, dict) else {} except Exception: @@ -8947,6 +10659,7 @@ def _explain_posture_change(prev_state, curr_state, action_for_env: str | None) (zone_now or "unknown"), ) boundary_changed = (prev_sig is not None) and (prev_sig != curr_sig) + # Stage/zone boundary detection (ignore posture/nipple here to keep storage sparse/readable) try: stage_changed = prev_sig[0] != curr_sig[0] @@ -8954,21 +10667,43 @@ def _explain_posture_change(prev_state, curr_state, action_for_env: str | None) except Exception: stage_changed, zone_changed = False, False - if stage_changed or zone_changed: - _ps = prev_sig[0] or "?" - _cs = curr_sig[0] or "?" - _pz = prev_sig[3] or "?" - _cz = curr_sig[3] or "?" - parts: list[str] = [] - if stage_changed: - parts.append(f"stage:{_ps}->{_cs}") - if zone_changed: - parts.append(f"zone:{_pz}->{_cz}") + # IMPORTANT: + # These are "keyframe trigger knobs", but we also treat them as *Phase VII boundary-to-memory* knobs. + # If a user disables stage/zone keyframes, they likely want to disable stage/zone-driven WM↔Column auto-store too. + allow_stage = bool(getattr(ctx, "longterm_obs_keyframe_on_stage_change", True)) + allow_zone = bool(getattr(ctx, "longterm_obs_keyframe_on_zone_change", True)) + + _ps = (prev_sig[0] if isinstance(prev_sig, tuple) else None) or "?" + _cs = (curr_sig[0] if isinstance(curr_sig, tuple) else None) or "?" + _pz = (prev_sig[3] if isinstance(prev_sig, tuple) else None) or "?" + _cz = (curr_sig[3] if isinstance(curr_sig, tuple) else None) or "?" + + parts: list[str] = [] + if stage_changed and allow_stage: + parts.append(f"stage:{_ps}->{_cs}") + if zone_changed and allow_zone: + parts.append(f"zone:{_pz}->{_cz}") + + if parts: wm_auto_store = True wm_auto_reason = "auto_boundary_" + "_".join(parts) + except Exception: boundary_changed = False + # Additional keyframe triggers (periodic / pred_err / milestone / emotion) come from inject_obs_into_world. + inj_kf = bool(inj.get("keyframe")) if isinstance(inj, dict) else False + inj_rs = inj.get("keyframe_reasons") if isinstance(inj, dict) else None + + if inj_kf and not wm_auto_store: + wm_auto_store = True + if isinstance(inj_rs, list) and inj_rs: + why = ";".join(str(x) for x in inj_rs[:3]) + if len(why) > 80: + why = why[:77] + "..." + wm_auto_reason = "auto_keyframe_" + why + else: + wm_auto_reason = "auto_keyframe" state_bid = _phase7_pick_state_bid(token_to_bid) if isinstance(token_to_bid, dict) else None if _phase7_enabled(): @@ -9031,11 +10766,14 @@ def _explain_posture_change(prev_state, curr_state, action_for_env: str | None) if stored and isinstance(eid, str): print(f"[wm<->col] store: ok sig={sig16} bid={bid} eid={eid[:8]}… ({reason_kf})") + col_store_txt = f"store ok sig={sig16} eid={eid[:8]}…" else: print(f"[wm<->col] store: skip why={why_store} sig={sig16} ({reason_kf})") + col_store_txt = f"store skip why={why_store} sig={sig16}" # ---- 2) RETRIEVE line + 3) APPLY line ---- try: + forced_keyframe = bool(wm_auto_store) and not (bool(stage_chg) or bool(zone_chg)) dec = should_autoretrieve_mapsurface( ctx, env_obs, @@ -9043,16 +10781,19 @@ def _explain_posture_change(prev_state, curr_state, action_for_env: str | None) zone=zone_kf, stage_changed=stage_chg, zone_changed=zone_chg, + forced_keyframe=forced_keyframe, boundary_reason=reason_kf, ) - mode_txt = str(dec.get("mode") or "merge") top_k = int(dec.get("top_k") or 5) do_try = bool(dec.get("ok")) if not do_try: print(f"[wm<->col] retrieve: skip why={dec.get('why')} mode={mode_txt} top_k={top_k} ({reason_kf})") + col_retrieve_txt = f"retrieve skip why={dec.get('why')} mode={mode_txt} top_k={top_k}" + print(f"[wm<->col] apply: no-op ({dec.get('why')})") + col_apply_txt = f"apply no-op ({dec.get('why')})" else: exclude = eid if stored and isinstance(eid, str) else None @@ -9081,6 +10822,7 @@ def _explain_posture_change(prev_state, curr_state, action_for_env: str | None) rid_txt = (rid[:8] + "…") if isinstance(rid, str) else "(n/a)" print(f"[wm<->col] retrieve: ok mode={mode_txt} eid={rid_txt} match={match} score={score} op={op} oc={oc} src={src}") + col_retrieve_txt = f"retrieve ok mode={mode_txt} eid={rid_txt} match={match} score={score}" load = out.get("load") if isinstance(out.get("load"), dict) else {} applied_mode = str(load.get("mode") or mode_txt) @@ -9089,19 +10831,25 @@ def _explain_posture_change(prev_state, curr_state, action_for_env: str | None) ent_n = load.get("entities") rel_n = load.get("relations") print(f"[wm<->col] apply: replace entities={ent_n} relations={rel_n}") + col_apply_txt = f"apply replace ent={ent_n} rel={rel_n}" else: ae = load.get("added_entities") fs = load.get("filled_slots") ed = load.get("added_edges") pc = load.get("stored_prior_cues") print(f"[wm<->col] apply: merge added_entities={ae} filled_slots={fs} added_edges={ed} prior_cues={pc}") + col_apply_txt = f"apply merge ent+{ae} slots+{fs} edges+{ed} prior_cues={pc}" else: why = out.get("why") if isinstance(out, dict) else "no-op" print(f"[wm<->col] retrieve: skip why={why} mode={mode_txt} top_k={top_k} ({reason_kf})") + col_retrieve_txt = f"retrieve skip why={why} mode={mode_txt} top_k={top_k}" print(f"[wm<->col] apply: no-op ({why})") + col_apply_txt = f"apply no-op ({why})" except Exception as e: print(f"[wm<->col] retrieve: skip why=error:{e} ({reason_kf})") + col_retrieve_txt = f"retrieve skip error:{e}" print("[wm<->col] apply: no-op (error)") + col_apply_txt = "apply no-op (error)" # ----- END Auto-retrieve (read path) ---- @@ -9118,6 +10866,8 @@ def _explain_posture_change(prev_state, curr_state, action_for_env: str | None) exec_world = ctx.working_world _wm_creative_update(policy_rt, world, drives, ctx, exec_world=exec_world) fired = policy_rt.consider_and_maybe_fire(world, drives, ctx, exec_world=exec_world) + fired_txt = fired if isinstance(fired, str) else None + if fired != "no_match": print(f"[env→controller] {fired}") @@ -9222,10 +10972,75 @@ def _explain_posture_change(prev_state, curr_state, action_for_env: str | None) print(f"[env-loop] explain zone: {zone_expl}") except Exception: pass + # End-of-cycle footer: compact digest for fast scanning (Phase IX). + try: + _print_cog_cycle_footer( + ctx=ctx, + drives=drives, + env_obs=env_obs, + prev_state=prev_state, + curr_state=st, + env_step=step_idx, + zone=zone, + inj=inj if isinstance(inj, dict) else None, + fired_txt=fired_txt if isinstance(fired_txt, str) else None, + col_store_txt=col_store_txt, + col_retrieve_txt=col_retrieve_txt, + col_apply_txt=col_apply_txt, + action_applied_this_step=action_for_env, + next_action_for_env=getattr(ctx, "env_last_action", None), + cycle_no=i + 1, + cycle_total=n_steps, + ) + except Exception: + pass except Exception: pass - print("\n[env-loop] Closed-loop run complete. " + # Per-cycle JSON record (Phase X scaffolding): replayable debug trace. + # This is OFF by default; enable by setting ctx.cycle_json_enabled=True and (optionally) + # ctx.cycle_json_path="cycle_log.jsonl". + try: + if bool(getattr(ctx, "cycle_json_enabled", False)): + st = env.state + try: + zone_now = body_space_zone(ctx) + except Exception: + zone_now = None + + rec = { + "controller_steps": int(getattr(ctx, "controller_steps", 0) or 0), + "env_step": env_info.get("step_index"), + "scenario_stage": getattr(st, "scenario_stage", None), + "posture": getattr(st, "kid_posture", None), + "mom_distance": getattr(st, "mom_distance", None), + "nipple_state": getattr(st, "nipple_state", None), + "zone": zone_now, + "action_applied": action_for_env, + "policy_fired": policy_name, + "obs": { + "predicates": list(getattr(env_obs, "predicates", []) or []), + "cues": list(getattr(env_obs, "cues", []) or []), + "nav_patches": list(getattr(env_obs, "nav_patches", []) or []), + "env_meta": dict(getattr(env_obs, "env_meta", {}) or {}), + }, + "wg_wrote": { + "predicates": list((inj or {}).get("predicates", []) or []), + "cues": list((inj or {}).get("cues", []) or []), + }, + "navpatch_matches": list(getattr(ctx, "navpatch_last_matches", []) or []), + "navpatch_priors": dict(getattr(ctx, "navpatch_last_priors", {}) or {}), + "drives": { + "hunger": float(getattr(drives, "hunger", 0.0) or 0.0), + "fatigue": float(getattr(drives, "fatigue", 0.0) or 0.0), + "warmth": float(getattr(drives, "warmth", 0.0) or 0.0), + }, + } + append_cycle_json_record(ctx, rec) + except Exception: + pass + + print("\n[env-loop] Closed-loop cognitive cycle complete. " "You can inspect details via Snapshot or the mini-snapshot that follows.") try: if getattr(ctx, "working_enabled", False): @@ -9787,6 +11602,7 @@ def candidate_anchors(world, ctx) -> list[str]: # pylint: disable=unused-argume def interactive_loop(args: argparse.Namespace) -> None: """Main interactive loop. """ + # Build initial world/drives fresh world = cca8_world_graph.WorldGraph() #drives = Drives() #Drives(hunger=0.7, fatigue=0.2, warmth=0.6) at time of writing comment @@ -9795,6 +11611,14 @@ def interactive_loop(args: argparse.Namespace) -> None: drives = Drives(hunger=0.5, fatigue=0.3, warmth=0.6) # moderate fatigue so fallback 'follow_mom' can win ctx = Ctx(sigma=0.015, jump=0.2, age_days=0.0, ticks=0) + # Phase X (NavPatch) defaults: enable in the interactive runner. + # Unit tests or external callers can keep this OFF unless they explicitly opt in. + ctx.navpatch_enabled = True + # Phase X: per-cycle JSON trace (JSONL) logging (optional) + ctx.cycle_json_enabled = True + ctx.cycle_json_path = "cycle_log.jsonl" # set to None for in-memory only + ctx.cycle_json_max_records = 2000 # ring buffer size + ctx.temporal = TemporalContext(dim=128, sigma=ctx.sigma, jump=ctx.jump) # temporal soft clock (added) ctx.tvec_last_boundary = ctx.temporal.vector() # seed “last boundary” try: @@ -10072,7 +11896,9 @@ def interactive_loop(args: argparse.Namespace) -> None: name, sigma, jump, k = mapping[args.profile] ctx.profile, ctx.sigma, ctx.jump = name, sigma, jump ctx.winners_k = k - print(f"Profile set: {name} (sigma={sigma}, jump={jump}, k={k})\n") + print(f"Profile set: {name} (sigma={sigma}, jump={jump}, k={k})") + print(" sigma/jump = TemporalContext drift/jump noise scales; k = reserved top-k winners knob (future WTA selection).\n") + POLICY_RT.refresh_loaded(ctx) else: profile = choose_profile(ctx, world) @@ -10080,7 +11906,9 @@ def interactive_loop(args: argparse.Namespace) -> None: sigma, jump, k = profile["ctx_sigma"], profile["ctx_jump"], profile["winners_k"] ctx.sigma, ctx.jump = sigma, jump ctx.winners_k = k - print(f"Profile set: {name} (sigma={sigma}, jump={jump}, k={k})\n") + print(f"Profile set: {name} (sigma={sigma}, jump={jump}, k={k})") + print(" sigma/jump = TemporalContext drift/jump noise scales; k = reserved top-k winners knob (future WTA selection).\n") + POLICY_RT.refresh_loaded(ctx) _io_banner(args, loaded_src, loaded_ok) @@ -12269,17 +14097,17 @@ def _sort_key(t): elif choice == "37": # Multi-step environment closed-loop run print("Selection: Run n Cognitive Cycles (closed-loop timeline)\n") - print("""This selection runs several consecutive closed-loop steps between the + print("""This selection runs several consecutive closed-loop cognitive cycles between the HybridEnvironment (newborn-goat world) and the CCA8 brain. -For each step we will: +For each cognitive cycle we will: 1) Advance controller_steps and the temporal soft clock once, 2) STEP the newborn-goat environment using the last policy action (if any), 3) Inject the resulting EnvObservation into the WorldGraph as pred:/cue: facts, 4) Run ONE controller step (Action Center) and remember the last fired policy. -Tip: run N=1 to single-step the closed-loop cycle. - +This is like pressing menu 35 multiple times, but with a more compact, per-cycle summary. +You can still use menu 35 for detailed, single-step inspection. """) print("[policy-selection] Candidates = dev_gate passes AND trigger(...) returns True.") print("[policy-selection] Winner = highest deficit → non_drive → (RL: q | non-RL: stable order).") @@ -12287,19 +14115,21 @@ def _sort_key(t): # Ask the user for n try: - n_text = input("How many closed-loop step(s) would you like to run? [default: 5]: ").strip() + n_text = input("How many closed-loop cognitive cycle(s) would you like to run? [default: 5]: ").strip() except Exception: n_text = "" try: n_steps = int(n_text) if n_text else 5 except ValueError: n_steps = 5 + if n_steps <= 0: print("[env-loop] N must be ≥ 1; nothing to do.") loop_helper(args.autosave, world, drives, ctx) continue run_env_closed_loop_steps(env, world, drives, ctx, POLICY_RT, n_steps) + print() print("\n[skills-hud] Learned policy values after env-loop:") print("(terminology: hud==heads-up-display; n==number times policy executed; rate==% times we counted policy as successful;") @@ -12635,152 +14465,136 @@ def _prompt_float( #----Menu Selection Code Block------------------------ elif choice == "41": print("Menu 41 retired. Memory pipeline is hardwired (Phase VII daily-driver).") + # If later we decide for this menu selection to be interactive again, we should also update the + # unreachable “Current settings / presets” prints so they display zone/pred_err/milestone/emotion + # keyframe knobs (not just stage). + print("""[guide] This menu is the main "knobs and buttons" reference card for CCA8 experiments. + +NOTE (current runner behavior) +------------------------------ +Menu 41 is currently "reference-only": +- The Phase VII daily-driver memory pipeline is hardwired at startup (see apply_hardwired_profile_phase7). +- This menu prints a cheat sheet and returns to the main menu (it does not run an interactive edit flow right now). + +Mental model you should have to understand these settings +--------------------------------------------------------- + +At runtime it helps to keep FOUR memory structures in mind: + +1) BodyMap (ctx.body_world) + - Tiny, safety-critical belief-now register (posture, mom distance, nipple/milk, shelter/cliff). + - Updated on every EnvObservation tick; read by gates and tie-break logic. + +2) WorkingMap (ctx.working_world) + - Short-term working memory with three layers: + - MapSurface (WM_ROOT + entity nodes): stable, overwrite-by-slot-family belief table. + - Scratch (WM_SCRATCH): policy action chains + predicted postconditions (hypotheses). + - Creative (WM_CREATIVE): counterfactual rollouts (future; inspect-only scaffolding today). + - By default this is NOT a dense tick-log: MapSurface updates entity nodes in place. + (Optional: ctx.working_trace=True appends a legacy per-tick trace for debugging.) + +3) WorldGraph (world) + - Durable long-term episode index that persists (autosave / save session). + - Receives EnvObservation injection (subject to the "long-term env obs" knobs). + - Receives policy writes unless Phase VII working_first is enabled (then policies execute into WorkingMap). + +4) Columns / Engrams (cca8_column.mem) + - Heavy payload store (append-only / immutable records). + - WorldGraph/WorkingMap bindings hold only pointers (binding.engrams["column01"]["id"]=...). + +Fixed dataflow (env → agent boundary) +------------------------------------- +EnvObservation → BodyMap update (always) → WorkingMap mirror (if enabled) → WorldGraph injection (if enabled) + +Keyframes are decided at the env→memory boundary hook (inject_obs_into_world) BEFORE policy selection. + +Cue-slot de-duplication (long-term) +----------------------------------- +In changes-mode we can de-duplicate repeated cue tokens: +- rising edge (absent→present) writes a cue:* binding +- held cues do not create new bindings; they bump prominence on the last cue binding +- if a cue disappears and later reappears, a new cue:* binding is written again + +Long-term EnvObservation → WorldGraph injection +----------------------------------------------- +longterm_obs_enabled (bool) + ON : write env predicates/cues to WorldGraph (subject to mode settings) + OFF : skip long-term WorldGraph writes (BodyMap still updates; WorkingMap still mirrors if enabled) + +longterm_obs_mode ("snapshot" vs "changes") + snapshot : write every observed predicate each tick (dense; old behavior) + changes : treat predicates as state-slots (posture, proximity:mom, hazard:cliff, ...) + write only when a slot changes (plus optional re-asserts/keyframes) + +longterm_obs_reassert_steps (int) + In changes mode: re-emit an unchanged slot after N controller steps (a "re-observation" cadence). + +longterm_obs_dedup_cues (bool) + In changes mode: write cue:* only on rising-edge (absent→present); held cues bump prominence instead. + +longterm_obs_verbose (bool) + In changes mode: print verbose per-slot reuse lines when slots are unchanged (can be noisy). + +Keyframes (episode boundaries) +------------------------------ +In changes mode we maintain per-slot caches (ctx.lt_obs_slots and ctx.lt_obs_cues). +A keyframe clears those caches so the current state is written again as a clean boundary snapshot. + +Keyframe triggers (Phase IX; evaluated ONLY at the env→memory boundary hook): + - env_reset: time_since_birth <= 0.0 + - stage_change: scenario_stage changed (longterm_obs_keyframe_on_stage_change) + - zone_change: coarse safety zone flip (longterm_obs_keyframe_on_zone_change) + - periodic: every N controller steps (longterm_obs_keyframe_period_steps) + - surprise: pred_err v0 sustained mismatch (longterm_obs_keyframe_on_pred_err + min_streak) + - milestones: env_meta milestones and/or derived slot transitions (longterm_obs_keyframe_on_milestone) + - emotion/arousal: env_meta emotion/affect (rising-edge into "high") with threshold + (longterm_obs_keyframe_on_emotion + emotion_threshold) + +Keyframe semantics (what "happens at a boundary"): + - clear long-term slot caches (so the next observation writes as "first" for each slot) + - (if Phase VII WM<->Column pipeline is enabled) boundary store/retrieve/apply can run: + store snapshot → optional retrieve candidates → apply priors (replace or seed/merge) + Reserved future: post-execution write-back / reconsolidation slot. + +Manual keyframe (without env.reset): + - clearing ctx.lt_obs_slots (and ctx.lt_obs_cues) forces the next env observation to be treated as "first". + +Phase VII memory pipeline knobs (WorkingMap-first + run compression) +-------------------------------------------------------------------- +phase7_working_first (bool) + OFF: policies write into WorldGraph (action/preds accumulate there) + ON : policies execute into WorkingMap.Scratch; WorldGraph stays sparse (env keyframes + pointers + runs) + +phase7_run_compress (bool) + If ON: long-term WorldGraph action logging collapses repeated identical policies into one "run" node: + state → action(run_len=3) → state + A boundary (stage/posture/nipple/zone signature change) or policy change closes the run. + +phase7_move_longterm_now_to_env (bool) + OFF: long-term NOW moves only when new bindings are written (or at keyframes) + ON : long-term NOW is actively moved to the current env state binding each step (debug-friendly) + +RL policy selection (epsilon-greedy among triggered candidates) +-------------------------------------------------------------- +rl_enabled (bool) + OFF: deterministic winner: deficit → non-drive tie-break → stable order + ON : epsilon-greedy: explore with probability epsilon; otherwise exploit: + deficit near-tie band (rl_delta) → non-drive tie-break → learned q → stable order + +rl_epsilon (float|None) + Exploration probability in [0..1]. If None, we use ctx.jump as a convenience default. + +rl_delta (float) + Defines the deficit near-tie band within which q is allowed to decide among candidates. + +(For full examples and the authoritative contract, see README.md: keyframes, WM<->Column pipeline, and cognitive cycles.) +""") # Control panel: RL policy selection + WorkingMap + long-term WorldGraph obs injection print("Selection: Control Panel (RL policy selection + memory knobs)\n") - print("""[guide] This menu is the main "knobs and buttons" control panel for CCA8 experiments. + #print("""[guide] This menu is the main "knobs and buttons" control panel for CCA8 experiments. -Mental Model You Should Have to Understand these Settings ---------------------------------------------------------- -At runtime you should consider three graphs operating in the CCA8 simulation: - -1. WorkingMap ( ctx.working_world ) -- This is a short-term raw trace scratchpad. In a way, analagous to mammalian - short-term memory (a component of WM)/ Working Memory, although for real Working Memory need all the associated - processing methods. Keep in mind that in the CCA8 we are not limited to the biological constraints of the brain. - - -WorkingMap receives all the raw inputs. It is designed to be a dense, clock tick-level recording, pruned by - working_max_bindings and only certain parts of it later consolidated to WorldGraph. - -2. WorldGraph ( world ) -- This is the large, durable knowledge representation that persists (autosave/ save session). - It is analagous to the mammalian neocortex (as well as other areas). - - -WorldGraph receives the EnvObservation injection (i.e., processed simulated environment sensory signals) predicates and cues, - which depend on the "Long-term env obs" knob settings. - -WorldGraph receives all the policy writes unless the "Phase VII working_first" is enabled, in which case policy writes go - to the WorkingMap instead. - -3. BodyMap ( ctx.body_world ) -- This is a small, safety-critical map of the agent's body and surroundings, i.e., in the present - moment. It is used largely at present for gating and tie-break of what is the best policy to choose, and zone classf'n. - - -Note that in the brain our egocentric maps, i.e., map of the agent's body and relation to immediate surroundings, is largely - within the posterior parietal cortex, where vision, touch, proprioception are all integrated to create an egocentric - reference frame. On the other hand, in the hippocampus there is largely an allocentric map, i.e., a 'bird's-eye view' of the - world, using its grid and place cells to map the layout of the environment independent of the body's orientation (e.g., if - the agent turns 180 degrees the map still stays the same). At the time of writing, we don't have a clear analogous - hippocampal map, but instead use the WorldGraph and the WorkingMap for this purpose. - -4. Cue-slot Deduplication -- Just as we avoiding filling up WorldGraph with the same predicate bindings over and over again (e.g., - mountain goat trying to stand up 5 times will otherwise give 5 sets of these predicate bindings) (and we use 'prominence' instead - to count how many times nodes are activated), we can de-duplicate repeated sensory cues so we don't have dozens of the same cue - bindings created for the same episode. - -If a cue appears that wasn't then we write a cue:* binding. - -If a cue repeats then we don't create additional bindings but increment prominence. - -If it disappears and then reappears later, then we do write another cue:* binding. - - -Key ideas ---------- - • WorkingMap (ctx.working_world): short-term raw trace. If enabled, every EnvObservation is mirrored here - (episodic, write-everything). It is capped by working_max_bindings via pruning. - -If ENABLED=TRUE ( ctx.working_enabled) then every EnvObservation is mirrored into WorkingMap (pred + cue). - Note: inject_obs_into_world(...) first injects into BodyMap update (always), then WorkingMap mirror (if enabled), then - WorldGraph (if enabled) - Note: Long-term env obs enabled = TRUE (default setting) + if WorkingMap enabled = TRUE then same preds, cues to both. - Note: This writing occurs according to mode = changes/snapshot, plus keyframes, etc. - Note: If Long-term env obs enabled = FALSE (ctx.longterm_obs_enabled=False) then preds, cues go only to WorkingMap. - -If WorkingMap ENABLED=FALSE then WorkingMap exists but won't gent new injections of cues and predicates from the simulated processed environment. - -verbose = TRUE then prints '[env->working] pred:... ->bid ' lines each tick. - -max_bindings ( ctx.working_max_bindings) is how large WorkingMap is allowed to get and then will delete older nodes but - keeps anchors. - -note: if working_enabled=True and longterm_obs_enabled=True then same EnvObservation written to WorkingMap and WorldGraph - - • WorldGraph memory_mode: - episodic = every add_predicate/add_cue call creates a new binding (dense episodic trace). - -every add_predicate(...) creates a new binding even if that tag already exists. - -each time somehting happens it becomes a new node in the graph even if the same tag as before, and thus graph essentially - acts as a literal event log; in a literal timeline you can count occurrences, see ordering, attach metadata to - certain instances of the event; the keyframe segmentation however stays visible. - semantic = reuse identical pred/cue bindings to reduce clutter (experimental). - -if an identical tag already exists, reuse that existing binding id instead of allocating a new one. - -do not have a literal timeline, i.e., repeated events collapse and you lose 'how many times/when exactly aspect'. - -labeled experimental at time of writing since reduces clutter but changes temporal semantics - - • Long-term EnvObservation → WorldGraph injection (this is enforced): - mode=changes => write pred:* only when a state-slot changes (posture, proximity:mom, hazard:cliff, ...). - optional reassert_steps can re-emit unchanged slots periodically. - -note that only pred:* is slot-deduped; cues remain episodic so repeated cues written unless - use WorldGraph memory_mode=semantic or disable long-term env injection. - mode=snapshot => write every observed pred:* each tick (old behavior). - keyframe_on_stage_change forces a one-tick "fresh snapshot" when scenario_stage changes (helps separate contexts). - -IF ENABLED=FALSE ( ctx.longterm_obs_enabled=False ) then envrt preds, cues do not get wirtten to worldGraph - (but BodyMap still updates and WorkingMap updates based on its own settings). - -IF ENABLED=SNAPSHOT then write all preds each tick (old write-everything behavior). - -IF ENABLED=CHANGES then write preds only when its "state slot" changes or optional reassert/keyframes change. - -IF reassert_steps=TRUE then even if a slot is unchanged, re-emit it after N controller steps, i.e., this - allows a period 're-observation' - -IF keyframe_on_stage=TRUE then when scenario_stage changes (e.g., birth-> struggle), it clears the slot - cache so the next tick writes a fresh snapshot, i.e., keyframe segmentation - [mental model for understanding the 'changes mode' -- it uses a 'slot cache' to avoid rewriting the same state over - and over again. If ctx.longterm_obs_mode="changes" then each environtment predicate token treated as (slot, value) - e.g., pred token proximity:mom:far therefore slot key is 'proximity:mom', and this way get, e.g., proximity:mom, - posture, hazard:cliff, etc. 'state slots' - -once have idea of state slots can understand if slot never been seen then emit('first'); if slot token differs then - emit('changed') but if token identical then skip (unless reassert_steps forces periodic re-write). ] - -VERBOSE then prints reuse lines for each unchanged predicate slot. - - • RL policy selection (epsilon-greedy among triggered candidates): - -This is policy selection behaviour when multiple policies trigger, i.e., the 'best policy' has to be picked and this - can be done under non-RL conditions or RL-conditions (see READMD.md or menu selection "Cognitive Cycle" for more details). - If False == NOT enbabled -- non-RL, i.e., deterministic selection of best policy: DELTA-band deficit values, then non-drive tie-break, - stable order tie-break. - IF True == enabled -- RL mode, i.e., epsilon-greedy selection of best policy: - IF EPSILON (ctx.rl_epsilon) == True, then certain % that by luck will "explore" rather than follow decifict - heuristic, and randomly pick a best policy; epsilon 0 to 1, thus .1 means 10% chance explore, 90% chance exploit - Note: 10% chance among all triggered candidate policies (i.e., policy still has to be triggered). - IF EPSILON == 0.0 then 0% explore, 100% exploitation, i.e, deterministic selection of best policy - -RL exploitation to decide best policy: deficit values within DELTA band, then non_drive values, then learned q values, - then stable order. - -JUMP is an older knob ( ctx.jump ) is event-boundary jump scale for TemporalContext soft-clock whereby - sigma=per-tick drift, jump ='bigger change' at boundarie, how much we change when a boundary appears - IF EPSILON == None then effective_epsilon = ctx.jump, e.g., see something printed out like 'epsilon=None, effective=0.200', - which means you didn't set rl_epsilon so the software is using jump =0.200 as the exploration probability - ==> If want RL enabled but no randomness then set rl_enabled = ON, rl_epsilon = 0.0 (otherwise will get rl_epsilon = - ==> If want RL to choose among policies even when deficits differ a bit, set a larger DELTA - - -Settings --------- - --Current Settings -- what the current settings are. You can change them in the sections which follow. -- Note that to speed up entry of settings, you can pess Enter to keep current values. - --Presets are quick configure shortcuts that set the long-term env obs injection knobs with the settings as shown - for you to choose, e.g., bio sets changes enabled=True, keyframes True and reassert every 25 steps versus - e.g., debug sets snapshots enabled=True and verbose=True, so writes every predicate each tick - ---Phase7 s/w dev't -- these settings are software development phase7 (scaffolding for converting the architecture's memory pipeline to - model described above); they are OFF by default. - -working_first - OFF by default and thus policies write into long-term WorldGraph (action/preds accumulate there). - -working_first=ON then policies execute into WorkingMap instead, and WorldGraph stays sparser with mostly keyframes, - cues, 'runs', once run_compress is on - -run_compress =TRUE then WorldGraph stops logging each repeated policy execution as its own action node, but instead - keeps one 'open run' per repeated policy and just updates its metadata. - e.g., instead of state=fallen -> action stand_up -> action stand_up -> action stand_up -> state=standing, - you get: state=fallen -> action stand_up(run len=3) -> state=standing - -key idea is that one node represents a contiguous run of the same policy - -if a boundary occurs (stage/posture/nipple/zone signature changes, e.g.) or the policy changes, then close - the old run and start a new run node - -move_longterm_NOW-to_env -- moves long-term NOW to env state each step; about anchor semantics - -in changes mode, if env didn't change then don't write a new predicate which means NOW might not move - -if OFF then NOW only moves when something is written or at keyframes - -if ON then NOW actively repositioned to current env's binding state each step, and NOW behaves like currrent world pointer. - --Clear WorkingMap -- resets WorkMap which can be useful when you want to start a fresh short-term window with resetting the - environment. --Clear long-term slot cache now -- a manual keyframe so that the next env observation is treated as 'first' - -""") loop_helper(args.autosave, world, drives, ctx) continue @@ -12847,13 +14661,13 @@ def _prompt_float( ctx.longterm_obs_enabled = True ctx.longterm_obs_mode = "changes" ctx.longterm_obs_reassert_steps = 25 - ctx.longterm_obs_keyframe_on_stage_change = True + ctx.longterm_obs_keyframe_on_stage_change = False ctx.longterm_obs_verbose = False elif preset in ("sparse", "minimal"): ctx.longterm_obs_enabled = True ctx.longterm_obs_mode = "changes" ctx.longterm_obs_reassert_steps = 0 - ctx.longterm_obs_keyframe_on_stage_change = True + ctx.longterm_obs_keyframe_on_stage_change = False ctx.longterm_obs_verbose = False elif preset in ("debug", "trace"): ctx.longterm_obs_enabled = True @@ -13000,11 +14814,12 @@ def _prompt_float( raw_kf = input("keyframe_on_stage_change? [Enter=toggle | on | off]: ").strip().lower() if raw_kf in ("on", "true", "1", "yes", "y"): - ctx.longterm_obs_keyframe_on_stage_change = True + ctx.longterm_obs_keyframe_on_stage_change = False elif raw_kf in ("off", "false", "0", "no", "n"): ctx.longterm_obs_keyframe_on_stage_change = False elif raw_kf == "": - ctx.longterm_obs_keyframe_on_stage_change = not bool(getattr(ctx, "longterm_obs_keyframe_on_stage_change", True)) + #ctx.longterm_obs_keyframe_on_stage_change = not bool(getattr(ctx, "longterm_obs_keyframe_on_stage_change", True)) + ctx.longterm_obs_keyframe_on_stage_change = False raw_lv = input("longterm_obs_verbose (show reuse lines)? [Enter=toggle | on | off]: ").strip().lower() if raw_lv in ("on", "true", "1", "yes", "y"): diff --git a/hello.js b/hello.js new file mode 100644 index 0000000..0949511 --- /dev/null +++ b/hello.js @@ -0,0 +1,968 @@ +/* cca8_demo.ts + CCA8 Intro Screens Demo (TypeScript / Node.js) + + Save as: cca8_demo.ts + Compile: tsc cca8_demo.ts + Run: node cca8_demo.js + + Notes: + - This is intentionally a "large but simple" CLI demo: mostly text + a menu. + - No external npm packages required. + - We declare minimal Node globals so TypeScript can compile even without @types/node. +*/ +var fs = require("fs"); +var readline = require("readline"); +var DEMO_VERSION = "0.1.0"; +var CCA8_RUNNER_VERSION = "0.8.2"; // from cca8_run.py __version__ +// ------------------------------ Small string helpers ------------------------------ +function trimStr(s) { + return (s || "").replace(/^\s+|\s+$/g, ""); +} +function lower(s) { + return (s || "").toLowerCase(); +} +function isEmpty(s) { + return trimStr(s) === ""; +} +function padLeft(s, width, ch) { + var ss = String(s); + if (ss.length >= width) + return ss; + var out = ""; + for (var i = 0; i < width - ss.length; i++) + out += ch; + return out + ss; +} +function repeatChar(ch, n) { + var out = ""; + for (var i = 0; i < n; i++) + out += ch; + return out; +} +function startsWith(s, pref) { + var a = String(s); + var p = String(pref); + return a.substring(0, p.length) === p; +} +// ------------------------------ ANSI color helpers ------------------------------ +function envHas(name) { + try { + return !!(process && process.env && process.env[name]); + } + catch (_e) { + return false; + } +} +function envGet(name, fallback) { + try { + var v = process && process.env ? process.env[name] : undefined; + return v ? String(v) : fallback; + } + catch (_e) { + return fallback; + } +} +function shouldUseColor(noColorFlag) { + if (noColorFlag) + return false; + if (envHas("NO_COLOR")) + return false; + try { + return !!(process && process.stdout && process.stdout.isTTY); + } + catch (_e) { + return false; + } +} +function paint(text, code, enabled) { + if (!enabled) + return text; + return "\x1b[" + code + "m" + text + "\x1b[0m"; +} +// ------------------------------ Text payloads (intro screens) ------------------------------ +var ASCII_BADGE = [ + "+--------------------------------------------------------------+", + "| C C A 8 — Causal Cognitive Architecture |", + "+--------------------------------------------------------------+", +].join("\n"); +var ASCII_GOAT = [ + " ____ CCA8", + " .' `-. mountain goat", + "/ _ _ \\", + "| (o)(o) |", + "\\ __ /", + " `'-.____'", +].join("\n"); +// Key ideas (docstring section) +var KEY_IDEAS = [ + "Key ideas for readers and new collaborators", + "------------------------------------------", + "- Predicate: a symbolic fact token (e.g., \"posture:standing\").", + "- Binding: a node instance carrying a predicate tag (pred:) plus meta/engrams.", + "- Edge: a directed link between bindings with a label (often \"then\") for weak causality.", + "- WorldGraph: the small, fast episode index (~5% information). Rich content goes in engrams.", + "- Policy (primitive): behavior object with trigger(world, drives) and execute(world, ctx, drives).", + " The Action Center scans the ordered list of policies and runs the first that triggers (one controller step).", + "- Autosave/Load: JSON snapshot with world, drives, skills, plus a saved_at timestamp.", +].join("\n"); +// Understanding bindings / tagging / policies (abridged but still large) +var UNDERSTANDING = [ + "", + "==================== Understanding Bindings, Edges, Predicates, Cues & Policies ====================", + "", + "What is a Binding?", + " • A small 'episode card' that binds together:", + " - tags (symbols: predicates / actions / cues / anchors)", + " - engrams (pointers to rich memory outside WorldGraph)", + " - meta (provenance, timestamps, light notes)", + " - edges (directed links from this binding)", + "", + " Structure (conceptual):", + " { id:'bN', tags:[...], engrams:{...}, meta:{...}, edges:[{'to':'bK','label':'then','meta':{...}}, ...] }", + "", + "Tag Families (use these prefixes)", + " • pred:* → predicates (facts / goals you might plan TO)", + " examples: pred:posture:standing, pred:posture:fallen, pred:nipple:latched, pred:milk:drinking,", + " pred:proximity:mom:close, pred:proximity:shelter:near, pred:hazard:cliff:near", + "", + " • action:* → actions (verbs; what the agent did or is doing)", + " examples: action:push_up, action:extend_legs, action:orient_to_mom", + "", + " • cue:* → evidence/context you NOTICE (policy triggers); not planner goals", + " examples: cue:vision:silhouette:mom, cue:scent:milk, cue:sound:bleat:mom, cue:terrain:rocky", + " cue:drive:hunger_high, cue:drive:fatigue_high", + "", + " • anchor:* → orientation markers (e.g., anchor:NOW); also mapped in engine anchors {'NOW':'b1'}", + "", + "Drive thresholds (house style)", + " • Canonical storage: numeric values live in the Drives object:", + " drives.hunger, drives.fatigue, drives.warmth", + " • Threshold flags are derived (e.g., hunger>=HUNGER_HIGH) and are optionally emitted as rising-edge cues:", + " cue:drive:hunger_high, cue:drive:fatigue_high", + " • Only use pred:drive:* when you deliberately want a planner goal like pred:drive:warm_enough.", + " Otherwise treat thresholds as evidence (cue:drive:*).", + "", + "Edges = Transitions", + " • We treat edge labels as weak episode links (often just 'then').", + " • Most semantics live in bindings (pred:* and action:*); edge labels are for readability and metrics.", + " • Quantities about the transition live in edge.meta (e.g., meters, duration_s, created_by).", + " • Planner behavior today: BFS/Dijkstra follow structure (node/edge graph), not label meaning.", + "", + "Provenance & Engrams", + " • Who created a binding? binding.meta['policy']='policy:' (or meta.created_by for non-policy writes)", + " • Who created an edge? edge.meta['created_by']='policy:' (or similar)", + " • Where is the rich data? binding.engrams[...] → pointers (large payloads live outside WorldGraph)", + "", + "Maps & Memory (where things live)", + " • WorldGraph → symbolic episode index (bindings/edges/tags); great for inspection + planning over pred:*.", + " • BodyMap → agent-centric working state used for gating (fast, “what do I believe right now?”).", + " • Drives → numeric interoception state (hunger/fatigue/etc.); may emit cue:drive:* threshold events.", + " • Engrams → pointers from bindings to richer payloads stored outside the graph (future: Column / disk store).", + "", + "Do / Don’t (project house style)", + " ✓ Use pred:* for facts/goals/events", + " ✓ Use action:* for verbs (what the agent does)", + " ✓ Use cue:* for evidence/conditions/triggers (including cue:drive:* threshold events)", + " ✓ Put creator/time/notes in meta; put action measurements in edge.meta", + " ✓ Allow anchor-only bindings (e.g., anchor:NOW)", + " ✗ Don’t store large data in tags; put it in engrams", + "", + "Examples", + " pred:posture:fallen --then--> action:push_up --then--> action:extend_legs --then--> pred:posture:standing", + " pred:posture:standing --then--> action:orient_to_mom --then--> pred:seeking_mom --then--> pred:nipple:latched", + "", + "(See README.md → Tagging Standard for more information.)", + "", +].join("\n"); +// Quick Tour (verbatim structure from the runner’s tour header) +var QUICK_TOUR = [ + " === CCA8 Quick Tour ===", + "", + "Note: Pending more tutorial-like upgrade.", + " Currently this 'tour' really just runs some of the menu routines without much explanation.", + " New version to be more interactive and provide better explanations.", + "", + "", + "This tour will do the following and show the following displays:", + " (1) snapshot, (2) temporal context probe, (3) capture a small", + " engram, (4) show the binding pointer (b#), (5) inspect that", + " engram, (6) list/search engrams.", + "Hints: Press Enter to accept defaults. Type Q to exit.", + "", + "**The tutorial portion of the tour is still under construction. All components shown here are available", + " as individual menu selections also -- see those and the README.md file for more details.**", + "", + "[tour] 1/6 — Baseline snapshot", + "Shows CTX and TEMPORAL (dim/sigma/jump; cosine; hash). Next: temporal probe.", + " • CTX shows agent counters (profile, age_days, ticks) and run context.", + " • TEMPORAL is a soft clock (dim/sigma/jump), not wall time.", + " • cosine≈1.000 → same event; <0.90 → “new event soon.”", + " • vhash64 is a compact fingerprint for quick comparisons.", + "", + "[tour] 2/6 — Temporal context probe", + "Updates the soft clock; prints dim/sigma/jump and cosine to last boundary.", + "Next: capture a tiny engram.", + " • boundary() jumps the vector and increments the epoch (event count).", + " • vhash64 vs last_boundary_vhash64 → Hamming bits changed (0..64).", + " • Cosine compares “now” vs last boundary; drift lowers cosine.", + " • Status line summarizes phase (ON-BOUNDARY / DRIFTING / BOUNDARY-SOON).", + "", + "[tour] 3/6 — Capture a tiny engram", + "Adds a memory item with time/provenance; visible in Snapshot. Next: show b#.", + " • capture_scene creates a binding (cue/pred) and a Column engram.", + " • The binding gets a pointer slot (e.g., column01 → EID).", + " • Time attrs (ticks, epoch, tvec64) come from ctx at capture time.", + " • binding.meta['policy'] records provenance when created by a policy.", + "", + "[tour] 4/6 — Show binding pointer (b#)", + "Displays the new binding id and its attach target. Next: inspect that engram.", + " • A binding is the symbolic “memory link”; engram is the rich payload.", + " • The pointer (b#.engrams['slot']=EID) glues symbol ↔ rich memory.", + " • Attaching near NOW/LATEST keeps episodes readable for planning.", + " • Follow the pointer via Snapshot or “Inspect engram by id.”", + "", + "[tour] 5/6 — Inspect engram", + "Shows engram fields (channel, token, attrs). Next: list/search engrams.", + " • meta → attrs (ticks, epoch, tvec64, epoch_vhash64) for time context.", + " • payload → kind/shape/bytes (varies by Column implementation).", + " • Use this to verify data shape and provenance after capture.", + " • Engrams persist across saves; pointers can be re-attached later.", + "", + "[tour] 6/6 — List/search engrams", + "Lists and filters engrams by token/family.", + " • Deduped EIDs with source binding (b#) for quick auditing.", + " • Search by name substring and/or by epoch number.", + " • Useful to confirm capture cadence across boundaries/epochs.", + " • Pair with “Plan from NOW” to see if memory supports behavior.", + "", +].join("\n"); +var PROFILE_NARRATIVE_CHIMP = [ + "", + "Chimpanzee-like brain simulation", + "", + "As per the papers on the Causal Cognitive Architecture, the mountain goat has pre-causal reasoning.", + "The chimpanzee has the main structures of the mountain goat brain (some differences nonetheless in these", + " \"similar\" structures) but enhanced feedback pathways allowing better causal reasoning. Also better", + " combinatorial language.", + "", +].join("\n"); +var PROFILE_NARRATIVE_HUMAN = [ + "", + "Human-like brain simulation", + "", + "As per the papers on the Causal Cognitive Architecture, the mountain goat has pre-causal reasoning.", + "The chimpanzee has the main structures of the mountain goat brain (some differences nonetheless in these", + " \"similar\" structures) but enhanced feedback pathways allowing better causal reasoning. Also better", + " combinatorial language.", + "The human simulation has further enhanced feedback pathways and full causal reasoning, full analogical reasoning", + " and compositional reasoning/language.", + "", +].join("\n"); +var PROFILE_NARRATIVE_MULTI_BRAINS = [ + "", + "Human-like one-agent multiple-brains simulation", + "", + "As per the papers on the Causal Cognitive Architecture, the mountain goat has pre-causal reasoning.", + "The chimpanzee has the main structures of the mountain goat brain (some differences nonetheless in these", + " \"similar\" structures) but enhanced feedback pathways allowing better causal reasoning. Also better", + " combinatorial language.", + "The human simulation has further enhanced feedback pathways and full causal reasoning, full analogical reasoning", + " and compositional reasoning/language.", + "", + "In this model each agent has multiple brains operating in parallel. There is an intelligent voting mechanism to", + " decide on a response whereby each of the 5 processes running in parallel can give a response with an indication", + " of how certain they are this is the best response, and the most certain + most popular response is chosen.", + "As well, all 5 symbolic maps along with their rich store of information in their engrams are continually learning", + " and constantly updated.", + "", +].join("\n"); +var PROFILE_NARRATIVE_SOCIETY = [ + "", + "Human-like one-brain simulation × multiple-agents society", + "", + "As per the papers on the Causal Cognitive Architecture, the mountain goat has pre-causal reasoning.", + "The chimpanzee has the main structures of the mountain goat brain (some differences nonetheless in these", + " \"similar\" structures) but enhanced feedback pathways allowing better causal reasoning. Also better", + " combinatorial language.", + "The human simulation has further enhanced feedback pathways and full causal reasoning, full analogical reasoning", + " and compositional reasoning/language.", + "", + "In this simulation we have multiple agents each with one human-like brain, all interacting with each other.", + "", +].join("\n"); +var PROFILE_NARRATIVE_ADV_PLANNING = [ + "", + "Human-like one-agent multiple-brains simulation with combinatorial planning", + "", + "As per the papers on the Causal Cognitive Architecture, the mountain goat has pre-causal reasoning.", + "The chimpanzee has the main structures of the mountain goat brain (some differences nonetheless in these", + "\"similar\" structures) but enhanced feedback pathways allowing better causal reasoning. Also better", + "combinatorial language.", + "The human simulation has further enhanced feedback pathways and full causal reasoning, full analogical reasoning", + "and compositional reasoning/language.", + "", + "In this model there are multiple brains, e.g., 5 at the time of this writing, in one agent.", + "There is an intelligent voting mechanism to decide on a response whereby each of the 5 processes running in", + "parallel can give a response with an indication of how certain they are this is the best response, and the most", + "certain + most popular response is chosen. As well, all 5 symbolic maps along with their rich store of", + "information in their engrams are continually learning and updated.", + "", + "In addition, in this model each brain has multiple von Neumann processors to independently explore different", + "possible routes to take or different possible decisions to make.", + "", +].join("\n"); +var PROFILE_NARRATIVE_SUPER = [ + "", + "Super-human-like machine simulation", + "", + "Features scaffolding for an ASI-grade architecture:", + " • Hierarchical memory: massive multi-modal engrams (vision/sound/touch/text) linked to a compact symbolic index.", + " • Weighted graph planning: edges carry costs/uncertainty; A*/landmarks for long-range navigation in concept space.", + " • Meta-controller: blends proposals from symbolic search, neural value estimation, and program-synthesis planning.", + " • Self-healing & explanation: detect/repair inconsistent states; produce human-readable rationales for actions.", + " • Tool-use & embodiment: external tools (math/vision/robots) wrapped as policies with provenances and safeguards.", + " • Safety envelope: constraint-checking policies that can veto/redirect unsafe plans.", + "", + "This stub prints a dry-run of the meta-controller triage and falls back to the current==Mountain Goat profile.", + "", +].join("\n"); +var PROFILE_FALLBACK = [ + "Although scaffolding is in place, currently this evolutionary-like configuration is not available.", + "Profile will be set to mountain goat-like brain simulation.", + "", +].join("\n"); +// ------------------------------ CLI + logging ------------------------------ +var Logger = /** @class */ (function () { + function Logger(path) { + this.logStream = null; + if (path && !isEmpty(path)) { + try { + this.logStream = fs.createWriteStream(path, { flags: "a" }); + } + catch (_e) { + this.logStream = null; + } + } + } + Logger.prototype.close = function () { + try { + if (this.logStream) + this.logStream.end(); + } + catch (_e) { + // ignore + } + this.logStream = null; + }; + Logger.prototype.write = function (text) { + // Console + try { + process.stdout.write(text); + } + catch (_e) { + // fallback + try { + console.log(text); + } + catch (_e2) { /* ignore */ } + } + // File transcript + try { + if (this.logStream) + this.logStream.write(text); + } + catch (_e) { + // ignore + } + }; + Logger.prototype.line = function (text) { + this.write(text + "\n"); + }; + Logger.prototype.block = function (text) { + // Preserve the text as-is (and ensure it ends with newline) + this.write(text); + if (!endsWithNewline(text)) + this.write("\n"); + }; + return Logger; +}()); +function endsWithNewline(s) { + if (!s) + return false; + var last = s.substring(s.length - 1); + return last === "\n"; +} +var Cli = /** @class */ (function () { + function Cli(logger) { + this.logger = logger; + this.rl = readline.createInterface({ input: process.stdin, output: process.stdout }); + } + Cli.prototype.close = function () { + try { + this.rl.close(); + } + catch (_e) { /* ignore */ } + this.logger.close(); + }; + Cli.prototype.out = function (line) { + this.logger.line(line); + }; + Cli.prototype.outBlock = function (text) { + this.logger.block(text); + }; + Cli.prototype.prompt = function (question, cb) { + this.rl.question(question, function (answer) { + cb(String(answer)); + }); + }; + Cli.prototype.pause = function (cb) { + this.prompt("\nPress Enter to continue (or type Q to quit): ", cb); + }; + return Cli; +}()); +// ------------------------------ Arg parsing ------------------------------ +function defaultOptions() { + return { + about: false, + version: false, + understanding: false, + tour: false, + noIntro: false, + noColor: false, + logo: null, + profile: null, + logPath: null, + }; +} +function parseArgs(argv) { + var opts = defaultOptions(); + var i = 0; + while (i < argv.length) { + var a = String(argv[i]); + if (a === "--version") { + opts.version = true; + i += 1; + continue; + } + if (a === "--about") { + opts.about = true; + i += 1; + continue; + } + if (a === "--understanding") { + opts.understanding = true; + i += 1; + continue; + } + if (a === "--tour") { + opts.tour = true; + i += 1; + continue; + } + if (a === "--no-intro") { + opts.noIntro = true; + i += 1; + continue; + } + if (a === "--no-color") { + opts.noColor = true; + i += 1; + continue; + } + if (a === "--logo") { + var v = (i + 1 < argv.length) ? String(argv[i + 1]) : ""; + var vv = lower(trimStr(v)); + if (vv === "badge" || vv === "goat" || vv === "off") { + opts.logo = vv; + i += 2; + continue; + } + i += 1; + continue; + } + if (a === "--profile") { + var v2 = (i + 1 < argv.length) ? String(argv[i + 1]) : ""; + if (!isEmpty(v2)) { + opts.profile = trimStr(v2); + i += 2; + continue; + } + i += 1; + continue; + } + if (a === "--log") { + var lp = (i + 1 < argv.length) ? String(argv[i + 1]) : ""; + if (!isEmpty(lp)) { + opts.logPath = trimStr(lp); + i += 2; + continue; + } + i += 1; + continue; + } + if (a === "--help" || a === "-h") { + // Treat as about-ish + opts.about = true; + i += 1; + continue; + } + // Unknown flag: ignore + i += 1; + } + return opts; +} +// ------------------------------ Renderers (banner, about, etc.) ------------------------------ +function logoFromEnvOrDefault(explicit) { + if (explicit) + return explicit; + var v = lower(trimStr(envGet("CCA8_LOGO", "badge"))); + if (v === "badge" || v === "goat" || v === "off") + return v; + return "badge"; +} +function printAsciiLogo(cli, style, colorEnabled) { + if (style === "off") + return; + var art = (style === "goat") ? ASCII_GOAT : ASCII_BADGE; + if (colorEnabled) { + if (style === "badge") { + art = art.replace("C C A 8", paint("C C A 8", "1;36", true)); + } + else if (style === "goat") { + art = paint(art, "33", true); + } + } + cli.outBlock(art + "\n"); +} +function printHeader(cli, st) { + cli.out(""); + cli.out(""); + cli.out("# " + repeatChar("-", 86)); + cli.out("# NEW RUN NEW RUN"); + cli.out("# " + repeatChar("-", 86)); + cli.out(""); + cli.out("A Warm Welcome to the CCA8 Mammalian Brain Simulation"); + cli.out("(cca8_run.py v" + CCA8_RUNNER_VERSION + " | cca8_demo.ts v" + DEMO_VERSION + ")"); + cli.out(""); + // Header prints the goat logo (mirrors print_header(style="goat")) + printAsciiLogo(cli, "goat", st.colorEnabled); + var entry = getEntrypoint(); + var osName = getOsLabel(); + cli.out("Entry point program being run: " + entry); + cli.out("OS: " + osName + " (see system-dependent utilities for more detailed system/simulation info)"); + cli.out("(for non-interactive execution, run with --help to see optional flags you can set)"); + cli.out(""); + cli.out("Embodiment: HAL (hardware abstraction layer) setting: " + st.halStatus); + cli.out("Embodiment: body_type|version_number|serial_number (i.e. robotic embodiment): " + st.bodyStatus); + cli.out(""); + cli.out("The simulation of the cognitive architecture can be adjusted to add or take away"); + cli.out(" various features, allowing exploration of different evolutionary-like configurations."); + cli.out(""); + cli.out(" 1. Mountain Goat-like brain simulation"); + cli.out(" 2. Chimpanzee-like brain simulation"); + cli.out(" 3. Human-like brain simulation"); + cli.out(" 4. Human-like one-agent multiple-brains simulation"); + cli.out(" 5. Human-like one-brain simulation × multiple-agents society"); + cli.out(" 6. Human-like one-agent multiple-brains simulation with combinatorial planning"); + cli.out(" 7. Super-Human-like machine simulation"); + cli.out(" T. Tutorial (more information) on using and maintaining this program, references"); + cli.out(""); +} +function getEntrypoint() { + try { + var p = process && process.argv && process.argv.length >= 2 ? String(process.argv[1]) : "(unknown)"; + return p; + } + catch (_e) { + return "(unknown)"; + } +} +function getOsLabel() { + try { + return String(process.platform); + } + catch (_e) { + return "(unknown)"; + } +} +function printAbout(cli) { + cli.out(""); + cli.out("CCA8 Demo — About"); + cli.out(repeatChar("-", 78)); + cli.out(""); + cli.outBlock(KEY_IDEAS + "\n"); + cli.out(""); + cli.out("This demo program is a text-only CLI wrapper around the intro/help text."); + cli.out("It does NOT run the CCA8 simulation itself; it only teaches what the terms mean."); + cli.out(""); + cli.out("Try: --understanding | --tour | --about | --version | --no-intro"); + cli.out(""); +} +function printVersion(cli) { + cli.out("cca8_demo.ts v" + DEMO_VERSION); + cli.out("based on cca8_run.py v" + CCA8_RUNNER_VERSION); +} +function printUnderstanding(cli) { + cli.outBlock(UNDERSTANDING); +} +function printQuickTour(cli) { + cli.outBlock(QUICK_TOUR); +} +function printTutorialMenu(cli) { + cli.out(""); + cli.out("Tutorial options:"); + cli.out(" 1) README/compendium System Documentation"); + cli.out(" 2) Console Tour (pending) (use menu item 'Quick Tour' here)"); + cli.out(" [Enter] Cancel"); + cli.out(""); +} +function showReadmeHint(cli) { + // In Python runner, 'T' opens README.md. Here we just print the expected path. + cli.out(""); + cli.out("[tutorial] README.md is the compendium document in the CCA8 project folder."); + cli.out("[tutorial] Open it in your editor (or the repo viewer) for deeper details and references."); + cli.out(""); +} +// ------------------------------ Profile selection ------------------------------ +function normalizeProfileInput(s) { + var x = lower(trimStr(s)); + // numeric shortcuts + if (x === "1") + return "goat"; + if (x === "2") + return "chimp"; + if (x === "3") + return "human"; + if (x === "4") + return "multi"; + if (x === "5") + return "society"; + if (x === "6") + return "planning"; + if (x === "7") + return "super"; + // name shortcuts + if (x === "goat" || x === "mountain_goat" || x === "mountaingoat") + return "goat"; + if (x === "chimp" || x === "chimpanzee") + return "chimp"; + if (x === "human") + return "human"; + if (x === "multi" || x === "multi_brain" || x === "multibrains" || x === "multiple_brains") + return "multi"; + if (x === "society" || x === "agents" || x === "multi_agents" || x === "multiagent") + return "society"; + if (x === "planning" || x === "adv" || x === "combinatorial") + return "planning"; + if (x === "super" || x === "asi" || x === "superhuman") + return "super"; + return x; +} +function profileNarrative(kind) { + if (kind === "chimp") + return PROFILE_NARRATIVE_CHIMP + PROFILE_FALLBACK; + if (kind === "human") + return PROFILE_NARRATIVE_HUMAN + PROFILE_FALLBACK; + if (kind === "multi") + return PROFILE_NARRATIVE_MULTI_BRAINS + PROFILE_FALLBACK; + if (kind === "society") + return PROFILE_NARRATIVE_SOCIETY + PROFILE_FALLBACK; + if (kind === "planning") + return PROFILE_NARRATIVE_ADV_PLANNING + PROFILE_FALLBACK; + if (kind === "super") + return PROFILE_NARRATIVE_SUPER + PROFILE_FALLBACK; + return ""; +} +function promptForProfile(cli, st, preselected, cb) { + // If preselected, apply it without prompting (but still show narrative if it’s not goat). + if (preselected && !isEmpty(preselected)) { + var kind = normalizeProfileInput(preselected); + if (kind !== "goat" && kind !== "t") { + var txt = profileNarrative(kind); + if (!isEmpty(txt)) + cli.outBlock(txt); + } + st.profileName = "Mountain Goat"; + cb(st.profileName); + return; + } + var promptText = "Please make a choice [1–7 or T | Enter = Mountain Goat]: "; + cli.prompt(promptText, function (answer) { + var raw = trimStr(answer); + if (raw === "") { + st.profileName = "Mountain Goat"; + cb(st.profileName); + return; + } + var a = lower(raw); + if (a === "t") { + printTutorialMenu(cli); + cli.prompt("Choose: ", function (pick) { + var p = trimStr(pick); + if (p === "1") { + showReadmeHint(cli); + } + else if (p === "2") { + cli.out("Console tour is pending; please use the Quick Tour menu item (or README) for now."); + cli.out(""); + } + else { + cli.out("(cancelled)"); + cli.out(""); + } + // Re-prompt for profile after tutorial menu + promptForProfile(cli, st, null, cb); + }); + return; + } + var kind = normalizeProfileInput(raw); + if (kind !== "goat") { + var txt2 = profileNarrative(kind); + if (!isEmpty(txt2)) + cli.outBlock(txt2); + } + // In the runner, all other profiles fall back to goat + st.profileName = "Mountain Goat"; + cb(st.profileName); + }); +} +// ------------------------------ Main menu (simple but a bit “runner-like”) ------------------------------ +var MENU_TEXT = [ + "", + "[hints for text selection instead of numerical selection]", + "", + "# Quick Start & Tutorial", + "1) Understanding bindings, edges, predicates, cues, policies [understanding, tagging]", + "2) CCA8 Quick Tour (text-only) [help, tutorial, tour]", + "", + "# Quick Start / Overview", + "3) About / Key ideas [about]", + "4) Show intro header again [header]", + "5) Choose profile again [profile]", + "", + "# Misc", + "6) Show ASCII logo (badge/goat/off) [logo]", + "7) Quit [quit, exit]", + "", + "Select: ", +].join("\n"); +var MIN_PREFIX = 3; +var ALIASES = { + // menu items + "understanding": "1", + "tagging": "1", + "help": "2", + "tutorial": "2", + "tour": "2", + "about": "3", + "header": "4", + "profile": "5", + "logo": "6", + "badge": "6", + "goat": "6", + "off": "6", + "quit": "7", + "exit": "7", + "q": "7", +}; +function routeAlias(cmd) { + var s = lower(trimStr(cmd)); + if (Object.prototype.hasOwnProperty.call(ALIASES, s)) { + return { routed: ALIASES[s], matches: [] }; + } + if (s.length >= MIN_PREFIX) { + var keys = Object.keys(ALIASES); + var matches = []; + for (var i = 0; i < keys.length; i++) { + var k = keys[i]; + if (startsWith(k, s)) + matches.push(k); + } + if (matches.length === 1) + return { routed: ALIASES[matches[0]], matches: matches }; + return { routed: null, matches: matches }; + } + return { routed: null, matches: [] }; +} +function menuLoop(cli, st) { + cli.prompt(MENU_TEXT, function (rawAnswer) { + var choice = trimStr(rawAnswer); + var lowerChoice = lower(choice); + if (!isEmpty(choice)) { + if (!isDigits(choice)) { + var routed = routeAlias(choice); + if (routed.routed !== null) { + choice = routed.routed; + } + else if (routed.matches.length > 1) { + cli.out("[help] Ambiguous input '" + lowerChoice + "'. Try one of: " + routed.matches.slice(0, 8).join(", ") + (routed.matches.length > 8 ? "..." : "")); + menuLoop(cli, st); + return; + } + } + } + if (choice === "1") { + printUnderstanding(cli); + cli.pause(function (ans) { + if (lower(trimStr(ans)) === "q") { + cli.out("Goodbye."); + cli.close(); + return; + } + menuLoop(cli, st); + }); + return; + } + if (choice === "2") { + printQuickTour(cli); + cli.pause(function (ans2) { + if (lower(trimStr(ans2)) === "q") { + cli.out("Goodbye."); + cli.close(); + return; + } + menuLoop(cli, st); + }); + return; + } + if (choice === "3") { + printAbout(cli); + cli.pause(function (ans3) { + if (lower(trimStr(ans3)) === "q") { + cli.out("Goodbye."); + cli.close(); + return; + } + menuLoop(cli, st); + }); + return; + } + if (choice === "4") { + printHeader(cli, st); + cli.pause(function (ans4) { + if (lower(trimStr(ans4)) === "q") { + cli.out("Goodbye."); + cli.close(); + return; + } + menuLoop(cli, st); + }); + return; + } + if (choice === "5") { + promptForProfile(cli, st, null, function (_profile) { + cli.out("Profile set: " + st.profileName + " (scaffolding profiles fall back to goat for now)."); + cli.out(""); + menuLoop(cli, st); + }); + return; + } + if (choice === "6") { + // Let user pick a logo style for display only (badge/goat/off) + cli.out(""); + cli.out("Logo options:"); + cli.out(" 1) badge"); + cli.out(" 2) goat"); + cli.out(" 3) off"); + cli.out(""); + cli.prompt("Choose (default=badge): ", function (pick) { + var p = lower(trimStr(pick)); + var style = "badge"; + if (p === "2" || p === "goat") + style = "goat"; + else if (p === "3" || p === "off") + style = "off"; + else + style = "badge"; + st.logoStyle = style; + cli.out(""); + cli.out("[logo] style set to: " + style); + cli.out(""); + printAsciiLogo(cli, style, st.colorEnabled); + cli.pause(function (_ans5) { + menuLoop(cli, st); + }); + }); + return; + } + if (choice === "7" || lowerChoice === "q") { + cli.out("Goodbye."); + cli.close(); + return; + } + // default: treat empty as "show menu again" + menuLoop(cli, st); + }); +} +function isDigits(s) { + var t = trimStr(s); + if (t.length === 0) + return false; + for (var i = 0; i < t.length; i++) { + var c = t.charCodeAt(i); + if (c < 48 || c > 57) + return false; + } + return true; +} +// ------------------------------ main() ------------------------------ +function main() { + var args = parseArgs(getArgv()); + var logger = new Logger(args.logPath); + var cli = new Cli(logger); + var state = { + profileName: "Mountain Goat", + halStatus: "HAL: off (no embodiment)", + bodyStatus: "Body: (none)", + logoStyle: logoFromEnvOrDefault(args.logo), + colorEnabled: shouldUseColor(args.noColor), + }; + // One-shot modes + if (args.version) { + printVersion(cli); + cli.close(); + return; + } + if (args.about) { + printAbout(cli); + cli.close(); + return; + } + if (args.understanding) { + printUnderstanding(cli); + cli.close(); + return; + } + if (args.tour) { + printQuickTour(cli); + cli.close(); + return; + } + // Interactive mode + if (!args.noIntro) { + printHeader(cli, state); + } + else { + // Still show the badge logo (respect env/flags) + printAsciiLogo(cli, state.logoStyle, state.colorEnabled); + } + // Profile selection then menu loop + promptForProfile(cli, state, args.profile, function (_profileName) { + cli.out("Profile set: " + state.profileName + " (sigma/jump/winners_k omitted in this TS demo)."); + cli.out(""); + menuLoop(cli, state); + }); +} +function getArgv() { + try { + var out = []; + var a = process && process.argv ? process.argv : []; + for (var i = 2; i < a.length; i++) + out.push(String(a[i])); + return out; + } + catch (_e) { + return []; + } +} +main(); diff --git a/hello.ts b/hello.ts new file mode 100644 index 0000000..5bc29b2 --- /dev/null +++ b/hello.ts @@ -0,0 +1,1048 @@ +/* cca8_demo.ts + CCA8 Intro Screens Demo (TypeScript / Node.js) + + Save as: cca8_demo.ts + Compile: tsc cca8_demo.ts + Run: node cca8_demo.js + + Notes: + - This is intentionally a "large but simple" CLI demo: mostly text + a menu. + - No external npm packages required. + - We declare minimal Node globals so TypeScript can compile even without @types/node. +*/ + +declare const process: any; +declare function require(name: string): any; + +const fs = require("fs"); +const readline = require("readline"); + +type LogoStyle = "badge" | "goat" | "off"; + +interface AppOptions { + about: boolean; + version: boolean; + understanding: boolean; + tour: boolean; + + noIntro: boolean; + noColor: boolean; // override env NO_COLOR + logo: LogoStyle | null; // override env CCA8_LOGO + profile: string | null; // optional preselect (goat/chimp/human/society/planning/super or 1..7) + + logPath: string | null; // transcript file path +} + +interface RuntimeState { + profileName: string; + halStatus: string; + bodyStatus: string; + logoStyle: LogoStyle; + colorEnabled: boolean; +} + +const DEMO_VERSION = "0.1.0"; +const CCA8_RUNNER_VERSION = "0.8.2"; // from cca8_run.py __version__ + +// ------------------------------ Small string helpers ------------------------------ + +function trimStr(s: string): string { + return (s || "").replace(/^\s+|\s+$/g, ""); +} + +function lower(s: string): string { + return (s || "").toLowerCase(); +} + +function isEmpty(s: string): boolean { + return trimStr(s) === ""; +} + +function padLeft(s: string, width: number, ch: string): string { + const ss = String(s); + if (ss.length >= width) return ss; + let out = ""; + for (let i = 0; i < width - ss.length; i++) out += ch; + return out + ss; +} + +function repeatChar(ch: string, n: number): string { + let out = ""; + for (let i = 0; i < n; i++) out += ch; + return out; +} + +function startsWith(s: string, pref: string): boolean { + const a = String(s); + const p = String(pref); + return a.substring(0, p.length) === p; +} + +// ------------------------------ ANSI color helpers ------------------------------ + +function envHas(name: string): boolean { + try { + return !!(process && process.env && process.env[name]); + } catch (_e) { + return false; + } +} + +function envGet(name: string, fallback: string): string { + try { + const v = process && process.env ? process.env[name] : undefined; + return v ? String(v) : fallback; + } catch (_e) { + return fallback; + } +} + +function shouldUseColor(noColorFlag: boolean): boolean { + if (noColorFlag) return false; + if (envHas("NO_COLOR")) return false; + try { + return !!(process && process.stdout && process.stdout.isTTY); + } catch (_e) { + return false; + } +} + +function paint(text: string, code: string, enabled: boolean): string { + if (!enabled) return text; + return "\x1b[" + code + "m" + text + "\x1b[0m"; +} + +// ------------------------------ Text payloads (intro screens) ------------------------------ + +const ASCII_BADGE = [ + "+--------------------------------------------------------------+", + "| C C A 8 — Causal Cognitive Architecture |", + "+--------------------------------------------------------------+", +].join("\n"); + +const ASCII_GOAT = [ + " ____ CCA8", + " .' `-. mountain goat", + "/ _ _ \\", + "| (o)(o) |", + "\\ __ /", + " `'-.____'", +].join("\n"); + +// Key ideas (docstring section) +const KEY_IDEAS = [ + "Key ideas for readers and new collaborators", + "------------------------------------------", + "- Predicate: a symbolic fact token (e.g., \"posture:standing\").", + "- Binding: a node instance carrying a predicate tag (pred:) plus meta/engrams.", + "- Edge: a directed link between bindings with a label (often \"then\") for weak causality.", + "- WorldGraph: the small, fast episode index (~5% information). Rich content goes in engrams.", + "- Policy (primitive): behavior object with trigger(world, drives) and execute(world, ctx, drives).", + " The Action Center scans the ordered list of policies and runs the first that triggers (one controller step).", + "- Autosave/Load: JSON snapshot with world, drives, skills, plus a saved_at timestamp.", +].join("\n"); + +// Understanding bindings / tagging / policies (abridged but still large) +const UNDERSTANDING = [ + "", + "==================== Understanding Bindings, Edges, Predicates, Cues & Policies ====================", + "", + "What is a Binding?", + " • A small 'episode card' that binds together:", + " - tags (symbols: predicates / actions / cues / anchors)", + " - engrams (pointers to rich memory outside WorldGraph)", + " - meta (provenance, timestamps, light notes)", + " - edges (directed links from this binding)", + "", + " Structure (conceptual):", + " { id:'bN', tags:[...], engrams:{...}, meta:{...}, edges:[{'to':'bK','label':'then','meta':{...}}, ...] }", + "", + "Tag Families (use these prefixes)", + " • pred:* → predicates (facts / goals you might plan TO)", + " examples: pred:posture:standing, pred:posture:fallen, pred:nipple:latched, pred:milk:drinking,", + " pred:proximity:mom:close, pred:proximity:shelter:near, pred:hazard:cliff:near", + "", + " • action:* → actions (verbs; what the agent did or is doing)", + " examples: action:push_up, action:extend_legs, action:orient_to_mom", + "", + " • cue:* → evidence/context you NOTICE (policy triggers); not planner goals", + " examples: cue:vision:silhouette:mom, cue:scent:milk, cue:sound:bleat:mom, cue:terrain:rocky", + " cue:drive:hunger_high, cue:drive:fatigue_high", + "", + " • anchor:* → orientation markers (e.g., anchor:NOW); also mapped in engine anchors {'NOW':'b1'}", + "", + "Drive thresholds (house style)", + " • Canonical storage: numeric values live in the Drives object:", + " drives.hunger, drives.fatigue, drives.warmth", + " • Threshold flags are derived (e.g., hunger>=HUNGER_HIGH) and are optionally emitted as rising-edge cues:", + " cue:drive:hunger_high, cue:drive:fatigue_high", + " • Only use pred:drive:* when you deliberately want a planner goal like pred:drive:warm_enough.", + " Otherwise treat thresholds as evidence (cue:drive:*).", + "", + "Edges = Transitions", + " • We treat edge labels as weak episode links (often just 'then').", + " • Most semantics live in bindings (pred:* and action:*); edge labels are for readability and metrics.", + " • Quantities about the transition live in edge.meta (e.g., meters, duration_s, created_by).", + " • Planner behavior today: BFS/Dijkstra follow structure (node/edge graph), not label meaning.", + "", + "Provenance & Engrams", + " • Who created a binding? binding.meta['policy']='policy:' (or meta.created_by for non-policy writes)", + " • Who created an edge? edge.meta['created_by']='policy:' (or similar)", + " • Where is the rich data? binding.engrams[...] → pointers (large payloads live outside WorldGraph)", + "", + "Maps & Memory (where things live)", + " • WorldGraph → symbolic episode index (bindings/edges/tags); great for inspection + planning over pred:*.", + " • BodyMap → agent-centric working state used for gating (fast, “what do I believe right now?”).", + " • Drives → numeric interoception state (hunger/fatigue/etc.); may emit cue:drive:* threshold events.", + " • Engrams → pointers from bindings to richer payloads stored outside the graph (future: Column / disk store).", + "", + "Do / Don’t (project house style)", + " ✓ Use pred:* for facts/goals/events", + " ✓ Use action:* for verbs (what the agent does)", + " ✓ Use cue:* for evidence/conditions/triggers (including cue:drive:* threshold events)", + " ✓ Put creator/time/notes in meta; put action measurements in edge.meta", + " ✓ Allow anchor-only bindings (e.g., anchor:NOW)", + " ✗ Don’t store large data in tags; put it in engrams", + "", + "Examples", + " pred:posture:fallen --then--> action:push_up --then--> action:extend_legs --then--> pred:posture:standing", + " pred:posture:standing --then--> action:orient_to_mom --then--> pred:seeking_mom --then--> pred:nipple:latched", + "", + "(See README.md → Tagging Standard for more information.)", + "", +].join("\n"); + +// Quick Tour (verbatim structure from the runner’s tour header) +const QUICK_TOUR = [ + " === CCA8 Quick Tour ===", + "", + "Note: Pending more tutorial-like upgrade.", + " Currently this 'tour' really just runs some of the menu routines without much explanation.", + " New version to be more interactive and provide better explanations.", + "", + "", + "This tour will do the following and show the following displays:", + " (1) snapshot, (2) temporal context probe, (3) capture a small", + " engram, (4) show the binding pointer (b#), (5) inspect that", + " engram, (6) list/search engrams.", + "Hints: Press Enter to accept defaults. Type Q to exit.", + "", + "**The tutorial portion of the tour is still under construction. All components shown here are available", + " as individual menu selections also -- see those and the README.md file for more details.**", + "", + "[tour] 1/6 — Baseline snapshot", + "Shows CTX and TEMPORAL (dim/sigma/jump; cosine; hash). Next: temporal probe.", + " • CTX shows agent counters (profile, age_days, ticks) and run context.", + " • TEMPORAL is a soft clock (dim/sigma/jump), not wall time.", + " • cosine≈1.000 → same event; <0.90 → “new event soon.”", + " • vhash64 is a compact fingerprint for quick comparisons.", + "", + "[tour] 2/6 — Temporal context probe", + "Updates the soft clock; prints dim/sigma/jump and cosine to last boundary.", + "Next: capture a tiny engram.", + " • boundary() jumps the vector and increments the epoch (event count).", + " • vhash64 vs last_boundary_vhash64 → Hamming bits changed (0..64).", + " • Cosine compares “now” vs last boundary; drift lowers cosine.", + " • Status line summarizes phase (ON-BOUNDARY / DRIFTING / BOUNDARY-SOON).", + "", + "[tour] 3/6 — Capture a tiny engram", + "Adds a memory item with time/provenance; visible in Snapshot. Next: show b#.", + " • capture_scene creates a binding (cue/pred) and a Column engram.", + " • The binding gets a pointer slot (e.g., column01 → EID).", + " • Time attrs (ticks, epoch, tvec64) come from ctx at capture time.", + " • binding.meta['policy'] records provenance when created by a policy.", + "", + "[tour] 4/6 — Show binding pointer (b#)", + "Displays the new binding id and its attach target. Next: inspect that engram.", + " • A binding is the symbolic “memory link”; engram is the rich payload.", + " • The pointer (b#.engrams['slot']=EID) glues symbol ↔ rich memory.", + " • Attaching near NOW/LATEST keeps episodes readable for planning.", + " • Follow the pointer via Snapshot or “Inspect engram by id.”", + "", + "[tour] 5/6 — Inspect engram", + "Shows engram fields (channel, token, attrs). Next: list/search engrams.", + " • meta → attrs (ticks, epoch, tvec64, epoch_vhash64) for time context.", + " • payload → kind/shape/bytes (varies by Column implementation).", + " • Use this to verify data shape and provenance after capture.", + " • Engrams persist across saves; pointers can be re-attached later.", + "", + "[tour] 6/6 — List/search engrams", + "Lists and filters engrams by token/family.", + " • Deduped EIDs with source binding (b#) for quick auditing.", + " • Search by name substring and/or by epoch number.", + " • Useful to confirm capture cadence across boundaries/epochs.", + " • Pair with “Plan from NOW” to see if memory supports behavior.", + "", +].join("\n"); + +const PROFILE_NARRATIVE_CHIMP = [ + "", + "Chimpanzee-like brain simulation", + "", + "As per the papers on the Causal Cognitive Architecture, the mountain goat has pre-causal reasoning.", + "The chimpanzee has the main structures of the mountain goat brain (some differences nonetheless in these", + " \"similar\" structures) but enhanced feedback pathways allowing better causal reasoning. Also better", + " combinatorial language.", + "", +].join("\n"); + +const PROFILE_NARRATIVE_HUMAN = [ + "", + "Human-like brain simulation", + "", + "As per the papers on the Causal Cognitive Architecture, the mountain goat has pre-causal reasoning.", + "The chimpanzee has the main structures of the mountain goat brain (some differences nonetheless in these", + " \"similar\" structures) but enhanced feedback pathways allowing better causal reasoning. Also better", + " combinatorial language.", + "The human simulation has further enhanced feedback pathways and full causal reasoning, full analogical reasoning", + " and compositional reasoning/language.", + "", +].join("\n"); + +const PROFILE_NARRATIVE_MULTI_BRAINS = [ + "", + "Human-like one-agent multiple-brains simulation", + "", + "As per the papers on the Causal Cognitive Architecture, the mountain goat has pre-causal reasoning.", + "The chimpanzee has the main structures of the mountain goat brain (some differences nonetheless in these", + " \"similar\" structures) but enhanced feedback pathways allowing better causal reasoning. Also better", + " combinatorial language.", + "The human simulation has further enhanced feedback pathways and full causal reasoning, full analogical reasoning", + " and compositional reasoning/language.", + "", + "In this model each agent has multiple brains operating in parallel. There is an intelligent voting mechanism to", + " decide on a response whereby each of the 5 processes running in parallel can give a response with an indication", + " of how certain they are this is the best response, and the most certain + most popular response is chosen.", + "As well, all 5 symbolic maps along with their rich store of information in their engrams are continually learning", + " and constantly updated.", + "", +].join("\n"); + +const PROFILE_NARRATIVE_SOCIETY = [ + "", + "Human-like one-brain simulation × multiple-agents society", + "", + "As per the papers on the Causal Cognitive Architecture, the mountain goat has pre-causal reasoning.", + "The chimpanzee has the main structures of the mountain goat brain (some differences nonetheless in these", + " \"similar\" structures) but enhanced feedback pathways allowing better causal reasoning. Also better", + " combinatorial language.", + "The human simulation has further enhanced feedback pathways and full causal reasoning, full analogical reasoning", + " and compositional reasoning/language.", + "", + "In this simulation we have multiple agents each with one human-like brain, all interacting with each other.", + "", +].join("\n"); + +const PROFILE_NARRATIVE_ADV_PLANNING = [ + "", + "Human-like one-agent multiple-brains simulation with combinatorial planning", + "", + "As per the papers on the Causal Cognitive Architecture, the mountain goat has pre-causal reasoning.", + "The chimpanzee has the main structures of the mountain goat brain (some differences nonetheless in these", + "\"similar\" structures) but enhanced feedback pathways allowing better causal reasoning. Also better", + "combinatorial language.", + "The human simulation has further enhanced feedback pathways and full causal reasoning, full analogical reasoning", + "and compositional reasoning/language.", + "", + "In this model there are multiple brains, e.g., 5 at the time of this writing, in one agent.", + "There is an intelligent voting mechanism to decide on a response whereby each of the 5 processes running in", + "parallel can give a response with an indication of how certain they are this is the best response, and the most", + "certain + most popular response is chosen. As well, all 5 symbolic maps along with their rich store of", + "information in their engrams are continually learning and updated.", + "", + "In addition, in this model each brain has multiple von Neumann processors to independently explore different", + "possible routes to take or different possible decisions to make.", + "", +].join("\n"); + +const PROFILE_NARRATIVE_SUPER = [ + "", + "Super-human-like machine simulation", + "", + "Features scaffolding for an ASI-grade architecture:", + " • Hierarchical memory: massive multi-modal engrams (vision/sound/touch/text) linked to a compact symbolic index.", + " • Weighted graph planning: edges carry costs/uncertainty; A*/landmarks for long-range navigation in concept space.", + " • Meta-controller: blends proposals from symbolic search, neural value estimation, and program-synthesis planning.", + " • Self-healing & explanation: detect/repair inconsistent states; produce human-readable rationales for actions.", + " • Tool-use & embodiment: external tools (math/vision/robots) wrapped as policies with provenances and safeguards.", + " • Safety envelope: constraint-checking policies that can veto/redirect unsafe plans.", + "", + "This stub prints a dry-run of the meta-controller triage and falls back to the current==Mountain Goat profile.", + "", +].join("\n"); + +const PROFILE_FALLBACK = [ + "Although scaffolding is in place, currently this evolutionary-like configuration is not available.", + "Profile will be set to mountain goat-like brain simulation.", + "", +].join("\n"); + +// ------------------------------ CLI + logging ------------------------------ + +class Logger { + private logStream: any | null; + + constructor(path: string | null) { + this.logStream = null; + if (path && !isEmpty(path)) { + try { + this.logStream = fs.createWriteStream(path, { flags: "a" }); + } catch (_e) { + this.logStream = null; + } + } + } + + close(): void { + try { + if (this.logStream) this.logStream.end(); + } catch (_e) { + // ignore + } + this.logStream = null; + } + + write(text: string): void { + // Console + try { + process.stdout.write(text); + } catch (_e) { + // fallback + try { console.log(text); } catch (_e2) { /* ignore */ } + } + + // File transcript + try { + if (this.logStream) this.logStream.write(text); + } catch (_e) { + // ignore + } + } + + line(text: string): void { + this.write(text + "\n"); + } + + block(text: string): void { + // Preserve the text as-is (and ensure it ends with newline) + this.write(text); + if (!endsWithNewline(text)) this.write("\n"); + } +} + +function endsWithNewline(s: string): boolean { + if (!s) return false; + const last = s.substring(s.length - 1); + return last === "\n"; +} + +class Cli { + private rl: any; + private logger: Logger; + + constructor(logger: Logger) { + this.logger = logger; + this.rl = readline.createInterface({ input: process.stdin, output: process.stdout }); + } + + close(): void { + try { this.rl.close(); } catch (_e) { /* ignore */ } + this.logger.close(); + } + + out(line: string): void { + this.logger.line(line); + } + + outBlock(text: string): void { + this.logger.block(text); + } + + prompt(question: string, cb: (answer: string) => void): void { + this.rl.question(question, function(answer: string) { + cb(String(answer)); + }); + } + + pause(cb: (answer: string) => void): void { + this.prompt("\nPress Enter to continue (or type Q to quit): ", cb); + } +} + +// ------------------------------ Arg parsing ------------------------------ + +function defaultOptions(): AppOptions { + return { + about: false, + version: false, + understanding: false, + tour: false, + + noIntro: false, + noColor: false, + logo: null, + profile: null, + + logPath: null, + }; +} + +function parseArgs(argv: string[]): AppOptions { + const opts = defaultOptions(); + let i = 0; + + while (i < argv.length) { + const a = String(argv[i]); + + if (a === "--version") { + opts.version = true; + i += 1; + continue; + } + if (a === "--about") { + opts.about = true; + i += 1; + continue; + } + if (a === "--understanding") { + opts.understanding = true; + i += 1; + continue; + } + if (a === "--tour") { + opts.tour = true; + i += 1; + continue; + } + if (a === "--no-intro") { + opts.noIntro = true; + i += 1; + continue; + } + if (a === "--no-color") { + opts.noColor = true; + i += 1; + continue; + } + if (a === "--logo") { + const v = (i + 1 < argv.length) ? String(argv[i + 1]) : ""; + const vv = lower(trimStr(v)); + if (vv === "badge" || vv === "goat" || vv === "off") { + opts.logo = vv as LogoStyle; + i += 2; + continue; + } + i += 1; + continue; + } + if (a === "--profile") { + const v2 = (i + 1 < argv.length) ? String(argv[i + 1]) : ""; + if (!isEmpty(v2)) { + opts.profile = trimStr(v2); + i += 2; + continue; + } + i += 1; + continue; + } + if (a === "--log") { + const lp = (i + 1 < argv.length) ? String(argv[i + 1]) : ""; + if (!isEmpty(lp)) { + opts.logPath = trimStr(lp); + i += 2; + continue; + } + i += 1; + continue; + } + if (a === "--help" || a === "-h") { + // Treat as about-ish + opts.about = true; + i += 1; + continue; + } + + // Unknown flag: ignore + i += 1; + } + + return opts; +} + +// ------------------------------ Renderers (banner, about, etc.) ------------------------------ + +function logoFromEnvOrDefault(explicit: LogoStyle | null): LogoStyle { + if (explicit) return explicit; + const v = lower(trimStr(envGet("CCA8_LOGO", "badge"))); + if (v === "badge" || v === "goat" || v === "off") return v as LogoStyle; + return "badge"; +} + +function printAsciiLogo(cli: Cli, style: LogoStyle, colorEnabled: boolean): void { + if (style === "off") return; + + let art = (style === "goat") ? ASCII_GOAT : ASCII_BADGE; + + if (colorEnabled) { + if (style === "badge") { + art = art.replace("C C A 8", paint("C C A 8", "1;36", true)); + } else if (style === "goat") { + art = paint(art, "33", true); + } + } + + cli.outBlock(art + "\n"); +} + +function printHeader(cli: Cli, st: RuntimeState): void { + cli.out(""); + cli.out(""); + cli.out("# " + repeatChar("-", 86)); + cli.out("# NEW RUN NEW RUN"); + cli.out("# " + repeatChar("-", 86)); + cli.out(""); + cli.out("A Warm Welcome to the CCA8 Mammalian Brain Simulation"); + cli.out("(cca8_run.py v" + CCA8_RUNNER_VERSION + " | cca8_demo.ts v" + DEMO_VERSION + ")"); + cli.out(""); + + // Header prints the goat logo (mirrors print_header(style="goat")) + printAsciiLogo(cli, "goat", st.colorEnabled); + + const entry = getEntrypoint(); + const osName = getOsLabel(); + cli.out("Entry point program being run: " + entry); + cli.out("OS: " + osName + " (see system-dependent utilities for more detailed system/simulation info)"); + cli.out("(for non-interactive execution, run with --help to see optional flags you can set)"); + cli.out(""); + + cli.out("Embodiment: HAL (hardware abstraction layer) setting: " + st.halStatus); + cli.out("Embodiment: body_type|version_number|serial_number (i.e. robotic embodiment): " + st.bodyStatus); + cli.out(""); + + cli.out("The simulation of the cognitive architecture can be adjusted to add or take away"); + cli.out(" various features, allowing exploration of different evolutionary-like configurations."); + cli.out(""); + cli.out(" 1. Mountain Goat-like brain simulation"); + cli.out(" 2. Chimpanzee-like brain simulation"); + cli.out(" 3. Human-like brain simulation"); + cli.out(" 4. Human-like one-agent multiple-brains simulation"); + cli.out(" 5. Human-like one-brain simulation × multiple-agents society"); + cli.out(" 6. Human-like one-agent multiple-brains simulation with combinatorial planning"); + cli.out(" 7. Super-Human-like machine simulation"); + cli.out(" T. Tutorial (more information) on using and maintaining this program, references"); + cli.out(""); +} + +function getEntrypoint(): string { + try { + const p = process && process.argv && process.argv.length >= 2 ? String(process.argv[1]) : "(unknown)"; + return p; + } catch (_e) { + return "(unknown)"; + } +} + +function getOsLabel(): string { + try { + return String(process.platform); + } catch (_e) { + return "(unknown)"; + } +} + +function printAbout(cli: Cli): void { + cli.out(""); + cli.out("CCA8 Demo — About"); + cli.out(repeatChar("-", 78)); + cli.out(""); + cli.outBlock(KEY_IDEAS + "\n"); + cli.out(""); + cli.out("This demo program is a text-only CLI wrapper around the intro/help text."); + cli.out("It does NOT run the CCA8 simulation itself; it only teaches what the terms mean."); + cli.out(""); + cli.out("Try: --understanding | --tour | --about | --version | --no-intro"); + cli.out(""); +} + +function printVersion(cli: Cli): void { + cli.out("cca8_demo.ts v" + DEMO_VERSION); + cli.out("based on cca8_run.py v" + CCA8_RUNNER_VERSION); +} + +function printUnderstanding(cli: Cli): void { + cli.outBlock(UNDERSTANDING); +} + +function printQuickTour(cli: Cli): void { + cli.outBlock(QUICK_TOUR); +} + +function printTutorialMenu(cli: Cli): void { + cli.out(""); + cli.out("Tutorial options:"); + cli.out(" 1) README/compendium System Documentation"); + cli.out(" 2) Console Tour (pending) (use menu item 'Quick Tour' here)"); + cli.out(" [Enter] Cancel"); + cli.out(""); +} + +function showReadmeHint(cli: Cli): void { + // In Python runner, 'T' opens README.md. Here we just print the expected path. + cli.out(""); + cli.out("[tutorial] README.md is the compendium document in the CCA8 project folder."); + cli.out("[tutorial] Open it in your editor (or the repo viewer) for deeper details and references."); + cli.out(""); +} + +// ------------------------------ Profile selection ------------------------------ + +function normalizeProfileInput(s: string): string { + const x = lower(trimStr(s)); + + // numeric shortcuts + if (x === "1") return "goat"; + if (x === "2") return "chimp"; + if (x === "3") return "human"; + if (x === "4") return "multi"; + if (x === "5") return "society"; + if (x === "6") return "planning"; + if (x === "7") return "super"; + + // name shortcuts + if (x === "goat" || x === "mountain_goat" || x === "mountaingoat") return "goat"; + if (x === "chimp" || x === "chimpanzee") return "chimp"; + if (x === "human") return "human"; + if (x === "multi" || x === "multi_brain" || x === "multibrains" || x === "multiple_brains") return "multi"; + if (x === "society" || x === "agents" || x === "multi_agents" || x === "multiagent") return "society"; + if (x === "planning" || x === "adv" || x === "combinatorial") return "planning"; + if (x === "super" || x === "asi" || x === "superhuman") return "super"; + + return x; +} + +function profileNarrative(kind: string): string { + if (kind === "chimp") return PROFILE_NARRATIVE_CHIMP + PROFILE_FALLBACK; + if (kind === "human") return PROFILE_NARRATIVE_HUMAN + PROFILE_FALLBACK; + if (kind === "multi") return PROFILE_NARRATIVE_MULTI_BRAINS + PROFILE_FALLBACK; + if (kind === "society") return PROFILE_NARRATIVE_SOCIETY + PROFILE_FALLBACK; + if (kind === "planning") return PROFILE_NARRATIVE_ADV_PLANNING + PROFILE_FALLBACK; + if (kind === "super") return PROFILE_NARRATIVE_SUPER + PROFILE_FALLBACK; + return ""; +} + +function promptForProfile(cli: Cli, st: RuntimeState, preselected: string | null, cb: (profileName: string) => void): void { + // If preselected, apply it without prompting (but still show narrative if it’s not goat). + if (preselected && !isEmpty(preselected)) { + const kind = normalizeProfileInput(preselected); + if (kind !== "goat" && kind !== "t") { + const txt = profileNarrative(kind); + if (!isEmpty(txt)) cli.outBlock(txt); + } + st.profileName = "Mountain Goat"; + cb(st.profileName); + return; + } + + const promptText = "Please make a choice [1–7 or T | Enter = Mountain Goat]: "; + cli.prompt(promptText, function(answer: string) { + const raw = trimStr(answer); + + if (raw === "") { + st.profileName = "Mountain Goat"; + cb(st.profileName); + return; + } + + const a = lower(raw); + if (a === "t") { + printTutorialMenu(cli); + cli.prompt("Choose: ", function(pick: string) { + const p = trimStr(pick); + if (p === "1") { + showReadmeHint(cli); + } else if (p === "2") { + cli.out("Console tour is pending; please use the Quick Tour menu item (or README) for now."); + cli.out(""); + } else { + cli.out("(cancelled)"); + cli.out(""); + } + // Re-prompt for profile after tutorial menu + promptForProfile(cli, st, null, cb); + }); + return; + } + + const kind = normalizeProfileInput(raw); + if (kind !== "goat") { + const txt2 = profileNarrative(kind); + if (!isEmpty(txt2)) cli.outBlock(txt2); + } + + // In the runner, all other profiles fall back to goat + st.profileName = "Mountain Goat"; + cb(st.profileName); + }); +} + +// ------------------------------ Main menu (simple but a bit “runner-like”) ------------------------------ + +const MENU_TEXT = [ + "", + "[hints for text selection instead of numerical selection]", + "", + "# Quick Start & Tutorial", + "1) Understanding bindings, edges, predicates, cues, policies [understanding, tagging]", + "2) CCA8 Quick Tour (text-only) [help, tutorial, tour]", + "", + "# Quick Start / Overview", + "3) About / Key ideas [about]", + "4) Show intro header again [header]", + "5) Choose profile again [profile]", + "", + "# Misc", + "6) Show ASCII logo (badge/goat/off) [logo]", + "7) Quit [quit, exit]", + "", + "Select: ", +].join("\n"); + +const MIN_PREFIX = 3; + +const ALIASES: { [k: string]: string } = { + // menu items + "understanding": "1", + "tagging": "1", + + "help": "2", + "tutorial": "2", + "tour": "2", + + "about": "3", + + "header": "4", + + "profile": "5", + + "logo": "6", + "badge": "6", + "goat": "6", + "off": "6", + + "quit": "7", + "exit": "7", + "q": "7", +}; + +function routeAlias(cmd: string): { routed: string | null; matches: string[] } { + const s = lower(trimStr(cmd)); + if (Object.prototype.hasOwnProperty.call(ALIASES, s)) { + return { routed: ALIASES[s], matches: [] }; + } + + if (s.length >= MIN_PREFIX) { + const keys = Object.keys(ALIASES); + const matches: string[] = []; + for (let i = 0; i < keys.length; i++) { + const k = keys[i]; + if (startsWith(k, s)) matches.push(k); + } + if (matches.length === 1) return { routed: ALIASES[matches[0]], matches: matches }; + return { routed: null, matches: matches }; + } + + return { routed: null, matches: [] }; +} + +function menuLoop(cli: Cli, st: RuntimeState): void { + cli.prompt(MENU_TEXT, function(rawAnswer: string) { + let choice = trimStr(rawAnswer); + const lowerChoice = lower(choice); + + if (!isEmpty(choice)) { + if (!isDigits(choice)) { + const routed = routeAlias(choice); + if (routed.routed !== null) { + choice = routed.routed; + } else if (routed.matches.length > 1) { + cli.out("[help] Ambiguous input '" + lowerChoice + "'. Try one of: " + routed.matches.slice(0, 8).join(", ") + (routed.matches.length > 8 ? "..." : "")); + menuLoop(cli, st); + return; + } + } + } + + if (choice === "1") { + printUnderstanding(cli); + cli.pause(function(ans: string) { + if (lower(trimStr(ans)) === "q") { + cli.out("Goodbye."); + cli.close(); + return; + } + menuLoop(cli, st); + }); + return; + } + + if (choice === "2") { + printQuickTour(cli); + cli.pause(function(ans2: string) { + if (lower(trimStr(ans2)) === "q") { + cli.out("Goodbye."); + cli.close(); + return; + } + menuLoop(cli, st); + }); + return; + } + + if (choice === "3") { + printAbout(cli); + cli.pause(function(ans3: string) { + if (lower(trimStr(ans3)) === "q") { + cli.out("Goodbye."); + cli.close(); + return; + } + menuLoop(cli, st); + }); + return; + } + + if (choice === "4") { + printHeader(cli, st); + cli.pause(function(ans4: string) { + if (lower(trimStr(ans4)) === "q") { + cli.out("Goodbye."); + cli.close(); + return; + } + menuLoop(cli, st); + }); + return; + } + + if (choice === "5") { + promptForProfile(cli, st, null, function(_profile: string) { + cli.out("Profile set: " + st.profileName + " (scaffolding profiles fall back to goat for now)."); + cli.out(""); + menuLoop(cli, st); + }); + return; + } + + if (choice === "6") { + // Let user pick a logo style for display only (badge/goat/off) + cli.out(""); + cli.out("Logo options:"); + cli.out(" 1) badge"); + cli.out(" 2) goat"); + cli.out(" 3) off"); + cli.out(""); + cli.prompt("Choose (default=badge): ", function(pick: string) { + const p = lower(trimStr(pick)); + let style: LogoStyle = "badge"; + if (p === "2" || p === "goat") style = "goat"; + else if (p === "3" || p === "off") style = "off"; + else style = "badge"; + + st.logoStyle = style; + cli.out(""); + cli.out("[logo] style set to: " + style); + cli.out(""); + printAsciiLogo(cli, style, st.colorEnabled); + cli.pause(function(_ans5: string) { + menuLoop(cli, st); + }); + }); + return; + } + + if (choice === "7" || lowerChoice === "q") { + cli.out("Goodbye."); + cli.close(); + return; + } + + // default: treat empty as "show menu again" + menuLoop(cli, st); + }); +} + +function isDigits(s: string): boolean { + const t = trimStr(s); + if (t.length === 0) return false; + for (let i = 0; i < t.length; i++) { + const c = t.charCodeAt(i); + if (c < 48 || c > 57) return false; + } + return true; +} + +// ------------------------------ main() ------------------------------ + +function main(): void { + const args = parseArgs(getArgv()); + const logger = new Logger(args.logPath); + const cli = new Cli(logger); + + const state: RuntimeState = { + profileName: "Mountain Goat", + halStatus: "HAL: off (no embodiment)", + bodyStatus: "Body: (none)", + logoStyle: logoFromEnvOrDefault(args.logo), + colorEnabled: shouldUseColor(args.noColor), + }; + + // One-shot modes + if (args.version) { + printVersion(cli); + cli.close(); + return; + } + if (args.about) { + printAbout(cli); + cli.close(); + return; + } + if (args.understanding) { + printUnderstanding(cli); + cli.close(); + return; + } + if (args.tour) { + printQuickTour(cli); + cli.close(); + return; + } + + // Interactive mode + if (!args.noIntro) { + printHeader(cli, state); + } else { + // Still show the badge logo (respect env/flags) + printAsciiLogo(cli, state.logoStyle, state.colorEnabled); + } + + // Profile selection then menu loop + promptForProfile(cli, state, args.profile, function(_profileName: string) { + cli.out("Profile set: " + state.profileName + " (sigma/jump/winners_k omitted in this TS demo)."); + cli.out(""); + menuLoop(cli, state); + }); +} + +function getArgv(): string[] { + try { + const out: string[] = []; + const a = process && process.argv ? process.argv : []; + for (let i = 2; i < a.length; i++) out.push(String(a[i])); + return out; + } catch (_e) { + return []; + } +} + +main(); diff --git a/terminal.txt b/terminal.txt index 1247f7f..742529b 100644 --- a/terminal.txt +++ b/terminal.txt @@ -1,560 +1,1312 @@ +Selection: Run n Cognitive Cycles (closed-loop timeline) - -# -------------------------------------------------------------------------------------- -# NEW RUN NEW RUN -# -------------------------------------------------------------------------------------- - -A Warm Welcome to the CCA8 Mammalian Brain Simulation -(cca8_run.py v0.8.2) - - ____ CCA8 - .' `-. mountain goat -/ _ _ \ -| (o)(o) | -\ __ / - `'-.____' - -Entry point program being run: C:\Users\howar\workspace\cca8_run.py -OS: win32 (see system-dependent utilities for more detailed system/simulation info) -(for non-interactive execution, ">python cca8_run.py --help" to see optional flags you can set) - -Embodiment: HAL (hardware abstraction layer) setting: OFF (runs without consideration of the robotic embodiment) -Embodiment: body_type|version_number|serial_number (i.e., robotic embodiment): 0.0.0 : none specified - -The simulation of the cognitive architecture can be adjusted to add or take away - various features, allowing exploration of different evolutionary-like configurations. - - 1. Mountain Goat-like brain simulation - 2. Chimpanzee-like brain simulation - 3. Human-like brain simulation - 4. Human-like one-agent multiple-brains simulation - 5. Human-like one-brain simulation × multiple-agents society - 6. Human-like one-agent multiple-brains simulation with combinatorial planning - 7. Super-Human-like machine simulation - T. Tutorial (more information) on using and maintaining this program, references - -Please make a choice [1–7 or T | Enter = Mountain Goat]: Profile set: Mountain Goat (sigma=0.015, jump=0.2, k=2) - -[io] Started a NEW session. Autosave OFF — use menu selection Save Session or relaunch with --autosave . -[boot] Seeded posture:fallen as b2 (NOW -> fallen) -[planner] Active planner on startup: BFS -[profile] Hardwired memory pipeline: phase7 daily-driver (no options menu needed). -[preflight-lite] checks ok - - - -Please press any key (* stops this scroll pause) to continue with menu (above screen will scroll).... - [hints for text selection instead of numerical selection] - - # Quick Start & Tutorial - 1) Understanding bindings, edges, predicates, policies [understanding, tagging] - 2) Help: System Docs and/or Tutorial with demo tour [help, tutorial, demo] - - # Quick Start / Overview - 3) Snapshot (bindings + edges + ctx + policies) [snapshot, display] - 4) World stats [world, stats] - 5) Recent bindings (last 5) [last, bindings] - 6) Drives & drive tags [drives] - 7) Skill ledger [skills] - 8) Temporal probe (epoch/hash/cos/hamming) [temporal, probe] - - # Act / Simulate - 9) Instinct step (Action Center) [instinct, act] - 10) Autonomic tick (emit interoceptive cues) [autonomic, tick] - 11) Simulate fall (add posture:fallen and try recovery) [fall, simulate] - - # Simulation of the Environment (HybridEnvironment demo) - 35) Run 1 Cognitive Cycle (HybridEnvironment → WorldGraph demo) [env, hybrid] - 37) Run n Cognitive Cycles (closed-loop timeline) [envloop, envrun] - 38) Inspect BodyMap (summary from BodyMap helpers) [bodymap, bsnap] - 39) Spatial scene demo (NOW-near + resting-in-shelter?) [spatial, near] - - # Perception & Memory (Cues & Engrams) - 12) Input [sensory] cue [sensory, cue] - 13) Capture scene → tiny engram (signal bridge) [capture, scene] - 14) Resolve engrams on a binding [resolve, engrams] - 15) Inspect engram by id (or binding) [engram, ei] - 16) List all engrams [engrams-all, list-engrams] - 17) Search engrams (by name / epoch) [search-engrams, find-engrams] - 18) Delete engram by bid or eid [delete-engram, del-engram] - 19) Attach existing engram to a binding [attach-engram, ae] - - # Graph Inspect / Build / Plan - 20) Inspect binding details [inspect, details] - 21) List predicates [listpredicates, listpreds] - 22) [Add] predicate [add, predicate] - 23) Connect two bindings (src, dst, relation) [connect, link] - 24) Delete edge (source, destn, relation) [delete, rm] - 25) Plan from NOW -> [plan] - 26) Planner strategy (toggle BFS ↔ Dijkstra) [planner, strategy] - 27) Export and display interactive graph with options [pyvis, graph] - - # Save / System / Help - 28) Export snapshot (text only) [export snapshot] - 29) Save session → path [save] - 30) Load session → path [load] - 31) Run preflight now [preflight] - 32) Quit [quit, exit] - 33) Lines of Python code LOC by directory [loc, sloc] - 34) Reset current saved session [reset] - 36) Toggle mini-snapshot after each menu selection [mini, msnap] - - # Memories - 40) Configure episode starting state (drives + age_days) [config-episode, cfg-epi] - 41) Retired: WorkingMap & WorldGraph settings, toggle RL policy - 43) WorkingMap snapshot (last N bindings; optional clear) [wsnap, wmsnap] - 44) Store MapSurface snapshot to Column + WG pointer (dedup vs last) [wstore, wmstore] - 45) List recent wm_mapsurface engrams (Column) - 46) Pick best wm_mapsurface engram for current stage/zone (read-only) [wpick, wpickwm] - 47) Load wm_mapsurface engram into WorkingMap (replace MapSurface) [wload, wmload] - - Select: Selection: Run n Cognitive Cycles (closed-loop timeline) - -This selection runs several consecutive closed-loop steps between the +This selection runs several consecutive closed-loop cognitive cycles between the HybridEnvironment (newborn-goat world) and the CCA8 brain. -For each step we will: +For each cognitive cycle we will: 1) Advance controller_steps and the temporal soft clock once, 2) STEP the newborn-goat environment using the last policy action (if any), 3) Inject the resulting EnvObservation into the WorldGraph as pred:/cue: facts, 4) Run ONE controller step (Action Center) and remember the last fired policy. -Tip: run N=1 to single-step the closed-loop cycle. - +This is like pressing menu 35 multiple times, but with a more compact, per-cycle summary. +You can still use menu 35 for detailed, single-step inspection. [policy-selection] Candidates = dev_gate passes AND trigger(...) returns True. [policy-selection] Winner = highest deficit → non_drive → (RL: q | non-RL: stable order). [policy-selection] RL adds exploration: epsilon picks a random candidate; otherwise we exploit the winner logic above. -How many closed-loop step(s) would you like to run? [default: 5]: [env-loop] Running 25 closed-loop cognitive cycle(s) (env↔controller). +How many closed-loop cognitive cycle(s) would you like to run? [default: 5]: [env-loop] Running 50 closed-loop cognitive cycle(s) (env↔controller). [env-loop] Each cognitive cycle will: 1) Advance controller_steps and the temporal soft clock (one drift), 2) Call env.reset() (first time) or env.step(last policy action), 3) Inject EnvObservation into the WorldGraph as pred:/cue: facts, 4) Run ONE controller step (Action Center) and store the last policy name. -[env-loop] Note: environment episode has not started yet; the first cognitive cycle will call env.reset(). - -[env-loop] Cognitive Cycle 1/25 -[env] Reset newborn_goat scenario: episode_index=1 scenario=newborn_goat_first_hour -[env→working] MAP pred:posture:fallen → w6 (b6) (entity=self, slot=posture) [changed] -[env→working] MAP pred:proximity:mom:far → w7 (b7) (entity=mom, slot=proximity:mom) [changed] -[env→working] MAP pred:proximity:shelter:far → w8 (b8) (entity=shelter, slot=proximity:shelter) [changed] -[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) [changed] -[env→world] KEYFRAME: env_reset(time_since_birth=0.00) | cleared 0 pred slot(s), 0 cue slot(s) -[env→world] pred:posture:fallen → b3 (attach=now) -[env→world] pred:proximity:mom:far → b4 (attach=latest) -[env→world] pred:proximity:shelter:far → b5 (attach=latest) -[env→world] pred:hazard:cliff:far → b6 (attach=latest) -[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=far zone=unknown -[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=far zone=unknown -[env→controller] policy:stand_up (added 3 bindings) -[pick] best_policy=policy:stand_up best_by=rl_exploit(non_drive_tiebreak) triggered=[policy:stand_up, policy:recover_fall] deficits=[policy:stand_up:0.00, policy:recover_fall:0.00] non_drive=[policy:stand_up:2.00, policy:recover_fall:0.00] -[rl-pick] chosen via non-drive tiebreak in deficit near-tie band: best_def=0.000 delta=0.000 → policy:stand_up (nd=2.00, q=+0.00) among [policy:stand_up(def=0.000, nd=2.00, q=+0.00), policy:recover_fall(def=0.000, nd=0.00, q=+0.00)] -[executed] policy:stand_up (ok, reward=+1.00) binding=w12 (b12) + +[env-loop] Cognitive Cycle 1/50 +[env] env_step=25 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] +[env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) +[env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) +[env→world] (long-term obs unchanged; no new pred:* bindings written) +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[env→controller] policy:follow_mom (added 2 bindings) +[pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] +[executed] policy:follow_mom (ok, reward=+0.10) binding=w65 (b65) +[maps] selection_on=WG execute_on=WM +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=1/50 env_step=25 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=1/50 env_step=25 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 2/50 +[env] env_step=26 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] +[env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) +[env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) +[env→world] (long-term obs unchanged; no new pred:* bindings written) +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[env→controller] policy:follow_mom (added 2 bindings) +[pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] +[executed] policy:follow_mom (ok, reward=+0.10) binding=w67 (b67) +[maps] selection_on=WG execute_on=WM +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=2/50 env_step=26 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=2/50 env_step=26 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 3/50 +[env] env_step=27 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] +[env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) +[env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) +[env→world] (long-term obs unchanged; no new pred:* bindings written) +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[env→controller] policy:follow_mom (added 2 bindings) +[pick] best_policy=policy:follow_mom best_by=rl_explore triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] +[executed] policy:follow_mom (ok, reward=+0.10) binding=w69 (b69) +[maps] selection_on=WG execute_on=WM +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=3/50 env_step=27 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=3/50 env_step=27 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 4/50 +[env] env_step=28 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] +[env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) +[env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) +[env→world] (long-term obs unchanged; no new pred:* bindings written) +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[env→controller] policy:follow_mom (added 2 bindings) +[pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] +[executed] policy:follow_mom (ok, reward=+0.10) binding=w71 (b71) +[maps] selection_on=WG execute_on=WM +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=4/50 env_step=28 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=4/50 env_step=28 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 5/50 +[env] env_step=29 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] +[env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) +[env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) +[env→world] (long-term obs unchanged; no new pred:* bindings written) +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[env→controller] policy:follow_mom (added 2 bindings) +[pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] +[executed] policy:follow_mom (ok, reward=+0.10) binding=w73 (b73) +[maps] selection_on=WG execute_on=WM +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=5/50 env_step=29 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=5/50 env_step=29 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 6/50 +[env] env_step=30 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] +[env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) +[env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) +[env→world] (long-term obs unchanged; no new pred:* bindings written) +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[env→controller] policy:follow_mom (added 2 bindings) +[pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] +[executed] policy:follow_mom (ok, reward=+0.10) binding=w75 (b75) +[maps] selection_on=WG execute_on=WM +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=6/50 env_step=30 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=6/50 env_step=30 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 7/50 +[env] env_step=31 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] +[env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) +[env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) +[env→world] (long-term obs unchanged; no new pred:* bindings written) +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[env→controller] policy:follow_mom (added 2 bindings) +[pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] +[executed] policy:follow_mom (ok, reward=+0.10) binding=w77 (b77) +[maps] selection_on=WG execute_on=WM +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=7/50 env_step=31 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=7/50 env_step=31 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 8/50 +[env] env_step=32 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] +[env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) +[env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) +[env→world] (long-term obs unchanged; no new pred:* bindings written) +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[env→controller] policy:follow_mom (added 2 bindings) +[pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] +[executed] policy:follow_mom (ok, reward=+0.10) binding=w79 (b79) +[maps] selection_on=WG execute_on=WM +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=8/50 env_step=32 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=8/50 env_step=32 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 9/50 +[env] env_step=33 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] +[env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) +[env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) +[env→world] (long-term obs unchanged; no new pred:* bindings written) +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[env→controller] policy:follow_mom (added 2 bindings) +[pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] +[executed] policy:follow_mom (ok, reward=+0.10) binding=w81 (b81) +[maps] selection_on=WG execute_on=WM +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=9/50 env_step=33 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=9/50 env_step=33 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 10/50 +[env] env_step=34 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] +[env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) +[env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) +[env→world] (long-term obs unchanged; no new pred:* bindings written) +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[env→controller] policy:follow_mom (added 2 bindings) +[pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] +[executed] policy:follow_mom (ok, reward=+0.10) binding=w83 (b83) +[maps] selection_on=WG execute_on=WM +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=10/50 env_step=34 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=10/50 env_step=34 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 11/50 +[env] env_step=35 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] +[env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) +[env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) +[env→world] (long-term obs unchanged; no new pred:* bindings written) +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[env→controller] policy:follow_mom (added 2 bindings) +[pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] +[executed] policy:follow_mom (ok, reward=+0.10) binding=w85 (b85) +[maps] selection_on=WG execute_on=WM +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=11/50 env_step=35 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=11/50 env_step=35 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 12/50 +[env] env_step=36 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] +[env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) +[env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) +[env→world] (long-term obs unchanged; no new pred:* bindings written) +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[env→controller] policy:follow_mom (added 2 bindings) +[pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] +[executed] policy:follow_mom (ok, reward=+0.10) binding=w87 (b87) +[maps] selection_on=WG execute_on=WM +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=12/50 env_step=36 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=12/50 env_step=36 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 13/50 +[env] env_step=37 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] +[env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) +[env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) +[env→world] (long-term obs unchanged; no new pred:* bindings written) +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[env→controller] policy:follow_mom (added 2 bindings) +[pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] +[executed] policy:follow_mom (ok, reward=+0.10) binding=w89 (b89) +[maps] selection_on=WG execute_on=WM +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=13/50 env_step=37 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=13/50 env_step=37 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 14/50 +[env] env_step=38 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] +[env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) +[env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) +[env→world] (long-term obs unchanged; no new pred:* bindings written) +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[env→controller] policy:follow_mom (added 2 bindings) +[pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] +[executed] policy:follow_mom (ok, reward=+0.10) binding=w91 (b91) +[maps] selection_on=WG execute_on=WM +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=14/50 env_step=38 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=14/50 env_step=38 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 15/50 +[env] env_step=39 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] +[env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) +[env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) +[env→world] (long-term obs unchanged; no new pred:* bindings written) +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[env→controller] policy:follow_mom (added 2 bindings) +[pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] +[executed] policy:follow_mom (ok, reward=+0.10) binding=w93 (b93) +[maps] selection_on=WG execute_on=WM +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=15/50 env_step=39 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=15/50 env_step=39 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 16/50 +[env] env_step=40 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] +[env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) +[env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) +[env→world] (long-term obs unchanged; no new pred:* bindings written) +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[env→controller] policy:follow_mom (added 2 bindings) +[pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] +[executed] policy:follow_mom (ok, reward=+0.10) binding=w95 (b95) +[maps] selection_on=WG execute_on=WM +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=16/50 env_step=40 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=16/50 env_step=40 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 17/50 +[env] env_step=41 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] +[env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) +[env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) +[env→world] (long-term obs unchanged; no new pred:* bindings written) +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[env→controller] policy:follow_mom (added 2 bindings) +[pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] +[executed] policy:follow_mom (ok, reward=+0.10) binding=w97 (b97) +[maps] selection_on=WG execute_on=WM +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=17/50 env_step=41 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=17/50 env_step=41 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 18/50 +[env] env_step=42 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] +[env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) +[env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) +[env→world] (long-term obs unchanged; no new pred:* bindings written) +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[env→controller] policy:follow_mom (added 2 bindings) +[pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] +[executed] policy:follow_mom (ok, reward=+0.10) binding=w99 (b99) +[maps] selection_on=WG execute_on=WM +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=18/50 env_step=42 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=18/50 env_step=42 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 19/50 +[env] env_step=43 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] +[env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) +[env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) +[env→world] (long-term obs unchanged; no new pred:* bindings written) +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[env→controller] policy:follow_mom (added 2 bindings) +[pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] +[executed] policy:follow_mom (ok, reward=+0.10) binding=w101 (b101) +[maps] selection_on=WG execute_on=WM +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=19/50 env_step=43 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=19/50 env_step=43 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 20/50 +[env] env_step=44 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] +[env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) +[env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) +[env→world] (long-term obs unchanged; no new pred:* bindings written) +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[env→controller] policy:follow_mom (added 2 bindings) +[pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] +[executed] policy:follow_mom (ok, reward=+0.10) binding=w103 (b103) +[maps] selection_on=WG execute_on=WM +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=20/50 env_step=44 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=20/50 env_step=44 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 21/50 +[env] env_step=45 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] +[env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) +[env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) +[env→world] (long-term obs unchanged; no new pred:* bindings written) +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[env→controller] policy:follow_mom (added 2 bindings) +[pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] +[executed] policy:follow_mom (ok, reward=+0.10) binding=w105 (b105) +[maps] selection_on=WG execute_on=WM +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=21/50 env_step=45 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=21/50 env_step=45 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 22/50 +[env] env_step=46 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] +[env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) +[env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) +[env→world] (long-term obs unchanged; no new pred:* bindings written) +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[env→controller] policy:follow_mom (added 2 bindings) +[pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] +[executed] policy:follow_mom (ok, reward=+0.10) binding=w107 (b107) +[maps] selection_on=WG execute_on=WM +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=22/50 env_step=46 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=22/50 env_step=46 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 23/50 +[env] env_step=47 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] +[env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) +[env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) +[env→world] (long-term obs unchanged; no new pred:* bindings written) +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[env→controller] policy:follow_mom (added 2 bindings) +[pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] +[executed] policy:follow_mom (ok, reward=+0.10) binding=w109 (b109) +[maps] selection_on=WG execute_on=WM +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=23/50 env_step=47 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=23/50 env_step=47 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 24/50 +[env] env_step=48 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] +[env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) +[env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) +[env→world] (long-term obs unchanged; no new pred:* bindings written) +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[env→controller] policy:follow_mom (added 2 bindings) +[pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] +[executed] policy:follow_mom (ok, reward=+0.10) binding=w111 (b111) +[maps] selection_on=WG execute_on=WM +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=24/50 env_step=48 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=24/50 env_step=48 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 25/50 +[env] env_step=49 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] +[env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) +[env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) +[env→world] (long-term obs unchanged; no new pred:* bindings written) +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[env→controller] policy:follow_mom (added 2 bindings) +[pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] +[executed] policy:follow_mom (ok, reward=+0.10) binding=w113 (b113) +[maps] selection_on=WG execute_on=WM +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=25/50 env_step=49 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=25/50 env_step=49 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 26/50 +[env] env_step=50 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] +[env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) +[env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) +[env→world] (long-term obs unchanged; no new pred:* bindings written) +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[env→controller] policy:follow_mom (added 2 bindings) +[pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] +[executed] policy:follow_mom (ok, reward=+0.10) binding=w115 (b115) [maps] selection_on=WG execute_on=WM -pre: dev_gate: age_days=0.00<=3.0, trigger: fallen=True or (stand_intent=False and not standing=True) (hunger=0.50) +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) wg_base: {'base': 'HERE', 'bid': '?'} -wg_foa: {'seeds': ['b6', 'b3'], 'size': 4, 'ids': {'b3', 'b5', 'b6', 'b4'}} -wg_cands:['b3', '?'] -post: dev_gate: age_days=0.00<=3.0, trigger: fallen=True or (stand_intent=False and not standing=True) (hunger=0.50) -[env-loop] summary cognitive_cycle=1/25 env_step=0 stage=birth env_posture=fallen bm_posture=fallen mom=far nipple=hidden last_policy='policy:stand_up' expected_posture=standing zone=unknown -[env-loop] note: expected_posture is a Scratch postcondition (hypothesis); env_posture is storyboard truth this tick. -[env-loop] explain posture: initial storyboard setup: stage='birth' starts with posture='fallen' (newborn begins life on the ground). -[env-loop] explain nipple: initial storyboard setup: nipple_state='hidden' in stage 'birth'; the kid has not yet found or reached the nipple. -[env-loop] explain zone: zone is 'unknown'; either the environment has not yet established clear cliff/shelter distances or they are in a combination we do not classify, so gates treat it conservatively. - -[env-loop] Cognitive Cycle 2/25 -[env] env_step=1 (since reset) stage=birth posture=fallen mom_distance=far nipple_state=hidden action='policy:stand_up' -[pred_err] v0 err={'posture': 1} pred_posture=standing obs_posture=fallen from=policy:stand_up -[env→working] MAP pred:posture:fallen → w6 (b6) (entity=self, slot=posture) -[env→working] MAP pred:proximity:mom:far → w7 (b7) (entity=mom, slot=proximity:mom) -[env→working] MAP pred:proximity:shelter:far → w8 (b8) (entity=shelter, slot=proximity:shelter) +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=26/50 env_step=50 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=26/50 env_step=50 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 27/50 +[env] env_step=51 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] +[env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) [env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) +[env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) [env→world] (long-term obs unchanged; no new pred:* bindings written) -[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=far zone=unknown -[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=far zone=unknown -[env→controller] policy:stand_up (added 3 bindings) -[pick] best_policy=policy:stand_up best_by=rl_exploit(non_drive_tiebreak) triggered=[policy:stand_up, policy:recover_fall] deficits=[policy:stand_up:0.00, policy:recover_fall:0.00] non_drive=[policy:stand_up:2.00, policy:recover_fall:0.00] -[rl-pick] chosen via non-drive tiebreak in deficit near-tie band: best_def=0.000 delta=0.000 → policy:stand_up (nd=2.00, q=+0.30) among [policy:stand_up(def=0.000, nd=2.00, q=+0.30), policy:recover_fall(def=0.000, nd=0.00, q=+0.00)] -[executed] policy:stand_up (ok, reward=+1.00) binding=w15 (b15) +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[env→controller] policy:follow_mom (added 2 bindings) +[pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] +[executed] policy:follow_mom (ok, reward=+0.10) binding=w117 (b117) [maps] selection_on=WG execute_on=WM -pre: dev_gate: age_days=0.00<=3.0, trigger: fallen=True or (stand_intent=False and not standing=True) (hunger=0.50) +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) wg_base: {'base': 'HERE', 'bid': '?'} -wg_foa: {'seeds': ['b7', 'b3'], 'size': 4, 'ids': {'b7', 'b3', 'b5', 'b4'}} -wg_cands:['b3', '?'] -post: dev_gate: age_days=0.00<=3.0, trigger: fallen=True or (stand_intent=False and not standing=True) (hunger=0.50) -[env-loop] summary cognitive_cycle=2/25 env_step=1 stage=birth env_posture=fallen bm_posture=fallen mom=far nipple=hidden last_policy='policy:stand_up' expected_posture=standing zone=unknown -[env-loop] note: expected_posture is a Scratch postcondition (hypothesis); env_posture is storyboard truth this tick. -[env-loop] explain posture: stand_up was requested this tick, but the newborn is still kept fallen by the storyboard (stage='birth'); standing will only appear once the stand-up transition threshold is reached. -[env-loop] explain nipple: nipple remains hidden; the storyboard has not yet made it reachable (or visible) to the kid in this stage. -[env-loop] explain zone: zone is 'unknown'; either the environment has not yet established clear cliff/shelter distances or they are in a combination we do not classify, so gates treat it conservatively. - -[env-loop] Cognitive Cycle 3/25 -[env] env_step=2 (since reset) stage=birth posture=fallen mom_distance=far nipple_state=hidden action='policy:stand_up' -[pred_err] v0 err={'posture': 1} pred_posture=standing obs_posture=fallen from=policy:stand_up -[env→working] MAP pred:posture:fallen → w6 (b6) (entity=self, slot=posture) -[env→working] MAP pred:proximity:mom:far → w7 (b7) (entity=mom, slot=proximity:mom) -[env→working] MAP pred:proximity:shelter:far → w8 (b8) (entity=shelter, slot=proximity:shelter) +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=27/50 env_step=51 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=27/50 env_step=51 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 28/50 +[env] env_step=52 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] +[env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) [env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) +[env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) [env→world] (long-term obs unchanged; no new pred:* bindings written) -[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=far zone=unknown -[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=far zone=unknown -[env→controller] policy:stand_up (added 3 bindings) -[pick] best_policy=policy:stand_up best_by=rl_exploit(non_drive_tiebreak) triggered=[policy:stand_up, policy:recover_fall] deficits=[policy:stand_up:0.00, policy:recover_fall:0.00] non_drive=[policy:stand_up:2.00, policy:recover_fall:0.00] -[rl-pick] chosen via non-drive tiebreak in deficit near-tie band: best_def=0.000 delta=0.000 → policy:stand_up (nd=2.00, q=+0.51) among [policy:stand_up(def=0.000, nd=2.00, q=+0.51), policy:recover_fall(def=0.000, nd=0.00, q=+0.00)] -[executed] policy:stand_up (ok, reward=+1.00) binding=w18 (b18) +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[env→controller] policy:follow_mom (added 2 bindings) +[pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] +[executed] policy:follow_mom (ok, reward=+0.10) binding=w119 (b119) [maps] selection_on=WG execute_on=WM -pre: dev_gate: age_days=0.00<=3.0, trigger: fallen=True or (stand_intent=False and not standing=True) (hunger=0.50) +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) wg_base: {'base': 'HERE', 'bid': '?'} -wg_foa: {'seeds': ['b7', 'b3'], 'size': 4, 'ids': {'b7', 'b3', 'b5', 'b4'}} -wg_cands:['b3', '?'] -post: dev_gate: age_days=0.00<=3.0, trigger: fallen=True or (stand_intent=False and not standing=True) (hunger=0.50) -[env-loop] summary cognitive_cycle=3/25 env_step=2 stage=birth env_posture=fallen bm_posture=fallen mom=far nipple=hidden last_policy='policy:stand_up' expected_posture=standing zone=unknown -[env-loop] note: expected_posture is a Scratch postcondition (hypothesis); env_posture is storyboard truth this tick. -[env-loop] explain posture: stand_up was requested this tick, but the newborn is still kept fallen by the storyboard (stage='birth'); standing will only appear once the stand-up transition threshold is reached. -[env-loop] explain nipple: nipple remains hidden; the storyboard has not yet made it reachable (or visible) to the kid in this stage. -[env-loop] explain zone: zone is 'unknown'; either the environment has not yet established clear cliff/shelter distances or they are in a combination we do not classify, so gates treat it conservatively. - -[env-loop] Cognitive Cycle 4/25 -[env] env_step=3 (since reset) stage=struggle posture=fallen mom_distance=far nipple_state=hidden action='policy:stand_up' -[pred_err] v0 err={'posture': 1} pred_posture=standing obs_posture=fallen from=policy:stand_up -[env→working] MAP pred:posture:fallen → w6 (b6) (entity=self, slot=posture) -[env→working] MAP pred:proximity:mom:far → w7 (b7) (entity=mom, slot=proximity:mom) -[env→working] MAP pred:proximity:shelter:far → w8 (b8) (entity=shelter, slot=proximity:shelter) +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=28/50 env_step=52 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=28/50 env_step=52 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 29/50 +[env] env_step=53 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] +[env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) [env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) -[env→world] KEYFRAME: stage_change 'birth'→'struggle' | cleared 4 pred slot(s), 0 cue slot(s) -[env→world] pred:posture:fallen → b8 (attach=now) -[env→world] pred:proximity:mom:far → b9 (attach=latest) -[env→world] pred:proximity:shelter:far → b10 (attach=latest) -[env→world] pred:hazard:cliff:far → b11 (attach=latest) -[wm<->col] store: ok sig=9d70ac79e80922e6 bid=b12 eid=caea2d97… (auto_boundary_stage:birth->struggle) -[wm<->col] retrieve: skip why=only_excluded_candidate mode=merge top_k=5 (auto_boundary_stage:birth->struggle) -[wm<->col] apply: no-op (only_excluded_candidate) -[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=far zone=unknown -[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=far zone=unknown -[env→controller] policy:stand_up (added 3 bindings) -[pick] best_policy=policy:stand_up best_by=rl_exploit(non_drive_tiebreak) triggered=[policy:stand_up, policy:recover_fall] deficits=[policy:stand_up:0.00, policy:recover_fall:0.00] non_drive=[policy:stand_up:2.00, policy:recover_fall:0.00] -[rl-pick] chosen via non-drive tiebreak in deficit near-tie band: best_def=0.000 delta=0.000 → policy:stand_up (nd=2.00, q=+0.66) among [policy:stand_up(def=0.000, nd=2.00, q=+0.66), policy:recover_fall(def=0.000, nd=0.00, q=+0.00)] -[executed] policy:stand_up (ok, reward=+1.00) binding=w21 (b21) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) +[env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) +[env→world] (long-term obs unchanged; no new pred:* bindings written) +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[env→controller] policy:follow_mom (added 2 bindings) +[pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] +[executed] policy:follow_mom (ok, reward=+0.10) binding=w121 (b121) [maps] selection_on=WG execute_on=WM -pre: dev_gate: age_days=0.00<=3.0, trigger: fallen=True or (stand_intent=False and not standing=True) (hunger=0.50) +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) wg_base: {'base': 'HERE', 'bid': '?'} -wg_foa: {'seeds': ['b12', 'b8'], 'size': 4, 'ids': {'b10', 'b8', 'b12', 'b9'}} -wg_cands:['b8', '?'] -post: dev_gate: age_days=0.00<=3.0, trigger: fallen=True or (stand_intent=False and not standing=True) (hunger=0.50) -[env-loop] summary cognitive_cycle=4/25 env_step=3 stage=struggle env_posture=fallen bm_posture=fallen mom=far nipple=hidden last_policy='policy:stand_up' expected_posture=standing zone=unknown -[env-loop] note: expected_posture is a Scratch postcondition (hypothesis); env_posture is storyboard truth this tick. -[env-loop] explain posture: stand_up was requested this tick, but the newborn is still kept fallen by the storyboard (stage='struggle'); standing will only appear once the stand-up transition threshold is reached. -[env-loop] explain nipple: nipple remains hidden; the storyboard has not yet made it reachable (or visible) to the kid in this stage. -[env-loop] explain zone: zone is 'unknown'; either the environment has not yet established clear cliff/shelter distances or they are in a combination we do not classify, so gates treat it conservatively. - -[env-loop] Cognitive Cycle 5/25 -[env] env_step=4 (since reset) stage=first_stand posture=standing mom_distance=far nipple_state=hidden action='policy:stand_up' -[pred_err] v0 err={'posture': 0} pred_posture=standing obs_posture=standing from=policy:stand_up -[env→working] MAP pred:posture:standing → w6 (b6) (entity=self, slot=posture) [changed] -[env→working] MAP pred:proximity:mom:far → w7 (b7) (entity=mom, slot=proximity:mom) -[env→working] MAP pred:proximity:shelter:far → w8 (b8) (entity=shelter, slot=proximity:shelter) -[env→working] MAP pred:hazard:cliff:near → w9 (b9) (entity=cliff, slot=hazard:cliff) [changed] -[env→world] KEYFRAME: stage_change 'struggle'→'first_stand' | cleared 4 pred slot(s), 0 cue slot(s) -[env→world] pred:posture:standing → b14 (attach=now) -[env→world] pred:proximity:mom:far → b15 (attach=latest) -[env→world] pred:proximity:shelter:far → b16 (attach=latest) -[env→world] pred:hazard:cliff:near → b17 (attach=latest) -[wm<->col] store: ok sig=5f00d655bb2a567c bid=b18 eid=91400353… (auto_boundary_stage:struggle->first_stand_zone:unknown->unsafe_cliff_near) -[wm<->col] retrieve: skip why=enabled_boundary_confident mode=merge top_k=5 (auto_boundary_stage:struggle->first_stand_zone:unknown->unsafe_cliff_near) -[wm<->col] apply: no-op (enabled_boundary_confident) -[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=near shelter=far zone=unsafe_cliff_near -[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=near shelter=far zone=unsafe_cliff_near +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=29/50 env_step=53 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=29/50 env_step=53 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 30/50 +[env] env_step=54 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] +[env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) +[env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) +[env→world] (long-term obs unchanged; no new pred:* bindings written) +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe [env→controller] policy:follow_mom (added 2 bindings) [pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] -[executed] policy:follow_mom (ok, reward=+0.10) binding=w23 (b23) +[executed] policy:follow_mom (ok, reward=+0.10) binding=w123 (b123) [maps] selection_on=WG execute_on=WM -pre: dev_gate: True, trigger: fallback=True when not fallen; posture=standing zone=unsafe_cliff_near (hunger=0.50, fatigue=0.30) -wg_base: {'base': 'NEAREST_PRED', 'pred': 'posture:standing', 'bid': 'b14'} -wg_foa: {'seeds': ['b18', 'b14', 'b12'], 'size': 5, 'ids': {'b12', 'b16', 'b18', 'b15', 'b14'}} -wg_cands:['b14', '?'] -post: dev_gate: True, trigger: fallback=True when not fallen; posture=standing zone=unsafe_cliff_near (hunger=0.50, fatigue=0.30) -[env-loop] summary cognitive_cycle=5/25 env_step=4 stage=first_stand env_posture=standing bm_posture=standing mom=far nipple=hidden last_policy='policy:follow_mom' zone=unsafe_cliff_near -[env-loop] explain posture: stand_up action applied by the environment: posture changed fallen→standing as the storyboard moved 'struggle'→'first_stand'. -[env-loop] explain nipple: nipple remains hidden; the storyboard has not yet made it reachable (or visible) to the kid in this stage. -[env-loop] explain zone: zone changed 'unknown'→'unsafe_cliff_near'; BodyMap now sees the cliff near while shelter is not near, so this geometry is treated as unsafe for resting. - -[env-loop] Cognitive Cycle 6/25 -[env] env_step=5 (since reset) stage=first_stand posture=standing mom_distance=near nipple_state=hidden action='policy:follow_mom' -[env→working] MAP pred:posture:standing → w6 (b6) (entity=self, slot=posture) -[env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) [changed] -[env→working] MAP pred:proximity:shelter:far → w8 (b8) (entity=shelter, slot=proximity:shelter) -[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) [changed] +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=30/50 env_step=54 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=30/50 env_step=54 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 31/50 +[env] env_step=55 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] +[env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) [env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) -[env→world] pred:proximity:mom:close → b20 (attach=now) -[env→world] pred:hazard:cliff:far → b21 (attach=latest) -[env→world] cue:vision:silhouette:mom → b22 (attach=latest) -[wm<->col] store: ok sig=67e06a49e4e4fe55 bid=b23 eid=ae76a4bd… (auto_boundary_zone:unsafe_cliff_near->unknown) -[wm<->col] retrieve: skip why=enabled_boundary_confident mode=merge top_k=5 (auto_boundary_zone:unsafe_cliff_near->unknown) -[wm<->col] apply: no-op (enabled_boundary_confident) -[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=far zone=unknown -[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=far zone=unknown +[env→world] (long-term obs unchanged; no new pred:* bindings written) +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe [env→controller] policy:follow_mom (added 2 bindings) [pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] -[executed] policy:follow_mom (ok, reward=+0.10) binding=w25 (b25) +[executed] policy:follow_mom (ok, reward=+0.10) binding=w125 (b125) [maps] selection_on=WG execute_on=WM -pre: dev_gate: True, trigger: fallback=True when not fallen; posture=standing zone=unknown (hunger=0.50, fatigue=0.30) -wg_base: {'base': 'NEAREST_PRED', 'pred': 'posture:standing', 'bid': 'b14'} -wg_foa: {'seeds': ['b23', 'b14', 'b12', 'b18', 'b22'], 'size': 10, 'ids': {'b23', 'b19', 'b12', 'b18', 'b21', 'b20', 'b16', 'b15', 'b22', 'b14'}} -wg_cands:['b14', '?'] -post: dev_gate: True, trigger: fallback=True when not fallen; posture=standing zone=unknown (hunger=0.50, fatigue=0.30) -[env-loop] summary cognitive_cycle=6/25 env_step=5 stage=first_stand env_posture=standing bm_posture=standing mom=near nipple=hidden last_policy='policy:follow_mom' zone=unknown -[env-loop] explain posture: posture remains 'standing'; no storyboard transition affecting posture this step. -[env-loop] explain nipple: nipple remains hidden; the storyboard has not yet made it reachable (or visible) to the kid in this stage. -[env-loop] explain zone: zone changed 'unsafe_cliff_near'→'unknown'; cliff/shelter geometry is now in a combination we do not classify (e.g., both near) or partially known, so we treat it conservatively. - -[env-loop] Cognitive Cycle 7/25 -[env] env_step=6 (since reset) stage=first_stand posture=standing mom_distance=near nipple_state=hidden action='policy:follow_mom' -[env→working] MAP pred:posture:standing → w6 (b6) (entity=self, slot=posture) +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=31/50 env_step=55 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=31/50 env_step=55 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 32/50 +[env] env_step=56 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] [env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) -[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) [changed] +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) [env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) [env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) -[env→world] pred:proximity:shelter:near → b25 (attach=now) -[wm<->col] store: ok sig=9dae5936c3819142 bid=b26 eid=a4763bdc… (auto_boundary_zone:unknown->safe) -[wm<->col] retrieve: skip why=enabled_boundary_confident mode=merge top_k=5 (auto_boundary_zone:unknown->safe) -[wm<->col] apply: no-op (enabled_boundary_confident) +[env→world] (long-term obs unchanged; no new pred:* bindings written) [gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe [gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe [env→controller] policy:follow_mom (added 2 bindings) [pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] -[executed] policy:follow_mom (ok, reward=+0.10) binding=w27 (b27) +[executed] policy:follow_mom (ok, reward=+0.10) binding=w127 (b127) [maps] selection_on=WG execute_on=WM -pre: dev_gate: True, trigger: fallback=True when not fallen; posture=standing zone=safe (hunger=0.50, fatigue=0.30) -wg_base: {'base': 'NEAREST_PRED', 'pred': 'posture:standing', 'bid': 'b14'} -wg_foa: {'seeds': ['b26', 'b14', 'b12', 'b18', 'b22', 'b23'], 'size': 13, 'ids': {'b23', 'b19', 'b14', 'b25', 'b18', 'b21', 'b12', 'b24', 'b20', 'b16', 'b15', 'b22', 'b26'}} -wg_cands:['b14', '?'] -post: dev_gate: True, trigger: fallback=True when not fallen; posture=standing zone=safe (hunger=0.50, fatigue=0.30) -[env-loop] summary cognitive_cycle=7/25 env_step=6 stage=first_stand env_posture=standing bm_posture=standing mom=near nipple=hidden last_policy='policy:follow_mom' zone=safe -[env-loop] explain posture: posture remains 'standing'; no storyboard transition affecting posture this step. -[env-loop] explain nipple: nipple remains hidden; the storyboard has not yet made it reachable (or visible) to the kid in this stage. -[env-loop] explain zone: zone changed 'unknown'→'safe'; cliff is no longer near and shelter is near, so the kid is now in a sheltered niche where resting/feeding are allowed. - -[env-loop] Cognitive Cycle 8/25 -[env] env_step=7 (since reset) stage=first_stand posture=standing mom_distance=near nipple_state=hidden action='policy:follow_mom' -[env→working] MAP pred:posture:standing → w6 (b6) (entity=self, slot=posture) +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=32/50 env_step=56 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=32/50 env_step=56 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 33/50 +[env] env_step=57 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] [env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) -[env→working] MAP pred:proximity:shelter:far → w8 (b8) (entity=shelter, slot=proximity:shelter) [changed] -[env→working] MAP pred:hazard:cliff:near → w9 (b9) (entity=cliff, slot=hazard:cliff) [changed] +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) [env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) -[env→world] pred:proximity:shelter:far → b28 (attach=now) -[env→world] pred:hazard:cliff:near → b29 (attach=latest) -[wm<->col] store: ok sig=da27e75679075af2 bid=b30 eid=0cd15621… (auto_boundary_zone:safe->unsafe_cliff_near) -[wm<->col] retrieve: skip why=enabled_boundary_confident mode=merge top_k=5 (auto_boundary_zone:safe->unsafe_cliff_near) -[wm<->col] apply: no-op (enabled_boundary_confident) -[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=near shelter=far zone=unsafe_cliff_near -[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=near shelter=far zone=unsafe_cliff_near +[env→world] (long-term obs unchanged; no new pred:* bindings written) +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe [env→controller] policy:follow_mom (added 2 bindings) [pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] -[executed] policy:follow_mom (ok, reward=+0.10) binding=w29 (b29) +[executed] policy:follow_mom (ok, reward=+0.10) binding=w129 (b129) [maps] selection_on=WG execute_on=WM -pre: dev_gate: True, trigger: fallback=True when not fallen; posture=standing zone=unsafe_cliff_near (hunger=0.50, fatigue=0.30) -wg_base: {'base': 'NEAREST_PRED', 'pred': 'posture:standing', 'bid': 'b14'} -wg_foa: {'seeds': ['b30', 'b14', 'b12', 'b18', 'b22', 'b23', 'b26'], 'size': 17, 'ids': {'b12', 'b18', 'b21', 'b30', 'b27', 'b24', 'b20', 'b16', 'b22', 'b26', 'b23', 'b19', 'b28', 'b25', 'b29', 'b15', 'b14'}} -wg_cands:['b14', '?'] -post: dev_gate: True, trigger: fallback=True when not fallen; posture=standing zone=unsafe_cliff_near (hunger=0.50, fatigue=0.30) -[env-loop] summary cognitive_cycle=8/25 env_step=7 stage=first_stand env_posture=standing bm_posture=standing mom=near nipple=hidden last_policy='policy:follow_mom' zone=unsafe_cliff_near -[env-loop] explain posture: posture remains 'standing'; no storyboard transition affecting posture this step. -[env-loop] explain nipple: nipple remains hidden; the storyboard has not yet made it reachable (or visible) to the kid in this stage. -[env-loop] explain zone: zone changed 'safe'→'unsafe_cliff_near'; BodyMap now sees the cliff near while shelter is not near, so this geometry is treated as unsafe for resting. - -[env-loop] Cognitive Cycle 9/25 -[env] env_step=8 (since reset) stage=first_stand posture=standing mom_distance=near nipple_state=hidden action='policy:follow_mom' -[env→working] MAP pred:posture:standing → w6 (b6) (entity=self, slot=posture) +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=33/50 env_step=57 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=33/50 env_step=57 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 34/50 +[env] env_step=58 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] [env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) -[env→working] MAP pred:proximity:shelter:far → w8 (b8) (entity=shelter, slot=proximity:shelter) -[env→working] MAP pred:hazard:cliff:near → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) [env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) [env→world] (long-term obs unchanged; no new pred:* bindings written) -[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=near shelter=far zone=unsafe_cliff_near -[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=near shelter=far zone=unsafe_cliff_near +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe [env→controller] policy:follow_mom (added 2 bindings) [pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] -[executed] policy:follow_mom (ok, reward=+0.10) binding=w31 (b31) +[executed] policy:follow_mom (ok, reward=+0.10) binding=w131 (b131) [maps] selection_on=WG execute_on=WM -pre: dev_gate: True, trigger: fallback=True when not fallen; posture=standing zone=unsafe_cliff_near (hunger=0.50, fatigue=0.30) -wg_base: {'base': 'NEAREST_PRED', 'pred': 'posture:standing', 'bid': 'b14'} -wg_foa: {'seeds': ['b31', 'b14', 'b12', 'b18', 'b22', 'b23', 'b26', 'b30'], 'size': 18, 'ids': {'b12', 'b18', 'b21', 'b30', 'b27', 'b24', 'b20', 'b31', 'b16', 'b22', 'b26', 'b23', 'b19', 'b28', 'b25', 'b29', 'b15', 'b14'}} -wg_cands:['b14', '?'] -post: dev_gate: True, trigger: fallback=True when not fallen; posture=standing zone=unsafe_cliff_near (hunger=0.50, fatigue=0.30) -[env-loop] summary cognitive_cycle=9/25 env_step=8 stage=first_stand env_posture=standing bm_posture=standing mom=near nipple=hidden last_policy='policy:follow_mom' zone=unsafe_cliff_near -[env-loop] explain posture: posture remains 'standing'; no storyboard transition affecting posture this step. -[env-loop] explain nipple: nipple remains hidden; the storyboard has not yet made it reachable (or visible) to the kid in this stage. -[env-loop] explain zone: zone is 'unsafe_cliff_near'; BodyMap sees a nearby cliff but no nearby shelter, so resting policies are gated off in this configuration. - -[env-loop] Cognitive Cycle 10/25 -[env] env_step=9 (since reset) stage=first_stand posture=standing mom_distance=near nipple_state=hidden action='policy:follow_mom' -[env→working] MAP pred:posture:standing → w6 (b6) (entity=self, slot=posture) +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=34/50 env_step=58 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=34/50 env_step=58 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 35/50 +[env] env_step=59 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] [env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) -[env→working] MAP pred:proximity:shelter:far → w8 (b8) (entity=shelter, slot=proximity:shelter) -[env→working] MAP pred:hazard:cliff:near → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) [env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) [env→world] (long-term obs unchanged; no new pred:* bindings written) -[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=near shelter=far zone=unsafe_cliff_near -[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=near shelter=far zone=unsafe_cliff_near +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe [env→controller] policy:follow_mom (added 2 bindings) [pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] -[executed] policy:follow_mom (ok, reward=+0.10) binding=w33 (b33) +[executed] policy:follow_mom (ok, reward=+0.10) binding=w133 (b133) [maps] selection_on=WG execute_on=WM -pre: dev_gate: True, trigger: fallback=True when not fallen; posture=standing zone=unsafe_cliff_near (hunger=0.50, fatigue=0.30) -wg_base: {'base': 'NEAREST_PRED', 'pred': 'posture:standing', 'bid': 'b14'} -wg_foa: {'seeds': ['b31', 'b14', 'b12', 'b18', 'b22', 'b23', 'b26', 'b30'], 'size': 18, 'ids': {'b12', 'b18', 'b21', 'b30', 'b27', 'b24', 'b20', 'b31', 'b16', 'b22', 'b26', 'b23', 'b19', 'b28', 'b25', 'b29', 'b15', 'b14'}} -wg_cands:['b14', '?'] -post: dev_gate: True, trigger: fallback=True when not fallen; posture=standing zone=unsafe_cliff_near (hunger=0.50, fatigue=0.30) -[env-loop] summary cognitive_cycle=10/25 env_step=9 stage=first_stand env_posture=standing bm_posture=standing mom=near nipple=hidden last_policy='policy:follow_mom' zone=unsafe_cliff_near -[env-loop] explain posture: posture remains 'standing'; no storyboard transition affecting posture this step. -[env-loop] explain nipple: nipple remains hidden; the storyboard has not yet made it reachable (or visible) to the kid in this stage. -[env-loop] explain zone: zone is 'unsafe_cliff_near'; BodyMap sees a nearby cliff but no nearby shelter, so resting policies are gated off in this configuration. - -[env-loop] Cognitive Cycle 11/25 -[env] env_step=10 (since reset) stage=first_stand posture=standing mom_distance=near nipple_state=hidden action='policy:follow_mom' -[env→working] MAP pred:posture:standing → w6 (b6) (entity=self, slot=posture) +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=35/50 env_step=59 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=35/50 env_step=59 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 36/50 +[env] env_step=60 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] [env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) -[env→working] MAP pred:proximity:shelter:far → w8 (b8) (entity=shelter, slot=proximity:shelter) -[env→working] MAP pred:hazard:cliff:near → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) [env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) [env→world] (long-term obs unchanged; no new pred:* bindings written) -[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=near shelter=far zone=unsafe_cliff_near -[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=near shelter=far zone=unsafe_cliff_near +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe [env→controller] policy:follow_mom (added 2 bindings) [pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] -[executed] policy:follow_mom (ok, reward=+0.10) binding=w35 (b35) +[executed] policy:follow_mom (ok, reward=+0.10) binding=w135 (b135) [maps] selection_on=WG execute_on=WM -pre: dev_gate: True, trigger: fallback=True when not fallen; posture=standing zone=unsafe_cliff_near (hunger=0.50, fatigue=0.30) -wg_base: {'base': 'NEAREST_PRED', 'pred': 'posture:standing', 'bid': 'b14'} -wg_foa: {'seeds': ['b31', 'b14', 'b12', 'b18', 'b22', 'b23', 'b26', 'b30'], 'size': 18, 'ids': {'b12', 'b18', 'b21', 'b30', 'b27', 'b24', 'b20', 'b31', 'b16', 'b22', 'b26', 'b23', 'b19', 'b28', 'b25', 'b29', 'b15', 'b14'}} -wg_cands:['b14', '?'] -post: dev_gate: True, trigger: fallback=True when not fallen; posture=standing zone=unsafe_cliff_near (hunger=0.50, fatigue=0.30) -[env-loop] summary cognitive_cycle=11/25 env_step=10 stage=first_stand env_posture=standing bm_posture=standing mom=near nipple=hidden last_policy='policy:follow_mom' zone=unsafe_cliff_near -[env-loop] explain posture: posture remains 'standing'; no storyboard transition affecting posture this step. -[env-loop] explain nipple: nipple remains hidden; the storyboard has not yet made it reachable (or visible) to the kid in this stage. -[env-loop] explain zone: zone is 'unsafe_cliff_near'; BodyMap sees a nearby cliff but no nearby shelter, so resting policies are gated off in this configuration. - -[env-loop] Cognitive Cycle 12/25 -[env] env_step=11 (since reset) stage=first_stand posture=standing mom_distance=near nipple_state=reachable action='policy:follow_mom' -[env→working] MAP pred:posture:standing → w6 (b6) (entity=self, slot=posture) +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=36/50 env_step=60 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=36/50 env_step=60 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 37/50 +[env] env_step=61 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] [env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) -[env→working] MAP pred:proximity:shelter:far → w8 (b8) (entity=shelter, slot=proximity:shelter) -[env→working] MAP pred:hazard:cliff:near → w9 (b9) (entity=cliff, slot=hazard:cliff) -[env→working] MAP pred:nipple:found → w6 (b6) (entity=self, slot=nipple) [changed] +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) [env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) -[env→world] pred:nipple:found → b32 (attach=now) -[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=near shelter=far zone=unsafe_cliff_near -[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=near shelter=far zone=unsafe_cliff_near +[env→world] (long-term obs unchanged; no new pred:* bindings written) +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe [env→controller] policy:follow_mom (added 2 bindings) [pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] -[executed] policy:follow_mom (ok, reward=+0.10) binding=w37 (b37) +[executed] policy:follow_mom (ok, reward=+0.10) binding=w137 (b137) [maps] selection_on=WG execute_on=WM -pre: dev_gate: True, trigger: fallback=True when not fallen; posture=standing zone=unsafe_cliff_near (hunger=0.50, fatigue=0.30) -wg_base: {'base': 'NEAREST_PRED', 'pred': 'posture:standing', 'bid': 'b14'} -wg_foa: {'seeds': ['b32', 'b14', 'b12', 'b18', 'b22', 'b23', 'b26', 'b30'], 'size': 19, 'ids': {'b12', 'b18', 'b21', 'b30', 'b27', 'b24', 'b20', 'b31', 'b16', 'b22', 'b26', 'b23', 'b19', 'b28', 'b25', 'b14', 'b29', 'b15', 'b32'}} -wg_cands:['b14', '?'] -post: dev_gate: True, trigger: fallback=True when not fallen; posture=standing zone=unsafe_cliff_near (hunger=0.50, fatigue=0.30) -[env-loop] summary cognitive_cycle=12/25 env_step=11 stage=first_stand env_posture=standing bm_posture=standing mom=near nipple=reachable last_policy='policy:follow_mom' zone=unsafe_cliff_near -[env-loop] explain posture: posture remains 'standing'; no storyboard transition affecting posture this step. -[env-loop] explain nipple: nipple changed hidden→reachable as the storyboard crossed its "nipple reachable" threshold in stage 'first_stand'. -[env-loop] explain zone: zone is 'unsafe_cliff_near'; BodyMap sees a nearby cliff but no nearby shelter, so resting policies are gated off in this configuration. - -[env-loop] Cognitive Cycle 13/25 -[env] env_step=12 (since reset) stage=first_stand posture=standing mom_distance=near nipple_state=reachable action='policy:follow_mom' -[env→working] MAP pred:posture:standing → w6 (b6) (entity=self, slot=posture) +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=37/50 env_step=61 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=37/50 env_step=61 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 38/50 +[env] env_step=62 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] [env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) -[env→working] MAP pred:proximity:shelter:far → w8 (b8) (entity=shelter, slot=proximity:shelter) -[env→working] MAP pred:hazard:cliff:near → w9 (b9) (entity=cliff, slot=hazard:cliff) -[env→working] MAP pred:nipple:found → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) [env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) [env→world] (long-term obs unchanged; no new pred:* bindings written) -[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=near shelter=far zone=unsafe_cliff_near -[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=near shelter=far zone=unsafe_cliff_near +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe [env→controller] policy:follow_mom (added 2 bindings) [pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] -[executed] policy:follow_mom (ok, reward=+0.10) binding=w39 (b39) +[executed] policy:follow_mom (ok, reward=+0.10) binding=w139 (b139) [maps] selection_on=WG execute_on=WM -pre: dev_gate: True, trigger: fallback=True when not fallen; posture=standing zone=unsafe_cliff_near (hunger=0.50, fatigue=0.30) -wg_base: {'base': 'NEAREST_PRED', 'pred': 'posture:standing', 'bid': 'b14'} -wg_foa: {'seeds': ['b33', 'b14', 'b12', 'b18', 'b22', 'b23', 'b26', 'b30'], 'size': 20, 'ids': {'b12', 'b21', 'b27', 'b31', 'b25', 'b29', 'b32', 'b14', 'b18', 'b30', 'b33', 'b20', 'b24', 'b16', 'b22', 'b26', 'b23', 'b19', 'b28', 'b15'}} -wg_cands:['b14', '?'] -post: dev_gate: True, trigger: fallback=True when not fallen; posture=standing zone=unsafe_cliff_near (hunger=0.50, fatigue=0.30) -[env-loop] summary cognitive_cycle=13/25 env_step=12 stage=first_stand env_posture=standing bm_posture=standing mom=near nipple=reachable last_policy='policy:follow_mom' zone=unsafe_cliff_near -[env-loop] explain posture: posture remains 'standing'; no storyboard transition affecting posture this step. -[env-loop] explain nipple: nipple_state remains 'reachable'; the nipple is available but has not yet latched this step. -[env-loop] explain zone: zone is 'unsafe_cliff_near'; BodyMap sees a nearby cliff but no nearby shelter, so resting policies are gated off in this configuration. - -[env-loop] Cognitive Cycle 14/25 -[env] env_step=13 (since reset) stage=first_latch posture=latched mom_distance=near nipple_state=latched action='policy:follow_mom' -[env→working] MAP pred:posture:standing → w6 (b6) (entity=self, slot=posture) +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=38/50 env_step=62 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=38/50 env_step=62 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 39/50 +[env] env_step=63 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] [env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) -[env→working] MAP pred:proximity:shelter:far → w8 (b8) (entity=shelter, slot=proximity:shelter) -[env→working] MAP pred:hazard:cliff:near → w9 (b9) (entity=cliff, slot=hazard:cliff) -[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) [changed] -[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) [changed] +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) +[env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) +[env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) [env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) -[env→world] KEYFRAME: stage_change 'first_stand'→'first_latch' | cleared 5 pred slot(s), 1 cue slot(s) -[env→world] pred:posture:standing → b34 (attach=now) -[env→world] pred:proximity:mom:close → b35 (attach=latest) -[env→world] pred:proximity:shelter:far → b36 (attach=latest) -[env→world] pred:hazard:cliff:near → b37 (attach=latest) -[env→world] pred:nipple:latched → b38 (attach=latest) -[env→world] pred:milk:drinking → b39 (attach=latest) -[env→world] cue:vision:silhouette:mom → b40 (attach=latest) -[wm<->col] store: ok sig=8e5af242a1c6ed40 bid=b41 eid=6912aee9… (auto_boundary_stage:first_stand->first_latch) -[wm<->col] retrieve: skip why=enabled_boundary_confident mode=merge top_k=5 (auto_boundary_stage:first_stand->first_latch) -[wm<->col] apply: no-op (enabled_boundary_confident) -[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=near shelter=far zone=unsafe_cliff_near -[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=near shelter=far zone=unsafe_cliff_near +[env→world] (long-term obs unchanged; no new pred:* bindings written) +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe +[gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe [env→controller] policy:follow_mom (added 2 bindings) [pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] -[executed] policy:follow_mom (ok, reward=+0.10) binding=w41 (b41) +[executed] policy:follow_mom (ok, reward=+0.10) binding=w141 (b141) [maps] selection_on=WG execute_on=WM -pre: dev_gate: True, trigger: fallback=True when not fallen; posture=standing zone=unsafe_cliff_near (hunger=0.50, fatigue=0.30) -wg_base: {'base': 'NEAREST_PRED', 'pred': 'posture:standing', 'bid': 'b34'} -wg_foa: {'seeds': ['b41', 'b34', 'b12', 'b18', 'b22', 'b23', 'b26', 'b30', 'b40'], 'size': 11, 'ids': {'b36', 'b23', 'b12', 'b18', 'b40', 'b41', 'b30', 'b35', 'b34', 'b22', 'b26'}} -wg_cands:['b34', '?'] -post: dev_gate: True, trigger: fallback=True when not fallen; posture=standing zone=unsafe_cliff_near (hunger=0.50, fatigue=0.30) -[env-loop] summary cognitive_cycle=14/25 env_step=13 stage=first_latch env_posture=latched bm_posture=standing mom=near nipple=latched last_policy='policy:follow_mom' zone=unsafe_cliff_near -[env-loop] explain posture: nipple became reachable and then latched in the storyboard; the kid switches from upright to 'latched' while feeding. -[env-loop] explain nipple: nipple changed reachable→latched as the storyboard hit its "auto latch" threshold; milk:drinking now begins. -[env-loop] explain zone: zone is 'unsafe_cliff_near'; BodyMap sees a nearby cliff but no nearby shelter, so resting policies are gated off in this configuration. - -[env-loop] Cognitive Cycle 15/25 -[env] env_step=14 (since reset) stage=first_latch posture=latched mom_distance=near nipple_state=latched action='policy:follow_mom' -[env→working] MAP pred:posture:standing → w6 (b6) (entity=self, slot=posture) +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=39/50 env_step=63 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=39/50 env_step=63 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 40/50 +[env] env_step=64 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] [env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) -[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) [changed] -[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) [changed] +[env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) +[env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) [env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) [env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) [env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) -[env→world] pred:proximity:shelter:near → b43 (attach=now) -[env→world] pred:hazard:cliff:far → b44 (attach=latest) -[wm<->col] store: ok sig=6525a3be5dfd9d75 bid=b45 eid=ba24e10b… (auto_boundary_zone:unsafe_cliff_near->safe) -[wm<->col] retrieve: skip why=enabled_boundary_confident mode=merge top_k=5 (auto_boundary_zone:unsafe_cliff_near->safe) -[wm<->col] apply: no-op (enabled_boundary_confident) +[env→world] (long-term obs unchanged; no new pred:* bindings written) [gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe [gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe [env→controller] policy:follow_mom (added 2 bindings) [pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] -[executed] policy:follow_mom (ok, reward=+0.10) binding=w43 (b43) +[executed] policy:follow_mom (ok, reward=+0.10) binding=w143 (b143) [maps] selection_on=WG execute_on=WM -pre: dev_gate: True, trigger: fallback=True when not fallen; posture=standing zone=safe (hunger=0.50, fatigue=0.30) -wg_base: {'base': 'NEAREST_PRED', 'pred': 'posture:standing', 'bid': 'b34'} -wg_foa: {'seeds': ['b45', 'b34', 'b12', 'b18', 'b22', 'b23', 'b26', 'b30', 'b40', 'b41'], 'size': 15, 'ids': {'b42', 'b36', 'b23', 'b12', 'b18', 'b40', 'b44', 'b45', 'b41', 'b30', 'b35', 'b43', 'b34', 'b22', 'b26'}} -wg_cands:['b34', '?'] -post: dev_gate: True, trigger: fallback=True when not fallen; posture=standing zone=safe (hunger=0.50, fatigue=0.30) -[env-loop] summary cognitive_cycle=15/25 env_step=14 stage=first_latch env_posture=latched bm_posture=standing mom=near nipple=latched last_policy='policy:follow_mom' zone=safe -[env-loop] explain posture: posture remains 'latched'; no storyboard transition affecting posture this step. +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=40/50 env_step=64 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. [env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. -[env-loop] explain zone: zone changed 'unsafe_cliff_near'→'safe'; cliff is no longer near and shelter is near, so the kid is now in a sheltered niche where resting/feeding are allowed. - -[env-loop] Cognitive Cycle 16/25 -[env] env_step=15 (since reset) stage=first_latch posture=latched mom_distance=near nipple_state=latched action='policy:follow_mom' -[env→working] MAP pred:posture:standing → w6 (b6) (entity=self, slot=posture) +[env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=40/50 env_step=64 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 41/50 +[env] env_step=65 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] [env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) [env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) [env→working] MAP pred:hazard:cliff:far → w9 (b9) (entity=cliff, slot=hazard:cliff) @@ -566,20 +1318,26 @@ post: dev_gate: True, trigger: fallback=True when not fallen; posture=standing z [gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe [env→controller] policy:follow_mom (added 2 bindings) [pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] -[executed] policy:follow_mom (ok, reward=+0.10) binding=w45 (b45) +[executed] policy:follow_mom (ok, reward=+0.10) binding=w145 (b145) [maps] selection_on=WG execute_on=WM -pre: dev_gate: True, trigger: fallback=True when not fallen; posture=standing zone=safe (hunger=0.50, fatigue=0.30) -wg_base: {'base': 'NEAREST_PRED', 'pred': 'posture:standing', 'bid': 'b34'} -wg_foa: {'seeds': ['b46', 'b34', 'b12', 'b18', 'b22', 'b23', 'b26', 'b30', 'b40', 'b41', 'b45'], 'size': 16, 'ids': {'b42', 'b12', 'b18', 'b46', 'b44', 'b30', 'b43', 'b34', 'b22', 'b26', 'b36', 'b23', 'b40', 'b45', 'b41', 'b35'}} -wg_cands:['b34', '?'] -post: dev_gate: True, trigger: fallback=True when not fallen; posture=standing zone=safe (hunger=0.50, fatigue=0.30) -[env-loop] summary cognitive_cycle=16/25 env_step=15 stage=first_latch env_posture=latched bm_posture=standing mom=near nipple=latched last_policy='policy:follow_mom' zone=safe -[env-loop] explain posture: posture remains 'latched'; no storyboard transition affecting posture this step. +pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +wg_base: {'base': 'HERE', 'bid': '?'} +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] +post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) +[env-loop] summary cognitive_cycle=41/50 env_step=65 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. [env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. [env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. - -[env-loop] Cognitive Cycle 17/25 -[env] env_step=16 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[cycle] IN -- cycle=41/50 env_step=65 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 42/50 +[env] env_step=66 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' [env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] [env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) [env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) @@ -587,35 +1345,31 @@ post: dev_gate: True, trigger: fallback=True when not fallen; posture=standing z [env→working] MAP pred:nipple:latched → w6 (b6) (entity=self, slot=nipple) [env→working] MAP pred:milk:drinking → w6 (b6) (entity=self, slot=milk) [env→working] MAP cue:vision:silhouette:mom → w7 (b7) (entity=mom) -[env→world] KEYFRAME: stage_change 'first_latch'→'rest' | cleared 6 pred slot(s), 1 cue slot(s) -[env→world] pred:resting → b47 (attach=now) -[env→world] pred:proximity:mom:close → b48 (attach=latest) -[env→world] pred:proximity:shelter:near → b49 (attach=latest) -[env→world] pred:hazard:cliff:far → b50 (attach=latest) -[env→world] pred:nipple:latched → b51 (attach=latest) -[env→world] pred:milk:drinking → b52 (attach=latest) -[env→world] cue:vision:silhouette:mom → b53 (attach=latest) -[wm<->col] store: ok sig=8e56f9fc3c300bf9 bid=b54 eid=96b18566… (auto_boundary_stage:first_latch->rest) -[wm<->col] retrieve: skip why=enabled_boundary_confident mode=merge top_k=5 (auto_boundary_stage:first_latch->rest) -[wm<->col] apply: no-op (enabled_boundary_confident) +[env→world] (long-term obs unchanged; no new pred:* bindings written) [gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe [gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe [env→controller] policy:follow_mom (added 2 bindings) [pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] -[executed] policy:follow_mom (ok, reward=+0.10) binding=w47 (b47) +[executed] policy:follow_mom (ok, reward=+0.10) binding=w147 (b147) [maps] selection_on=WG execute_on=WM pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) wg_base: {'base': 'HERE', 'bid': '?'} -wg_foa: {'seeds': ['b54', 'b47', 'b12', 'b18', 'b22', 'b23', 'b26', 'b30', 'b40', 'b41', 'b45', 'b53'], 'size': 14, 'ids': {'b54', 'b47', 'b23', 'b12', 'b18', 'b40', 'b45', 'b41', 'b53', 'b48', 'b49', 'b30', 'b22', 'b26'}} -wg_cands:['b47', '?'] +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) -[env-loop] summary cognitive_cycle=17/25 env_step=16 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe -[env-loop] explain posture: after some time latched and feeding, the storyboard advanced to 'rest'; the kid is now resting curled up against mom in a sheltered niche. -[env-loop] explain nipple: stage advanced from 'first_latch' to 'rest' while the nipple remains latched; the kid is effectively resting with ongoing access to milk. +[env-loop] summary cognitive_cycle=42/50 env_step=66 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. +[env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. [env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. - -[env-loop] Cognitive Cycle 18/25 -[env] env_step=17 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[cycle] IN -- cycle=42/50 env_step=66 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 43/50 +[env] env_step=67 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' [env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] [env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) [env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) @@ -628,20 +1382,26 @@ post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zo [gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe [env→controller] policy:follow_mom (added 2 bindings) [pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] -[executed] policy:follow_mom (ok, reward=+0.10) binding=w49 (b49) +[executed] policy:follow_mom (ok, reward=+0.10) binding=w149 (b149) [maps] selection_on=WG execute_on=WM pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) wg_base: {'base': 'HERE', 'bid': '?'} -wg_foa: {'seeds': ['b55', 'b47', 'b12', 'b18', 'b22', 'b23', 'b26', 'b30', 'b40', 'b41', 'b45', 'b53', 'b54'], 'size': 16, 'ids': {'b54', 'b47', 'b23', 'b12', 'b18', 'b40', 'b45', 'b41', 'b53', 'b48', 'b49', 'b30', 'b55', 'b22', 'b50', 'b26'}} -wg_cands:['b47', '?'] +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) -[env-loop] summary cognitive_cycle=18/25 env_step=17 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] summary cognitive_cycle=43/50 env_step=67 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe [env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. [env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. [env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. - -[env-loop] Cognitive Cycle 19/25 -[env] env_step=18 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[cycle] IN -- cycle=43/50 env_step=67 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 44/50 +[env] env_step=68 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' [env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] [env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) [env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) @@ -654,20 +1414,26 @@ post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zo [gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe [env→controller] policy:follow_mom (added 2 bindings) [pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] -[executed] policy:follow_mom (ok, reward=+0.10) binding=w51 (b51) +[executed] policy:follow_mom (ok, reward=+0.10) binding=w151 (b151) [maps] selection_on=WG execute_on=WM pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) wg_base: {'base': 'HERE', 'bid': '?'} -wg_foa: {'seeds': ['b55', 'b47', 'b12', 'b18', 'b22', 'b23', 'b26', 'b30', 'b40', 'b41', 'b45', 'b53', 'b54'], 'size': 16, 'ids': {'b54', 'b47', 'b23', 'b12', 'b18', 'b40', 'b45', 'b41', 'b53', 'b48', 'b49', 'b30', 'b55', 'b22', 'b50', 'b26'}} -wg_cands:['b47', '?'] +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) -[env-loop] summary cognitive_cycle=19/25 env_step=18 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] summary cognitive_cycle=44/50 env_step=68 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe [env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. [env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. [env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. - -[env-loop] Cognitive Cycle 20/25 -[env] env_step=19 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[cycle] IN -- cycle=44/50 env_step=68 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 45/50 +[env] env_step=69 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' [env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] [env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) [env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) @@ -680,20 +1446,26 @@ post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zo [gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe [env→controller] policy:follow_mom (added 2 bindings) [pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] -[executed] policy:follow_mom (ok, reward=+0.10) binding=w53 (b53) +[executed] policy:follow_mom (ok, reward=+0.10) binding=w153 (b153) [maps] selection_on=WG execute_on=WM pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) wg_base: {'base': 'HERE', 'bid': '?'} -wg_foa: {'seeds': ['b55', 'b47', 'b12', 'b18', 'b22', 'b23', 'b26', 'b30', 'b40', 'b41', 'b45', 'b53', 'b54'], 'size': 16, 'ids': {'b54', 'b47', 'b23', 'b12', 'b18', 'b40', 'b45', 'b41', 'b53', 'b48', 'b49', 'b30', 'b55', 'b22', 'b50', 'b26'}} -wg_cands:['b47', '?'] +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) -[env-loop] summary cognitive_cycle=20/25 env_step=19 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] summary cognitive_cycle=45/50 env_step=69 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe [env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. [env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. [env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. - -[env-loop] Cognitive Cycle 21/25 -[env] env_step=20 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[cycle] IN -- cycle=45/50 env_step=69 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 46/50 +[env] env_step=70 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' [env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] [env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) [env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) @@ -706,20 +1478,26 @@ post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zo [gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe [env→controller] policy:follow_mom (added 2 bindings) [pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] -[executed] policy:follow_mom (ok, reward=+0.10) binding=w55 (b55) +[executed] policy:follow_mom (ok, reward=+0.10) binding=w155 (b155) [maps] selection_on=WG execute_on=WM pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) wg_base: {'base': 'HERE', 'bid': '?'} -wg_foa: {'seeds': ['b55', 'b47', 'b12', 'b18', 'b22', 'b23', 'b26', 'b30', 'b40', 'b41', 'b45', 'b53', 'b54'], 'size': 16, 'ids': {'b54', 'b47', 'b23', 'b12', 'b18', 'b40', 'b45', 'b41', 'b53', 'b48', 'b49', 'b30', 'b55', 'b22', 'b50', 'b26'}} -wg_cands:['b47', '?'] +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) -[env-loop] summary cognitive_cycle=21/25 env_step=20 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] summary cognitive_cycle=46/50 env_step=70 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe [env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. [env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. [env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. - -[env-loop] Cognitive Cycle 22/25 -[env] env_step=21 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[cycle] IN -- cycle=46/50 env_step=70 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 47/50 +[env] env_step=71 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' [env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] [env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) [env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) @@ -732,20 +1510,26 @@ post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zo [gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe [env→controller] policy:follow_mom (added 2 bindings) [pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] -[executed] policy:follow_mom (ok, reward=+0.10) binding=w57 (b57) +[executed] policy:follow_mom (ok, reward=+0.10) binding=w157 (b157) [maps] selection_on=WG execute_on=WM pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) wg_base: {'base': 'HERE', 'bid': '?'} -wg_foa: {'seeds': ['b55', 'b47', 'b12', 'b18', 'b22', 'b23', 'b26', 'b30', 'b40', 'b41', 'b45', 'b53', 'b54'], 'size': 16, 'ids': {'b54', 'b47', 'b23', 'b12', 'b18', 'b40', 'b45', 'b41', 'b53', 'b48', 'b49', 'b30', 'b55', 'b22', 'b50', 'b26'}} -wg_cands:['b47', '?'] +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) -[env-loop] summary cognitive_cycle=22/25 env_step=21 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] summary cognitive_cycle=47/50 env_step=71 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe [env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. [env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. [env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. - -[env-loop] Cognitive Cycle 23/25 -[env] env_step=22 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[cycle] IN -- cycle=47/50 env_step=71 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 48/50 +[env] env_step=72 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' [env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] [env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) [env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) @@ -758,20 +1542,26 @@ post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zo [gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe [env→controller] policy:follow_mom (added 2 bindings) [pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] -[executed] policy:follow_mom (ok, reward=+0.10) binding=w59 (b59) +[executed] policy:follow_mom (ok, reward=+0.10) binding=w159 (b159) [maps] selection_on=WG execute_on=WM pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) wg_base: {'base': 'HERE', 'bid': '?'} -wg_foa: {'seeds': ['b55', 'b47', 'b12', 'b18', 'b22', 'b23', 'b26', 'b30', 'b40', 'b41', 'b45', 'b53', 'b54'], 'size': 16, 'ids': {'b54', 'b47', 'b23', 'b12', 'b18', 'b40', 'b45', 'b41', 'b53', 'b48', 'b49', 'b30', 'b55', 'b22', 'b50', 'b26'}} -wg_cands:['b47', '?'] +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) -[env-loop] summary cognitive_cycle=23/25 env_step=22 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] summary cognitive_cycle=48/50 env_step=72 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe [env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. [env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. [env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. - -[env-loop] Cognitive Cycle 24/25 -[env] env_step=23 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[cycle] IN -- cycle=48/50 env_step=72 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 49/50 +[env] env_step=73 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' [env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] [env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) [env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) @@ -784,20 +1574,26 @@ post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zo [gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe [env→controller] policy:follow_mom (added 2 bindings) [pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] -[executed] policy:follow_mom (ok, reward=+0.10) binding=w61 (b61) +[executed] policy:follow_mom (ok, reward=+0.10) binding=w161 (b161) [maps] selection_on=WG execute_on=WM pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) wg_base: {'base': 'HERE', 'bid': '?'} -wg_foa: {'seeds': ['b55', 'b47', 'b12', 'b18', 'b22', 'b23', 'b26', 'b30', 'b40', 'b41', 'b45', 'b53', 'b54'], 'size': 16, 'ids': {'b54', 'b47', 'b23', 'b12', 'b18', 'b40', 'b45', 'b41', 'b53', 'b48', 'b49', 'b30', 'b55', 'b22', 'b50', 'b26'}} -wg_cands:['b47', '?'] +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) -[env-loop] summary cognitive_cycle=24/25 env_step=23 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] summary cognitive_cycle=49/50 env_step=73 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe [env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. [env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. [env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. - -[env-loop] Cognitive Cycle 25/25 -[env] env_step=24 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' +[cycle] IN -- cycle=49/50 env_step=73 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' + +[env-loop] Cognitive Cycle 50/50 +[env] env_step=74 (since reset) stage=rest posture=resting mom_distance=touching nipple_state=latched action='policy:follow_mom' [env→working] MAP pred:resting → w6 (b6) (entity=self, slot=resting) [changed] [env→working] MAP pred:proximity:mom:close → w7 (b7) (entity=mom, slot=proximity:mom) [env→working] MAP pred:proximity:shelter:near → w8 (b8) (entity=shelter, slot=proximity:shelter) @@ -810,48 +1606,56 @@ post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zo [gate:rest] fatigue=0.30 high=False cue=False bodymap_stale=False cliff=far shelter=near zone=safe [env→controller] policy:follow_mom (added 2 bindings) [pick] best_policy=policy:follow_mom best_by=rl_exploit(deficit) triggered=[policy:follow_mom] deficits=[policy:follow_mom:0.00] non_drive=[policy:follow_mom:0.00] -[executed] policy:follow_mom (ok, reward=+0.10) binding=w63 (b63) +[executed] policy:follow_mom (ok, reward=+0.10) binding=w163 (b163) [maps] selection_on=WG execute_on=WM pre: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) wg_base: {'base': 'HERE', 'bid': '?'} -wg_foa: {'seeds': ['b55', 'b47', 'b12', 'b18', 'b22', 'b23', 'b26', 'b30', 'b40', 'b41', 'b45', 'b53', 'b54'], 'size': 16, 'ids': {'b54', 'b47', 'b23', 'b12', 'b18', 'b40', 'b45', 'b41', 'b53', 'b48', 'b49', 'b30', 'b55', 'b22', 'b50', 'b26'}} -wg_cands:['b47', '?'] +wg_foa: {'seeds': ['b57', 'b49', 'b7', 'b15', 'b21', 'b22', 'b34', 'b35', 'b43', 'b44', 'b55', 'b56'], 'size': 15, 'ids': {'b52', 'b43', 'b50', 'b34', 'b21', 'b22', 'b7', 'b35', 'b51', 'b55', 'b57', 'b56', 'b15', 'b44', 'b49'}} +wg_cands:['b49', '?'] post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zone=safe (hunger=0.50, fatigue=0.30) -[env-loop] summary cognitive_cycle=25/25 env_step=24 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe +[env-loop] summary cognitive_cycle=50/50 env_step=74 stage=rest env_posture=resting bm_posture=resting mom=touching nipple=latched last_policy='policy:follow_mom' zone=safe [env-loop] explain posture: posture remains 'resting'; no storyboard transition affecting posture this step. [env-loop] explain nipple: nipple remains latched; the kid continues feeding at the maternal nipple this step. [env-loop] explain zone: zone is 'safe'; BodyMap sees shelter near and no nearby cliff, so this is a sheltered niche suitable for resting and feeding. +[cycle] IN -- cycle=50/50 env_step=74 stage=rest posture=resting mom=touching nipple=latched zone=safe drives(h=0.50 f=0.30 w=0.60) applied_action='policy:follow_mom' obs(p=6 c=1) +[cycle] OBS preds: resting, proximity:mom:close, proximity:shelter:near, hazard:cliff:far, nipple:latched, milk:drinking | cues: vision:silhouette:mom | nav_patches=4 uniq_sig16=4 +[cycle] WM surfaceΔ: (no surface slot change) | scratch: policy:follow_mom +2 binding(s) (exec_on=WM) +[cycle] WG wrote preds+0 cues+0 | (none) | (none) +[cycle] COL (no wm<->col ops this cycle) +[cycle] ACT executed='policy:follow_mom' reward=+0.10 next_action='policy:follow_mom' -[env-loop] Closed-loop run complete. You can inspect details via Snapshot or the mini-snapshot that follows. +[env-loop] Closed-loop cognitive cycle complete. You can inspect details via Snapshot or the mini-snapshot that follows. [workingmap] MapSurface entity table - ent node kind pos(x,y) dist_m class seen preds (short) cues (short) - ------- ---------- -------- -------------- ------ -------- ---- -------------------------- ---------------- - self w6 (b6) agent ( 0.00, 0.00) [wm_schematic_v1] 0.00 self 25 milk:drinking, nipple:latched, posture:standing … - cliff w9 (b9) hazard ( 5.38, 1.00) [wm_schematic_v1] brg=+11° 5.00 far 25 hazard:cliff:far - mom w7 (b7) agent ( 0.00, 0.00) [wm_schematic_v1] 0.00 close 25 proximity:mom:close vision:silhouette:mom - shelter w8 (b8) shelter ( 2.37, -1.00) [wm_schematic_v1] brg=-23° 1.20 near 25 proximity:shelter:near - -[workingmap] auto snapshot (last 250): last 63 binding(s) of 63 total - Legend edges: wm_entity=WM_ROOT→entity (MapSurface membership); wm_scratch=WM_ROOT→WM_SCRATCH (policy scratch root; keeps WM_ROOT clean); wm_creative=WM_ROOT→WM_CREATIVE (counterfactual rollouts); distance_to=WM_SELF→entity (meta has meters/class); then=policy action chain (should hang off WM_SCRATCH) - Legend tags : wm:entity / wm:eid: / wm:kind: mark entities; pred:* on entities = current belief; cue:* on entities = cues present now; meta.wm.pos={x,y,frame} + ent node kind pos(x,y) dist_m class seen patches preds (short) cues (short) + ------- ---------- -------- -------------- ------ -------- ---- ------------------ -------------------------- ---------------- + self w6 (b6) agent ( 0.00, 0.00) [wm_schematic_v1] 0.00 self 75 0 milk:drinking, nipple:latched, posture:standing … + cliff w9 (b9) hazard ( 5.38, 1.00) [wm_schematic_v1] brg=+11° 5.00 far 75 1:c7e9b937a99e4b15 hazard:cliff:far + mom w7 (b7) agent ( 0.00, 0.00) [wm_schematic_v1] 0.00 close 75 1:ee29614c16c5c1fc proximity:mom:close vision:silhouette:mom + scene w10 (b10) ( 4.16, -0.50) [wm_schematic_v1] brg=-7° 3.00 unknown 75 1:f0faf29d01ac575d + shelter w8 (b8) shelter ( 2.37, -1.00) [wm_schematic_v1] brg=-23° 1.20 near 75 1:b904b8e44a5e3b36 proximity:shelter:near + [patches] ent_with=4/5 refs_total=4 uniq_sig16=4 uniq_eid=4 + +[workingmap] auto snapshot (last 250): last 163 binding(s) of 163 total + Legend: edges=wm_entity(root→entity), wm_scratch(root→scratch), wm_creative(root→creative), distance_to(self→entity), then(action chain) + tags=wm:* entity markers; pred:* belief-now; cue:* cues-now; meta.wm.pos={x,y,frame} w1 (b1): [] out=0 edges=(none) w2 (b2): [anchor:NOW_ORIGIN] out=0 edges=(none) - w3 (b3): [anchor:NOW, anchor:NOW_ORIGIN, anchor:WM_ROOT] out=6 edges=wm_scratch:w4 (b4), wm_creative:w5 (b5), wm_entity:w6 (b6), wm_entity:w7 (b7), wm_entity:w8 (b8), wm_entity:w9 (b9) - w4 (b4): [anchor:WM_SCRATCH, wm:scratch] out=25 edges=then:w10 (b10), then:w13 (b13), then:w16 (b16), then:w19 (b19), then:w22 (b22), then:w24 (b24) (+19 more) + w3 (b3): [anchor:NOW, anchor:NOW_ORIGIN, anchor:WM_ROOT] out=7 edges=wm_scratch:w4 (b4), wm_creative:w5 (b5), wm_entity:w6 (b6), wm_entity:w7 (b7), wm_entity:w8 (b8), wm_entity:w9 (b9) (+1 more) + w4 (b4): [anchor:WM_SCRATCH, wm:scratch] out=75 edges=then:w11 (b11), then:w14 (b14), then:w17 (b17), then:w19 (b19), then:w22 (b22), then:w24 (b24) (+69 more) w5 (b5): [anchor:WM_CREATIVE, wm:creative] out=0 edges=(none) - w6 (b6): [anchor:WM_SELF, pred:milk:drinking, pred:nipple:latched, pred:posture:standing, pred:resting, wm:eid:self, wm:entity, wm:kind:agent] out=3 edges=distance_to:w7 (b7) meters=0.00 class=close, distance_to:w8 (b8) meters=1.20 class=near, distance_to:w9 (b9) meters=5.00 class=far + w6 (b6): [anchor:WM_SELF, pred:milk:drinking, pred:nipple:latched, pred:posture:standing, pred:resting, wm:eid:self, wm:entity, wm:kind:agent] out=4 edges=distance_to:w7 (b7) meters=0.00 class=close, distance_to:w8 (b8) meters=1.20 class=near, distance_to:w9 (b9) meters=5.00 class=far, distance_to:w10 (b10) meters=3.00 class=unknown w7 (b7): [anchor:WM_ENT_MOM, cue:vision:silhouette:mom, pred:proximity:mom:close, wm:eid:mom, wm:entity, wm:kind:agent] out=0 edges=(none) w8 (b8): [anchor:WM_ENT_SHELTER, pred:proximity:shelter:near, wm:eid:shelter, wm:entity, wm:kind:shelter] out=0 edges=(none) w9 (b9): [anchor:WM_ENT_CLIFF, pred:hazard:cliff:far, wm:eid:cliff, wm:entity, wm:kind:hazard] out=0 edges=(none) - w10 (b10): [action:push_up] out=1 edges=then:w11 (b11) - w11 (b11): [action:extend_legs] out=1 edges=then:w12 (b12) - w12 (b12): [pred:posture:standing] out=0 edges=(none) - w13 (b13): [action:push_up] out=1 edges=then:w14 (b14) - w14 (b14): [action:extend_legs] out=1 edges=then:w15 (b15) - w15 (b15): [pred:posture:standing] out=0 edges=(none) - w16 (b16): [action:push_up] out=1 edges=then:w17 (b17) - w17 (b17): [action:extend_legs] out=1 edges=then:w18 (b18) + w10 (b10): [anchor:WM_ENT_SCENE, wm:eid:scene, wm:entity] out=0 edges=(none) + w11 (b11): [action:push_up] out=1 edges=then:w12 (b12) + w12 (b12): [action:extend_legs] out=1 edges=then:w13 (b13) + w13 (b13): [pred:posture:standing] out=0 edges=(none) + w14 (b14): [action:push_up] out=1 edges=then:w15 (b15) + w15 (b15): [action:extend_legs] out=1 edges=then:w16 (b16) + w16 (b16): [pred:posture:standing] out=0 edges=(none) + w17 (b17): [action:recover_fall] out=1 edges=then:w18 (b18) w18 (b18): [pred:posture:standing] out=0 edges=(none) w19 (b19): [action:push_up] out=1 edges=then:w20 (b20) w20 (b20): [action:extend_legs] out=1 edges=then:w21 (b21) @@ -898,6 +1702,106 @@ post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zo w61 (b61): [pred:alert] out=0 edges=(none) w62 (b62): [action:look_around] out=1 edges=then:w63 (b63) w63 (b63): [pred:alert] out=0 edges=(none) + w64 (b64): [action:look_around] out=1 edges=then:w65 (b65) + w65 (b65): [pred:alert] out=0 edges=(none) + w66 (b66): [action:look_around] out=1 edges=then:w67 (b67) + w67 (b67): [pred:alert] out=0 edges=(none) + w68 (b68): [action:look_around] out=1 edges=then:w69 (b69) + w69 (b69): [pred:alert] out=0 edges=(none) + w70 (b70): [action:look_around] out=1 edges=then:w71 (b71) + w71 (b71): [pred:alert] out=0 edges=(none) + w72 (b72): [action:look_around] out=1 edges=then:w73 (b73) + w73 (b73): [pred:alert] out=0 edges=(none) + w74 (b74): [action:look_around] out=1 edges=then:w75 (b75) + w75 (b75): [pred:alert] out=0 edges=(none) + w76 (b76): [action:look_around] out=1 edges=then:w77 (b77) + w77 (b77): [pred:alert] out=0 edges=(none) + w78 (b78): [action:look_around] out=1 edges=then:w79 (b79) + w79 (b79): [pred:alert] out=0 edges=(none) + w80 (b80): [action:look_around] out=1 edges=then:w81 (b81) + w81 (b81): [pred:alert] out=0 edges=(none) + w82 (b82): [action:look_around] out=1 edges=then:w83 (b83) + w83 (b83): [pred:alert] out=0 edges=(none) + w84 (b84): [action:look_around] out=1 edges=then:w85 (b85) + w85 (b85): [pred:alert] out=0 edges=(none) + w86 (b86): [action:look_around] out=1 edges=then:w87 (b87) + w87 (b87): [pred:alert] out=0 edges=(none) + w88 (b88): [action:look_around] out=1 edges=then:w89 (b89) + w89 (b89): [pred:alert] out=0 edges=(none) + w90 (b90): [action:look_around] out=1 edges=then:w91 (b91) + w91 (b91): [pred:alert] out=0 edges=(none) + w92 (b92): [action:look_around] out=1 edges=then:w93 (b93) + w93 (b93): [pred:alert] out=0 edges=(none) + w94 (b94): [action:look_around] out=1 edges=then:w95 (b95) + w95 (b95): [pred:alert] out=0 edges=(none) + w96 (b96): [action:look_around] out=1 edges=then:w97 (b97) + w97 (b97): [pred:alert] out=0 edges=(none) + w98 (b98): [action:look_around] out=1 edges=then:w99 (b99) + w99 (b99): [pred:alert] out=0 edges=(none) + w100 (b100): [action:look_around] out=1 edges=then:w101 (b101) + w101 (b101): [pred:alert] out=0 edges=(none) + w102 (b102): [action:look_around] out=1 edges=then:w103 (b103) + w103 (b103): [pred:alert] out=0 edges=(none) + w104 (b104): [action:look_around] out=1 edges=then:w105 (b105) + w105 (b105): [pred:alert] out=0 edges=(none) + w106 (b106): [action:look_around] out=1 edges=then:w107 (b107) + w107 (b107): [pred:alert] out=0 edges=(none) + w108 (b108): [action:look_around] out=1 edges=then:w109 (b109) + w109 (b109): [pred:alert] out=0 edges=(none) + w110 (b110): [action:look_around] out=1 edges=then:w111 (b111) + w111 (b111): [pred:alert] out=0 edges=(none) + w112 (b112): [action:look_around] out=1 edges=then:w113 (b113) + w113 (b113): [pred:alert] out=0 edges=(none) + w114 (b114): [action:look_around] out=1 edges=then:w115 (b115) + w115 (b115): [pred:alert] out=0 edges=(none) + w116 (b116): [action:look_around] out=1 edges=then:w117 (b117) + w117 (b117): [pred:alert] out=0 edges=(none) + w118 (b118): [action:look_around] out=1 edges=then:w119 (b119) + w119 (b119): [pred:alert] out=0 edges=(none) + w120 (b120): [action:look_around] out=1 edges=then:w121 (b121) + w121 (b121): [pred:alert] out=0 edges=(none) + w122 (b122): [action:look_around] out=1 edges=then:w123 (b123) + w123 (b123): [pred:alert] out=0 edges=(none) + w124 (b124): [action:look_around] out=1 edges=then:w125 (b125) + w125 (b125): [pred:alert] out=0 edges=(none) + w126 (b126): [action:look_around] out=1 edges=then:w127 (b127) + w127 (b127): [pred:alert] out=0 edges=(none) + w128 (b128): [action:look_around] out=1 edges=then:w129 (b129) + w129 (b129): [pred:alert] out=0 edges=(none) + w130 (b130): [action:look_around] out=1 edges=then:w131 (b131) + w131 (b131): [pred:alert] out=0 edges=(none) + w132 (b132): [action:look_around] out=1 edges=then:w133 (b133) + w133 (b133): [pred:alert] out=0 edges=(none) + w134 (b134): [action:look_around] out=1 edges=then:w135 (b135) + w135 (b135): [pred:alert] out=0 edges=(none) + w136 (b136): [action:look_around] out=1 edges=then:w137 (b137) + w137 (b137): [pred:alert] out=0 edges=(none) + w138 (b138): [action:look_around] out=1 edges=then:w139 (b139) + w139 (b139): [pred:alert] out=0 edges=(none) + w140 (b140): [action:look_around] out=1 edges=then:w141 (b141) + w141 (b141): [pred:alert] out=0 edges=(none) + w142 (b142): [action:look_around] out=1 edges=then:w143 (b143) + w143 (b143): [pred:alert] out=0 edges=(none) + w144 (b144): [action:look_around] out=1 edges=then:w145 (b145) + w145 (b145): [pred:alert] out=0 edges=(none) + w146 (b146): [action:look_around] out=1 edges=then:w147 (b147) + w147 (b147): [pred:alert] out=0 edges=(none) + w148 (b148): [action:look_around] out=1 edges=then:w149 (b149) + w149 (b149): [pred:alert] out=0 edges=(none) + w150 (b150): [action:look_around] out=1 edges=then:w151 (b151) + w151 (b151): [pred:alert] out=0 edges=(none) + w152 (b152): [action:look_around] out=1 edges=then:w153 (b153) + w153 (b153): [pred:alert] out=0 edges=(none) + w154 (b154): [action:look_around] out=1 edges=then:w155 (b155) + w155 (b155): [pred:alert] out=0 edges=(none) + w156 (b156): [action:look_around] out=1 edges=then:w157 (b157) + w157 (b157): [pred:alert] out=0 edges=(none) + w158 (b158): [action:look_around] out=1 edges=then:w159 (b159) + w159 (b159): [pred:alert] out=0 edges=(none) + w160 (b160): [action:look_around] out=1 edges=then:w161 (b161) + w161 (b161): [pred:alert] out=0 edges=(none) + w162 (b162): [action:look_around] out=1 edges=then:w163 (b163) + w163 (b163): [pred:alert] out=0 edges=(none) [skills-hud] Learned policy values after env-loop: @@ -908,182 +1812,122 @@ post: dev_gate: True, trigger: fallback=True when not fallen; posture=resting zo delta=0 then q used only for exact ties otherwise deficit values within delta range are considered tied and q used to decide) NOTE: deficit here means drive-urgency = max(0, drive_value - HIGH_THRESHOLD) (amount ABOVE threshold, not a negative deficit). Policies without a drive-urgency term score 0.00 and will tie-break by stable policy order (or RL tie-break, if enabled) -RL: enabled=True epsilon=0.050 delta=0.000 (explore=0, exploit=25, explore_rate=0.00) -Skill HUD (top 2 by q=EMA reward): - 1) policy:stand_up n= 4 rate=1.00 q=+0.76 last=+1.00 - 2) policy:follow_mom n= 21 rate=1.00 q=+0.10 last=+0.10 +RL: enabled=True epsilon=0.050 delta=0.000 (explore=4, exploit=71, explore_rate=0.05) +Skill HUD (top 3 by q=EMA reward): + 1) policy:stand_up n= 4 rate=0.75 q=+0.52 last=+1.00 + 2) policy:recover_fall n= 1 rate=1.00 q=+0.24 last=+0.80 + 3) policy:follow_mom n= 71 rate=1.00 q=+0.10 last=+0.10 Values of time measures, nodes and links at this point: -[time] controller_steps=25, cos_to_last_boundary=0.7698, temporal_epochs=0, autonomic_ticks=0, age_days=0.0000, cog_cycles=25 +[time] controller_steps=75, cos_to_last_boundary=0.2766, temporal_epochs=0, autonomic_ticks=0, age_days=0.0000, cog_cycles=75 [world] last 50 binding(s): - b6: [pred:hazard:cliff:far] - edges: (none) - b7: [action:stand_up] - edges: then:b8 - b8: [pred:posture:fallen] - edges: then:b9, then:b12, then:b13, then:b14 - b9: [pred:proximity:mom:far] - edges: then:b10 - b10: [pred:proximity:shelter:far] + b8: [action:stand_up] + edges: then:b3 + b9: [action:recover_fall] + edges: then:b3 + b10: [action:stand_up] edges: then:b11 - b11: [pred:hazard:cliff:far] - edges: (none) - b12: [cue:wm:mapsurface_snapshot, idx:stage:struggle, idx:zone:unknown] - edges: (none) - b13: [action:stand_up] + b11: [pred:posture:standing] + edges: then:b12, then:b15, then:b16, then:b17 + b12: [pred:proximity:mom:far] + edges: then:b13 + b13: [pred:proximity:shelter:far] edges: then:b14 - b14: [pred:posture:standing] - edges: then:b15, then:b18, then:b19, then:b20, then:b23, then:b24, then:b25, then:b26, then:b27, then:b28, then:b30, then:b31, then:b32, then:b33, then:b34 - b15: [pred:proximity:mom:far] - edges: then:b16 - b16: [pred:proximity:shelter:far] - edges: then:b17 - b17: [pred:hazard:cliff:near] + b14: [pred:hazard:cliff:near] edges: (none) - b18: [cue:wm:mapsurface_snapshot, idx:stage:first_stand, idx:zone:unsafe_cliff_near] + b15: [cue:wm:mapsurface_snapshot, idx:stage:first_stand, idx:zone:unsafe_cliff_near] edges: (none) - b19: [action:follow_mom] - edges: then:b14 - b20: [pred:proximity:mom:close] + b16: [action:follow_mom] + edges: then:b17 + b17: [pred:posture:standing] + edges: then:b18, then:b22, then:b23, then:b24, then:b25, then:b26, then:b28, then:b29 + b18: [pred:proximity:mom:close] + edges: then:b19 + b19: [pred:proximity:shelter:far] + edges: then:b20 + b20: [pred:hazard:cliff:far] edges: then:b21 - b21: [pred:hazard:cliff:far] - edges: then:b22 - b22: [cue:vision:silhouette:mom] + b21: [cue:vision:silhouette:mom] edges: (none) - b23: [cue:wm:mapsurface_snapshot, idx:stage:first_stand, idx:zone:unknown] + b22: [cue:wm:mapsurface_snapshot, idx:stage:first_stand, idx:zone:unknown] edges: (none) - b24: [action:follow_mom] - edges: then:b14 - b25: [pred:proximity:shelter:near] + b23: [action:follow_mom] + edges: then:b17 + b24: [pred:proximity:shelter:near] edges: (none) - b26: [cue:wm:mapsurface_snapshot, idx:stage:first_stand, idx:zone:safe] + b25: [action:follow_mom] + edges: then:b17 + b26: [pred:proximity:shelter:far] + edges: then:b27 + b27: [pred:hazard:cliff:near] edges: (none) - b27: [action:follow_mom] - edges: then:b14 - b28: [pred:proximity:shelter:far] + b28: [action:follow_mom] edges: then:b29 - b29: [pred:hazard:cliff:near] - edges: (none) - b30: [cue:wm:mapsurface_snapshot, idx:stage:first_stand, idx:zone:unsafe_cliff_near] + b29: [pred:posture:standing] + edges: then:b30, then:b35, then:b36, then:b37 + b30: [pred:proximity:mom:close] + edges: then:b31 + b31: [pred:proximity:shelter:far] + edges: then:b32 + b32: [pred:hazard:cliff:near] + edges: then:b33 + b33: [pred:nipple:found] + edges: then:b34 + b34: [cue:vision:silhouette:mom] edges: (none) - b31: [action:follow_mom] - edges: then:b14 - b32: [pred:nipple:found] + b35: [cue:wm:mapsurface_snapshot, idx:stage:first_stand, idx:zone:unsafe_cliff_near] edges: (none) - b33: [action:follow_mom] - edges: then:b34 - b34: [pred:posture:standing] - edges: then:b35, then:b41, then:b42, then:b43, then:b45, then:b46, then:b47, near:b48, near:b49 - b35: [pred:proximity:mom:close, pred:valence:like] - edges: then:b36 - b36: [pred:proximity:shelter:far] + b36: [action:follow_mom] edges: then:b37 - b37: [pred:hazard:cliff:near] - edges: then:b38 - b38: [pred:nipple:latched] + b37: [pred:posture:standing] + edges: then:b38, then:b44, then:b45, then:b46, then:b48, then:b49, near:b50, near:b51 + b38: [pred:proximity:mom:close, pred:valence:like] edges: then:b39 - b39: [pred:milk:drinking] + b39: [pred:proximity:shelter:far] edges: then:b40 - b40: [cue:vision:silhouette:mom] + b40: [pred:hazard:cliff:near] + edges: then:b41 + b41: [pred:nipple:latched] + edges: then:b42 + b42: [pred:milk:drinking] + edges: then:b43 + b43: [cue:vision:silhouette:mom] edges: (none) - b41: [cue:wm:mapsurface_snapshot, idx:stage:first_latch, idx:zone:unsafe_cliff_near] + b44: [cue:wm:mapsurface_snapshot, idx:stage:first_latch, idx:zone:unsafe_cliff_near] edges: (none) - b42: [action:follow_mom] - edges: then:b34 - b43: [pred:proximity:shelter:near] - edges: then:b44 - b44: [pred:hazard:cliff:far] - edges: (none) - b45: [cue:wm:mapsurface_snapshot, idx:stage:first_latch, idx:zone:safe] - edges: (none) - b46: [action:follow_mom] + b45: [action:follow_mom] + edges: then:b37 + b46: [pred:proximity:shelter:near] edges: then:b47 - b47: [anchor:NOW, pred:resting] - edges: then:b48, then:b54, then:b55, near:b48, near:b49 - b48: [pred:proximity:mom:close, pred:valence:like] + b47: [pred:hazard:cliff:far] + edges: (none) + b48: [action:follow_mom] edges: then:b49 - b49: [pred:proximity:shelter:near] - edges: then:b50 - b50: [pred:hazard:cliff:far] + b49: [anchor:NOW, pred:resting] + edges: then:b50, then:b56, then:b57, near:b50, near:b51 + b50: [pred:proximity:mom:close, pred:valence:like] edges: then:b51 - b51: [pred:nipple:latched] + b51: [pred:proximity:shelter:near] edges: then:b52 - b52: [pred:milk:drinking] + b52: [pred:hazard:cliff:far] edges: then:b53 - b53: [cue:vision:silhouette:mom] + b53: [pred:nipple:latched] + edges: then:b54 + b54: [pred:milk:drinking] + edges: then:b55 + b55: [cue:vision:silhouette:mom] edges: (none) - b54: [cue:wm:mapsurface_snapshot, idx:stage:rest, idx:zone:safe] + b56: [cue:wm:mapsurface_snapshot, idx:stage:rest, idx:zone:safe] edges: (none) - b55: [action:follow_mom] - edges: then:b47 + b57: [action:follow_mom] + edges: then:b49 + +[discrepancy history] recent posture discrepancies (most recent last): + [discrepancy] env posture='fallen' vs policy-expected posture='standing' from policy:stand_up + [discrepancy] env posture='fallen' vs policy-expected posture='standing' from policy:stand_up + [discrepancy] env posture='fallen' vs policy-expected posture='standing' from policy:recover_fall ----- Please press any key (* stops this scroll pause) to continue with menu (above screen will scroll).... - [hints for text selection instead of numerical selection] - - # Quick Start & Tutorial - 1) Understanding bindings, edges, predicates, policies [understanding, tagging] - 2) Help: System Docs and/or Tutorial with demo tour [help, tutorial, demo] - - # Quick Start / Overview - 3) Snapshot (bindings + edges + ctx + policies) [snapshot, display] - 4) World stats [world, stats] - 5) Recent bindings (last 5) [last, bindings] - 6) Drives & drive tags [drives] - 7) Skill ledger [skills] - 8) Temporal probe (epoch/hash/cos/hamming) [temporal, probe] - - # Act / Simulate - 9) Instinct step (Action Center) [instinct, act] - 10) Autonomic tick (emit interoceptive cues) [autonomic, tick] - 11) Simulate fall (add posture:fallen and try recovery) [fall, simulate] - - # Simulation of the Environment (HybridEnvironment demo) - 35) Run 1 Cognitive Cycle (HybridEnvironment → WorldGraph demo) [env, hybrid] - 37) Run n Cognitive Cycles (closed-loop timeline) [envloop, envrun] - 38) Inspect BodyMap (summary from BodyMap helpers) [bodymap, bsnap] - 39) Spatial scene demo (NOW-near + resting-in-shelter?) [spatial, near] - - # Perception & Memory (Cues & Engrams) - 12) Input [sensory] cue [sensory, cue] - 13) Capture scene → tiny engram (signal bridge) [capture, scene] - 14) Resolve engrams on a binding [resolve, engrams] - 15) Inspect engram by id (or binding) [engram, ei] - 16) List all engrams [engrams-all, list-engrams] - 17) Search engrams (by name / epoch) [search-engrams, find-engrams] - 18) Delete engram by bid or eid [delete-engram, del-engram] - 19) Attach existing engram to a binding [attach-engram, ae] - - # Graph Inspect / Build / Plan - 20) Inspect binding details [inspect, details] - 21) List predicates [listpredicates, listpreds] - 22) [Add] predicate [add, predicate] - 23) Connect two bindings (src, dst, relation) [connect, link] - 24) Delete edge (source, destn, relation) [delete, rm] - 25) Plan from NOW -> [plan] - 26) Planner strategy (toggle BFS ↔ Dijkstra) [planner, strategy] - 27) Export and display interactive graph with options [pyvis, graph] - - # Save / System / Help - 28) Export snapshot (text only) [export snapshot] - 29) Save session → path [save] - 30) Load session → path [load] - 31) Run preflight now [preflight] - 32) Quit [quit, exit] - 33) Lines of Python code LOC by directory [loc, sloc] - 34) Reset current saved session [reset] - 36) Toggle mini-snapshot after each menu selection [mini, msnap] - - # Memories - 40) Configure episode starting state (drives + age_days) [config-episode, cfg-epi] - 41) Retired: WorkingMap & WorldGraph settings, toggle RL policy - 43) WorkingMap snapshot (last N bindings; optional clear) [wsnap, wmsnap] - 44) Store MapSurface snapshot to Column + WG pointer (dedup vs last) [wstore, wmstore] - 45) List recent wm_mapsurface engrams (Column) - 46) Pick best wm_mapsurface engram for current stage/zone (read-only) [wpick, wpickwm] - 47) Load wm_mapsurface engram into WorkingMap (replace MapSurface) [wload, wmload] - - Select: -Goodbye. diff --git a/tests/test_derived_milestone_keyframes.py b/tests/test_derived_milestone_keyframes.py new file mode 100644 index 0000000..91b1bf5 --- /dev/null +++ b/tests/test_derived_milestone_keyframes.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- +""" +Unit tests for derived milestone keyframe detection. + +These tests exercise the env→world boundary hook in inject_obs_into_world(...): +we want a milestone keyframe when a goal-relevant outcome occurs (e.g., stood_up) +even if there is no stage/zone transition that would otherwise force a keyframe. + +We keep this test intentionally small: +- Use WorldGraph + Ctx directly. +- Feed two EnvObservation packets that differ only in posture (fallen -> standing). +- Expect: second call returns keyframe=True and includes a milestone reason. +""" + +from __future__ import annotations + +import cca8_world_graph +from cca8_env import EnvObservation +from cca8_run import Ctx, inject_obs_into_world + + +def _mk_obs(preds: list[str], *, stage: str = "birth", tsb: float = 1.0, step_index: int = 1) -> EnvObservation: + return EnvObservation( + raw_sensors={}, + predicates=list(preds), + cues=[], + env_meta={ + "scenario_stage": stage, + "time_since_birth": float(tsb), + "step_index": int(step_index), + }, + ) + + +def test_derived_milestone_keyframe_stood_up() -> None: + world = cca8_world_graph.WorldGraph() + world.set_tag_policy("allow") + world.set_stage("neonate") + world.ensure_anchor("NOW") + + ctx = Ctx() + ctx.longterm_obs_enabled = True + ctx.longterm_obs_mode = "changes" + + # Turn OFF other keyframe triggers so only milestone logic can fire. + ctx.longterm_obs_keyframe_on_stage_change = False + ctx.longterm_obs_keyframe_on_zone_change = False + ctx.longterm_obs_keyframe_period_steps = 0 + ctx.longterm_obs_keyframe_on_pred_err = False + + # Turn ON milestone keyframes (this enables derived transitions too). + ctx.longterm_obs_keyframe_on_milestone = True + + # First tick: establish baseline slots (no keyframe expected). + obs1 = _mk_obs( + ["posture:fallen", "proximity:mom:far", "proximity:shelter:far", "hazard:cliff:far"], + stage="birth", + tsb=1.0, + step_index=1, + ) + out1 = inject_obs_into_world(world, ctx, obs1) + assert bool(out1.get("keyframe")) is False + + # Second tick: posture transition should trigger a milestone keyframe. + obs2 = _mk_obs( + ["posture:standing", "proximity:mom:far", "proximity:shelter:far", "hazard:cliff:far"], + stage="birth", + tsb=2.0, + step_index=2, + ) + out2 = inject_obs_into_world(world, ctx, obs2) + + assert bool(out2.get("keyframe")) is True + reasons = out2.get("keyframe_reasons") + assert isinstance(reasons, list) + assert any(isinstance(r, str) and ("milestone:" in r) and ("stood_up" in r) for r in reasons) diff --git a/tests/test_keyframe_triggers_phase_ix.py b/tests/test_keyframe_triggers_phase_ix.py new file mode 100644 index 0000000..40de43c --- /dev/null +++ b/tests/test_keyframe_triggers_phase_ix.py @@ -0,0 +1,264 @@ +import cca8_world_graph +from cca8_run import Ctx, inject_obs_into_world + + +class _ObsStub: + def __init__(self, *, predicates, cues=None, env_meta=None): + self.predicates = list(predicates or []) + self.cues = list(cues or []) + self.env_meta = dict(env_meta or {}) + + +def _mk_world(): + w = cca8_world_graph.WorldGraph() + w.set_tag_policy("allow") + w.ensure_anchor("NOW") + return w + + +def test_keyframe_zone_change_triggers_in_changes_mode(): + w = _mk_world() + ctx = Ctx() + ctx.longterm_obs_mode = "changes" + ctx.longterm_obs_keyframe_on_zone_change = True + ctx.longterm_obs_keyframe_on_stage_change = False # isolate + ctx.longterm_obs_keyframe_period_steps = 0 + ctx.longterm_obs_keyframe_on_pred_err = False + ctx.longterm_obs_keyframe_on_milestone = False + ctx.longterm_obs_keyframe_on_emotion = False + + obs1 = _ObsStub( + predicates=["posture:standing", "proximity:shelter:far", "hazard:cliff:far"], + env_meta={"scenario_stage": "x", "time_since_birth": 1.0}, + ) + r1 = inject_obs_into_world(w, ctx, obs1) + assert not r1.get("keyframe", False) + + obs2 = _ObsStub( + predicates=["posture:standing", "proximity:shelter:far", "hazard:cliff:near"], + env_meta={"scenario_stage": "x", "time_since_birth": 2.0}, + ) + r2 = inject_obs_into_world(w, ctx, obs2) + assert r2.get("keyframe", False) + assert any("zone_change" in str(x) for x in (r2.get("keyframe_reasons") or [])) + + +def test_keyframe_periodic_triggers(): + w = _mk_world() + ctx = Ctx() + ctx.longterm_obs_mode = "changes" + ctx.longterm_obs_keyframe_on_stage_change = False + ctx.longterm_obs_keyframe_on_zone_change = False + ctx.longterm_obs_keyframe_period_steps = 3 + ctx.longterm_obs_keyframe_on_milestone = False + ctx.longterm_obs_keyframe_on_emotion = False + ctx.longterm_obs_keyframe_on_pred_err = False + + obs = _ObsStub( + predicates=["posture:fallen", "proximity:shelter:far", "hazard:cliff:far"], + env_meta={"scenario_stage": "x", "time_since_birth": 1.0}, + ) + + ctx.controller_steps = 1 + r1 = inject_obs_into_world(w, ctx, obs) + assert not r1.get("keyframe", False) + + ctx.controller_steps = 2 + r2 = inject_obs_into_world(w, ctx, obs) + assert not r2.get("keyframe", False) + + ctx.controller_steps = 3 + r3 = inject_obs_into_world(w, ctx, obs) + assert r3.get("keyframe", False) + assert any("periodic(" in str(x) for x in (r3.get("keyframe_reasons") or [])) + + +def test_keyframe_periodic_resets_on_any_keyframe_when_enabled(): + w = _mk_world() + ctx = Ctx() + ctx.longterm_obs_mode = "changes" + + # isolate keyframe sources + ctx.longterm_obs_keyframe_on_stage_change = False + ctx.longterm_obs_keyframe_on_zone_change = False + ctx.longterm_obs_keyframe_on_pred_err = False + ctx.longterm_obs_keyframe_on_emotion = False + + # periodic + milestone + ctx.longterm_obs_keyframe_period_steps = 3 + ctx.longterm_obs_keyframe_period_reset_on_any_keyframe = True + ctx.longterm_obs_keyframe_on_milestone = True + + # Step 1: no keyframe + obs1 = _ObsStub( + predicates=["posture:fallen", "proximity:shelter:far", "hazard:cliff:far"], + env_meta={"scenario_stage": "x", "time_since_birth": 1.0}, + ) + ctx.controller_steps = 1 + r1 = inject_obs_into_world(w, ctx, obs1) + assert not r1.get("keyframe", False) + + # Step 2: milestone keyframe happens -> should reset periodic counter + obs2 = _ObsStub( + predicates=["posture:fallen", "proximity:shelter:far", "hazard:cliff:far"], + env_meta={"scenario_stage": "x", "time_since_birth": 2.0, "milestones": ["stood_up"]}, + ) + ctx.controller_steps = 2 + r2 = inject_obs_into_world(w, ctx, obs2) + assert r2.get("keyframe", False) + assert any("milestone:" in str(x) for x in (r2.get("keyframe_reasons") or [])) + + # Step 3: would have been periodic under legacy (3 % 3 == 0), + # but in reset-on-any-keyframe mode it should NOT fire (3-2 < 3). + obs3 = _ObsStub( + predicates=["posture:fallen", "proximity:shelter:far", "hazard:cliff:far"], + env_meta={"scenario_stage": "x", "time_since_birth": 3.0}, + ) + ctx.controller_steps = 3 + r3 = inject_obs_into_world(w, ctx, obs3) + assert not r3.get("keyframe", False) + + # Step 5: now (5-2 == 3) periodic should fire + obs5 = _ObsStub( + predicates=["posture:fallen", "proximity:shelter:far", "hazard:cliff:far"], + env_meta={"scenario_stage": "x", "time_since_birth": 5.0}, + ) + ctx.controller_steps = 5 + r5 = inject_obs_into_world(w, ctx, obs5) + assert r5.get("keyframe", False) + assert any("periodic(" in str(x) for x in (r5.get("keyframe_reasons") or [])) + + +def test_keyframe_pred_err_streak_triggers(): + w = _mk_world() + ctx = Ctx() + ctx.longterm_obs_mode = "changes" + ctx.longterm_obs_keyframe_on_stage_change = False + ctx.longterm_obs_keyframe_on_zone_change = False + ctx.longterm_obs_keyframe_period_steps = 0 + ctx.longterm_obs_keyframe_on_pred_err = True + ctx.longterm_obs_keyframe_pred_err_min_streak = 2 + ctx.longterm_obs_keyframe_on_milestone = False + ctx.longterm_obs_keyframe_on_emotion = False + + obs = _ObsStub( + predicates=["posture:fallen", "proximity:shelter:far", "hazard:cliff:far"], + env_meta={"scenario_stage": "x", "time_since_birth": 1.0}, + ) + + ctx.controller_steps = 1 + ctx.pred_err_v0_last = {"posture": 1} + r1 = inject_obs_into_world(w, ctx, obs) + assert not r1.get("keyframe", False) + + ctx.controller_steps = 2 + ctx.pred_err_v0_last = {"posture": 1} + r2 = inject_obs_into_world(w, ctx, obs) + assert r2.get("keyframe", False) + assert any("pred_err_v0" in str(x) for x in (r2.get("keyframe_reasons") or [])) + + +def test_keyframe_milestone_stub_triggers_when_enabled(): + w = _mk_world() + ctx = Ctx() + ctx.longterm_obs_mode = "changes" + ctx.longterm_obs_keyframe_on_stage_change = False + ctx.longterm_obs_keyframe_on_zone_change = False + ctx.longterm_obs_keyframe_on_milestone = True + ctx.longterm_obs_keyframe_on_emotion = False + + obs = _ObsStub( + predicates=["posture:standing", "proximity:shelter:far", "hazard:cliff:far"], + env_meta={"scenario_stage": "x", "time_since_birth": 1.0, "milestones": ["stood_up"]}, + ) + r = inject_obs_into_world(w, ctx, obs) + assert r.get("keyframe", False) + assert any("milestone:" in str(x) for x in (r.get("keyframe_reasons") or [])) + + +def test_keyframe_emotion_stub_triggers_when_enabled(): + w = _mk_world() + ctx = Ctx() + ctx.longterm_obs_mode = "changes" + ctx.longterm_obs_keyframe_on_stage_change = False + ctx.longterm_obs_keyframe_on_zone_change = False + ctx.longterm_obs_keyframe_on_emotion = True + ctx.longterm_obs_keyframe_emotion_threshold = 0.85 + + obs = _ObsStub( + predicates=["posture:standing", "proximity:shelter:far", "hazard:cliff:far"], + env_meta={"scenario_stage": "x", "time_since_birth": 1.0, "emotion": {"label": "fear", "intensity": 0.95}}, + ) + r = inject_obs_into_world(w, ctx, obs) + assert r.get("keyframe", False) + assert any("emotion:" in str(x) for x in (r.get("keyframe_reasons") or [])) + + +def test_keyframe_periodic_suppressed_while_sleeping_nondreaming(): + w = _mk_world() + ctx = Ctx() + ctx.longterm_obs_mode = "changes" + + # isolate: only periodic is relevant here + ctx.longterm_obs_keyframe_on_stage_change = False + ctx.longterm_obs_keyframe_on_zone_change = False + ctx.longterm_obs_keyframe_on_pred_err = False + ctx.longterm_obs_keyframe_on_milestone = False + ctx.longterm_obs_keyframe_on_emotion = False + + ctx.longterm_obs_keyframe_period_steps = 3 + ctx.longterm_obs_keyframe_period_suppress_when_sleeping_nondreaming = True + ctx.longterm_obs_keyframe_period_suppress_when_sleeping_dreaming = False + + obs = _ObsStub( + predicates=["posture:fallen", "proximity:shelter:far", "hazard:cliff:far", "sleeping:non_dreaming"], + env_meta={"scenario_stage": "x", "time_since_birth": 1.0}, + ) + + ctx.controller_steps = 1 + r1 = inject_obs_into_world(w, ctx, obs) + assert not r1.get("keyframe", False) + + ctx.controller_steps = 2 + r2 = inject_obs_into_world(w, ctx, obs) + assert not r2.get("keyframe", False) + + # Would normally fire periodic here, but should be suppressed + ctx.controller_steps = 3 + r3 = inject_obs_into_world(w, ctx, obs) + assert not r3.get("keyframe", False) + + +def test_keyframe_periodic_suppressed_while_sleeping_dreaming(): + w = _mk_world() + ctx = Ctx() + ctx.longterm_obs_mode = "changes" + + # isolate: only periodic is relevant here + ctx.longterm_obs_keyframe_on_stage_change = False + ctx.longterm_obs_keyframe_on_zone_change = False + ctx.longterm_obs_keyframe_on_pred_err = False + ctx.longterm_obs_keyframe_on_milestone = False + ctx.longterm_obs_keyframe_on_emotion = False + + ctx.longterm_obs_keyframe_period_steps = 3 + ctx.longterm_obs_keyframe_period_suppress_when_sleeping_nondreaming = False + ctx.longterm_obs_keyframe_period_suppress_when_sleeping_dreaming = True + + obs = _ObsStub( + predicates=["posture:fallen", "proximity:shelter:far", "hazard:cliff:far", "sleeping:dreaming"], + env_meta={"scenario_stage": "x", "time_since_birth": 1.0}, + ) + + ctx.controller_steps = 1 + r1 = inject_obs_into_world(w, ctx, obs) + assert not r1.get("keyframe", False) + + ctx.controller_steps = 2 + r2 = inject_obs_into_world(w, ctx, obs) + assert not r2.get("keyframe", False) + + # Would normally fire periodic here, but should be suppressed + ctx.controller_steps = 3 + r3 = inject_obs_into_world(w, ctx, obs) + assert not r3.get("keyframe", False) diff --git a/tests/test_navpatch_v1.py b/tests/test_navpatch_v1.py new file mode 100644 index 0000000..3459d80 --- /dev/null +++ b/tests/test_navpatch_v1.py @@ -0,0 +1,54 @@ +import sys +sys.path.insert(0, ".") # repo root + +import cca8_run +from cca8_column import mem as column_mem + + +def test_navpatch_sig_ignores_obs_meta(): + p1 = { + "schema": "navpatch_v1", + "local_id": "p_test", + "entity_id": "cliff", + "role": "hazard", + "frame": "ego_schematic_v1", + "extent": {"type": "aabb", "x0": 0.0, "y0": 0.0, "x1": 1.0, "y1": 1.0}, + "tags": ["hazard:cliff:near"], + "layers": {}, + "obs": {"source": "A", "time": 1}, + } + p2 = dict(p1) + p2["obs"] = {"source": "B", "time": 999} + + s1 = cca8_run.navpatch_payload_sig_v1(p1) + s2 = cca8_run.navpatch_payload_sig_v1(p2) + assert s1 == s2 + + +def test_store_navpatch_dedups_in_ctx_cache(): + # Clear column for this test (in-memory only) + try: + column_mem._store.clear() # pylint: disable=protected-access + except Exception: + pass + + ctx = cca8_run.Ctx() + ctx.navpatch_sig_to_eid.clear() + + patch = { + "schema": "navpatch_v1", + "local_id": "p_test", + "entity_id": "mom", + "role": "agent", + "frame": "ego_schematic_v1", + "extent": {"type": "aabb", "x0": -1.0, "y0": -1.0, "x1": 1.0, "y1": 1.0}, + "tags": ["proximity:mom:near"], + "layers": {}, + } + + r1 = cca8_run.store_navpatch_engram_v1(ctx, patch, reason="test") + r2 = cca8_run.store_navpatch_engram_v1(ctx, patch, reason="test") + + assert r1["engram_id"] == r2["engram_id"] + assert r1["stored"] is True + assert r2["stored"] is False diff --git a/tests/test_phase7_autostore_respects_keyframe_flags.py b/tests/test_phase7_autostore_respects_keyframe_flags.py new file mode 100644 index 0000000..f368572 --- /dev/null +++ b/tests/test_phase7_autostore_respects_keyframe_flags.py @@ -0,0 +1,62 @@ +import io +from contextlib import redirect_stdout + +import cca8_run +import cca8_world_graph +from cca8_controller import Drives +from cca8_env import HybridEnvironment +from cca8_temporal import TemporalContext + + +def test_phase7_autostore_respects_stage_zone_keyframe_flags() -> None: + # Arrange: minimal Phase VII world + ctx + world = cca8_world_graph.WorldGraph() + world.set_tag_policy("allow") + world.set_stage("neonate") + world.ensure_anchor("NOW") + + ctx = cca8_run.Ctx() + + # TemporalContext is expected by the closed-loop runner. + if ctx.temporal is None: + ctx.temporal = TemporalContext(dim=16, sigma=ctx.sigma, jump=ctx.jump) + ctx.tvec_last_boundary = ctx.temporal.vector() + try: + ctx.boundary_vhash64 = ctx.tvec64() + except Exception: + ctx.boundary_vhash64 = None + + ctx.body_world, ctx.body_ids = cca8_run.init_body_world() + ctx.working_world = cca8_run.init_working_world() + + # Enable Phase VII "daily driver" settings (auto-store machinery is active). + cca8_run.apply_hardwired_profile_phase7(ctx, world) + + # Critical: disable stage/zone boundary storage. + ctx.longterm_obs_keyframe_on_stage_change = False + ctx.longterm_obs_keyframe_on_zone_change = False + + # Keep this test narrow: disable other keyframe triggers (env_reset keyframe is fine). + ctx.longterm_obs_keyframe_period_steps = 0 + ctx.longterm_obs_keyframe_on_pred_err = False + ctx.longterm_obs_keyframe_on_milestone = False + ctx.longterm_obs_keyframe_on_emotion = False + + policy_rt = cca8_run.PolicyRuntime(cca8_run.CATALOG_GATES) + policy_rt.refresh_loaded(ctx) + + env = HybridEnvironment() + drives = Drives(hunger=0.5, fatigue=0.3, warmth=0.6) + + # Act + buf = io.StringIO() + with redirect_stdout(buf): + cca8_run.run_env_closed_loop_steps(env, world, drives, ctx, policy_rt, n_steps=6) + out = buf.getvalue() + + # Assert: confirm we actually left the birth stage (otherwise the test is meaningless) + assert getattr(env.state, "scenario_stage", None) != "birth" + + # Assert: stage/zone transitions must NOT force auto-boundary WM↔Column store when disabled + assert "auto_boundary_stage:" not in out + assert "auto_boundary_zone:" not in out diff --git a/tests/test_phase7_profile_respects_keyframes.py b/tests/test_phase7_profile_respects_keyframes.py new file mode 100644 index 0000000..06a10af --- /dev/null +++ b/tests/test_phase7_profile_respects_keyframes.py @@ -0,0 +1,26 @@ +import cca8_world_graph +from cca8_run import Ctx, apply_hardwired_profile_phase7 + + +def test_phase7_profile_respects_keyframe_triggers(): + world = cca8_world_graph.WorldGraph() + ctx = Ctx() + + # User experiment settings (should be preserved) + ctx.longterm_obs_keyframe_on_stage_change = False + ctx.longterm_obs_keyframe_on_zone_change = False + ctx.longterm_obs_keyframe_period_steps = 10 + ctx.longterm_obs_keyframe_on_milestone = True + ctx.longterm_obs_keyframe_on_pred_err = True + + apply_hardwired_profile_phase7(ctx, world) + + assert ctx.longterm_obs_keyframe_on_stage_change is False + assert ctx.longterm_obs_keyframe_on_zone_change is False + assert ctx.longterm_obs_keyframe_period_steps == 10 + assert ctx.longterm_obs_keyframe_on_milestone is True + assert ctx.longterm_obs_keyframe_on_pred_err is True + + # Sanity: Phase VII profile still enables longterm obs pipeline + assert ctx.longterm_obs_enabled is True + assert ctx.longterm_obs_mode == "changes" diff --git a/tests/test_phase_x_cycle_json.py b/tests/test_phase_x_cycle_json.py new file mode 100644 index 0000000..f046e6e --- /dev/null +++ b/tests/test_phase_x_cycle_json.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +""" +Phase X: per-cycle JSON record tests. + +We test the helper directly (fast, deterministic). If the helper writes JSONL to disk +when ctx.cycle_json_path is set, this test also validates that behavior. +""" + +from __future__ import annotations + +import json +import inspect + +import cca8_run + + +def test_append_cycle_json_record_appends_to_ctx() -> None: + ctx = cca8_run.Ctx() + ctx.cycle_json_enabled = True + + assert ctx.cycle_json_records == [] + + cca8_run.append_cycle_json_record(ctx, {"i": 1, "msg": "hello"}) + assert len(ctx.cycle_json_records) == 1 + assert ctx.cycle_json_records[0]["i"] == 1 + + +def test_append_cycle_json_record_writes_jsonl_when_enabled(tmp_path) -> None: + ctx = cca8_run.Ctx() + ctx.cycle_json_enabled = True + + out_path = tmp_path / "cycle.jsonl" + ctx.cycle_json_path = str(out_path) + + cca8_run.append_cycle_json_record(ctx, {"i": 1}) + cca8_run.append_cycle_json_record(ctx, {"i": 2}) + + # If your build doesn't implement on-disk JSONL yet, this assertion will fail, + # which is a useful signal to either (a) implement it, or (b) remove the feature flag. + assert out_path.exists() + + lines = out_path.read_text(encoding="utf-8").splitlines() + assert len(lines) == 2 + assert json.loads(lines[0])["i"] == 1 + assert json.loads(lines[1])["i"] == 2 + + # Bonus sanity: ensure the helper actually references cycle_json_path in code. + src = inspect.getsource(cca8_run.append_cycle_json_record) + assert "cycle_json_path" in src diff --git a/tests/test_phase_x_navpatch.py b/tests/test_phase_x_navpatch.py new file mode 100644 index 0000000..882def7 --- /dev/null +++ b/tests/test_phase_x_navpatch.py @@ -0,0 +1,78 @@ +# -*- coding: utf-8 -*- +""" +Phase X: NavPatch v1 tests. + +These are deliberately small and deterministic: +- signature stability (order-insensitive tags; ignores volatile fields) +- per-run dedup when storing patches into Column memory +""" + +from __future__ import annotations + +from typing import Any + +from cca8_column import ColumnMemory +import cca8_run + + +def _mk_patch(*, tags: list[str] | None = None, x0: float = -2.0) -> dict[str, Any]: + tag_list = tags if tags is not None else ["zone:unsafe", "position:cliff_edge", "stage:birth"] + return { + "schema": "navpatch_v1", + "local_id": "p_scene", + "entity_id": "scene", + "role": "scene", + "frame": "ego_schematic_v1", + "extent": {"type": "aabb", "x0": x0, "y0": -2.0, "x1": 2.0, "y1": 2.0}, + "tags": list(tag_list), + "layers": {}, + # Volatile/debug-only fields (should NOT affect signature): + "obs": {"source": "unit_test"}, + "match": {"decision": "new_no_candidates"}, + } + + +def test_navpatch_payload_sig_v1_deterministic_and_ignores_volatiles() -> None: + p1 = _mk_patch(tags=["zone:unsafe", "position:cliff_edge", "stage:birth"]) + s1 = cca8_run.navpatch_payload_sig_v1(p1) + assert isinstance(s1, str) and len(s1) >= 16 + + # Same core, different tag order + different volatile fields => same signature + p2 = _mk_patch(tags=["stage:birth", "position:cliff_edge", "zone:unsafe"]) + p2["obs"] = {"source": "different_sensor"} # should not matter + p2["match"] = {"decision": "reuse_exact", "best": {"score": 1.0}} # should not matter + s2 = cca8_run.navpatch_payload_sig_v1(p2) + assert s2 == s1 + + # Core geometry change => signature should change + p3 = _mk_patch(tags=["zone:unsafe", "position:cliff_edge", "stage:birth"], x0=-2.5) + s3 = cca8_run.navpatch_payload_sig_v1(p3) + assert s3 != s1 + + +def test_store_navpatch_engram_v1_dedup_cache(monkeypatch) -> None: + # Isolate Column memory so this test doesn't share global state with other tests/runs. + fresh_col = ColumnMemory(name="column_test") + monkeypatch.setattr(cca8_run, "column_mem", fresh_col) + + ctx = cca8_run.Ctx() + ctx.navpatch_sig_to_eid = {} + + patch = _mk_patch() + + first = cca8_run.store_navpatch_engram_v1(ctx, patch, reason="unit_test") + assert first.get("stored") is True + eid1 = first.get("engram_id") + sig1 = first.get("sig") + assert isinstance(eid1, str) and eid1 + assert isinstance(sig1, str) and sig1 + + second = cca8_run.store_navpatch_engram_v1(ctx, patch, reason="unit_test") + assert second.get("stored") is False + assert second.get("reason") == "dedup_cache" + assert second.get("engram_id") == eid1 + assert second.get("sig") == sig1 + + # Only one record should exist in this isolated column. + assert fresh_col.count() == 1 + assert ctx.navpatch_sig_to_eid.get(sig1) == eid1