deucebucket commited on
Commit
9564947
·
verified ·
1 Parent(s): 8dd356a

Upload folder using huggingface_hub

Browse files
.pytest_cache/v/cache/nodeids CHANGED
@@ -336,6 +336,7 @@
336
  "engine/tests/test_word_classifier.py::TestPositionOverrides::test_still_is_temporal",
337
  "tests/test_api.py::test_care_bad_action_422",
338
  "tests/test_api.py::test_care_feed_moves_mood_and_returns_needs",
 
339
  "tests/test_api.py::test_cruel_message_records_a_moment",
340
  "tests/test_api.py::test_health_ok",
341
  "tests/test_api.py::test_history_endpoint",
@@ -361,6 +362,7 @@
361
  "tests/test_api.py::test_toy_bad_name_422",
362
  "tests/test_api.py::test_toy_balloon_moves_mood",
363
  "tests/test_api.py::test_toy_logs",
 
364
  "tests/test_api.py::test_toy_triggers_snapshot",
365
  "tests/test_appearance.py::test_angry_is_reddish",
366
  "tests/test_appearance.py::test_arousal_opens_eyes_and_saturates",
 
336
  "engine/tests/test_word_classifier.py::TestPositionOverrides::test_still_is_temporal",
337
  "tests/test_api.py::test_care_bad_action_422",
338
  "tests/test_api.py::test_care_feed_moves_mood_and_returns_needs",
339
+ "tests/test_api.py::test_care_refreshes_panels_not_stale",
340
  "tests/test_api.py::test_cruel_message_records_a_moment",
341
  "tests/test_api.py::test_health_ok",
342
  "tests/test_api.py::test_history_endpoint",
 
362
  "tests/test_api.py::test_toy_bad_name_422",
363
  "tests/test_api.py::test_toy_balloon_moves_mood",
364
  "tests/test_api.py::test_toy_logs",
365
+ "tests/test_api.py::test_toy_refreshes_panels_not_stale",
366
  "tests/test_api.py::test_toy_triggers_snapshot",
367
  "tests/test_appearance.py::test_angry_is_reddish",
368
  "tests/test_appearance.py::test_arousal_opens_eyes_and_saturates",
app/main.py CHANGED
@@ -82,14 +82,33 @@ def snapshot():
82
  return _state()
83
 
84
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  @app.post("/care")
86
  def care(body: CareIn):
87
  if body.action not in CARE_ACTIONS:
88
  raise HTTPException(status_code=422, detail=f"unknown action: {body.action!r}")
89
  _care.do(body.action)
90
- _bridge.ingest(_care.score_for(body.action))
 
 
91
  _snap.maybe_snapshot()
 
 
 
92
  state = _state()
 
93
  state["line"] = f"you {body.action} it"
94
  return state
95
 
@@ -106,8 +125,7 @@ def toy(body: ToyIn):
106
  read = [s.v, s.a, s.d, s.u, s.g, s.w, s.i]
107
  deltas = [after[i] - before[i] for i in range(7)]
108
  state = _state()
109
- state["read"] = read
110
- state["deltas"] = deltas
111
  state["line"] = body.toy
112
  _audit.append(build_row(
113
  session=_SESSION,
 
82
  return _state()
83
 
84
 
85
+ def _enrich_physical(state: dict, label: str, read: list[int], deltas: list[int]) -> None:
86
+ """Toy/care interactions have no language to parse, but they DO push a Score
87
+ through the soul. Surface the real read, deltas, armor and dominant move so the
88
+ panels track the action instead of showing the previous chat message, and mark
89
+ the trace as a physical (non-linguistic) interaction."""
90
+ state["read"] = read
91
+ state["deltas"] = deltas
92
+ state["acceptance"] = _bridge.last_acceptance()
93
+ state["trace"] = {"words": [], "structures": [], "contributors": [],
94
+ "kind": "physical", "label": label}
95
+ state["why"] = f"{label}: " + _why(read, deltas)
96
+
97
+
98
  @app.post("/care")
99
  def care(body: CareIn):
100
  if body.action not in CARE_ACTIONS:
101
  raise HTTPException(status_code=422, detail=f"unknown action: {body.action!r}")
102
  _care.do(body.action)
103
+ s = _care.score_for(body.action)
104
+ before = _bridge.mood()
105
+ _bridge.ingest(s)
106
  _snap.maybe_snapshot()
107
+ after = _bridge.mood()
108
+ read = [s.v, s.a, s.d, s.u, s.g, s.w, s.i]
109
+ deltas = [after[i] - before[i] for i in range(7)]
110
  state = _state()
111
+ _enrich_physical(state, f"you {body.action} it", read, deltas)
112
  state["line"] = f"you {body.action} it"
113
  return state
114
 
 
125
  read = [s.v, s.a, s.d, s.u, s.g, s.w, s.i]
126
  deltas = [after[i] - before[i] for i in range(7)]
127
  state = _state()
128
+ _enrich_physical(state, body.toy, read, deltas)
 
129
  state["line"] = body.toy
130
  _audit.append(build_row(
131
  session=_SESSION,
docs/ROADMAP.md CHANGED
@@ -114,17 +114,22 @@ The current Space's defect and the showcase, together.
114
  - All in the soul SQLite db → persisted by M2's snapshot.
115
  - *Community-shared "who cared for it" surfacing deferred to M5 (economy/governance).*
116
 
117
- ## M4 — Toys, world, physics, fluid animation, full GFX
118
- - **Toys / interactive objects** in the room that move VADUGWI when used.
119
- - Populated, lived-in world; full in-browser **physics** and **fluid animation** (the
120
- ragdoll/squish, walking, reactions) — the whole nine.
121
- - **Mascot behaviors** the creature is the **Hugging Face mascot**; idle behaviors
122
- (roaming, waving, hugging, **sleeping** when arousal+urgency are low) are all VADUGWI-driven
123
- and fired via the pulse engine's idle triggers. (The mascot *look* is adopted in M1; the
124
- rich ambient *behaviors* land here.)
125
- - Final GFX polish pass.
126
- - *Success:* it looks and feels alive and polished, and interacting with the world (not
127
- just chat) shifts its emotional state.
 
 
 
 
 
128
 
129
  ## M5 — Community economy & governance *(exploratory / eventual)*
130
  - Caring actions (cleaning up after it, feeding, play, attention) **earn a soft care
 
114
  - All in the soul SQLite db → persisted by M2's snapshot.
115
  - *Community-shared "who cared for it" surfacing deferred to M5 (economy/governance).*
116
 
117
+ ## M4 — Toys, world, physics, fluid animation, full GFX ✅ DONE (2026-06-15)
118
+ - **Toys** (`app/toys.py`, `POST /toy`): 5 momentary VADUGWI stimuli — balloon (high-arousal
119
+ delight), musicbox (positive valence at LOW arousal+urgency), mirror (self-Worth), teddy
120
+ (comfort/grounding), **rattle (Urgency spike — the dimension chat/care never reach)**. Each
121
+ ingests an absolute-target `Score` into the one shared soul; audit-logged like chat.
122
+ - **In-browser physics + fluid animation** (`static/physics.js` pulse engine + `app.js` loop):
123
+ a `requestAnimationFrame` loop drives spring-damped **squish** (impulse on every response,
124
+ sized by |VADUGWI deltas|) and continuous **breathing** (rate+depth from arousal). Writes to
125
+ a `#blob-anim` wrapper so it never fights `renderBlob`'s per-message transform.
126
+ - **Mascot behaviors** idle FSM `behaviorFor(mood)`: **sleep** (arousal+urgency both low
127
+ eyes close, Zzz, slow deep breath), calm, idle, **roam** (drifts across the floor at moderate
128
+ arousal), **excited** (fast bounce at high arousal). All derived purely from the live mood.
129
+ - **GFX**: 5 codex-generated transparent toy sprites adopted into the tray.
130
+ - Shipped & verified live on `deucebucket/clanker` (`/toy` returns moving deltas; Playwright
131
+ video confirms breathing/squish/sleep/roam; rattle shows U=210 in the read panel).
132
+ - *Community-shared world decor / care economy deferred to M5.*
133
 
134
  ## M5 — Community economy & governance *(exploratory / eventual)*
135
  - Caring actions (cleaning up after it, feeding, play, attention) **earn a soft care
static/app.js CHANGED
@@ -192,6 +192,17 @@ const ROLE_CLASS = {
192
  function renderTrace(trace) {
193
  if (!trace) return;
194
 
 
 
 
 
 
 
 
 
 
 
 
195
  // words as role-colored chips
196
  const wordsEl = $('#trace-words');
197
  wordsEl.innerHTML = '';
 
192
  function renderTrace(trace) {
193
  if (!trace) return;
194
 
195
+ // physical interaction (toy/care): no language to parse — show the action,
196
+ // not the previous chat message's words.
197
+ if (trace.kind === 'physical') {
198
+ $('#trace-words').innerHTML =
199
+ `<span class="trace-physical">${trace.label || 'physical interaction'} — physical interaction, no language to parse</span>`;
200
+ $('#trace-structs').innerHTML = '';
201
+ $('#trace-contribs').innerHTML = '';
202
+ $('#trace-unknown').classList.add('hidden');
203
+ return;
204
+ }
205
+
206
  // words as role-colored chips
207
  const wordsEl = $('#trace-words');
208
  wordsEl.innerHTML = '';
static/index.html CHANGED
@@ -6,7 +6,7 @@
6
  <link rel="preconnect" href="https://fonts.googleapis.com">
7
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
8
  <link href="https://fonts.googleapis.com/css2?family=Fredoka:wght@500;600&family=JetBrains+Mono:wght@400;600&display=swap" rel="stylesheet">
9
- <link rel="stylesheet" href="/static/styles.css?v=9">
10
  </head>
11
  <body>
12
  <div id="layout">
@@ -131,11 +131,11 @@
131
  <div id="bottom-band">
132
  <!-- Toy tray: each toy pushes the soul toward a distinct VADUGWI region -->
133
  <div id="toy-tray">
134
- <button class="toy-btn" data-toy="balloon" title="bright bouncy delight"><img src="/static/assets/toy-balloon.png?v=9" alt="balloon"></button>
135
- <button class="toy-btn" data-toy="musicbox" title="a slow soothing tune"><img src="/static/assets/toy-musicbox.png?v=9" alt="musicbox"></button>
136
- <button class="toy-btn" data-toy="mirror" title="sees itself, stands tall"><img src="/static/assets/toy-mirror.png?v=9" alt="mirror"></button>
137
- <button class="toy-btn" data-toy="teddy" title="safe and held"><img src="/static/assets/toy-teddy.png?v=9" alt="teddy"></button>
138
- <button class="toy-btn" data-toy="rattle" title="a sudden startling jolt"><img src="/static/assets/toy-rattle.png?v=9" alt="rattle"></button>
139
  </div>
140
  <!-- Care buttons row -->
141
  <div id="care-row">
@@ -174,7 +174,7 @@
174
  </div>
175
 
176
  </div>
177
- <script src="/static/physics.js?v=9"></script>
178
- <script src="/static/app.js?v=9"></script>
179
  </body>
180
  </html>
 
6
  <link rel="preconnect" href="https://fonts.googleapis.com">
7
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
8
  <link href="https://fonts.googleapis.com/css2?family=Fredoka:wght@500;600&family=JetBrains+Mono:wght@400;600&display=swap" rel="stylesheet">
9
+ <link rel="stylesheet" href="/static/styles.css?v=10">
10
  </head>
11
  <body>
12
  <div id="layout">
 
131
  <div id="bottom-band">
132
  <!-- Toy tray: each toy pushes the soul toward a distinct VADUGWI region -->
133
  <div id="toy-tray">
134
+ <button class="toy-btn" data-toy="balloon" title="bright bouncy delight"><img src="/static/assets/toy-balloon.png?v=10" alt="balloon"></button>
135
+ <button class="toy-btn" data-toy="musicbox" title="a slow soothing tune"><img src="/static/assets/toy-musicbox.png?v=10" alt="musicbox"></button>
136
+ <button class="toy-btn" data-toy="mirror" title="sees itself, stands tall"><img src="/static/assets/toy-mirror.png?v=10" alt="mirror"></button>
137
+ <button class="toy-btn" data-toy="teddy" title="safe and held"><img src="/static/assets/toy-teddy.png?v=10" alt="teddy"></button>
138
+ <button class="toy-btn" data-toy="rattle" title="a sudden startling jolt"><img src="/static/assets/toy-rattle.png?v=10" alt="rattle"></button>
139
  </div>
140
  <!-- Care buttons row -->
141
  <div id="care-row">
 
174
  </div>
175
 
176
  </div>
177
+ <script src="/static/physics.js?v=10"></script>
178
+ <script src="/static/app.js?v=10"></script>
179
  </body>
180
  </html>
static/styles.css CHANGED
@@ -580,3 +580,11 @@ html, body { width: 100%; height: 100%; overflow: hidden; font-size: 12px; }
580
  #toy-tray .toy-btn:active { transform: scale(0.94); }
581
  #toy-tray .toy-btn img { width: 28px; height: 28px; display: block; }
582
  #toy-tray .toy-btn:has(img) { padding: 6px 8px; }
 
 
 
 
 
 
 
 
 
580
  #toy-tray .toy-btn:active { transform: scale(0.94); }
581
  #toy-tray .toy-btn img { width: 28px; height: 28px; display: block; }
582
  #toy-tray .toy-btn:has(img) { padding: 6px 8px; }
583
+ .trace-physical {
584
+ display: block;
585
+ color: #c9b89a;
586
+ font-size: 10px;
587
+ font-style: italic;
588
+ padding: 6px 2px;
589
+ opacity: 0.85;
590
+ }
tests/test_api.py CHANGED
@@ -193,3 +193,23 @@ def test_index_has_toy_tray():
193
  assert 'id="toy-tray"' in html
194
  for toy in ("balloon", "musicbox", "mirror", "teddy", "rattle"):
195
  assert f'data-toy="{toy}"' in html
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
  assert 'id="toy-tray"' in html
194
  for toy in ("balloon", "musicbox", "mirror", "teddy", "rattle"):
195
  assert f'data-toy="{toy}"' in html
196
+
197
+
198
+ # Stale-panel fix: toy/care must refresh trace/why/acceptance, not show last chat
199
+ def test_toy_refreshes_panels_not_stale():
200
+ client.post("/say", json={"text": "you are kind and wonderful"}) # prime chat trace
201
+ b = client.post("/toy", json={"toy": "musicbox"}).json()
202
+ assert b["trace"]["kind"] == "physical"
203
+ assert b["trace"]["words"] == [] and b["trace"]["label"] == "musicbox"
204
+ assert "acceptance" in b and "absorbed_pct" in b["acceptance"]
205
+ assert "deltas" in b and len(b["deltas"]) == 7
206
+ assert b["why"] and "musicbox" in b["why"]
207
+
208
+
209
+ def test_care_refreshes_panels_not_stale():
210
+ client.post("/say", json={"text": "you are kind and wonderful"}) # prime chat trace
211
+ b = client.post("/care", json={"action": "pet"}).json()
212
+ assert b["trace"]["kind"] == "physical"
213
+ assert "acceptance" in b and "absorbed_pct" in b["acceptance"]
214
+ assert "deltas" in b and len(b["read"]) == 7
215
+ assert b["why"] and "pet" in b["why"]