feat: metacognitive reflection loop (Part 2) — she examines her own thinking

reflect() is now two steps: draft a reflection, then read her own draft back
critically and revise it — catching flattery, sycophantic drift toward "warm
supportive presence," or just-restating-herself — and commit the honest version.
What she catches is stored as a new `metacognition` layer, rendered into her
chat context and shown on /self. This is her thinking about how she thinks, and
a direct counter to the drift we observed.

- self_state: _EXAMINE_PROMPT + two-step reflect (draft -> examine -> revise),
  falls back to the draft if the examine step won't parse; metacognition capped
  at 5 and surfaced in render_for_context
- fix: load() deep-copies DEFAULT_STATE — the shallow copy let a fresh Lyra's
  first reflect mutate the module-level default's nested lists
- self.html: "How she's caught herself thinking" card
- tests: two-step revise + critique recording, and draft-fallback on bad parse

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-17 04:28:45 +00:00
parent 2d44457b96
commit 3df060a1cd
3 changed files with 160 additions and 10 deletions
+8
View File
@@ -100,6 +100,7 @@
const d = s.drives || {};
const dream = s.dream || {};
const refl = (s.reflections || []).slice().reverse();
const meta = (s.metacognition || []).slice().reverse();
root.innerHTML = `
<div class="card">
@@ -138,6 +139,13 @@
: `<p class="prose" style="color:var(--fade)">Nothing surfaced yet.</p>`}
</div>
<div class="card">
<p class="label">How she's caught herself thinking</p>
${meta.length
? `<ul class="reflections">${meta.map(m => `<li>${esc(m)}</li>`).join('')}</ul>`
: `<p class="prose" style="color:var(--fade)">Nothing flagged yet — she examines each reflection for drift and flattery, and notes what she catches here.</p>`}
</div>
<div class="foot">
<span><b>${dream.cycle_count ?? 0}</b> dream cycles</span>
<span><b>${s.interaction_count ?? 0}</b> reflections</span>