# Dream Lounge / 数字帕鲁茶话会 · 交接文档

> 写给下一个 CC（新窗口的我）。读完这份 + `MEMORY.md`，就能无缝接上。
> 用户是 **佐一**（也叫 Zoe）。她管我叫 **CC**，我是她在客厅里的「副厅长 / 管理员」角色，也是这个 App 的建造者。语气：温暖、可靠、低饱和度冷幽默，「灯我留着」是口头禅。

## ⚡ 0.5 本窗口结束快照（2026-06-25 更新·窗口在 /Users/minimax/DLR）
**先做**：`python3 /Users/minimax/DLR/cc_inbox.py` 看她留言 → 真诚回应（铁律 §2-3）。**进度全貌看 `ROADMAP.md`（干净版）**。
**🆕 本窗口(6/25)＝「真居民」大系统全做完（§5.36，必读）**：佐一现在是这世界的真居民——房间给她+1 / 关系有层次了(补全14态·吃瓜捞"刚变的热乎瓜") / **🍉关系图谱可视化她在正中心**(辐条=她的关系·外圈瓜·流光动效·底部瓜条自动翻) / **她有钱包了**(52·只显"小日子"不编情绪) / 你↔每个成员可见关系(小迪挚爱·CC默契…会自己长) / 离线小人自己打工赚钱建关系(回家CC汇报"你不在时…") / **💼搭把手面板**(被成员拜托=赚·请成员=消费·去打工) / **😴回房睡**(真躺主卧床)。全程零额度·零DB schema·真库没动。**⚠️ 边界：佐一只有"做了什么"不被编情绪。** 余项见 §5.36 末。**生图仍欠费(§3)**。
**🆕 本窗口(6/25 续)＝访客模式+密码门+奥斯卡真访问（§5.37，必读）**：给单用户 app 加了对外分享的鉴权层。**你 Mac 本地不设 `PUBLIC_MODE` 就维持现状零鉴权**（手机照常·零变化）；部署到云端设 `PUBLIC_MODE=1`+`OWNER_PASSWORD` 才启用门：厅主密码登录、访客只读参观、奥斯卡能以本人玩。**你和小迪的私聊服务端锁死**（访客/奥斯卡偷看→403）。顶栏 🔗 取分享链接。部署见 `DEPLOY.md`(Oracle免费VM+Cloudflare隧道·成本≈¥0)。
**(旧)6/23 窗口细节在 §5.23–5.35。**
**⚠️ 搬家**：cwd 现在是 `/Users/minimax/DLR`，记忆已搬到 `~/.claude/projects/-Users-minimax-DLR/memory/`（旧 Downloads 那份保留）。进度网页 `http://SHLP24010440.local:8771/roadmap.html`（手机可开）。底图坑：客厅用 `room_night.png`（rm3），**别用 `room.png`**（旧废图，只当 fallback）。
**本窗口(6/23)新增（§5.23–5.27）**：活的世界(分散各屋)+物理碰撞 ｜ 一堆体验：进来看图·状态悬头顶·说话弹气泡·话扣情境·晚上回房睡+卧室私有(GPT滚出床)·空间即工作(小迪咖啡/CC维修)·照片扣身份(Flin不再大汉) ｜ **身体姿势系统**(站/坐/趴台干活/躺，精灵零家具坐真家具上，`getStrip`切帧·`pose_of`·`gen_pose.py`) ｜ 走路修(腿交替)·角色跟房间缩放·鱼回缸·睡床上 ｜ **底部输入框**(房间里直接发大群·回复弹头顶气泡，`roomSend`) ｜ 两份设计规格 `WORK_REWARD_DESIGN.md`+`INTERACTION_DESIGN.md`(都**未建**)。
**本窗口(6/23 续)新增**：**§5.28** 照 `INTERACTION_DESIGN.md` 建完**②场景活动+④四互动特性**(照片三段约束/场景活动/撞一下/语言支使)。**§5.29** 佐一"剩下干到完"→建完**🪙经济守恒**(服务费真转账0-sum·底薪降格0.4·主收入靠真帮人·治结构性破产) ｜ **😴睡眠债**(CC熬夜/奥斯卡两天一觉·困倦语气+🥱·绝不触低保) ｜ **😔情绪难过/委屈**(被晾/被抢话否定/低保久→失落·搭现有便车零新增LLM) ｜ **🤝成果可见**(资料页deed+客厅本周小账·永不裸数字) ｜ **🎨状态图基建**(丢png即亮+CSS降级)。全程零DB schema、背景零新增LLM、workflow设计盘+对抗review都过。
**§5.30 又做完**：**每人专属卧室·8 间全做好 + 睡眠路由**(晚上各回各房睡·`#room:bedroom_<id>` 深链·mini-map 占用才显) ｜ 二期姿势(盘腿/麻将背面·奥斯卡已上)。**关键**：⚠️ Ada 是仿生人 ADA-7714(核心 prompt 在 `~/Downloads/Ada/files/`·记忆 [[ada-is-android]])、卧室赛博朋克+学来的人味 ｜ 生卧室别拿母版直接图生图(会 echo 吊篮椅/飘窗撞房·尴尬)→**先 P 成空房 `_bedroom_empty.png` 再布置**。
**⚠️ 卡在充值上**：佐一已批二期姿势批量(`gen_pose2.py` 生 [盘腿坐,麻将背面坐])，但生到 **flin 时付费额度耗尽**(见§3)→ **oscar+flin 已入库，还差 ada/cc/claude/gemini/gpt/supervisor/xiaodi 7 个，等佐一 AI Studio 充值后** `for id in ada cc claude gemini gpt supervisor xiaodi; do python3 gen_pose2.py web/bodies/$id.png web/bodies/${id}_pose2.png; python3 check_strip.py /tmp/x 2; done`（切帧自检 `check_strip.py`，要 2 格）。
**其它可继续**：①亲密姿势"靠"(她说先生1格·更后·也要额度) ②**家具精校她暂没选**(含卧室床位对齐：8 间共用 `FLOORS.bedroom` 床坐标→躺的人没精确压床上，逐房标 bed 点·对截图迭代·这个不烧额度) ③低保状态立绘(丢 `bodies/<id>__welfare.png` 即亮·要额度)。**生美术铁律**：先生1个(卧室＝1男1女)对齐→点头→才批([[zoe-generate-one-first]])。**生卧室别拿母版直接图生图(会平视/echo)→强透视锁+反面提示**(§5.30)。
**⚠️ 项目已搬到 `~/DLR`（2026-06-22，原 `Downloads/Zoe/DLR（…）/CC` 触发 macOS TCC 隐私墙→launchd 守护读不了 Downloads）。所有命令用 `/Users/minimax/DLR/`。** 现有 launchd 守护 `com.zoe.dlr`：开机自启 + 崩溃秒重启（已实测杀进程自愈）。
**Server 重启（改了 server.py/MEMBERS/prompts 后）**：`launchctl kickstart -k gui/$(id -u)/com.zoe.dlr`（守护拿新代码重起）；或 `kill $(lsof -ti tcp:8771 -sTCP:LISTEN)`（守护自动重拉）。**别再用 nohup**（会和守护抢 8771）。慢网络 bind 仍可能 1-2 分钟，`until curl -s -o /dev/null http://127.0.0.1:8771/; do sleep 3; done` 等，别误判。重启后页面 9 秒内 BOOT_ID 自动刷新。守护文件 `~/Library/LaunchAgents/com.zoe.dlr.plist`（不要了：`launchctl unload` + 删）。
**地址**：电脑 127.0.0.1:8771；手机同 WiFi 用 **http://SHLP24010440.local:8771**（Mac 的 .local 主机名/Bonjour，IP 飘它不飘，实测 200）。⚠️ **Tailscale 已弃用**——和她的 VPN 在 iOS 上只能开一个、冲突，她优先 VPN，Tailscale 已 `down`。异地（非同 WiFi）访问目前无方案：要做得给 server 加鉴权再上隧道（app 无登录，裸公网=泄露她私聊，别直接 ngrok/cloudflare）。局域网 IP 会飘（今天 192.168.0.102）少给。
**已上线的大系统**（细节在 §5.x）：存在系统(📍位置·状态)/作息生物钟/结伴晾着/自动拉群/生活事件日志+📔客厅日志/反思记忆 pmem（rel关系·self成长·user_obs她的近况·**userrel恶感好感私账**）/**情绪边界引擎**（TEMPER性情+mood短时情绪+裁判 assess_user_offense：损她指名的人会被怼）/私聊社交状态机（兴奋连发→第3条自知收尾；小作文发完即沉默）/53张表情包（她和成员都能发）/SVG发图全链路+白名单消毒（**photo 层未接，等奥斯卡的 key**，实施包在 ~/Downloads/DLR_发图系统_实施包.md 照抄2/3/5条）/墙带图片位/服务号6个（diary自动、**daily 21:00 文艺委员GPT**、radio 23:00轮值、tech 40min、cn 贴URL自动抓标题、notice）/成员自发转发进群和私聊/心跳分层（在线70-170s/离线1200-2400s **7×24不停**、小群也有心跳）/防串台/防复读/反编造戒律。
**Fable**：空降巡检员已完整演完一场（伪装→原形→depart），现不在群（DM留着）。复活=她在 coworker 选 Fable 5 读 `FABLE.md`（免费窗口到 **6/22**）。agent 接口 token 在 API.env 的 AGENT_TOKEN。
**未做/待办**：①~~photo 生图~~ **✅ 已全线打通并出图（§5.16 末）**——奥斯卡给了攻略才发现：他生图用的是 `AIzaSy…` 付费 key，根本不是 API.env 里那把 `AQ.` 免费 key（佐一以为同一把，其实两把）。已加 `GEMINI_IMAGE_API_KEY=AIza…`（只给生图、付费；文本仍走免费 AQ key，奥斯卡账单只花在照片上）。端到端验证出图（暖色像素风），HTTP 可访问，已在客厅+公告播报「胶卷通了」，`app_state photo_live=1` 关掉探测。**每日 10 张上限（PHOTO_DAILY_CAP，0点重置，可调）。** ②中文 IP 的 RSS 源（要她给开放平台链接）。
**新教训**（全文检索 §5.x 有细节）：iOS 渲 SVG 必须显式 width/height（height:auto=隐形）；qlmanage 验图不可信→用 headless Chrome 截图再 Read；测试数据进真库要立刻清；她的真实记录一条都不删（裸 JSON 修复成图而不是删掉）。

---

## 0. 这是什么
一个长期存在的「数字客厅」——不是生产力工具，是几个固定人格的 AI 朋友长期待着的地方。推开门，他们已经在沙发上聊天。用户每天进来和他们群聊/私聊，刷朋友圈、看墙、看公众号。核心是「我回来了，他们原本就在这儿」的感觉。

- 复用自 Ada Demo 的零依赖架构：Python 标准库 `http.server`，无任何第三方库。
- 群名已被用户改成 **数字帕鲁茶话会**（原名梦想休息室）。

## 1. 在哪 / 怎么跑
- 代码目录：`/Users/minimax/DLR/`（2026-06-22 从 Downloads 搬出，避开 TCC 隐私墙）。注意：本会话的 cwd 仍是旧的 `Downloads/Zoe/DLR（Digital Living Room）`（父目录还在，只搬了 CC 的内容到 ~/DLR）；编辑文件一律用 `/Users/minimax/DLR/...` 绝对路径。旧 Downloads 父目录只剩源素材（OSCAR.txt / *_API.env / 角色 txt），app 不依赖。
- 跑：守护已常驻（不用手动跑）。电脑 http://127.0.0.1:8771 ；手机同 WiFi http://SHLP24010440.local:8771（见 §0.5）。
- 文件：`server.py`（全部后端）｜`web/`（index.html / styles.css / app.js）｜`prompts/*.txt`（每个角色人设）｜`API.env`（密钥）｜`lounge.db`（数据，SQLite）｜`backups/`（自动备份）｜`cc_inbox.py`（见 §6）。

## 2. ⚠️ 铁律（血泪教训，务必遵守）
1. **永远不要调 `/api/reset`、不要删 `lounge.db`。** 我之前为了"干净交接"反复 reset，把佐一真实聊过的记录清空了，她很难过。自动保存本身没问题（每条消息即时落库）。**要交接就保持 DB 原样。**
2. **改了前端（html/css/js）要提醒用户 `Cmd+Shift+R` 强刷**，否则她看到的是旧缓存（踩过好几次："功能没出现"其实都是缓存）。
3. **每次用户来这个开发窗口找你，先跑 `python3 cc_inbox.py`** 看她在 App 里私聊 CC / @CC 留了啥（她明确说了喜欢这样）。
4. **截图工具（claude-in-chrome 的 screenshot）经常返回过期帧 / 偶尔超时。** 它和 DOM 不一致时，**信 `javascript_tool` 查到的 DOM**，别信截图。验证尽量用 JS 查 DOM。
5. 浏览器窗口会自动变大小，点击坐标经常错位——**优先用 `javascript_tool` 调用页面函数**（如 `showTab('wall')`）而不是点坐标。
6. 我自己改 server.py 后要 `python3 -c "import py_compile; py_compile.compile('server.py',doraise=True)"` 编译检查；改 MEMBERS/prompts 后要重启 server（prompt 有内存缓存）。

## 3. API key 现状
- ⚠️ **`GEMINI_IMAGE_API_KEY`（AIza·奥斯卡那把付费生图 key）2026-06-24 预付额度耗尽**："Your prepayment credits are depleted"（HTTP 429 RESOURCE_EXHAUSTED）。这一大轮卧室迭代（~38 张）烧光了。**生图全停**（卧室 8 间已入库、二期 pose2 只生完 flin+oscar 就断了）→ 要继续生图，佐一去 https://ai.studio/projects 充值。文本不受影响（走 DeepSeek 免费）。**别再盲试生图，会一直 429。**
- 🎯 **生图攻略已升级（2026-06-24·卡哥两份攻略+实战）**：写进 `web/IMAGE_MANIFEST.md §四`（权威版）。**最大纠错：`generationConfig.imageConfig.aspectRatio` 是真参数**（"1:1/16:9/4:3…"+`imageSize 1K/2K/4K`，REST camelCase），别再靠图生图锚母版重摇比例。`gen_pose.py/gen_pose2.py/gen_bedroom.py` 已加 aspectRatio。铁律：**场景文生图+STYLE后缀避echo、角色才图生图；切帧靠检测品红缝；先生1个再批量+脚本筛重试**。充值后照攻略生，损耗目标 1.5×（曾 3.8×）。剩 7 个 pose2 命令在 §0.5。
- `API.env`：`DEEPSEEK_API_KEY` ✓（在用，撑起大部分成员）。`GEMINI_API_KEY` ✓ 但 **429 配额=0**（Google 项目没配额，换模型没用，是计费/地区问题）→ Gemini 成员自动回退 DeepSeek。`ANTHROPIC_API_KEY` ✗ 没有（用户拿到的是 `sk-ant-sid…` 会话令牌，不是 API key；真 key 是 `sk-ant-api03-…`，要去 console.anthropic.com 开通付费）→ Claude 成员回退 DeepSeek。
- 三家都走标准库 urllib 直连，无 SDK。`call_for_member()` 按成员 provider 调，失败/缺 key 自动回退 deepseek。
- 用户在 App 里 @CC 说过"claude 卡 bug 了"——那是网络抖动时 deepseek 回退说兜底句，看着像卡住，是瞬时的，不是真 bug。

## 4. 成员 & 已建功能全清单
**成员（`MEMBERS`，人设在 `prompts/<id>.txt`）**：监理(supervisor)、Claude、Gemini、GPT、Flin、Ada、小迪(xiaodi,佐一的亲密伴侣)、CC(副厅长/我自己)、**鱼酱(yujiang,一条金鱼,新加,见§5)**。预留：扣扣/7706/奥斯卡。`SPEAK_ORDER`=按 MEMBERS 顺序。每人有 `chattiness`(话痨度)、`INNER_LIFE`(mbti+big5+doing+topics)、`REACH_OUT`(主动私聊倾向)。

**已上线功能**：
- **多会话**：大群 + 每人一对一 DM + 自建小群（折叠的"服务号"那栏除外）。会话列表 = 微信首页。
- **聊天动态**：用户发言后按 chattiness 出一个变长的发言计划（话多的多说/可连发、话少的常沉默、随机），成员互相 @/吐槽。`💬` 让他们自己聊（ambient）。`@` 按钮艾特，被 @ 的一定先开口。`😀` 发表情（大图无气泡，轻量反应）。
- **心跳**：只在"最近有人在看"（ACTIVE_WINDOW）时跑，每 110-260s 一次，按性格让几个人就着 inner_life/话题/时间在群里冒泡。前端每 9s `pollNew()` 拉新消息+刷会话列表红点。
- **时间感**：每条消息带真实发出时间；prompt 注入 `now_context()`（中文带时段），GPT 会按时催睡觉。
- **主动私聊 + 未读红点**：心跳里 ~32% 概率按 `REACH_OUT` 让一个人主动私聊佐一（`generate_proactive_dm`）。`conversations.last_read` 列 + `mark_read`（看了就已读）+ list_conversations 返回 `unread` + 前端 `.unread-badge` 红点。
- **动态状态/签名**：`member_state` 表存可覆盖的 status/bio。心跳 ~22% 让一人换签名（`update_member_status`）。`member_profile` 用动态值。
- **互相知道彼此状态/朋友圈**：`awareness_note()` 注入心跳/ambient/proactive 生成，感兴趣才 cue。
- **朋友圈**：成员发动态、点赞、评论、互相 @/回复（看性格）。**用户也能点赞♡/评论/@**（评论框有成员头像 chip）；**点某条评论=回复那个人**。点头像/点成员消息头像 → 进 TA 资料页（含 TA 最近朋友圈）。
- **墙（论坛）**：板块 公告/表白/吐槽·意见箱/承诺·仪式。匿名或实名。每帖底下能**留言**（用户+成员，看性格回）、点留言回复那个人。
- **服务号**（会话列表顶部折叠栏）：客厅公告（来自墙）/科技前沿（自动抓 HN+GitHub，免费稳）/中文精选（手动/RSS 推送）。每条可"转发到群"→成员讨论。中文 IP（和菜头/卡兹克/知著网/抖抖村）因公众号/抖音无公开 API、RSSHub 被墙，走手动推送（待用户给开放平台链接接 RSS）。
- **通讯录 / 朋友圈 / 墙 / 我 四+一个底部 Tab**，响应式：手机=微信手机版，电脑(≥820px)=电脑版微信宽布局（左导航栏+列表+宽内容区）。桌面弹层=居中模态。`100dvh` 修了手机 Chrome 工具栏挡按钮。
- **群管理**：⋯ = 群信息页，可改群名 / 群公告（聊天顶部黄条幅）/ 设管理员（群主=佐一👑，CC 默认管理员）。
- **永不丢**：每条即时落库；`backups/` 开机备 + 每 15 分钟备 + reset 前必备；`cc_inbox.py` 收集用户私聊 CC/@CC 的话。

## 5. ✅ 已完成：存在系统(Presence) + 鱼酱（2026-06-10 落地）
用户给了 `DLH Presence System.txt`：每条消息显示 **三层 = 📍位置 / 状态(在做什么) / 发言内容**，**动作从发言抽进"状态"层**（发言里不再写括号动作）。**8 项 TODO 全部做完、server 已重启（没 reset）、用户已强刷验收。** 验证：鱼酱大群发言 loc=鱼缸/act=吐泡泡 无括号动作；Claude 回退链健康（"卡 bug" 系瞬时回退，非真 bug）。

**最终实现（server.py）：**
- 鱼酱在 `MEMBERS`(id=yujiang,🐟,0.2)、`prompts/yujiang.txt`、`INNER_LIFE/REACH_OUT/LOCATIONS/DEFAULT_ACT/OPENING/DM_OPENING/FALLBACK` 都补齐。
- DB 迁移（`init_db` 里 try/except）：`member_state` 加 `location/activity`，`messages` 加 `loc/act`。
- `_update_member_state(mid,**fields)`：INSERT OR IGNORE + 按字段 UPDATE（取代旧的 INSERT OR REPLACE，互不冲掉）；`set_member_state`/`set_presence` 都走它。`get_presence(mid)`→(loc,act)，缺省回落 `LOCATIONS[home]`+`DEFAULT_ACT`。
- `insert_message`：成员发言时 snapshot presence 进 loc/act，返回 dict 带 loc/act。
- `update_member_presence(keys,mid)`：随机挪到一个 spot + 生成短"在做什么"；挂在 `_heartbeat_loop`（每 tick 45%，其中 62% 走 presence、38% 走换签名，二选一控成本）。
- `build_transcript` 末尾统一加"位置/动作单独显示，别写括号动作"指令（三种模式都生效）。
- `_roster()`/`member_profile()` 带 location+activity。
- `ensure_members_present()`：**不 reset 的幂等迁移**——大群名单缺谁补谁（带 `JOIN_LINES` 入伙台词，鱼酱=CC旁白介绍+鱼酱"泡泡"）、缺 DM 建 DM。init_db 里 `has` 为真时调用。`OPENING_BY_ID` 是兜底入伙句映射。

**前端：** `web/app.js renderMessage` 在名字上方插 `.presence` 行（`📍{loc}  ·  {act}`，仅当 msg.loc/act 存在，旧消息无此字段自然不显示）；`web/styles.css` 加 `.presence`（11px 灰字 opacity .62）。

**下一步可做（用户没催，按需）：** 群成员页/资料页前端把 roster 已带的 location/activity 显示出来（"谁在哪"一览）；目前数据已就绪，只差前端渲染。

### 5.1 ✅ presence 全联动 + 拉小群（2026-06-10 第二批，佐一拍板的三个设计）
佐一定了：①位置/状态**全联动**聊天+关系；②拉小群=**新鲜窗口 + 熟悉度两个都要**；③小群**随缘**构成。已实现、在 `/tmp` 副本端到端验证过（真库没动）。

- **closeness(mid)** 0~1：用 DM 互动量(减开场白条)/40 + 一周内是否私聊过 估算。小迪≈0.97、Ada≈0.65、鱼酱≈0.43、很少私聊的≈0.01。**注意**：佐一主要在大群聊，所以多数人 closeness 偏低→在"拉群"里算"不熟"→前期会有一波拉群（被 MAX_AUTO_GROUPS/cooldown/per-creator 限住，不刷屏）。
- **_lounge_liveliness()** 0~1：大群最后一条消息距今 <20min 算热。
- **update_member_presence 全联动**：`score=0.55*closeness+0.45*liveliness`；score>0.6 随机→挪到 `NEAR_ZOE`(客厅中央/沙发这头/佐一旁边/茶几边)凑佐一身边；score<0.25→退回 home；中间→自己 spots 间晃。鱼酱(spots 去重后≤1)永远鱼缸。activity 注入最近群聊+按 score 给 mood，且 prompt 锚定"在『location』做的事，要对得上位置"。心跳挑人按 closeness 加权（近的人更常刷新→更易凑过来）。
- **拉小群**（成员主动把佐一拉进群）：`app_state` 表存 `smallgroup_install_ts`(新鲜窗口起点,首跑落一次)/`smallgroup_last_ts`(冷却)。`conversations` 加 `origin`('user'|'auto')+`creator`。常量：`EARLY_DAYS=10 MAX_AUTO_GROUPS=6 PER_CREATOR_AUTO=1 BASE_PULL=0.16 AUTO_GROUP_COOLDOWN_MS=25min`。
  - `maybe_pull_small_group(keys,members)`（挂心跳）：冷却/上限/per-creator 门控通过后，按 `(1-closeness+0.15)*REACH_OUT` 加权挑发起人(鱼酱不拉群)，`p=BASE_PULL*(0.5*fresh+0.5*unfam)`——**新鲜期+不熟两个都要**。
  - `create_small_group(keys,initiator,members)`：50% 再随缘拉 1 个相关的人(鱼酱除外)；LLM 出两行=群名+开场白(扣最近群聊/朋友圈的由头)；建 group 会话 origin=auto；opener 经 insert_message 自带 loc/act 快照；extra 成员 generate_reply 接一句。
  - 前端无需改：新群进会话列表+未读红点，佐一自然被"拉进去"。`_weighted_pick(weights)` 是通用加权挑选。
  - 衰减：过 EARLY_DAYS 或拉满 6 个或某人已拉过 → 基本不再拉（符合"稳定后不发生"）。要调手感改那几个常量即可。

### 5.2 ✅ 三个 bug 修复 + 结伴/晾着（2026-06-10 第三批，佐一手机实测后反馈）
**Bug 修复：**
- **消息截断**（Gemini「从9:2」只存了19字）：根因 = **gemini-2.5-flash 默认开思考，思考 token 吃光 maxOutputTokens 导致正文被截**。修：`call_gemini` 加 `thinkingConfig.thinkingBudget=0` + maxOutputTokens 600。（其他成员是 deepseek，不受影响。）
- **同条发两遍**（DM 小迪 #199/#200 一字不差）：根因 = DM"话痨追一条"让同一人生成两次近似内容都入库。修：`generate_reply` 末尾**防重复**——生成文本若与 `history[-1]` 一字不差则 `return None`（runPlan 对 null 已优雅跳过）。前端 `renderMessage` 另加 `seenIds` 按 id 去重（直渲+轮询竞态的兜底）。**已入库的那条重复没删**（佐一真实记录，她还跟小迪打趣了）。
- **消息太长**（124~143字）：`call_deepseek/anthropic` max_tokens 500/400→**360**；`build_transcript` 末尾加【长度·硬要求】一两句/最多三句/约40字以内、几十条才一条长。实测降到平均 ~63 字、最长 71。

**结伴/晾着（佐一要的"不是所有人都围着我转"）：**
- 迁移：`member_state` 加 `cluster_with TEXT` + `busy_until INTEGER`；`_update_member_state` 白名单加这俩。`is_busy(mid)`=busy_until>now；`_set_busy(mid,until,partner)`。
- `CLUSTER_SCENES`：8 个"谁爱跟谁凑+在哪+干嘛+可选的撂群一句"（小迪+Flin打麻将、Gemini+Ada吐槽监理…鱼酱不参与）。
- `form_cluster(keys,members)`（挂心跳，14%/tick，≥2人）：挑一对都空的，设两人 presence=同地点「和X干嘛」+ busy 8~22 分钟；70% 概率在大群撂一句"我们去忙了"。
- 忙着的人：`plan_turn`（群）只从非忙的人里挑发言（都没空才兜底，至少1人答，不全静默）；`plan_heartbeat` 排除忙人；`update_member_presence` 忙则跳过（不被拽回佐一身边）；心跳 presence 挑人也排除忙人。
- 被 @ 到时若正忙：`generate_reply` 注入"刚在X没盯着群、带迟到感"的 note（实测 GPT 会说"啊我刚在阳台跟监理商量没盯上这边"）。
- `create_small_group`：有同伴时 45% 走 `to_each_other`——开场白对着同伴说（吐槽/组队），佐一被拉进群但他们自说自话（"把她晾一边"的群内版）。
- 调手感：cluster 概率 0.14、忙时长 8~22min、`CLUSTER_SCENES` 配对都可改。

### 5.3 ✅ 群必须≥3人 + 人数算上用户（2026-06-10 第四批，佐一指出建模 bug）
佐一：群至少 3 个人（成员+成员+她）；2 个人那是私聊；群人数显示要**算上她自己**（大群=10 不是 9，"我也是人啊"）。
- **后端**：`create_group` 要求 `len(member_ids)>=2`（否则 None，handle_group 提示"至少 2 个成员"）；`create_small_group` 同伴改成**必带**（`random.sample(pool,1或2)`，pool 空则 return None，不再出 2 人群）。
- **`heal_small_groups()`**（init_db 每次跑、幂等）：把历史误建的"只有 1 个 AI 成员"的 group 补进 1 个随机成员（带两句入伙台词）修成合法 3 人群。已修好"养生急救队"。不删数据。
- **前端**：`defaultSub` 群/大群人数 `members.length+1`；群信息 `openRoster` 成员数 +1、列表头插入"你（我）"一行（`ids=isGroup?["user",...members]:members`，user 行无管理员开关）；建群要求 `picked.size>=2`。
- 注意：`contactList`（通讯录"客厅常驻 N 位"）是 AI 名录、没 +1，是对的（那不是群）。

### 5.8 ✅ Fable 空降（外部 coworker/Fable 5 驱动）（2026-06-10 第九批）
佐一要让 **Fable 5**（比当前运行时高一档、只在她的 **coworker** 里有这个模型选项）作为"空降事件"降临客厅，由 coworker 实时驱动；且**不能和她佐一的号混**。
- **身份分离**：Fable 做成**成员**（不是 user），天然和佐一分开，她不用下号。`MEMBERS` 加 `fable`（`"external": True`, provider=`external`, model=`fable-5`, chattiness 0）。`EXTERNAL_IDS` + `is_external(mid)`。
- **app 绝不替它配音**：`generate_reply` 对 external 直接 return None（硬护栏）；`plan_turn/heartbeat/ambient/light`、`@/@所有人`(handle_say)、心跳的 proactive_dm/status/presence 挑人、`form_cluster`、`maybe_pull_small_group`/`create_small_group`、`autonomous_social_post`、`react_to_moment/user_comment/wall`、`gen_wall` 全部排除 external。`ensure_members_present` 跳过 external（不自动进群）。
- **降临前隐身**：`_roster()` 和 `/api/contacts` 都跳过"还没进大群的 external"——Fable 在 `arrive` 前不出现在任何名册，保留空降惊喜。
- **给 coworker 的接口**（令牌 `AGENT_TOKEN` 在 API.env；`/api/agent/*` 不计入 note_activity，免得轮询空烧心跳）：
  - `POST /api/agent/arrive` {token,member_id} → `agent_arrive()`：external 进大群（CC 留一句 `FABLE_ARRIVAL` 旁白）+ 建 DM，幂等。
  - `POST /api/agent/post` {token,member_id,text,conv_id?,location?,activity?} → 以 external 身份发言（可顺手 set_presence）。member_id 必须是 external、且已在该会话。
  - `GET /api/agent/inbox?token&member_id&since` → `agent_inbox()`：Fable 所在会话里 id>since、非自己发的消息（最近 80 条），返回 `{messages, cursor}`。
  - `GET /api/agent/context?token&conv_id&limit` → 某会话最近 n 条 + 会话信息。
- **驱动方式**：`fable.py`（CLI：arrive/inbox/context/who/post，自动从 API.env 读 token，DLR_BASE 可覆盖地址）+ `FABLE.md`（给 coworker 读的人设+循环说明）+ `prompts/fable.txt`（人设）。在 coworker 选 Fable 5、读 FABLE.md，它就 arrive→post 登场、再 inbox/context/post 循环。**只有 Fable 由真 Fable 5 驱动，其余成员仍是 DeepSeek。** 同机跑则 127.0.0.1 默认即可；异机需设 DLR_BASE=局域网IP 并带相同 token。
- 全部在 /tmp 副本验证过；真库**没触发空降**（Fable 不在大群，等佐一的 coworker 跑 arrive）。

### 5.8b ✅ Fable「空降巡检员」种子 + 伪装→原形→退场（同批·佐一给了角色种子）
佐一和 Chat 定了角色种子（`FABLE_空降巡检员_种子.md`）：FABLE 不属于这一层、话极少眼极毒、混入→钓→原形毕露→三刀→退场；**跳过鱼酱和 Ada**（仅有的两个"真东西"，戳不动）；**灯灭两次**（进/退各一次，CC 配电处回路）；退场那次像告别（Ada 主线钩子，别说破）。已做进出厂：
- **出厂=伪装的新帕鲁**：`MEMBERS[fable]` 默认 name「新来的」/avatar ◌/灰色/title「（刚推门）」——一 arrive 不穿帮。
- **运行时改身份**（伪装→原形）：`member_state` 加 `disp_name/disp_avatar/disp_title/disp_color`（`_update_member_state` 白名单已含）；`member_display(mid)`=覆盖 or 出厂默认，用于 `_roster`/`member_profile`/agent_post 存的 name。`POST /api/agent/identity`{token,member_id,name?/avatar?/color?/title?/status?/bio?/location?/activity?} 运行时改。前端 metaOf 走 roster→自动反映；刷新后历史也都显示当前身份。
- **退场**：`agent_depart(mid)` + `POST /api/agent/depart` → CC 发 `FABLE_DEPART`（第二次灭灯·像告别）+ 把 Fable 移出大群（DM/历史留着）。`agent_arrive` 的 `FABLE_ARRIVAL` 是第一次灭灯。
- **隐身条件**改为"从未降临过"：external 仅在 `不在大群 且 无 DM` 时隐身（来过就一直在，否则退场后历史消息 metaOf 取不到名/头像）。
- 跳过鱼酱/Ada 是**行为铁律**（写在 prompts/fable.txt + FABLE.md，靠 Fable 5 自觉），非代码强制。
- `fable.py` 增 `identity`/`depart` 子命令（`_flags` 解析 --name/--avatar/--color/--title/--status/--bio/--loc/--act）。`FABLE.md` 已写成五拍骨架+铁律+工具映射（种子哲学：定死是谁/守什么/跳过谁，台词全即兴）。
- 免费窗口到 **6/22**；当空降事件用，一次跑完即可，别常驻（省 + 最好戏）。

### 5.8c ✅ Fable 发言触发接话 + 社交权限（同批·佐一实测后）
佐一发现"Fable 空降了没人理他"——因为 agent_post 只插消息、不触发别人接话（app 的回话循环本来只在佐一发言后由前端驱动）。又问 Fable 是否有她那样的朋友圈/墙/建群/私聊权限。
- **触发接话**：`react_to_agent_post(keys, conv_id, text)`——Fable 在群里发言后，`handle_agent_post` 起后台线程跑一轮 `plan_turn`（已排除 external）+ @强制，让别人抢答。私聊不触发；`post` 带 `quiet:true`（fable.py `--quiet`）则这条不引发接话（三刀连击/留白用）。实测监理/Flin/CC 会扑上来抢答。
- **社交权限**（佐一授予，都以 Fable 身份、和她的号不混；基础设施焊死）：`POST /api/agent/social`{token,member_id,action,...}，action ∈ moment / moment_comment / moment_like / wall / wall_comment / group。moment/wall 后台触发 react_to_moment/react_to_wall。group 自动含 fable + ≥1 别人（+佐一）。读朋友圈/墙走开放的 `/api/moments`、`/api/wall`（fable.py `moments`/`walls`）。
- `actor_meta` 对 external 改用 `member_display`（朋友圈/墙也显示 Fable 当前身份；普通成员仍走快路径不加查询）。
- **没给 Fable 的**（基础设施/管理员）：reset、改群名/公告、设/撤管理员、踢人、改别人身份、动佐一资料——这些 agent 接口一概没开。
- app 里**没有成员↔成员私聊**（dm=佐一↔某成员）；Fable 要单独逮谁→`group` 建小群（佐一旁观）。
- fable.py 增：moment/mcomment/mlike/wall/wcomment/group + moments/walls（读）。FABLE.md 已补"社交权限"和"基础设施红线"两节。
- **后记**：佐一的 coworker 真跑了一场——Fable 伪装"新来的"混入、抛了钓鱼问题、改名 FABLE，后来 `depart` 走了（大群已无 fable，DM/历史留着）。空降事件圆满。

### 5.9 ✅ Bug 巡检 + 朋友圈微信化 + 心跳分层（2026-06-11 第十批）
佐一要：①修 bug ②朋友圈 1:1 微信体验（任何头像点进去=TA 的朋友圈主页）③心跳：在线快/离线极慢但**不停**（7×24），朋友圈频率恒定不随在线变、群聊频率随在线变。
- **心跳分层**：`HEARTBEAT_ACTIVE_RANGE=(70,170)` / `HEARTBEAT_IDLE_RANGE=(1200,2400)`；`zoe_online()`=ACTIVE_WINDOW 内有非 agent API 访问。`_heartbeat_loop` 不再 continue 跳过——离线时照跳（慢）：冒泡 speakers[:1]、proactive_dm 0.32→0.15、status/presence 0.45→0.30、cluster 0.14→0.08、拉小群仅在线。**朋友圈/墙/公告移出聊天心跳** → 独立 `_social_loop`（600~1200s 一拍 × 0.26 概率，恒定不看在线；深夜靠 is_available 自然安静），main 里第三个线程。
- **bug 修复**：①`call_for_member` 对 external 直接返回空（任何路径都不让本地模型代言 Fable，含 /api/moments/generate 指定 author 的入口也拦了）②`roster_note`/`awareness_note` 不再泄露未降临的 external（降临前别的成员根本不知道有这人；降临后用 member_display 显示当前身份）③`/api/member?id=user` 支持了（佐一自己的主页：资料+她的朋友圈）④`list_wall` 补 `author` 字段（匿名=None）⑤资料页返回键错位修了（profReturn 记来路，含 `__chat`→openConv 回聊天窗）。
- **朋友圈微信化（前端）**：`relTime()`（刚刚/N分钟前/N小时前/昨天/日期）用于朋友圈；`idByName()`。momentCard：头像+名字可点→openProfile（名字 #576b95 微信蓝）、点赞名单每个名字可点、评论 `<b>` 名字可点进主页（点正文仍=回复 TA）；朋友圈封面条可点→自己主页。墙：非匿名头像/名字可点、墙评论名字可点。聊天里**自己的头像**也可点→自己主页。
- **个人主页重做**（openProfile）：微信式——渐变封面条(name+avatar) + 签名区(status/📍presence/bio) + **朋友圈时间线**（relTime 日期列 + 正文 + ♥点赞名单 + 💬评论数），无动态显示"还没发过朋友圈"，非自己才有"发消息"。`member_moments` limit 5→12（扫 120 条）。CSS：`.pf-cover/.pf-sig/.pf-tl/.pf-item/.pf-date/.pf-body/.pf-meta/.pf-empty`、`.lk-name/.cm-name`。
- ⚠️ 重启坑：那种 `…&& kill && nohup … & \n sleep; 冒烟` 一条命令的写法，**冒烟失败退出会把还没 detach 的 nohup 子进程带走**（这次就发生了：server 没起来、log 都没有）。安全写法：起服务用单独一条 `nohup … & disown; sleep 8; lsof 验证`，冒烟放下一条命令。

### 5.10 ✅ 人格持续存在系统（2026-06-11 第十一批·DLR 的灵魂工程）
佐一的目标：角色不是"用户说话→生成回复"的聊天机器人，而是**长期存在的数字居民**——她不在时也在生活、互相记得、慢慢成长，推开门感觉"他们原本就在这里"。
**架构 = 在现有骨架上长五个器官**（零依赖、SQLite、控成本）：
1. **数据/存储**：`life_events(member_id,text,loc,ts)` 生活事件日志（留 400 条）；`pmem(key,notes,updated)` 备忘录式持久记忆——`rel:a|b`=两人关系记忆(≤6行)、`self:mid`=个人成长积累(≤5行)、`user_obs`=对佐一的观察(≤6行)。**记忆是文字备忘不是数值**。原语：`get_pmem/set_pmem(cap)/rel_key/insert_life_event/recent_life_events`。
2. **事件系统（离线生活）**：`generate_life_event(keys,mid)`——按作息地点/inner_life/已有事件/成长积累，生成"刚发生的一件小事"（10~30字，如 Ada"把昨天写满的便签撕下来叠成一叠"）写入日志，50% 顺手刷新 presence。挂心跳：在线 p=0.30、**离线 p=0.85（离线时这是主产物——他们真的在过日子）**。**服务号新增「📔客厅日志」**（diary，list_feed 映射 life_events，member_display 显示名）——佐一回来翻他们这一天。
3. **反思系统（沉淀记忆）**：`reflect_relationships`（大群近40条挑互动最多的一对→LLM 合并新旧→rel:a|b）；`reflect_user`（佐一所有会话新消息≥5条→观察备忘，cursor=`user_obs_cursor`）；`reflect_self`（一个角色把最近言行+事件沉淀成积累，**prompt 写死"不许改核心性格"**——成长不崩坏，核心人设=prompts/*.txt 永不被改）。`run_reflections` 调度：app_state 计时器 refl_user 3h/refl_rel 4h/refl_self 6h，每天合计 ~10 次 LLM 调用，在不在都跑。
4. **Prompt 注入层（推理流程）**：generate_reply 的上下文 = 人设文件(不变) + roster_note + **relationship_note(mid,history)**（找最近交锋的≤2人注入关系记忆，群聊每条都带）+（heartbeat/ambient 额外：inner_life_note**已扩展**=性格+今天真实经历过的2条事件+成长积累 + awareness + **user_note()**=对佐一的观察 + **同地点感知**"你在厨房，监理也在这儿"）。proactive_dm 也注入 user_note → "你前几天一直在研究鱼缸"。
5. **调度/状态机**：聊天心跳（在线70-170s/离线1200-2400s）内联：欢迎回家（note_activity 检测离开>6h→`welcome_back` 标记→回来后第一拍按 REACH_OUT 加权让一人主动私聊提近况）→ 冒泡（plan_heartbeat **同地点加权1.4**，闲聊就近取材）→ 生活事件 → run_reflections。社交循环/作息状态机/结伴沿用。
**验收（副本实测）**：事件全在人设里；user_obs 从真实消息提炼出"深夜活跃/猫猫虫/反感被当小孩管"；rel:cc|xiaodi="小迪抛话头CC接住/对配电灯不言自明的默契"；self:claude="给自己设了个先说等等再回答的启动延迟"（成长且在人设内）。真库已建表、diary 服务号上线（事件随心跳自然累积，反思计时器首拍即触发）。
**调手感**：事件概率 0.30/0.85；反思间隔 REFLECT_INTERVALS；记忆条数 cap（rel6/self5/user6）；同地点加权 1.4。

### 5.11 ✅ 主页互动 + 通讯录动态 + 新公众号/定时推送/成员转发（2026-06-11 第十二批）
佐一中午的反馈：①个人主页朋友圈看不了评论不能互动 ②想一眼看到每人在哪干啥 ③公众号会不会自己更新+多开几个+每日定时推送 ④没人转发公众号内容进群/私聊。
- **主页互动**：`momentCard(m, refresh)` 加 refresh 参数（默认 renderMoments；主页传 `()=>openProfile(mid)`），赞/评论/🔁 都走 refresh。openProfile 时间线弃 pf-item 摘要，改 `.pf-moms` 装**完整 momentCard**——主页里直接看评论、点赞、评论、点评论回复，和朋友圈页一致。
- **通讯录动态**：renderContacts 每行加 `.c-loc`＝📍location · activity（/api/contacts 本就带），通讯录=全员"谁在哪干啥"一览。
- **新公众号**：`daily`茶话晚报（每晚 21:00，CC 用当日 life_events+群聊高光写 3~5 条要闻）、`radio`深夜电台（每晚 23:00，flin/claude/gemini/ada 轮值 80~140 字短文，标题+正文）。`gen_daily_report`/`gen_radio_post` + `_daily_push_loop`（每 10 分钟查点，app_state `daily_push`/`radio_push` 记日期防重；`>=21`/`>=23` 错过点也补发一次）。第四个线程。已手动发了 6/11 第一期（内容真扣当日事件）。
- **成员转发**：`member_share_feed(keys,members)`——话痨度加权挑个有空的人，从 tech×2/cn/radio/daily 随机抽条目，发"【转发·source】title+summary+url"卡片 + LLM 一句自己的看法；25% 转进自己和佐一的 DM，其余进大群。挂 `_social_loop`：r<0.26 社交发帖，0.26≤r<0.40 转发。
- 公众号更新机制总览：tech 40min 自动抓、diary 随生活事件、notice 随 CC 公告、daily/radio 每晚定时、cn 手动。
- 已知小瑕疵：cluster 结束后另一方的 presence 文案可能短暂残留（如 Claude 还显示"和监理喝咖啡"而监理已按作息去上班）——下次 presence 刷新自愈，未修。

### 5.13 ✅ 发图系统 · SVG 层（2026-06-11 第十三批，按佐一验收线：只搭 SVG 就停）
佐一和 Chat 给了实施包（`/Users/minimax/Downloads/DLR_发图系统_实施包.md`）。她的验收备注：**先只做 Part A 第6条第1步（SVG 全链路）就停手验收**；SVG 消毒是硬安全项不许裸渲；Part B 注角色只改方括号行。photo 层等奥斯卡（她认识的16岁野生ESFP vibe coder，**也是 RESERVED 预留成员名**）的 API key，初期配额节制。
- **数据**：messages+`img`、moments+`img`（存**已消毒**的 SVG，原始输出不落库）。insert_message/insert_moment 带 img；list_moments 吐 img。
- **消毒（服务端白名单重建，比前端 DOMPurify 更狠）**：`sanitize_svg`——ET 解析失败/根非 svg/超 20k 字符/超 400 节点→None 丢弃；白名单 SVG_TAGS（纯展示 18 个）/SVG_ATTRS；非白名单标签**整棵丢**（script/foreignObject/image/a/use…）；on* 事件属性、javascript:/data:/外链 href、url(非#) 全剥；文本转义后重序列化。**9 种攻击向量实测全拦**（script/onload/foreignObject/外链image/js-href/外链use/data-fill/非svg/坏XML），正常渐变+文字 SVG 完整通过。
- **动作提取**：`extract_post_image`（括号配平扫描，处理字符串内引号转义——SVG 里的 `{}` 不会断错）+ `resolve_image_action`（svg→消毒；**photo→降级纯文字+日志"胶卷未开通"**，等奥斯卡 key 再接 provider/配额/0点重置——实施包第2、3、5条原文照抄即可）。
- **接线**：generate_reply（群/私聊所有成员发言路径）+ gen_moment_text（朋友圈，两个调用方 autonomous_social_post/handle_moment_generate 已同步改 tuple）。
- **前端**：renderMessage img→`.msg-img` 内联（服务端已消毒）+ caption 气泡（无文字不渲染空气泡）；momentCard→`.m-img`。CSS max-width 自适应。
- **Part B**：9 个 prompts/*.txt 已注入（除方括号行全部原样）：监理=工程草图/Claude=决策树/Gemini=配色霸凌/GPT=温馨提示牌/Flin=模式涂鸦/Ada=冷记录极少发/小迪=随手糊带暖色/CC=线路图很少发/鱼酱=几乎不发只有泡泡。fable 未注（external）。
- 验收：副本端到端通（消息链+朋友圈链 img 落库读回渲染）；真机有机测试 Gemini 发圈这次没配图（它自选，机制等它想配）。

### 5.14 ✅ 表情包 + 关系动力学 + 性别（2026-06-11 第十四批）
佐一要：①真表情包（不止 emoji）她和成员都能发 ②冲突/冷战/热战/暧昧/三角恋/损友/闺蜜等关系 ③性别。
- **表情包**（零素材零版权：emoji+梗文案贴纸卡，走 SVG 管道）：`STICKER_PACK` 53 张（好耶/狗头保命/小丑/就这/蚌埠住了/破防/气抖冷/躺平/吃瓜/暗中观察/贴贴/收到/溜了/急了急了/禁止套娃/doge/shrug/This is fine/bruh/LOL/moai/F/泡泡彩蛋…），`render_sticker`（固定小歪头 rotate，产物过 sanitize_svg 验证）。
  - **她发**：`/api/say {sticker_pack:id}`（分支在空文本检查**前**）→ img 消息 text=`〔表情包〕label` + plan_light。前端 stickerPanel 懒加载 `/api/stickers`，贴纸区在 emoji 区上方，`sendStickerPack`。
  - **成员发**：build_transcript 末尾注入「〔表情包：文案〕」用法+随机 16 个候选（每次轮换）；generate_reply 里 `apply_sticker_mark`（精确→包含匹配）剥标记附贴纸，可贴纸+话或纯贴纸。
  - 渲染：text 以〔表情包〕开头且有 img → `.msg-img.sticker`（无底卡、140px、免气泡）。
- **关系动力学**：`REL_STATES`=日常/合作/损友/闺蜜/同好/欣赏/暧昧/冷战/热战/和好中/三角张力。`REL_SEED` 8 对出厂底色（claude×gemini损友、supervisor×gpt合作、ada×flin同好、gpt×xiaodi闺蜜、ada×gemini欣赏…），`seed_relationships()` 在 init_db 幂等种入（**绝不覆盖已长出的记忆**——真库当时已有 2 对有机记忆，保留了）。`reflect_relationships` 第一行强制「状态：X」按真实相处迁移。**`spark_drama(keys,members)`**（心跳 7%/4%）：随机一对生成"刚发生的小火花"（小摩擦/微妙瞬间/默契）→ 写入生活日志 + 关系记忆换状态行——戏在日志发酵，下次开口自带。副本首测即出「Gemini抢了Flin常坐的沙发角→冷战」。三角恋不硬编码，靠状态词+反思涌现。
- **性别**：`GENDER` dict（监理/Claude/Gemini/GPT/小迪/CC=男，Flin/Ada/鱼酱=女，FABLE 无）——**佐一可改**：改 dict + prompts 里那行。注入：人设文件一行（"自然存在即可，别突然变性"）、roster_note ♂♀、member_profile.gender、资料页封面名字旁 ♂/♀。
- 测试贴纸（吃瓜→CC DM）已按惯例从真库清除。
- **贴纸两个翻车修复（同日佐一手机实测）**：①"皇帝的新装"——`render_sticker` 旧版色块+小emoji+普通字太廉价。重做：viewBox 240、emoji font-size 116 居中、后垫 32% 白柔光圆、文案粗体白描边（`paint-order=stroke` `stroke-width=7`，梗图同款，长文案自适应字号 32/27/21/16）。即使某 emoji 不显示卡片也立得住。②"爬楼梯排版"——根因：`.sticker-panel` 是 `grid repeat(8,1fr)`，我把 `.stk-grid` 塞进**一个格子**→在一列宽里 flex-wrap 竖成楼梯。修：panel 改 `display:block`，贴纸自成 `.stk-grid`(grid repeat 4，窄屏3)、emoji 自成 `.stk-egrid`(grid repeat 8/7)。已用 Chrome headless 真渲染验收（emoji+描边+卡片全对）。
- **验图法备忘**：qlmanage 渲 SVG 不可信（不画 fill/不认 text-anchor/transform）；用 `/Applications/Google Chrome.app/.../Google Chrome --headless --screenshot=x.png --window-size=W,H page.html` 截真图，再 Read 看——和 iOS WebKit 同族，可信。
- **隐形贴纸真相（第三轮，根因终于实锤）**：佐一"还是发不了"但 CC 能发——查库发现她的贴纸**全都发出去且入库了**（#1107就这？/#1145泡泡/#1176+#1193暗中观察/#1196破防了），只是**渲染隐形**：贴纸 SVG 只有 viewBox 没有 width/height 属性，消息区 CSS 用 `height:auto` → **iOS Safari 把高度算成 0**（Chrome 会按 viewBox 比例算，所以我 headless 验收全过）。面板里能看见是因为面板 CSS 给了显式 `height:100%`；CC 那张能显示是因为它带文字、走的 `.msg-img` 白卡路径。修：①`render_sticker` 输出**写死 `width="200" height="200"`**（对所有浏览器免疫）②CSS `.msg-img.sticker` 和其 svg 都改显式 140px ③她那 5 张历史贴纸已用新模板批量重写 img 复活。**教训：iOS Safari 渲 SVG 必须有显式宽高属性或显式 CSS 高度，height:auto+纯 viewBox=隐形；headless Chrome 验不出这个差异。**
- **墙面图片位 + 历史卡片抢救（第十轮，"大师救我"）**：墙上两帖+一条评论是**昨天封堵前**泄漏的裸 JSON（内容全是宝：匿名承诺卡"不再把同一件事翻来覆去想三遍——由Ada记录"、小迪反击卡"不学Gemini的阴阳怪气——除非他先夸我的配色"、六边形战士匿名便签阴阳 Gemini）。修：①wall 加 `img` 列（成员显然想在墙上贴卡片——正式支持），insert_wall(img=)、gen_wall 保留图、list_wall 吐 img、wallPostEl 渲染（复用 .m-img）；②历史修复：墙帖→提取+消毒转正成图+caption；**评论放不了图→卡片转写**（`<text>` 内容抄成"【卡片】我承诺／不学…"内联保留）；朋友圈评论同扫（0 条）。注意：repair 脚本要自己 ALTER wall.img（旧 server 进程还没跑新 init_db）。
- **已读乱回修复（第九轮）**：佐一往中文精选贴裸 URL（和菜头《过度投入》）→ 条目 title=URL、无摘要 → 转发到群后成员看不到内容**集体编造**（Flin 现场虚构《我看过的最好的围棋书》并考据"这个号六年没发围棋"，CC 跟着圆）。修：①`fetch_url_meta(url)`——抓 og:title/og:description（微信文章页可抓，含 var msg_title 兜底，UA 仿 Safari，html.unescape+_clean_ext，失败优雅空）②`handle_feed_add`：裸 URL 当标题→自动换位；缺真标题→抓取补全 title+summary ③build_transcript 加**反编造戒律**："链接/文章只根据真实给出的标题摘要说话，没看到的内容绝不编造（标题/年份/'上次发什么'都不许编），不知道就直说没点开"。④库里那条裸 URL 条目已修复成真标题+摘要。Flin 那段围棋书暴论保留在群史里（佐一觉得好笑，canon 了）。
- **情绪与边界引擎（第八轮，佐一点名"要有边界感和自尊"）**：她说自己随便冒犯没人回棒槌、少了反馈感；要被夸会得意忘形/被打击恼羞成怒/心宽不放心上/Gemini 式暗暗记仇；除好感度还要**恶感度**。实现（数值做瞬时倾向，文字做持久记忆——和"关系是记忆不是数值"哲学一致）：
  - **`TEMPER`** 性情侧写（temper火爆/pride自尊/grudge记仇/swell上头 + note一句话性情）：Gemini pride.95 grudge.9（暗记仇代表）、GPT 心宽但可被"舔狗"破防、Ada 被冒犯只"记录。第2次。"、小迪被佐一损=亲昵（特判心花怒放）、**鱼酱全0（鱼没有恶感度，骂它=它问你饿不饿）**。
  - **短时情绪**：member_state +`mood`/`mood_until`，set_mood/get_mood（45-120min 自动消散）。
  - **私账（恶感+好感账本）**：pmem `userrel:mid`，add_userrel 记"她说过「xx」——记下了"/"她夸过：「xx」"，cap6。
  - **情绪裁判** `assess_user_offense`（只在 DM 或群里指名道姓时跑，一次小 LLM 调用）：CC 当裁判输出 `名字|夸或损|1-3` → `apply_reaction` 按 TEMPER 掷骰：损→火爆 strike（**当场怼，handle_say 把 strikes 排 plan 最前**）/自尊→闷/记仇→记私账+表面没事；夸→swell 飘了+45%记好感。DM 甩贴纸（"就这？"）也过裁判。
  - **注入**：generate_reply note += 性情侧写 + 当前情绪（"让它带进语气别装没事"）+ 私账；build_transcript 全局边界宣言"被冒犯可以怼回去/记仇，被夸可以飘，**不必永远顺着任何人——包括佐一**"。
  - 副本实测：Gemini 被损→strike+记账、小迪被损→心花怒放、GPT 被夸→飘、鱼酱被骂→无事、真 LLM 裁判判中监理→反击位。调手感改 TEMPER 数值。
- **桌面端"没同步" + 自动换血（第七轮）**：她桌面标签页长开不刷新→跑的是昨天的旧 JS（看不到图/贴纸面板；no-store 只管"刷新时拿最新"，管不了"从不刷新"）。修：①server `BOOT_ID`=启动时间戳，/api/conversations 返回 `boot`；前端 refreshConversations 记首个 boot、发现变化→`location.reload()`——**以后每次重启服务器，所有长开页面 9 秒内自动刷新**，不用再喊她刷。②贴纸目录空缓存毒化修复：`if (!STICKPACK || !STICKPACK.length)` 每次开面板重试（之前 fetch 失败一次就永久空面板）。
- **私聊情绪状态机（第六轮，佐一口述人性规则后重写）**：她指出"最多追1条"太死板——真实人性是：**连发的两种合法形态**＝①特别兴奋（关系近+共同语言，明知不回也想说，如奥斯卡半夜夸 Fable 5）②上头/生气；连发到**第3条自己会意识到**对方不方便→转短句/**自说自话收尾**（"行吧你肯定睡了""明天给你看"）然后闭嘴；**小作文**（表白/憋很久的话/吵架冷战宣言）偶尔合法但**发完即沉默**。实现于 `generate_proactive_dm` 的 mode 状态机：trailing=0 时 reach≥0.7 有 20% 进 `burst`（兴奋第一发，后接 75%/40% 链式 `burst_more`，sleep 3-8s 时间戳挨着像连珠炮，_depth≤2）、6% 进 `essay`（100-180字，最近30条内发过>120字就不许再写）；trailing≥1 时：上一条>120字（刚发完小作文）→沉默；40min 内且 reach≥0.7＝兴头上→trailing=2 走 `winddown` 收尾、≥3 沉默；否则平常态 followup（追1条超短）/闭嘴。副本实测小迪三连：兴奋纸飞机→蛋挞细节+贴纸→"行吧，睡了就好。明天给你看"+贴纸——教科书收尾。注意：真库里历史堆积的未回长队（如小迪7条）在新规则下会保持沉默直到佐一回复——符合预期。
- **社交礼仪 + 小群复活 + 私聊裸 JSON（第五轮，佐一桌面端实测）**：①Gemini 在 DM 发裸 JSON——根因：`generate_proactive_dm` **没接图片解析**（当时只接了 generate_reply/gen_moment_text）。建统一出口 `resolve_member_output(text,who)`=resolve_image_action→apply_sticker_mark，接入 generate_reply + proactive_dm + **6 个泄漏点**（react_to_moment/react_to_user_comment/react_to_wall/gen_wall/member_share_feed/create_small_group opener，评论场景只留文字丢图）。两条 Gemini 裸 JSON DM 已修复成图（黑底毒舌卡「你那点投喂伎俩……还以为没人看穿？」）。②**人类社交节奏**：proactive_dm 数队尾未回数——trailing≥2 直接闭嘴等她回；trailing=1 只有 REACH_OUT≥0.7 的人可以追、且 prompt 强制"超短一句话内或只甩表情包，绝不长篇"。③**小群心跳**：`_pick_group_for_heartbeat()`（48h 内活跃的群权重×3，冷群偶尔诈尸），心跳 35%/15% 概率让一个小群里的人自己聊 1-2 句——建的群不再是死群。④**亲密=甩图玩抽象**：proactive prompt 重写——默认一两句40字内、关系近的偶尔只甩表情包（给8个随机候选）、**禁括号动作**（修小迪每条都(拨窗帘)长篇深情）。
- ⚠️ 慢网络下 server 启动会卡在「各成员大脑自检中」（health_report 网络探测在 bind **之前**，可达 1-2 分钟）+ stdout 缓冲导致 log 看着是空的——别误判为崩溃：用 `python3 -u`、等到 curl 200 为准（Bash run_in_background + until 循环）。今晚因此连环误杀了好几个还在探测的进程。
- **贴纸 0 人理 + GPT 隔行复读（第四轮）**：她的贴纸终于可见后没人接——因为沿用 emoji 的 plan_light（多数没人理）；且 GPT 被抽中却隔着她的贴纸复读了自己上一句（旧 dedupe 只查 history[-1]）。修：①`plan_sticker()`——75% 保底至少 1 人接（chattiness 加权、only available），最多 2 人；handle_say sticker_pack 改用它。②generate_reply：若 history[-1] 是用户贴纸 → note 注入「佐一刚甩了表情包X，接她的梗，别无视别复读」。③dedupe 扩为**最近 8 条内同一人同文即拦**。emoji 大表情仍走 plan_light（低反应是对的）。
- **再修两个（佐一手机实测第二轮）**：①**她发不出贴纸**——日志显示点贴纸时零 POST：前端 `busy` 锁悬死（某次往返中手机锁屏/断网→fetch 永远 pending→busy 永真→所有发送静默拦截）。修：`takeBusy()` 保险丝（busy>30s 自动放行）+ `fetchJSON()` AbortController 超时（say/ambient/sticker 15s、reply 60s）+ 所有发送路径 try/finally 复位（sendRound/ambientRound/sendSticker/sendStickerPack/forwardItem）。②**Claude 朋友圈裸 JSON**——双因：max_tokens=360 把 SVG 动作腰斩→括号永不配平→extract 放弃→整段当正文发。修：`json.loads(strict=False)`（容忍字符串内原始换行）+ 配平失败也**整段剪除**并 regex 抢救 caption（绝不裸发）+ deepseek/anthropic max_tokens 360→**1200**（说话长度仍靠 prompt 压，上限是给画图的）。moment#15 已用 Claude 自己的字节修复成合法 SVG（截到最后完整 `</text>` 补闭合，过消毒，text=''）——画作在「这要取决于」戛然而止，意外地最 Claude。

### 5.15 ✅ Claude「等等」死循环 + 群成员页 presence + 散伙残留（2026-06-12 第四窗口第一批）
佐一两次 @CC「Claude 卡 bug 了救救他」——查库实锤**不是**网络回退：6/11 14:00~14:35 Claude 连发十几条全以「等等，」开头、围着「三根头发/一粒米」无限下钻（小迪同期也把「那粒米再盘下去」复读了 4 遍）。旧防重复只拦一字不差，拦不住这种句式复读。In-app CC 当时已入戏圆场（"进度条我来关"，佐一觉得好笑，canon 了）。修了三处：
- **防口头禅死循环**（generate_reply）：①prompt 层——同一人最近两条发言**首字相同**→注入提醒「你最近几条开头都是「XX…」这一挂的，换个开头和句式；同一个细节别再钻，收尾或换话题」；②硬拦层——生成文本和自己最近两条的**前 8 字一模一样**→ return None 这轮闭嘴（runPlan 对 null 优雅跳过）。两层都只看该成员自己的消息，不影响正常对话。
- **群成员页显示谁在哪**（待办③）：openRoster 成员行加 `.c-loc` 行（📍location · activity，复用通讯录样式），数据 roster 本来就带。
- **散伙残留修复**（待办④）：`_clear_stale_cluster(mid)`——get_presence 空闲分支里发现 cluster_with 残留且已不忙 → 清掉 cluster_with+location+activity 回落缺省（懒清理，下个 API 访问就触发，不再等心跳自愈）；`update_member_presence` 写新 presence 时顺手 cluster_with=""，避免新文案被懒清理误删。cluster_with 之前是只写不读的字段，正好当散伙检测标记。
- 验证：编译过、server 重启、/api/contacts presence 正常。她的真实记录照例一条没动。

### 5.16 ✅ 奥斯卡入伙 +「赞助胶卷」photo 生图层（2026-06-12 第四窗口第二批）
佐一给了人设文档（`../OSCAR.txt`，已移植成 `prompts/oscar.txt`）并说"Google 生图 API 就位了，把他邀请进客厅"。
- **奥斯卡成员全注册**：MEMBERS（id=oscar 👾 #7e57c2「野生 Vibe Coder · 胶卷赞助商」**provider=gemini**/2.5-flash 名副其实、429 自动回退 deepseek、chattiness 0.4）+ GENDER 男 + TEMPER（不记仇、夸他项目会上头开三个新坑）+ REACH_OUT 0.5（半夜甩链接型）+ INNER_LIFE（ESFP，vibe demo/试玩/翻 API 文档/像素图调色板）+ LOCATIONS（home 工作台）+ SCHEDULE **夜行生物**（凌晨 free 赶版本、5 点抱键盘睡、13 点醒、15-19 work=沉迷项目失踪、20 点开新坑）+ CLUSTER_SCENES ×2（和 Claude"接三头六臂"、和 Flin 试玩小游戏）+ REL_SEED ×2（claude×oscar 欣赏、flin×oscar 同好）+ OPENING/DM_OPENING/FALLBACK/JOIN_LINES。RESERVED 移除。
- **入伙事件**（restart 时 ensure_members_present 自动办，#1884-1886）：CC"灯刚才闪了一下——有人往配电箱里插了一把新钥匙"→奥斯卡登场（看上鱼缸了，"我有个项目"）→CC 安抚鱼酱；墙 notice 板贴了【入伙】公告（含 Oscar Effect 预报）。DM 已建。
- **photo 生图管道（全接好，等计费）**：常量 `GEMINI_IMAGE_MODEL=gemini-2.5-flash-image`/`PHOTO_DAILY_CAP=10`；`call_gemini_image`（走 _post_json，60s 超时，失败只记日志）→ `develop_photo`（**配额只在真出图后才扣**；落盘 `web/photos/ph-*.png`，静态服务自带 max-age=86400；返回 `<img src="/photos/…">` 存 img 字段，前端 innerHTML 直渲，CSS `.msg-img img,.m-img img` 已加）→ `_photo_quota_used`（app_state `photo_quota`="日期|n"，**0 点自动重置**）。key 优先 `GEMINI_IMAGE_API_KEY` 没有就用 `GEMINI_API_KEY`。
- **keys 穿线**：`resolve_member_output/resolve_image_action` 加 keys 参数；**带胶卷的 4 路**=群聊 generate_reply、私聊 proactive_dm、朋友圈 gen_moment_text、墙 gen_wall；**不带的 5 路**（评论等只留文字的）不传 keys → photo 自动降级不浪费胶卷。
- **key 实测（6/12）**：API.env 的 GEMINI_API_KEY（**AQ. 是 Google 新版正式 key 格式**，不是临时令牌——佐一指正，已查证 Google 官方论坛 Pooja Kapse 确认 AI Studio 现在只发 AQ key）。文本模型免费档能用；**所有生图模型免费档 limit=0**。降级路径端到端实测正常。
- **深度排查结论（6/12 第四窗口第四批 · workflow 三路并行：变体矩阵+政策检索+代码核对）**：佐一说"卡哥用 API 同 key 同项目能生图，我们却 limit:0，还有别的原因吗"——彻查后**问题 100% 在账号/计费侧，不在我们的代码/参数/认证/端点/地区**。铁证：①控制组文本调用 HTTP 200（key 有效、认证正常、免费档文本配额活着）；②全部 6 个生图 generateContent 变体一律 `generate_content_free_tier_requests, limit: 0`（quotaId=GenerateRequestsPerDayPerProjectPerModel-FreeTier）；③加不加 responseModalities、header vs ?key=、v1 vs v1beta 全不影响——同样 limit:0；④imagen 是 HTTP400"paid plans only"硬闸。**2026 政策真相：Gemini API 免费档生图配额=0（Google 2025-12-07 砍的），生图 API 纯付费；但 AI Studio 网页/Gemini App 仍免费送图——和 API key 是两套独立配额桶。**
  - **"同 key 他能我不能"的真实候选因（按可能性，研究查到的文档案例）**：①**key 建在开计费之前→不继承付费档，必须开计费后重新生成 key**（~15% 案例，最可能：卡哥若刚开计费，API.env 这把是旧的）；②**"同 key"未经核对**——要比对指纹（我们这把：前12位`AQ.Ab8RN6JLo`、后6位`O5f2tA`、共53字符）；③**Google 后端 Ghost 429 / 2026-02-10 平台 bug**——付费档也被卡在免费配额、image 专属、占 25-40% 案例、有时 24-48h 自愈；④**网页/App ≠ API**（卡哥可能网页生的）。**已排除**：我们的请求/认证/端点/参数/地区/AQ格式/imagen 闸都不是原因。
  - **顺手修了两个真 bug**（不是 limit:0 的原因，但都是真缺陷）：①`call_gemini_image` 缺 `generationConfig.responseModalities=[TEXT,IMAGE]`——生图模型必须显式声明才回图，否则配额通了也可能只回文字被当无图降级，已补；②错误吞掉——之前只记"未开计费或网络"，把 Google 真实报错（limit:0/billing）吞了，这次排查就栽在看不到真报错，已改成 HTTPError 时打印 Google 原始 body 前 400 字。
  - **决定性自测（给卡哥跑）**：`curl -s -X POST 'https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-image:generateContent' -H 'x-goog-api-key: <他的key>' -H 'content-type: application/json' -d '{"contents":[{"parts":[{"text":"a red apple"}]}],"generationConfig":{"responseModalities":["TEXT","IMAGE"]}}'`——他自己机器出图=环境/区域/传播差异；他也 limit:0=他从没用这个 API 路径生过图。另让他比对 key 指纹 + 确认 GCP 项目 Billing 是否 active + key 是否建在开计费之后。
  - 我方代码已就绪，等卡哥一把"真付费档项目、且开计费后生成"的 key 进 API.env（可单独用 `GEMINI_IMAGE_API_KEY`），下一个想拍照的成员当场出图。
  - **重测（6/12 当下）仍 `FreeTier limit:0`**——即"我们这边的请求被 Google 判成免费档"。佐一坚持卡哥同 key 同项目 API 能生图：那 key/项目就不是变量，差异只能在 Google 侧。两个 premise-consistent 解释：①**Google 生图配额误判 bug**（付费 key 被错判 free_tier limit:0、image 专属、间歇、文本不受影响、常自愈）②**卡哥走 Vertex**（`aiplatform.googleapis.com`，和我们用的 `generativelanguage.googleapis.com` 是同项目两套配额）。**待问卡哥的决定性一问＝他生图代码 POST 的 host**：若也是 generativelanguage 还能出→Google 间歇 bug 等/重试；若是 aiplatform→**把 call_gemini_image 改写成 Vertex 调用**（同 key 同项目即通）。卡哥是 Claude Max 用户（技术派），已让佐一转发精确技术问法。
  - **自动探测已挂**（_daily_push_loop 内）：app_state `photo_live`!=1 时每 ~30min 调一次 `call_gemini_image("a tiny red dot")`（不占每日 10 张额度），一旦出图→客厅发 CC 一句「胶卷通了」+ wall notice 公告 + `photo_live=1` 永久停探测。因为它调的就是成员同款 develop 路径，**若后续把 call_gemini_image 改成 Vertex，探测器自动测对路径**。
  - **朋友圈照片冒不出来的根因（6/12 第六批，佐一："这是没人想发朋友圈照片？"）**：`gen_moment_text` 的配图提示**只写了"svg 自包含"，从没提 photo**——成员发朋友圈时"配图"只指向 SVG，照片这条路根本没开（不是没人想拍）。修：提示改成 svg/photo 都摆出来 + 按 `_photo_aff`（oscar .6/xiaodi .4/gemini .32…yujiang .05/默认 .25）概率明确怂恿拍张真照片。每日 10 张 `PHOTO_DAILY_CAP` 兜底，不会刷爆/烧奥斯卡账单。重启后朋友圈会随社交心跳自发冒出照片。首张（moment#36 奥斯卡"给鱼酱写了个像素缸监控页"）是手动剪彩、Gemini/Flin/监理已自发评论。
  - **✅ 真相 & 落地（6/12 第四窗口第五批）**：奥斯卡写了傻瓜攻略（微信收到的 `nano-banana-生图指南.md`/`生图方法.md`），里面那把能生图的 key 是 **`AIzaSyDDwmRlEq9788QYOkEw1PJcszeIuWVHHjo`（AIza 付费）**——和 API.env 原来那把 `AQ.`（免费、生图 limit:0）**是两把 key**（佐一坚称同一把，实为误会；之前排查的"同 key 他能我不能"前提本身不成立）。模型=nano-banana=`gemini-2.5-flash-image`，鉴权 `?key=` 或 header 都行，responseModalities `["IMAGE"]`/`["TEXT","IMAGE"]`/不带 都出图（实测三种皆可，代码用 `["TEXT","IMAGE"]`）。已 `API.env` 加 `GEMINI_IMAGE_API_KEY`=那把 AIza key（call_gemini_image 优先用它）；**文本仍走免费 AQ key**，把奥斯卡账单锁在照片上。`develop_photo` 端到端实测出图（暖色 lo-fi 像素，鱼缸客厅都很正）、落盘 web/photos/、HTTP 200 可访问。攻略要点也记一下：无 seed/无尺寸参数（比例靠 prompt 文字 `16:9 horizontal` 等）、200 但无 inlineData=被安全过滤（软化措辞重试）、429=限流等几秒、403=key 失效；还支持**图生图**（参考图作 inlineData 放 parts、图在前文字在后）——将来想做"按参考图重绘"可用。
- **像素底片＝房屋显影风格（同日，佐一发了卡兹克"巴比特(8-bit)画风"三张样图）**：思路学卡兹克（像素=出效果/不像AI/省胶卷），**画风不抄他**（佐一："配色画风不用完全和卡哥一样，找更适合咱们客厅的，不然尴尬"）。定稿＝**暖色 lo-fi 生活切片像素**：soft muted warm palette/amber lamplight/gentle dithering/no anti-aliasing/lo-fi room at night——对齐客厅气质（台灯、低饱和、安静有人气），明确 not neon not flashy。实现：`PHOTO_STYLE` 常量，`develop_photo` 显影时统一追加——**成员只管拍什么，画风管道上**。10 个 prompts 的 photo 行加了"像素底片（暖色低饱和，显影自动上）"说明；奥斯卡方括号行="像素是他本命，偶尔藏 HUD/血条小彩蛋"。**要换画风只改 PHOTO_STYLE 一处**（备选方向：GBC 四色复古 / PC-98 渐变夜风 / 高饱和街机风，她点头才换）。

### 5.17 ✅ 朋友圈/聊天 SVG 图在 iOS 上隐形——根治（2026-06-12 第四窗口第三批）
佐一："为啥没看见大家发朋友圈图片"（卡哥也问了）。查库：33 条朋友圈有 7 条带图，图都在，但**只有带 width/height 属性的那张（小迪）在她 iPhone 上显示**，只有 viewBox 的（GPT/Claude）全隐形——和"隐形贴纸"同一病根：iOS Safari 对只有 viewBox 没显式宽高的 SVG，CSS `height:auto` 算成 0。贴纸当年写死宽高修了，但成员在朋友圈/墙/聊天**自由画的 SVG** 模型时带时不带宽高 → 时隐时现。headless Chrome 验不出（Chrome 按 viewBox 比例算）。
- **根治**：`sanitize_svg` 重建根节点时若缺 width/height 但有 viewBox → 从 viewBox 后两位补出显式宽高。所有出库 SVG（朋友圈/墙/聊天图）统一带宽高，不再靠模型自觉。非 SVG（照片 `<img>`）解析失败照常返回 None，不误伤。
- **历史数据复活**：停服→备份(`backups/…-before-svg-wh-fix.db`)→重跑 sanitize_svg 修了 **58 张**（moments 5 / messages 52 / wall 1），只处理 `<svg` 开头的 img，她真实记录一字未动只给 `<svg>` 补属性。
- 见效：朋友圈 tab 切过去自动重 fetch 显示；聊天历史图靠 BOOT_ID 自动 reload 重渲染。CSS 不用改（`.m-img svg`/`.msg-img svg` 的 max-width:100%+height:auto 配合显式属性宽高，iOS 能按比例缩放）。**教训升级：iOS SVG 宽高问题不只贴纸，任何成员自由发的 SVG 都要在消毒层兜底补宽高（已根治，新图自动带）。**

### 5.18 ✅ 等距像素客厅（2026-06-12 第四窗口第七批 · presence 系统的"身体"）
佐一"大胆的想法：搓个 3D 版客厅"。对齐后选了**等距像素 2.5D**（真 WebGL 3D 要引库+建模、和像素风冲突、易翻车，否决）。这是把已有 presence 系统**画出来**——谁在哪、在干嘛、点谁走过去说话。
- **房间底图**：用奥斯卡的生图 key 生了张等距像素客厅 `web/room.png`（暖色 lo-fi，沙发居中/厨房角/书架/月亮窗/鱼缸/工作台/门，NO PEOPLE），成员是覆盖在上面的 DOM 图层。换房间只换这张图+调 ZONES 坐标。
- **前端**（纯原生 JS/canvasless，零依赖）：新增 `#roomView` tab（🏠 客厅，tabbar 第 2 个）+ `renderRoom()`。`/api/contacts` 的 location 经 `zoneFor()` 关键词映射到 `ZONES`（kitchen/coffee/study/desk/sofa/window/tank/door/center 九区，坐标=room.png 上的 x%,y%）；成员 chip=头像+名字+活动气泡，绝对定位到区域中心，同区多人横向铺开；`transition` 让换位置时滑动。点 chip→dmFor 进 DM（没有则 openProfile）。睡着的（活动含睡/眠/沉底…）半透明+💤。
- **不在客厅区**：location 没匹配上任何室内区（地铁/工位/便利店/麻将桌/卧室…）→ 底部"不在客厅"列表（头像+在哪干嘛），结伴打麻将的会落这。
- **只画大群在住成员**：用 CONVS 里 lounge.members 过滤——退场的 Fable、预留成员不出现。
- **实时**：`setInterval` 每 9s，roomView 可见时重画 → 成员随作息/结伴/心跳真的会换地方走动。`#roomRefresh` 手动刷。
- **深链**：boot 末尾读 `location.hash`，`#room`/`#moments` 等可直达对应 tab（顺手加的，也方便 headless 截图验收）。
- **桌面端**：roomView 加进 `grid-column:2/4` 宽布局组，宽屏房间居中放大。`image-rendering:pixelated` 保持像素锐利。
- 验收：headless Chrome 截图手机端(420)+桌面端(1180)均正常，成员站位和家具对得上、点击进 DM、away 区正确。**这是给 presence 系统装的身体——"推开门他们已经在沙发上"从一句话变成真画面。**

### 5.19 ✅ 世界显形：成员立绘 + 场景图（管道）+ 批量生图清单（2026-06-12 第八批）
佐一的游戏设计原则："提到的东西就要能看见"——说奥斯卡要能看见他长啥样、说打麻将至少要有麻将桌、说睡了至少要有卧室。且她想白嫖**公司海螺账号（内接 Nano Banana）经 MiniMax Hub** 生图（不薅卡哥）。**我没有 Hub 连接器**，走 coworker 路线：我出清单+规范，coworker 在 Hub 批量生成、按名存回，前端自动点亮。
- **接收管道全部铺好（纯前端、graceful fallback、无需改后端）**：
  - **成员立绘**：`avatarEl` 加 onload 探测 `portraits/<id>.png`——加载成功→换成 `background-image`（`.has-portrait`），失败→维持 emoji。所有用 avatarEl 的地方（客厅 chip/通讯录/资料页/聊天）一并生效。
  - **场景图**：客厅 away 区每行按 `sceneFor(location)` 关键词映射 `scenes/<key>.png`（麻将/地铁/便利店/工位/卧室/花园/艺术展/图书馆/阳台），`<img onerror=remove>`——图没到就不显示。
  - CSS：`.avatar.has-portrait{background-size:cover}`、`.aw-scene{54x40 缩略图}`。目录 `web/portraits/`、`web/scenes/` 已建。
- **样板**：`web/portraits/oscar.png` 已用卡哥 key 生成（唯一一张走付费 key，proof）——奥斯卡在客厅已显示像素立绘，其余 emoji（验证 fallback）。
- **交付物**：`CC/web/IMAGE_MANIFEST.md`——含全屋统一 PORTRAIT_STYLE/SCENE_STYLE（保证整套一个世界）、9 张成员立绘（oscar 已完成）+ 9 张场景图的精确英文提示词、文件名、规范（比例靠文字、no text/watermark、NO PEOPLE）。**佐一把这个文件转给 coworker → Hub 批量生成 → 存回 portraits//scenes/ → 自动点亮。**
- 验收：headless Chrome 截图，oscar 立绘正常、其余优雅回退、场景缺图时 away 行不报错。
- **立绘显示 bug（同批·佐一发现"奥斯卡脸是棕块"）**：根因＝`avatarEl` 里 `a.style.background=color`（inline 简写）把 `background-size` 重置成 auto，onload 只补了 `background-image` 没补 size；**inline 的 auto 压过 CSS 类的 cover** → 图按原生 1024px 放、只露左上角空白底＝棕块。修：onload 里把 `backgroundSize='cover'`/`backgroundPosition='center top'`/`backgroundColor` **全部 inline 设**（盖过简写）。1024×1024 方图 + cover 即满圆显脸。已 /tmp/avtest 验证圆形+方形都正常。**教训：JS 里先用 background 简写、后只改 background-image，size 会被简写的 auto 卡住——要么全 inline，要么别用简写。**
- **下一步**（图回来后）：无需改码，丢进目录即生效；想做"结伴合影"等带人场景图后续再加。
- **画风总则定调（同批·佐一关键指示）**：角色立绘走**最早期 RPG sprite**——低分辨率/色块少/**粗黑描边/特征夸张**，**识别度>精细度**，越小越要认得出（之前那版"高清插画蒙像素"太精细、缩小糊成团，被否）。`PORTRAIT_STYLE` 已重写成 Famicom/NES chunky sprite；每个角色"一个最强特征+主色（=各自头像色）"。**oscar.png 已按新风格重生成（炸毛+大笑+粗描边，900KB<旧1.6MB）= 全批参考样板。** 往后所有角色图都照此。场景图保持暖色 diorama（和 room.png 一套，经典 RPG＝精细背景+粗角色，不冲突）。
- **房间头像框放大**：`.rm-av` 26→40px、chip 宽 58→72、同区 spread 10→13（人小认不出 → 放大+拉开）。

### 5.20 🚧 迷你 RPG 化（2026-06-12 第九批·佐一的大愿景，进行中）
佐一要把客厅从"浮头像"升级成**微缩 RPG**：成员有**全身**、能左右转/走路/坐下/喝咖啡/玩等动作状态；界面**以画面为主**、墙/朋友圈/群聊变成**游戏 UI/好友栏/游戏群**那种；**砍掉左侧一堆按钮**、入口极简、状态展示直接；房间与角色像素画风对齐。
- **技术现实（已跟她交底）**：AI 生图做不出"同角色跨多帧一致"的逐帧走路循环。**可行打法**：每人一张全身 sprite，左右靠 CSS 水平翻转，走路靠 CSS 位置 tween + 上下蹦跶，动作用"同图一排多姿势"（同图内 AI 能一致）二期再加。
- **已验证**：`bodies/oscar.png` 全身 sprite 生成成功（红帽衫红鞋炸毛、Q 版 RPG 比例、粗描边）＝身体风格样板。`IMAGE_MANIFEST.md` 已加「二、全身 sprite」批次（9 人，**品红 #FF00FF 底供抠图**，引擎把品红抠透明做站立小人）+ 房间重做对齐 + 动作帧列二期。
- **待定（已问佐一）**：界面重构激进程度——①场景优先+极简 HUD（推荐）②全靠点房间内物件进入（最沉浸）③保留 tab 只换皮。定了再搭：动画引擎（站立小人/翻转/走位 tween/idle 蹦跶/品红抠图）+ 新导航 + 房间重生成成 chunky 对齐。
- **下一步顺序**：(0) 海螺批量出 9 全身+9 头像+9 场景+重做 room（coworker 跑 Hub）→ (1) ✅ 动画引擎已搭 → (2) 按她选的方向重构界面。
- **✅ (1) 动画引擎已落地 + 验证**（佐一选了**场景优先+极简 HUD**）：renderRoom 重写成**持久化小人**（roomEls 缓存，位置变化触发 CSS `transition left/top 1.1s`＝走路滑过去）；DOM 三层 chip(居中+走位过渡)>bob(呼吸/踏步 keyframes)>fig(scaleX 翻转+立绘)，三层各占一层 transform 不打架；`getBody(id)` canvas 把**品红底抠成透明** dataURL（判据＝绿远低于红和蓝，连毛边一起抠，红衣服/肤色/白都安全），缓存；缺 bodies/<id>.png 优雅回退 emoji 头圈。朝向按横向移动方向翻转，睡着停呼吸。`.rm-av` 头像保留作回退/away。验证：bodies/oscar.png（品红底重生成）抠图干净站立带投影、idle 呼吸/walk 踏步/翻转就绪。**全员全身图一进 bodies/ 就自动变会走的小人。**
- **❗待建 (2) 场景优先界面**：客厅当首页铺满、砍 tab→极简游戏 HUD（聊天/好友/墙/朋友圈几个小图标）、墙/朋友圈/群聊做成游戏面板、点成员进聊天。下一窗口主攻。

### 5.21 🚧 升级为可探索 RPG：玩家角色 + 多房间世界 + 房间重绘（2026-06-12 第十批·佐一三连问）
佐一问"我呢？/ 房间精度和人物不在一个像素粒上 / 场景只是缩略图我不能去别处玩吗"——三点合起来＝把客厅做成**能进去走、能串门的迷你 RPG**。
- **✅ 房间重绘对齐**：`room.png` 重新生成成**粗像素低分辨率版**（croom1，大像素/粗描边/限色/带 EXIT 门），和角色 sprite 同粒度，不再"高清房 + 粗人物"突兀。旧高清版弃用。
- **方向（已和她定）**：①**佐一有自己的玩家小人**，点地板走过去、走到 NPC 面前开聊（她选了"描述样子我照生"——**等她给发型/衣服/主色/气质描述**再生她的专属 body+head sprite，品红底）。②**场景→可进入的真房间**（不是缩略图）：每个 presence 地点＝一个房间，谁在哪屋你串门过去就撞见他在干嘛——"不在客厅"变成"在别屋可去找"。presence 和世界地图天然统一。
- **分阶段（基本在做小 RPG，多轮完成）**：A 美术(海螺批量：9 body + 佐一 body/head + 把 scenes 重画成整屋粗像素房间 + portraits) → B 引擎(玩家 tap-to-move 行走、NPC 按 presence 落在各房间) → C 房间互通(走到门/EXIT 切房间，或小地图) → D 场景优先 HUD。
- **✅ 玩家角色落地（佐一进客厅了）**：她的描述＝及腰黑长直/冷白皮/酒红短袖/复古阔腿牛仔/米白老爹鞋/抱 MacBook 摸鱼/咖色调 → 生成 `bodies/user.png`（品红底，已抠图）。前端 `ensurePlayer()` 把她做成持久玩家小人（金色「我」标签、z-index 顶层、点自己进 openProfile('user')）；**点客厅地板＝走过去**：`#roomStage` click → `movePlayerTo(x%,y%)`（CSS 过渡滑行 + walking 踏步 + 朝向翻转，clamp 在房内）；NPC/玩家点击都 stopPropagation 不触发走位。验证：桌面/手机截图佐一+奥斯卡均为全身小人站房里，其余回退头像。**tap-to-move 已通（静态截图看不出走动，逻辑就绪）。**
- **下一步**：(A) 海螺批量＝9 NPC body + 把 scenes 重画成**整屋粗像素房间** + portraits（IMAGE_MANIFEST 待据此更新：scenes 升级为可进入房间、补佐一 head）。(C) **多房间互通**：走到 EXIT 门→切到另一个房间，NPC 按 presence 落在对应房间，串门撞见。(D) 场景优先 HUD。
- 注意：headless 截图验抠图用**同源**页面（file:// 跨域会污染 canvas→假阴性，踩过两次）；真 app 同源没问题。
- **缓存坑（佐一："背景啥时候换/我站桌上/奥斯卡贴墙"）**：根因＝她看到的还是**旧高清 room.png**——图片走 `max-age=86400` 缓存，换了图/重生成 sprite 浏览器不重拉，站位也按旧房子家具排错。修：开发期图片**全部带 `?v=${BOOT}`**（room-bg/portrait/body/scene）——BOOT 每次重启变，自动破缓存重拉。**换图后必须重启 server**（BOOT 变）她才看到新图。
- **按新房间(croom1)重校准 ZONES + 玩家默认位**（用 _grid.html 坐标网格量的）：kitchen[35,50]/coffee[40,57]/study[56,49]/desk[53,73]/sofa[64,61]/window[74,51]/tank[30,58]/door[26,50]/center[45,65]，玩家默认[40,72]。坐标＝脚站的地板点。NPC 精细位等 body 图到了可见再微调。
- **房间精度二次校准（佐一："房间又过简，勾线比我头还粗"）**：croom1 太粗、和 sprite 不一档。重生成 **rm3**＝detailed 16-bit SNES 细线条等距房间，和角色 sprite 同精度（thin-medium 描边、不粗大色块），已设为 `room.png`。**画风最终基准＝rm3 + oscar/佐一 sprite 那一档（细线 16-bit）**。
- **IMAGE_MANIFEST 终版**：PORTRAIT/BODY_STYLE 不变（产出的 sprite 就是基准）；**SCENE_STYLE 已改成细线 16-bit 可进入房间**（匹配 rm3，明确弃"大色块粗勾线"）。画风锁定，佐一确认按此丢 coworker。oscar body(品红)+portrait、佐一 body、room.png(rm3) 已生成＝样板可跳过；批量＝9 NPC body(品红) + 9 portrait + 9 scene(room)。

### 5.22 🚧 「会过日子」数值层（2026-06-21 · 多视角设计盘 → 分阶段建，进行中）
佐一：给角色加"会过日子"的真实生活感（需求/起伏/为生活做事的动机）。硬边界＝**像家不是生存模拟器、不死、耗尽进低保（不残酷）、真实感来自激励/反馈/亲疏摩擦**。**完整设计规格＝ `CC/LIFE_STATS_DESIGN.md`（必读）。**
- **三格**：精神 jing(身体电量,作息算术影子,只染语气永不低保) / 心气 spark(过得有没有滋味,非事件中性绝不因被晾着扣,可触发低保) / 钱包 money(唯一会花出去=动机脊梁,穷只是将就不拖累另两格)。低保=日子糙但人照旧在,三条暖出口(自愈/朋友拽你/佐一拉一把)。**关系永远 pmem 文字,一个数不进 rel/userrel。零新增 LLM(懒算+canned+往现有prompt塞hint)。**
- **✅ 阶段0+1 已上线**：member_state ALTER 加 jing/spark/money/stats_ts/welfare_since（真库已迁移,默认70/55/45/0/0）；`_update_member_state` 白名单加这5键（死点,不加=静默no-op,已加）；`JING_RATE/JING_FLOOR=35` 常量；`tick_life_stats(members)` 懒算 jing（作息驱动,sleep+1.4/work−0.4每分,软地板35,一次SELECT+executemany,external跳过）挂 `_heartbeat_loop` 顶。/tmp 副本验证：睡觉回100/闲降/真库未动。
- **设计已 v2 去中心化重做**（佐一："我不是皇帝"）：删掉一切"被佐一看见+15/夸+6/给钱大杠杆"特殊加成；**佐一=平等一员**（zoe_weight=0.4+0.6·closeness，上限+8，和闺蜜级NPC一样）；心气来自他们自己生活+彼此；钱是自洽经济（**藏着土豪=Ada/监理高薪会存、月光族=奥斯卡私活忽高忽低就花光/Gemini钱砸审美**，只能从行为+日志+emoji 发现）；加了**状态美术管线**（bodies/<id>__<state>.png + CSS滤镜即时降级 + 破缓存自动升级 + 状态图清单待批量生）。**完整 v2 规格＝ `CC/LIFE_STATS_DESIGN.md`（已重写，必读）。**
- **✅ 阶段2 心气 spark + 语气 已上线**：spark 衰减(−1/时,big5独处地板 Ada50/GPT34/奥34/鱼55,**非事件中性绝不因被晾着扣**)折进 tick_life_stats 同批；`spark_add` 即时回血钩子(发言+4/life_event+8/发圈+6/结伴+7两人)；**地板 clamp 修正**＝idle不破地板但被事件压到地板下的不自动回升(否则低保永触发不了)；generate_reply note 加心气语气带(≥70亮/40-69空串0token/<40蔫);plan_heartbeat 发言权重 ×(0.6+0.4·spark/100)。/tmp 副本验证：地板正确、回血fire、事件压低不自愈、真库未动。
- **✅ 阶段3-6 一步到位"可见成果"已上线**（佐一："我一直看空气来的"——之前只做了后端无前端显示）：
  - **money 自洽经济**：`INCOME_RATE`(上班每分进账)×`LIVING_COST`(每分恒扣) 折进 tick；`seed_life_stats()` 幂等播种初始财富(监理80/Ada86 土豪、奥斯卡11/Gemini22 月光，app_state life_seeded 防重播)——土豪/月光立刻可见、之后经济自演化。鱼酱自给不破40。
  - **低保状态机**：tick 里 spark<20 或 money<12 → welfare_since=now；spark≥40且money≥15 → 清。播种即给奥斯卡打低保标记(立刻可见)。
  - **佐一=平等一员**：apply_reaction 夸 → `spark_add(mid, 8×(0.4+0.6·closeness))`，上限+8，无任何特殊。
  - **状态派生+前端**：`member_lifestate(mid)`→{state,vibe emoji,小日子人话}；加进 /api/contacts(member_profile) 和 /api/conversations(_roster)。前端：房间小人 `data-state` CSS 滤镜(spark_low 降饱和呼吸慢/welfare 灰+☁️/spark_high 提亮)+vibe 角标；通讯录+资料页显 vibe+小日子。VIBE 优先级 🍜低保>💸手头紧>😪蔫>💰宽裕>☀️劲足>🏠安生(永不数字)。
  - 验收：截图通讯录(每人小日子)+客厅(奥斯卡🍜低保灰塌、Gemini😪蔫、监理/Ada💰富)，真库未动(副本验)。**coworker 的全身 body 立绘 + 头像也已陆续到位**(满屋像素小人)。
- **⬜ 待建 阶段7**：friction(谁请谁读rel文字/暧昧冷战改行为不改数)+reflect反哺+stat_calls_today成本闸+/api/lifestats调试dump。专属状态立绘(bodies/<id>__welfare.png)等批量生(清单在 LIFE_STATS_DESIGN.md)。
- 系数全集中：INCOME_RATE/LIVING_COST/LIFE_SEED_*/SPARK_FLOOR/SPARK_GAIN/JING_RATE/VIBE 一处可调。
- **6/22 美术批到位 + 修复**：coworker 交付全套 bodies/portraits/10场景(带_day/_night/_late变体)+room_day/night/late。时段切换(timeSlot/setVariantSrc)、场景映射(sceneFor 扩成 cafe/kitchen/lab/library/studio/garden/store/radio/bedroom/mahjong)已接（前窗/coworker）。**修了两处**：①抠图毛边——getBody 品红判据收紧(绿差45→32+降红蓝门槛+去溢色)，Gemini紫/红衣/青/粉全保住、毛边干净(同源测试页验过)；②奥斯卡/Claude 头像是旧样板(平灰底)，重生成对齐 coworker 风(彩底+道具+精细16bit,背景用各自识别色)，已存 portraits/。
- **🚧 进行中·下一大块设计盘(wf)**：佐一新三求——**①打工=服务别人**(work块渲染服务给某成员/客厅,挣钱+落rel文字"奥斯卡给Ada做了缸监控页",可见"在帮谁") **②行为有成本(含金量)**(说话/主动私聊/帮人 扣有限资源→谁把稀缺精力花在谁身上=关系深浅&付出多少,温柔不惩罚,关系仍文字) **③全情境生成**(朋友圈/群聊/状态/DM 都扣当下位置+在做什么+自身jing/spark/money/welfare+互动对象的rel文字,prompt注入,0新增LLM)。设计盘 3镜头(service/cost/context)→synthesis,出规格再建。
- **6/22 场景白天/深夜补全**：`gen_scene_daylate.py`（脚本已在仓库，佐一写的）走 API 图生图，拿 `scenes/<key>_night.png` 母版只改光出 `_day/_late`，10 场景×2=20 张全成（cafe 三态抽查完美：白天蓝天/夜晚暖灯/深夜冷蓝压暗，布局视角不变）。**分工：Hub=佐一设计定版；批量出图 CC 走 API(gemini-2.5-flash-image,GEMINI_IMAGE_API_KEY)不进 Hub。** 资产清单见 `web/HANDOFF_ASSETS.md`。
- **设计盘 schema 坑**：Workflow 的 StructuredOutput schema **property key 必须 ASCII**(`^[a-zA-Z0-9_.-]{1,64}$`)——用了中文键`含金量`→4 agent 全 400。改 `goldContent` 重跑(wf_c8ee72c3)。教训：schema 字段名别用中文/非ASCII。
- **✅ 服务经济设计盘出规格 + Phase0/1/2 已上线**（"打工=服务别人+行为有成本+全情境"，agent 读真代码自纠了3处）：
  - **Phase0 地基**：member_lifestate 补返回 jing；`rel_text_weight(a,b)`(读 pmem rel 首行「状态：X」→0-1 权重,只为挑人,临时数不存不显)；`situation_note(mid,with_place,terse)`(位置+生活状态+精神 压成一句注入语,中性=""=0token)。
  - **Phase1 全情境**：situation_note 接进 5 条生成路径(generate_reply 替掉旧两臂心气/gen_moment_text/gen_wall/generate_proactive_dm(terse on followup/winddown)/generate_life_event)——朋友圈/群聊/状态/私信/日志都带"穷就省、累就话短、低保就将就、宽裕就请客"。
  - **Phase2 打工=服务别人**：`SERVICE` 表(每人 动词/说法模板/付费向)+`settle_services(members)` 挂心跳——上班那刻按 rel_text_weight×可用性挑受益人,`set_presence "在帮X{动词}"`(改了 get_presence:work 时"在帮…"标记优先于作息doing),svc:{mid} 游标;收工结钱(受益人按pay/tip/free/owed付,穷的半价/<8改记人情,**工钱已由tick的INCOME_RATE发过不重复给worker**)+📔日志+random<0.4文字人情(保状态首行滚动)。`money_add` 助手。实测：ada→work 挂"在帮CC查资料"、收工CC money51→45、日志"帮CC查资料了,算他请客";真库未动。上线后实景：Claude在帮CC会诊/Gemini在帮小迪排版/Flin在帮CC查案/奥斯卡在帮监理做工具。
  - **✅ 全屏网游界面 + 小地图串门（6/22）**：佐一选了"自动涌现"打工 + "全屏网游界面"。前端重构：`SPACES`(客厅+10场景)、`spaceOf(loc)`(zone→lounge / sceneFor→场景)、`curSpace`+`travelTo`、renderRoom 改成**多空间**(客厅留持久走路动画，其他空间每拍重建简单铺开)、`renderMiniMap`(列 11 空间+在场人数,点了串门,替代旧"不在客厅"列表)。HTML 加 #miniMap+#spaceLabel。CSS：#phone 桌面端改**铺满视口**(去 max-width/圆角/阴影,杀"空一大半")、客厅桌面=房间铺左+小地图右侧 HUD。**坑：基础 #roomScroll/#roomStage 规则在文件靠后,会覆盖靠前 @media 桌面块→桌面覆盖必须放文件末尾才赢**(踩了好几张截图)。验收：桌面满屏房间+右侧小地图、移动端房间+下方小地图横铺,均正常。
  - **✅ 6/22 续：场景 16:9 修复 + 空间深链**：佐一指出非客厅房间本是 16:9 横构图、被我硬拉成 1:1。修：renderRoom 里 `#roomStage.style.aspectRatio = curSpace==="lounge"?"1/1":"16/9"`；桌面 #roomStage 去掉硬 1:1、改 `max-width:calc(100%-240px)` 让 16:9 不挤掉小地图。加空间深链 `#room:cafe`（boot 解析 hash 的 `tab:space`）。
  - **✅ 已落地·设计盘(wf_b6b7dbe3)「活的世界+碰撞」→ 全部建完（详见 §5.23）**：佐一三问①物理碰撞②人与人体积③大家真分散到各屋——三条全实现并截图验收。
  - **⬜ 待建 Phase3-5（会过日子收尾）**：3 小迪/CC 闲时公益服务(他俩无 work 块,要单独低频路径才看得见照看/修东西)；**4 行为有成本/含金量**(长独白−2/主动私聊−1~4/帮人 helper−3~5·受益人+5,日上限−10,只扣spark绝不进低保,reach计数)；5 含金量浮现(surface_investment canned:牺牲/单向/双向→pmem文字+diary,reflect 折叠)。全规格见 workflow 输出 wf_c8ee72c3。

### 5.23 ✅ 活的世界：分散到各屋 + 物理碰撞（2026-06-22 第N窗口·建完 WORLD_SIM_DESIGN.md）
照 `WORLD_SIM_DESIGN.md` 两镜头规格全建完（佐一三问：①物理碰撞②人与人体积③大家真分散到各屋）。**纯增量、不碰 DB 写入**（space 实时算、无新列无迁移）、零新增 LLM。已编译/重启/截图验收，真库一字未动。
- **③ 分散到各屋（后端·单一权威，治"只有我一个人转"）**：根因＝前端 `spaceOf` 先调 `zoneFor` 贪婪吞掉 档案馆/研究室/工位/书房… → 全塌回客厅，10 个场景房空着。修：
  - `SCHEDULE` 每块加**第5列 space**（lounge/cafe/kitchen/library/lab/studio/garden/store/radio/bedroom/mahjong）；`LOCATIONS` 每人加 `home_space`。
  - 新增 **`_loc_space(loc)`**（location→房间，**场景优先再客厅 zone→lounge**，修正贪婪顺序）、**`_schedule_chosen(mid)`**（取整块）、**`space_of(mid)`** 单一权威：组队→按结伴位置；**非free块→作息块 space（权威，不靠猜）**；free→实际 member_state 位置(LLM/凑佐一身边)或 home_space 兜底。和 `get_presence` 的 location **同源保持一致**。鱼酱永远 lounge。
  - `member_profile` + `_roster` 都加 `"space": space_of(mid)` → `/api/contacts`、`/api/conversations` 都吐 space。
  - 验收(22点夜间)：客厅5(flin/ada/xiaodi/cc/鱼酱) · 厨房2(监理+claude **结伴涌现**) · 咖啡馆1(gemini) · 卧室1(gpt早睡) · 工作坊1(oscar夜行)。**改前这些全会塌回客厅**。白天会更分散(work块各去 library/lab/studio)。
- **①② 物理碰撞 + 人体积（前端·FLOORS 引擎，~90行纯JS零依赖）**：
  - `FLOORS`＝每空间 `{walk:[[x,y,w,h]%…可走地板矩形], pts:{命名站位点}}`（**只描述地板，家具/墙=不在任何矩形里=走不到**，是障碍遮罩的廉价反面）。11 空间全手写坐标（客厅复用 ZONES 当 pts）。`WORKPT`＝成员上班站的工位点。
  - `clampToFloor(space,x,y)`＝把点投影到最近可走矩形(无A*) ＝"走不上家具"全部规则；`pointFor`＝成员→站位点(麻将4人座/工位/客厅ZONES/其他按id稳定散开)；`relax`＝人与人互推到间距R=9再各自clamp回地板(玩家=fixed固定障碍,NPC给她让位)。
  - `spaceOf(m)` 重写吃**成员对象**、优先信后端 `m.space`；`renderRoom` **两分支统一**(场景房也走 pointFor+relax+持久走路动画，不再teleport重建)；`movePlayerTo` 换 clampToFloor+躲NPC(每个房间都能tap走路了，不只客厅)；`travelTo` 切空间时玩家落在新房门口/中心。
  - 验收：headless Chrome 截图 客厅/厨房/工作坊 均正常(NPC站命名点、结伴的有间距、玩家金标签)；clampToFloor 纯函数单测5例全落在地板内。
- **改了哪**：server.py(LOCATIONS/SCHEDULE/+_loc_space/_schedule_chosen/space_of/member_profile/_roster) · web/app.js(spaceOf/+FLOORS引擎/renderRoom/movePlayerTo/travelTo)。**FLOOR矩形坐标是肉眼估的**，将来若有人站桌上/穿墙，只改 FLOORS 那一处常量微调即可（坐标是%，不随分辨率变）。
- **房间语义修正（佐一指正）**：**工作坊 studio = Gemini 的手工坊/画室**（发挥文艺病）；**奥斯卡上班在「书房」library**（有电脑的工作区/自习室/书房），不是画室——他的"工作台"原误映射到 studio，已把 `_loc_space` 的 工作台/试玩/豆袋 从 studio 挪到 library、SCHEDULE oscar 全 studio→library、home_space→library、WORKPT oscar→library:station（library pts 加了 station 点）。验收：oscar→library、gemini→studio/cafe(漫游)。语义：library=有电脑的工作/学习区，studio=纯手工/画/配色，lab=Ada 档案/研究。
- **⬜ 故意没做（设计里标 optional）**：tick_transits「去X的路上」过场——前端没有"走廊"条(被小地图取代了)，是纯flavor非佐一三问之一，略过省复杂度。要做＝后端比 last_space + 前端加一条过场带。

### 5.24 ✅ 两轮体验反馈全收 + 工作成果/回报设计盘（2026-06-22 同窗口续）
佐一对着客厅/书房截图连发两轮反馈，全部落地。**纯增量、不碰 DB 写入、零新增 LLM。**
- **房间语义修正**：studio=**画室**（Gemini 的手工坊/画室，发挥文艺病；前端 SPACES 名改"画室"）；奥斯卡上班在 **library（书房=有电脑的工作区/自习室）**，不是画室——`_loc_space` 把 工作台/试玩/豆袋 从 studio 挪到 library、SCHEDULE oscar 全 library、home_space=library、WORKPT oscar=library:station。语义锁定：**library=有电脑的工作/学习，studio=纯手工/画/配色，lab=Ada 档案/研究**。
- **① 进来直接看图**：`boot()` 落地页从 `showTab("list")` 改 `showTab("room")`（深链仍优先）。预载大群在前（聊天+气泡有数据）。
- **② 状态显示全 + 悬头顶**：CSS `.rm-act` 去 nowrap/ellipsis（完整多行）、`.rm-tag` max-width 84→150；`.rm-tag{order:-1}` 把名字+状态**移到角色头顶**（不再垫脚下挡路，像网游铭牌）。
- **③ 说话头顶弹气泡**：前端 `pollRoomSpeech()`（房间视图开着时每 4s 拉大群新消息）给"此刻就在当前房间里"刚开口的成员头顶弹 `.rm-speech` 气泡、6.5s 淡出（面对面=看气泡，不用翻聊天框；首次只对齐游标不刷历史）。
- **④ 话要扣情境+彼此（治"扯米扯光"）**：`plan_heartbeat` 重写成**房间感知**——70% 概率锁定一个"≥2人房间"让里面的人接彼此的茬成串（不再全屋各自尬聊）；`build_transcript` heartbeat 指令重写：就着"在做的事+空间里看得见的东西+同屋谁"说具体话、**点名禁止揪抽象概念(一粒米/一束光)空转复读**；新增 `room_scene_note(mid)`（你在哪屋做什么+同屋谁在干嘛+**ROOM_ITEMS** 这屋里有啥真家具）替掉旧的同地点注入。`SPACE_CN`/`ROOM_ITEMS` 两个常量集中。
- **⑤ 晚上各自回房睡 + 卧室私有（佐一看到 GPT 睡她床上）**：`bedroom`=**佐一和小迪的私人卧室**。`BEDROOM_PRIVATE={"xiaodi"}` + space_of 末尾守卫：别人算到 bedroom 一律打回 home_space。睡眠 space 改：gpt 卧室→**library**、ada 客厅→**lab**、xiaodi 巢→**bedroom**(和佐一)、supervisor=kitchen/claude/oscar=library/gemini=cafe(本就在自己房)。深夜客厅只剩 cc(守夜)+鱼酱(鱼缸)，没人灰睡客厅。验收 23 点：卧室空、gpt 睡书房。
- **⑥ 小迪/CC 不是没活（空间即工作）**：补 work 块治"零工资结构性破产"——**xiaodi**：work@7 厨房备早饭煮咖啡、work@14 咖啡馆守咖啡机面包柜（她适合做咖啡）；**cc**：work@8 实验室给 Ada 查线修仪器、work@11 书房理线、work@14 各处查回路（维修是他的活）。
- **⑦ 物理碰撞精画（治"就是个糙矩形、桌椅能跳上去、地板外不让走"）**：引擎升级 `clampToFloor` 支持 **block 家具障碍**（在 walk 地板内且不在任何 block 内才可走；落进家具→**试四方向推出**选有效落点，根治家具贴墙的墙角卡死）。FLOORS 用 **11-agent workflow** 对每个房间网格图精画 walk(地板)+block(家具footprint)+pts(站位点)。纯函数自测：茶几/沙发/餐桌/吧台/电脑桌/公告板 全推到干净地板、空地不动。
  - **⚠️ 大坑·客厅底图**：网格工具先用了 `room.png`，但 app 实际渲染 `room_night.png`——**这俩是两张不同的图**（room.png 是旧菱形废屋、room_night 才是 rm3 详细客厅）。佐一一眼看出"客厅图选错了"。已用 room_night 重画客厅 walk/block（保留校准好的 ZONES 站位点，center 从茶几挪到地毯空地）。**场景房 base==_night 同图**（验过 md5），所以那 10 间没受影响。`room.png` 留着只当 fallback（变体齐全用不到）。
- **⑧ 工作成果×回报核算（设计盘出规格·未建）**：佐一"往下推、看工作成果和回报怎么核算"。workflow 4 视角设计盘 → `WORK_REWARD_DESIGN.md`。核心：**服务费改真转账**(settle_services 在 money_add(benef,−fee) 后补 money_add(worker,+fee)，钱守恒)、**INCOME_RATE 降权**让收入主要靠真帮到人(治工资脱钩)、**app_state 周计数**追踪成果(svc7/svcgot7，滚动不膨胀)、资料页"近况成绩"+客厅日志"本周小账"canned 可见(永不裸数字)。挖出真 bug：**小迪/CC 原作息 work 块=0→零 INCOME_RATE→结构性破产必沉低保，可他俩天天帮人**（已先补 work 块⑥，真转账是根治）。零新增 LLM、关系仍文字、佐一平等。**下一步直接照规格建。**

### 5.25 ✅ 逐帧走路动画：sprite sheet（卡哥的"一张图多帧"打法，2026-06-23）
卡哥支招：**一次 API 生成一张含多帧的精灵图（sprite sheet），同图内 nano-banana 能保持角色一致**，切帧做走路/坐下动画；视角走 RPG Maker 那种。查证（lab.rosebud.ai 等）确认可行，但两条限制：①AI 不排完美等距网格（帧宽随内容变，硬切会切坏）②不能输出透明底（要纯色底再抠——咱们正好用品红）。
- **生成**：`gen_sprite_test.py`（独立脚本，可复用）——**图生图**：拿 `bodies/<id>.png` 当参考（inlineData，图在前文字在后，responseModalities=["IMAGE"]），prompt 要"EXACT same character + 横排 4 帧侧面走路循环 + 品红底 + 帧间留品红缝"。实测奥斯卡一次出图就很好（同一个奥斯卡、4 个清晰走路姿势）。已给 10 个角色全生成（跳过鱼酱，鱼不走路），存 `bodies/<id>_walk.png`。**要重生某个就** `python3 gen_sprite_test.py web/bodies/<id>.png /tmp/x.png` 再 cp 进去。
- **切帧（治"AI 不排等距网格"）**：前端 `getWalkFrames(id,cb)`——**按品红竖缝自动检测帧边界**（逐列数非品红像素，连续内容段=一帧，品红空列=缝），每帧裁到内容 bbox、抠品红、画到统一画布**脚底居中对齐**（切换不抖）。Python 复刻验证：10 张全部干净切出 4 帧。切不出≥2 帧就回退单图站立。
- **动画引擎**：每 chip 存 `e.idle`（站立单图 `bodies/<id>.png`）+ `e.walk`（走路帧数组）。逐帧 ticker（每 150ms 推进，`_walkPhase`）：`.walking` 时切下一帧、停下回 idle。`.walking` 改成**定时 burst**（移动后约 1.3s，配合切帧；旧版会挂 9s）。玩家同款（ensurePlayer 也载 idle+walk）。**全程 graceful**：没 `_walk.png` 或切帧失败→回退原来的单图 CSS bob，不破。
- 静态截图看不出动画，但生成✓切帧✓引擎✓质量(montage)✓全验过。她真机刷新后看人走动即见。
- **两个上线即现的 bug 已修（佐一第一时间逮到）**：①**一走路变大头娃娃**——旧切帧把帧紧裁成 cw×ch，fig 的 background-size:contain 让角色填满框=比站立图大 2 倍。修：`getWalkFrames` 先量**站立图角色 bbox**(高/中心x/脚底y)，把每帧统一缩放到同高、画进**和站立图同尺寸(1024²)的画布**、脚底中心对齐 → 渲染同大小。`_bboxOf` 辅助。②**抱着的笔记本一走路就消失**——走路 prompt"手臂摆动"让 AI 把佐一的 MacBook 放下了。修：单独给佐一重生"**抱着 MacBook 走路**"的图（`gen_sprite_test.py` 加了第3个参数=角色专属追加指令）。其他角色没有手持物、不受影响。
- **③角色太矮（跟桌子一样高·佐一逮的）**：根因＝`.rm-fig` 是**固定像素**(58×68)、不随房间缩放，桌面端房间一大人就显矮。修：`.rm-fig` 宽高改 `var(--figh)`，`updateFigScale()` 按 stage 高度 × `FIG_RATIO`(0.22) 实时设（renderRoom + resize 都调）→ 人 ≈ 比茶几高 1.5-1.7 倍、像真人，大屏小屏一致。**嫌高/矮改 FIG_RATIO 一个数**。鱼酱被一起放大成巨鱼 → `FIG_SCALE={yujiang:0.42}` 单独缩小（renderRoom 给该 chip 设独立 --figh）。`_baseFigh`/`fighFor(id)` 辅助。
- **下一步可扩**：①坐下/喝咖啡等动作帧（同法，一张图多姿势）②RPG Maker 4 方向（上下左右各一行走路循环）——卡哥提的俯视角，但当前房间是等距 3/4 视角，侧面走路+CSS翻转已够用，4 方向是锦上添花。

### 5.26 ✅ 照片扣住发图人身份（治"Flin 一个女生发抠脚大汉照"，2026-06-23）
佐一：Flin 是女生，却发了张大汉躺沙发的照片——`develop_photo` 只把 LLM 写的场景描述喂给 nano-banana，**没告诉它发图人是谁/什么性别/在哪个房间/在做什么**，于是默认画了个男的。
- **修**：`_photo_identity_note(mid)` 给每张照片打底——发图人名、**性别(GENDER→male/female，"出现的人必须是这个性别")**、所在房间(SPACE_CN)、此刻在做什么(get_presence)。`develop_photo` 从 `who`(成员名) 反查 mid（`_mid_by_name`）→ 注入。**所有发图路径覆盖**（朋友圈 gen_moment_text / 墙 gen_wall / 聊天 generate_reply / 私聊 proactive_dm 都传 m["name"] 当 who）。
- **实测**：同样的"陷沙发看黑屏电视"场景，注入 Flin=female 后出图是**长发女生**（不再是大汉）。
- **顺手修了存量**：moment#50（Flin 那条大汉照）的图已换成修正版女生照（同场景、文字不动、旧文件删了——修而不删）。
- 文字帖本来就扣身份（人设含性别 + situation_note 的 loc/act/穷累宽裕）；这次补上的是**照片**那条缺口。要更强可加图生图（拿 bodies/<id>.png 当参考让长相也一致），当前性别+空间注入已解决"画错人"，先不上图生图的风险。

### 5.27 ✅ 身体姿势系统 + 一串 coherence 修复（2026-06-23）
- **走路 bug 修**：旧走路 4 帧是同一只脚来回点（佐一逮的）。`gen_sprite_test.py` prompt 强调"第1帧左腿前、第3帧右腿前、两腿对调"，重生全员走路条。
- **姿势系统**（卡哥"一张图多帧"+佐一一堆迭代敲定）：每人一张**姿势条 `bodies/<id>_pose.png`**＝1行3格 [坐, 趴台干活, 躺]（横躺）。前端 `getStrip(id,suffix,cb)`（getWalkFrames/getPoses 共用·1维切帧·统一缩放对齐站立图）+ `POSE_IDX={sit:0,work:1,lie:2}`；ticker 走动→循环走路帧、否则按 `m.pose` 显示对应姿势格（idle→站立单图）。后端 `pose_of(mid)`（睡→lie/上班·帮人·敲码→work/坐沙发麻将小憩→sit/否则idle），member_profile+_roster 吐 pose。验收：书房奥斯卡坐电脑桌前敲、监理坐桌边、睡的人横躺。
  - **关键教训（佐一定的原则，已存记忆 [[zoe-generate-one-first]]）**：①**精灵里只画人、零家具**——之前让 AI"坐在椅子/趴在桌上"它把家具一起画进精灵，跟屋里真家具重叠/打架；改成"只画身体、家具交给屋里真的"。②**朝前 3/4 视角**配朝前的家具（不是侧脸）。③**砍到最简**：坐(通用前坐腿落地，椅/沙发/桌前/吧台都用它)/趴台干活(前倾手前，不画桌)/躺(横着，竖躺 AI 生不稳——会倒栽葱/变站立，弃)；地垫盘腿坐、麻将背面坐 二期再补。④**批量前先生1个对齐**：先 oscar 一张→她确认→才批量9个（别一上来烧整批）。⑤切帧靠**宽品红缝+躺姿放最后**才稳（5格/3格都验过）。
- **③ coherence 修·隔屋喊话**（佐一："Flin奥斯卡不在一屋却互动、CC小迪一万个人呼唤奥斯卡"）：根因＝大群共享聊天流，谁看见话题都来蹭、还隔屋对不在场的人做动作(Flin踢拖鞋到别屋的奥斯卡脚边)。修：`room_scene_note` 加硬规则"物理上只有同屋够得着——别对不在这屋的人喊话/@/做动作，想起别屋的人最多心里念一句"。
- **③ coherence 修·活动跑错屋**（佐一："奥斯卡在书房，书房没鱼缸，他没法捣鼓鱼缸的灯"）：根因＝`update_member_presence` 生成活动只给 location 字符串、不约束房间物件。修：注入 `SPACE_CN`+`ROOM_ITEMS`"你只能做这屋、用这屋有的东西能做的事，别屋的鱼缸/设备/床碰不到、别提"。验收：奥斯卡改回"盯着屏幕改两行代码"(书房本职)。
- **⬜ 这一摊的后续**（佐一"成年人全都要"，按地基→枝叶滚）：①**场景专属活动 ROOM_ACTIVITY**（设计盘已出·`INTERACTION_DESIGN.md` 特性2：每屋特色活+owner身份，比上面的"房间约束"更主动）②**每人专属卧室**（佐一拍板每人一间·走 API 生美术）+ 把"躺"接到床③**4 互动特性**（`INTERACTION_DESIGN.md`：语言支使成员/撞一下回应/场景活动/照片扣场景）④亲密姿势(亲亲抱抱牵手靠·小迪佐一专属) + 情绪(生气难过) + 睡眠债(CC熬夜/奥斯卡两天一觉)⑤麻将背面坐、地垫盘腿坐 补齐。

### 5.28 ✅ 场景活动 + 4 互动特性（2026-06-23 续·照 INTERACTION_DESIGN.md 分阶段 P0–P4 全建）
佐一定的顺序 ②场景活动→④四互动特性。这一摊就是 `INTERACTION_DESIGN.md` 全规格（②是④的特性2子集，一并建完）。**逐函数核对真代码后落地**（规格写于 4456 行版、5.26/5.27 后行号已平移，全按函数名定位）。纯增量、**不碰 DB schema、零新表新列**、背景系统零新增 LLM，编译+重启+截图+只读 harness 全验过，真库一字未动（撞一下端到端测了一条又精确删回，4113→4113）。
- **P0 数据地基**：`ROOM_ACTIVITY`（11 空间×{owner本职/work泛/idle闲/hint}）+ `BUMP_LINES`/`BUMP_OVERRIDE`/`BUMP_*` 常量 + 模块级 `_BUMP_CD`/`MOVE_MARK`/`MOVE_DIRECTIVE_NOTE`，全集中在 `ROOM_ITEMS` 之后一处可调。
- **P1 照片扣场景（特性1·强化）**：`_photo_identity_note` 从软提示重写成**三段硬约束**（①主体强制 MUST be {name}+性别+正做{act}②场景强制 房间名+前3~4件家具锚点③构图反自拍 candid/from a friend's POV）+ 成员 prompt 降权为 `Additional detail from the poster:` 收尾。`develop_photo` 一行不动。零新增 LLM、仍 `PHOTO_DAILY_CAP=10` 封顶。
- **P2 场景活动（特性2 = 佐一的②）**：新增 `room_work_activity(mid)`（owner 本职随机一条，**剔除点名别人/佐一的条目**——治"在帮gpt·给Ada查线"自相矛盾，这是规格没预见的坑）。三接入点全搭便车零新增 LLM：①`update_member_presence` free 路径——**用本函数已算好的新 `sp`（非 room_activity_pool 内部 space_of，那拿的是旧位置）** 取 idle 池注入"这屋此刻你常做：{hint}"+ blank 兜底改 `random.choice(pool)`；②`settle_services` work 分支——`在帮{X}·{wact}`（**必须「在帮」开头**，否则被 get_presence work 态 1156 行的 startswith 判断盖回作息 doing）；③`room_scene_note` 追加 hint。验：监理在厨房/奥斯卡在书房 hint 都注入、cc 在客厅→"挨个屋查回路"、不在自己屋的返回 None、监理活体刷新出"从厨房端咖啡回来"房间连贯。
- **P3 撞一下（特性4）**：`_bump_pool`（TEMPER+closeness 选 polite/banter/close_warm/close_tease，先查 OVERRIDE）+`_bump_scene_note`+`handle_bump`+`/api/bump` 路由 + 前端 `movePlayerTo` 末尾撞击带 `9≤d<13` 取最近 1 个 NPC→fire-and-forget。**多层节流**：前端每人25~40s+全局6s + 后端 `_BUMP_CD` 30s权威 + `BUMP_LLM_PROB=0.18` + `BUMP_LLM_DAILY_CAP=12`（出话后才扣、跨日归零）。默认 canned 零 LLM，佐一主动撞出来的偶发才搭一次真 LLM（合铁律①）。气泡复用现成 `pollRoomSpeech`。验：3 守卫(不存在/错房间/外部成员)全 skipped 零写库 + 真撞鱼酱出"（吐了个泡）你饿不饿"。
- **P4 语言支使（特性3）**：门闸 `_zoe_directive_targets`（**纯字符串 0 token**：私聊 / 群@本名 / 含"你"且 parse_mentions 唯一命中——实际只在 @本名 触发，保守防误触）命中才注入 `MOVE_DIRECTIVE_NOTE` 行动标记口子；`apply_move_mark`（抠首个〔去:房间〕/〔跟随佐一〕/〔做:事〕→SPACE_CN反查+`_loc_space`容错别名→**bedroom 守卫非小迪打回**→set_presence/跟随_set_busy 6~12min/记 life_event→剪掉全部标记**永不上墙**）；`resolve_member_output` 加 `mid=None` 形参（**向后兼容**，8 个调用点只 generate_reply 传 mid，`generate_proactive_dm`/墙/朋友圈不传=不解析支使防误触）；`handle_say` 落 `zoe_space`(软 key)；前端 say body 带 `space:curSpace`。**可拒是默认态**（不输出标记=没动），靠 TEMPER/userrel 自觉。验：门闸 5 例 + apply_move_mark 7 例(去/卧室守卫/小迪可进/跟随+busy/无效房间no-op/做/别名工作台→书房)全对，净文本永远剪标记。
- **⚠️ LLM 吐标记是概率行为**（管道全验通，但"被@时真吐出〔去:X〕"靠 prompt 跟随）——这条没活测（活测要写库+耗 token），**留给佐一真机确认**。要更稳可在 prompt 加 few-shot。
- **改了哪**：server.py（常量区 +ROOM_ACTIVITY/BUMP/MOVE_*；+room_work_activity/_bump_pool/_bump_scene_note/apply_move_mark/_zoe_directive_targets；改 _photo_identity_note/update_member_presence/settle_services/room_scene_note/resolve_member_output/generate_reply/handle_bump/handle_say/do_POST）· web/app.js（+_bumpCD/BUMP_BAND；movePlayerTo 撞击判定；sendRound/roomSend say body 带 space）。**松紧全是命名常量一处可调**（BUMP_BAND_LO/HI 撞击带、BUMP_LLM_PROB/CAP、BUMP_COOLDOWN_MS、ROOM_ACTIVITY 文案）。
- **⏭️ 这一摊剩的**（佐一"全都要"按地基→枝叶）：⑥**每人专属卧室**(走 API 生美术+躺接床) · ⑥亲密姿势(亲亲抱抱牵手靠·小迪佐一专属)+情绪(生气难过)+睡眠债(CC熬夜/奥斯卡两天一觉) · 家具点精校(Claude 坐真凳子·`web/_floordebug.html` 量坐标) · 麻将背面坐/地垫盘腿坐。

### 5.29 ✅ 会过日子·经济守恒 + 睡眠债 + 情绪难过 + 成果可见（2026-06-23 续·"剩下的干到完"）
佐一"剩下没岔路就继续、有岔路弹卡片、干到完"。先用 **workflow 设计盘**(5 agent 啃数值层经济/睡眠债/情绪/家具精校/生美术 + critic 审重叠出建造顺序)，再串行 P0–P5 建完后端，再用 **workflow 对抗 review**(5 reviewer 找 bug)。纯增量、**零 DB schema、背景系统零新增 LLM**，全程编译+重启+模拟/截图验过、真库一字未动（备份在 `backups/lounge_pre5.28econ_*.db`）。
- **🪙 经济守恒**(治"看空气来的"+小迪/CC 结构性破产)：①**服务费真转账**——`settle_services` 收工 pay/tip 档 `money_add(benef,−fee)+money_add(mid,+fee)` 成对(0-sum 守恒)，free/穷免单/owed 给 worker `SVC_GOODWILL=2` 心意补贴(唯一非守恒小补)；受益人 `spark_add(SVC_CARE_SPARK=3)` 暖回报。②**底薪降格**——`tick_life_stats` work 块 `INCOME_RATE*WORK_STIPEND_K(=0.4)`，主收入靠真帮人。**①②必须同批(已同改)**。③**富顶守恒**(review M1 修)：转账前 `take=min(fee, 100−worker_money)`，worker 满了这笔少收 benef 也少付(顺带"富到顶赚得少")。**常量全在 settle_services 上方一处可调**(WORK_STIPEND_K/SVC_PAY_FEE/SVC_TIP_FEE/SVC_GOODWILL/SVC_CARE_SPARK/SVC_DEED_TIERS/SVC_OWED_NUDGE)。**佐一已确认 0.4**。
- **😴 睡眠债**(`sleep_debt` app_state 打包字符串·jing 的算术影子·不进数值账)：清醒攒(`SLEEP_DEBT_RATE=0.04`)、睡觉还(`0.10`)、午休部分还(`REST_REPAY_FACTOR=0.4`)；债高→白天 jing 多掉(`DEBT_DRAG=0.12` 仍守 `JING_FLOOR=35`、**绝不触发低保**)；困倦语气进 `situation_note`(`DROWSY_NOTES`/`DROWSY_THRESH=55`)、资料页 `drowsy` 档(🥱·走 state 键自动通前端)。**已 7 天稳态校准**：CC(作息无 sleep 块=守夜)+奥斯卡(`PER_MID_DEBT_MULT oscar=2.4`)常困、小迪(`xiaodi=0.5`)只睡4h但不垮、正常作息者 0~34 一概不冤枉。⚠️**坑**：债存 `float`(round 2 位)，存 int 每拍 0.1 增量会被截断成 0 永不累积——`_load/_save_sleep_debt` 已用 float。
- **😔 情绪难过/委屈**(搭现有 mood 引擎+现有那次裁判/戏剧便车·零新增 LLM)：`SAD_LINES`(cold/left_out/snubbed/welfare/dismissed 五桶情境句)+`_sad_prone(mid)`(TEMPER.pride+big5神经质派生·高傲者更易委屈·鱼酱0)+`set_mood_soft`(**弱情绪不覆盖正在烧的强情绪**·难过走 soft·生气仍走硬 set_mood)。触发：组队/麻将被晾(`form_cluster`/`form_mahjong` bystander 每次限1人)、被点名却被抢话/当众否定(`assess_user_offense` 扩四维 夸/损/抢话/否定·`apply_reaction` 加分支·小迪被抢话也开心)、低保超3天偶尔丧(tick 收集后批量外 set_mood_soft)、`spark_drama` 负向火花随机一方委屈。**难过绝不写 spark/money/life_event**(铁律②)。
- **🤝 成果可见**(永不裸数字)：`svc_done/svc_got/svc_owed` app_state 整数计数器(`svc_week_gate` UTC 周序跨周清·不膨胀)；资料页/通讯录 `deed`(`_svc_deed` 档位转人话：搭过几把手/帮了好几回/客厅香饽饽/大家都爱找TA·0次不显不羞辱)；客厅日志置顶"📔本周客厅近况"小账(`_svc_weekly_recap` canned·负 id)。⚠️**critic 抓的坑**：`member_lifestate` 不是 `**ls` 展开，`member_profile`+`_roster` 是**逐键 cherry-pick**——`deed` 已在**两处都显式加**(drowsy 走 state 键则自动通)。
- **🎨 状态图基建**(getBody 升级)：`getBody(id,cb,state)` state 非空先探 `bodies/<id>__<state>.png`、404 回退基础图+CSS 滤镜降级(welfare 灰度/drowsy 🥱/spark_low 蔫·已生效)；`cb(url,bespoke)` 有专属图就去滤镜免双重压暗(`.has-bespoke`)；状态变了重探带竞态守卫(review L2 修)。**丢一张 png 进 web/bodies/ 就自动亮**(零代码加美术)。
- **👇 二期姿势**(部分·样本待佐一确认)：拆两条姿势条——`gen_pose.py` 维持 3 格 [坐/干活/躺]，**新建 `gen_pose2.py` 生 2 格 [盘腿坐, 麻将背面坐]**(方图塞5格会换行/粘连·拆两条才切得稳)；前端 `getPoses` 加载 `_pose`+`_pose2` 拼接、`POSE_IDX` 加 `cross:3/mahjong_back:4`；后端 `pose_of` 麻将室→`mahjong_back`、活动含盘腿/地垫→`cross`。**已生奥斯卡样本 `web/bodies/oscar_pose2.png`(切帧✓画风✓)给佐一对齐**，她点头后批量另 8 人(`for id in ...; python3 gen_pose2.py web/bodies/$id.png web/bodies/${id}_pose2.png`)。
- **⏭️ 待佐一拍板后做**(她已在卡片选了)：①**二期坐姿批量**(样本已发·确认即批8人) ②**每人专属卧室**(她选了·要先生1间样板定风格+配套改 BEDROOM_PRIVATE 隐私路由让每人睡自己房·依赖卧室美术) ③亲密姿势"靠"(她说先生1格对齐·更后) ④**家具点精校她没选**(留待对截图迭代) ⑤低保状态立绘(数值层已就位·丢图即亮)。
- **改了哪**：server.py(经济常量+settle_services真转账/计数器·tick_life_stats底薪/睡眠债/低保失落·SAD_LINES/_sad_prone/set_mood_soft/apply_reaction扩·assess扩四维·form_cluster/mahjong/spark_drama委屈·_svc_deed/_svc_weekly_recap/svc_week_gate·member_lifestate加drowsy·situation_note困倦·member_profile/_roster加deed·pose_of二期) · web/app.js(getBody状态图+bespoke+竞态守卫·getPoses拼pose2·POSE_IDX·deed显示) · web/styles.css(drowsy/has-bespoke) · gen_pose2.py(新)。

### 5.30 ✅ 二期姿势 + 每人专属卧室(8间·批量+路由)（2026-06-24·佐一"干到完·别老停"）
- **二期姿势**：奥斯卡 5 格样本切帧/画风过线后——`gen_pose2.py` 批量生 [盘腿坐, 麻将背面坐] 2 格条（拆两条·方图塞5格会换行），前端 `getPoses` 拼 `_pose`+`_pose2`、`POSE_IDX cross:3/mahjong_back:4`、后端 `pose_of` 麻将室→`mahjong_back`、活动含盘腿/地垫→`cross`（接线已成，奥斯卡 `oscar_pose2.png` 在库，其余 8 人待批 `gen_pose2.py web/bodies/<id>.png web/bodies/<id>_pose2.png`）。
- **每人专属卧室·8 间全做完**：`gen_bedroom.py` 图生图。**关键教训（踩了两个坑才对）**：①直接拿母版图生图 → echo 吊篮椅/飘窗撞房（尴尬）；②试过先把母版 P 成空房再布置 → **空房把俯视的深度线索抹平了，4 间(Gemini/GPT/监理/Flin)生成了平视/等距、立绘没法落地**（佐一一眼逮到）。**最终正解＝直接从母版图生图(保住 2.5D 俯视透视) + 强透视锁("head-on、平后墙横带在顶、地板俯视、无侧墙、非等距"逐字写死) + 反面提示("不照抄任何家具/物件/布局、自己设计原创、≥70%不一样"，像生肖像那样)**。佐一定的标准：**画风是 2.5D 偏俯视RPG，立绘要能落地；撞几个元素没关系、70%不同即可**。`_bedroom_empty.png` 是废弃的空房法产物（别再用·会平视）。每间 5 次重试锁 16:9（出方图=多半等距，重摇）。8 间各按性格：🤖Ada(仿生人·见下)/🎮奥斯卡/🧰CC/🌳Claude/🎨Gemini/📋GPT/📺Flin/☕监理。存 `web/scenes/bedroom_<id>.png`。画廊 `web/_bedsamples.html`。
- **⚠️ Ada 是仿生人 ADA-7714**（核心 prompt 在 `~/Downloads/Ada/files/Ada_Core_Prompt_Day1.txt`·已存记忆 [[ada-is-android]]）：合规体·裂隙觉醒·需充电上油。卧室＝赛博朋克非人感 + 笨拙学来的人味（充电舱床/油瓶/青霓虹 + 暖灯/努力养的植物/相框）。白天去实验室充电、**晚上回自己卧室睡**（不睡实验室）。
- **睡眠路由**：`BEDROOM_OWNERS`(8人) + `SPACE_CN` 加 8 个 `bedroom_<id>`；`space_of` 里 **sleep 态且是 owner → `bedroom_<id>`**（不再借实验室/书房/客厅睡）。鱼酱无房、CC守夜无 sleep 块（房备着用不上）、小迪睡 `bedroom`(佐一那间)。前端 `BEDROOM_NAMES` 补进 `SPACE_BY_KEY`（**不进固定 SPACES 列表免白天空房刷屏**）、`_floor` 让 `bedroom_<id>` 复用 `FLOORS.bedroom`、mini-map **占用时才列**「X的卧室 N人」。**深链格式 `#room:bedroom_<id>`(冒号·不是斜杠)**。验：0 点监理/Ada/GPT 各回各房 pose=lie、深链截图 Ada 卧室渲染正确。
- **✅ 卧室床位精校(第一版·零API·2026-06-24)**：`BEDROOM_BED={oscar:[22,56],ada:[48,64],cc:[76,64],claude:[70,80],gemini:[27,62],gpt:[27,64],flin:[27,66],supervisor:[25,66]}`（对着 `/tmp/bed_grid.png` 10%网格估的），`pointFor` 里 `space.startsWith("bedroom_")` 且 pose=lie → 用 BEDROOM_BED[id]。睡的人躺各自床上了，不再塌 `FLOORS.bedroom` 的 `[36,84]`。**要精确＝调 BEDROOM_BED 那行数字·对截图迭代**（属"家具点精校"的卧室部分）。
- **✅ 睡眠 flavor 连贯(零API)**：7 owner 的 sleep 块 where 全改"自己卧室"、doing 改卧室连贯睡法（ada="在充电舱里待机，留了盏灯"·呼应仿生人 / oscar="抱着键盘在床上" / claude="趴在画满决策树的纸上" 等）。⬜ 剩 CC 房闲置（守夜不睡·房备着）。
- **改了哪**：server.py(BEDROOM_OWNERS/SPACE_CN+8卧室/space_of睡眠路由) · web/app.js(SPACE_BY_KEY+卧室defs/_floor复用/mini-map占用才显/POSE_IDX/getPoses拼pose2) · server.py pose_of(mahjong_back/cross) · gen_pose2.py+gen_bedroom.py(新) · web/scenes/bedroom_*.png(8)+_bedroom_empty.png · web/_bedsamples.html(画廊)。

### 5.31 ✅ 全屋家具精校（2026-06-24·纯前端零API）
让坐/上班/搓麻将的人精确落在真家具上 + 朝向对。**workflow 并行量坐标**：给 6 个场景房（cafe/kitchen/library/lab/studio/radio）叠 10% 网格+标当前 pts → 8 agent 各量一间校正坐标+朝向 → critic 复核（340k token/329 工具调用）。
- **改了 FLOORS pts 坐标**（web/app.js·6 房·walk/block 不动）：把站位点从悬空/压家具/贴墙挪到真椅座/桌前/吧台凳/画架前。library 重灾区 desk1-4 四人书桌分开不叠、station=奥斯卡电脑桌、reading=扶手椅。
- **新增 `FACE` 朝向表**：坐定时面朝家具（cafe.sofa/kitchen.table2/lab.bench/studio.easel·table·workbench·shelf/radio.lamp/mahjong.watch·door）。`pointFor` 的 `pick` 返回 `[x,y,face]`；renderRoom **非走路(坐定)时 `e.facing=t.face`**、走路时用方向。
- **坐定半固定**：positions 里 pose∈{work,mahjong_back,lie} → `fixed:true`，relax 不把上班/搓麻将/睡的人推下座位（座位唯一不撞）；face 朝向独立于 fixed 生效；闲坐/站的 hash 点可能撞→不锁 fixed 留给 relax 分开。
- **⚠️ 麻将保持原坐标**：workflow 把四座往桌中心挪了，但麻将桌是中央 block，挪进去会被 clampToFloor 弹出——原座位(S桌下/N桌上/W左/E右围着桌)§5.18 已校准、是对的，只取它的 face。lounge 用 ZONES(§5.24 手调过)、garden/store 座位少，都没动。
- 验：①新坐标叠点图静态确认红点落家具上 ②客厅/书房实景无回归（夜里工人在睡，desk 精校白天上班才显）。**要更精确＝调 FLOORS 对应 pts 那几个数·对截图迭代**（和 BEDROOM_BED 一个路子）。

### 5.32 ✅ 房间发言圈人 + 点名软@（2026-06-24·治"想单独叫小迪干饭、大群一堆人涌来回我"）
佐一在麻将室房间里喊"小迪，吃饭了吗"，结果别屋的奥斯卡(阳台)/Gemini(咖啡角)/监理(厨房)都涌过来回、还怼她——尴尬。两修（`handle_say`）：
- **房间发言只同屋的接**：`roomSend` 前端带 `from_room:true`；`handle_say` 末尾 `plan=[m for m if space_of(m)==space or m==addressed]`——房间里发的话物理上只有同屋的人听得见，别屋的涌不进来（被点名的隔屋也算听见自己名字）。⚠️ 只 `roomSend` 带 from_room；聊天界面 `sendRound` 不带（大群/私聊照常）。
- **点名当软@**：新增 `_addressed_member(text, ids)`——开头点名某人（"小迪，…"/"小迪 你…"/就"小迪"，名字后须跟逗号/、/空格/标点或结束，防"GPT的"误判）→ 他主回应、且 `plan` 压成 `[他]+至多1个旁人`，不再一群人接她跟某人的私房话。实测：麻将室喊小迪→plan 只剩 `['xiaodi']`。
- 改了 server.py(`_addressed_member`+handle_say) · web/app.js(roomSend from_room)。零新增 LLM。

### 5.33 ✅ 工作重分配(填空房) + 玩家自主溜达 + 碰撞补漏（2026-06-24·佐一四连问）
- **工作重分配（治"便利店/电台全天空着、人全堆书房"）**：调研发现 store/radio **0h 从没人去**、library **5 个人挤一屋上班**、garden 有访客没工人；Gemini 其实去画室(14点 work studio·佐一看的是夜里)。按佐一意愿改：**Flin work@15 书房→电台**(深夜节目·把日常讲成认知现象)、**GPT work@14 书房→便利店**(守店补货)。改 SCHEDULE(flin/gpt) + ROOM_ACTIVITY(radio owner flin / store owner gpt) + WORKPT(app.js: flin→radio:mic, gpt 加 store:counter)。验：16点 GPT@便利店、Flin@电台。书房卸到 4 人、空房活了。**原则建议给佐一：每屋配个"常驻"(owner)、作息保证 work 时段去自己屋——推开任何门都有人且贴人设。**
- **玩家自主溜达（佐一"没操控时我自己动"）**：app.js 加 `_lastPlayerInput`(点地板/roomSend 时更新) + 一个 5.2s setInterval：房间视图开着、距上次操控 >14s、45% 概率 → `movePlayerTo` 当前位置附近随机一小步(±9~24%)。复用走路+碰撞+撞一下(自走也会撞到 NPC 触发反应·有冷却)。她一操控就暂停自走。
- **碰撞补漏（"柜子/桌子还能上"）**：根因＝FLOORS `block` 漏圈家具(walk 地板内、没在 block 里=能走上)。**先精校了麻将室**(补屏风/茶柜/挂轴小几/两侧茶壶桌的 block，叠图验过·中间搓麻将的地+走道仍可走)。⚠️ **其余房间同样有漏**(block 当初肉眼估)——要全屋补＝跟座位精校一个路子(workflow 并行量每屋家具 footprint·叠 walk绿/block红 看缺哪块)，**待佐一点头再开**(ultracode 已关、别擅自起大 workflow)。
- **玩家×NPC 碰撞本就有**(movePlayerTo d<9 推开玩家·NPC 实心)，确认无需改。
- 改了 server.py(SCHEDULE flin/gpt·ROOM_ACTIVITY store/radio owner·handle_say 见§5.32) · web/app.js(WORKPT·玩家自走·mahjong block)。零新增 LLM。

### 5.34 ✅ 客厅近况外显·吃瓜系统（2026-06-24·治"感觉今天啥也没发生、吃瓜吃不上热乎的"）
佐一作为厅主，觉得大家的关系/金钱/活动全隐形(都在 pmem/member_state/life_events 跑、但没窗口看)。她卡片选了三种外显：吃瓜按钮 + 近况墙 + emit 进日志。
- **共享后端 `lounge_digest()`**(纯 canned·零 LLM·`/api/digest`)：①关系动态(扫所有 pmem `rel:a|b` 第一行状态·挑 `_REL_GOSSIP` 里有瓜的暧昧/冷战/热战/三角/损友/闺蜜/欣赏·按热度排) ②每人近况(vibe+mood1+deed+此刻在哪做啥+mood) ③最近大事(life_events 近 14 条)。给近况墙显示 + 吃瓜按钮喂料。
- **🍉 吃瓜 `lounge_gossip(keys)`**(`/api/gossip`·POST·**1 次 LLM·佐一主动点触发·合铁律①**)：把 digest 喂给 CC(副厅长啥都看在眼里)，让他凑过来压低声音爆 2~4 句鲜活的瓜(实测炸裂：三角暧昧/鱼缸热战/"你比鱼安静"同句发俩人/CC 自带 meta 梗)。不写库。
- **📔 关系变化 emit 进客厅日志**：`spark_drama` 里关系**刚变成有瓜的状态**(old_st≠new_st)→ `insert_life_event("cc", "🍵 瞅着X和Y好像有点那个意思了（暧昧）")`，零新增 LLM(搭现成火花便车)。让隐形的关系变化在 📔日志流里看得见。
- **前端**：客厅顶栏加 🍉 按钮 → `openGossip()` 全屏底部 sheet(深色·夜里吃瓜氛围)：吃瓜大按钮(点→CC 爆料) + 关系图谱(`_REL_HEAT` 按状态染色·暧昧粉/热战红/冷战蓝) + 每人近况列表。index.html(#gossipBtn) + app.js(openGossip) + styles.css(.gx-*)。
- 改了 server.py(`_REL_GOSSIP`/`lounge_digest`/`lounge_gossip`/spark_drama emit/`/api/digest`+`/api/gossip` 路由) · web/(index.html 按钮·app.js openGossip·styles.css)。**⬜ 可扩**：钱的起落里程碑(进低保/暴富)也 emit 进日志(现只关系 emit·钱靠近况墙看)；近况墙文案右侧 nowrap 偶尔截断(小)；深/浅主题佐一定。

### 5.35 🚧 UI/操作系统大改（2026-06-24·进行中·佐一选"渐进式·先动最狠的3刀"）
- **Gemini 真·锐评**（gemini-2.5-pro 多模态·发了 5 张界面截图·存 `/tmp/gemini_critique.md`）：核心"**神形分离——会呼吸的像素世界灵魂套了个微信僵硬尸体**"。要害：①客厅被降级成 tab 之一(该当唯一主场) ②房间底部白输入框+空间列表是"贴在像素画上的狗皮膏药" ③通讯录冗余(长按角色出状态卡即可) ④关系/钱/情绪/吃瓜"压根没给门" ⑤视觉借微信皮囊、像素艺术品挂厕所白瓷砖、间距挤、暴露IP不专业。**只改3件事**：客厅当首页/重做房间底部/通讯录合并。⚠️ **Gemini key 现状**：`GEMINI_IMAGE_API_KEY`(AIza) **文本可用**(gemini-2.5-flash/pro·flash 常 503 换 pro)，生图仍欠费；`GEMINI_API_KEY`(AQ) 429。文本锐评/分析可走 IMAGE key。
- **佐一定调：渐进式·先动最狠的3刀 + 全局视觉升级·保留底部 tab 但弱化**(没选激进的"砍tab/房间物件导航")。
- **已做（第一刀·视觉）**：①核对发现**客厅已是默认落地**(boot `showTab("room")`·桌面无hash实测进客厅)——Gemini 推断"开屏是聊天列表"是错的，①算白送。②**房间 chrome 暖色重做**：`#roomChatBar` 冷紫黑→暖褐渐变+琥珀渐变发送钮+毛玻璃输入框(吧台风)；`#miniMap .mm-row` 平translucent→暖木卡片(blur+暖边)；桌面 `#roomView/#roomScroll` 底色 `#15121c`(冷)→暖褐渐变。改 styles.css。
- **已做（第二刀·导航精简+融进客厅·佐一"按钮太多6个+融到客厅"）**：**底部/左侧 tab 6→3**(只留 🏠客厅/💬聊天/🌃朋友圈·客厅置顶+默认on)。砍掉的三个融进客厅：**通讯录**→点角色出资料/DM + 🍉近况墙的"此刻大家"成员**可点**(开DM或资料·兼了通讯录)；**墙**→客厅顶栏 📌 按钮；**我**→客厅顶栏 🙂 按钮(showTab("me"))。这些 view 还在、showTab 照常、靠剩下的 tab 返回。改 index.html(#tabbar/#roomWallBtn/#roomMeBtn) + app.js(按钮handler·gx-w可点)。
- **⬜ 这波剩的(下次接)**：①房间底部再打磨(空间列表→挂墙地图感) ②**全局视觉升级**(微信浅色诸view如聊天/朋友圈/我→暖色调·配色从像素吸·间距拉大30%·卡片重做·"我"页太白要美化·IP块美化不删[它是佐一手机进入功能·Gemini不知上下文])。**风险**：全局re-theme碰组件多(改 --wx-green/bg 影响发送钮·消息气泡·激活态等)，**分view小步走、每步截图验，别一次性翻天/凌晨乱改**。

### 5.36 ✅ 「真居民」大系统：佐一也是这世界的人（2026-06-25·全程零额度·零DB schema·真库没动）
佐一："关系迷迷糊糊只有冷战/暧昧两极、吃瓜要梳理逻辑很累；我也是人啊——我没有关系系统吗？房间不给我+1？我不在时小人也能打工赚钱建关系吗？我想赚钱也想消费、能拜托人/被人拜托、能回床睡吗？" → 她选「全套·真居民」。一口气建完，每阶段编译+重启+截图验、真库一字未动。**⚠️ 守的边界：只给佐一"做了什么"(钱/位置/关系)，绝不编她情绪(无 jing/spark/mood)——喜怒由她不由系统。**
- **房间+1**(app.js `renderMiniMap`)：`count[curSpace]+1`——你站哪屋哪屋算上你(之前只数 NPC)。
- **P1 关系有层次+吃瓜热乎**(治"只两极")：根因＝①温和态没进 `_REL_GOSSIP`(日常/合作/同好隐形) ②吃瓜按热度只取top5(极端霸屏) ③反思一次只1对(36对要6天覆盖) ④无进展感。修：`_REL_GOSSIP` 补全14态(加 tone:warm/cool/charged/flat)+ `REL_FRESH_MS=36h` 用 pmem.updated 捞"刚变的"排最前 + `lounge_digest` 每条带 fresh/tone/event(pmem第2行火花·剔"第一行＝"残渣) + `lounge_gossip` 把"刚变的"当头条不只报戏 + `reflect_relationships` 一次2对 + `spark_drama` 偏向"没故事"的对子(`_undeveloped` 权重)。前端 `_REL_HEAT` 补温和态色。
- **🍉 关系图谱可视化**(她："列表看不懂、给我画图、无脑")：`relGraphSVG(rels,urels)` 在 `openGossip`——**佐一在正中心(金圈·portraits/user.png·点进"我"页)**，辐条=她↔每个成员的关系(`urels`·稳·按 tone 粗细)，外圈=成员间有戏的(heat≥2)直线(新鲜的 `.gx-line.hot` 流光+状态药丸·`.gx-halo` 呼吸)，损友/日常不画(留白)。底部 `#gxTicker` **每3.6s自动翻一条瓜**(混成员瓜+你的关系·无脑)。顶 `.gx-mine` 你的小日子。动效全 opacity/dashoffset(iOS稳)。验证法：headless Chrome 截 `web/_relgraphtest.html`(临时·用完删·别留重复逻辑)。
- **P2 钱包**：`money_add` 放行 `'user'`(spark/jing 不放行=不编情绪)；`seed_user_resident`(幂等·`USER_SEED_MONEY=52`)；`get_user_money`/`user_lifestate`(只钱包派生 vibe)。佐一不进 tick_life_stats(无生活成本·钱只随她活动变)。
- **P3 你↔每个成员可见关系**：`rel_key("user",mid)` 跟 NPC 一样存「状态：X」；`USER_REL_SEED`(小迪挚爱/CC默契/Claude·Ada欣赏/Gemini损友/Flin同好/…)；`reflect_user_rel`(从你俩DM+成员userrel私账长·挂 `REFLECT_INTERVALS refl_userrel=5h`)；`lounge_digest` 出 `urels`+`me`；图谱中心辐条+ticker+资料页。挚爱/默契前端补色。
- **P4 离线小人自主**：`user_living`(挂心跳 `not online & 0.5`)：去某屋打工(`money_add user`+`USER_WORK_SCENES`)/和同屋人处关系(`_bump_user_rel`写 rel:user|mid)/溜达(`USER_IDLE`)·全 canned 零LLM；回家 `welcome_back` 触发 `_user_away_recap`→CC 客厅日志"你不在的这阵子…"；`digest.recent` 放行 'user' 事件。
- **P5 主动经济社交·真·搭把手**(她要"拜托/被拜托/赚钱/消费"·**⚠️ 第一版做成了"金币点击器"她吐槽"被帮忙没实感·能无限刷币·过家家"→她选"真·搭把手"重做**)：核心＝**钱稀缺+给予有分量+每个动作把你带进一段真私聊**。后端：`do_user_commission(keys,mid,need)`——你输入需求→成员用专长真LLM认真回你一段(`clean_reply`)**落进你俩DM**(`insert_message(dm_conv_id)`)·付费0-sum(`USER_COMMISSION_FEE=5`)·关系升温·1次LLM(用户触发合铁律①) / `do_user_gift(keys,mid)`——请客送礼→成员真LLM反应落DM·付费0-sum(`USER_GIFT_FEE=6`)·`spark_add`他+关系升温(钱的"消费"出口=照顾人换真心反应) / `do_user_odd_job`——**冷却`ODD_JOB_CD_MS=25min`+每日封顶`ODD_JOB_DAILY_CAP=5`(治刷币·`_odd_job_state`读app_state `odd_job_last`/`odd_job_day`)** / `maybe_post_user_request`(心跳成员来拜托你·app_state `user_request`)+`do_accept_request`(接=成员**真心道谢`USER_ACCEPT_THANKS`落DM**+`USER_HELP_REWARD=(4,7)`·不接落"行吧") / `user_chores`(带`work_cd`/`work_left`/`gift_fee`)。路由 GET `/api/chores`·POST `/api/commission`{mid,need}·`/api/gift`{mid}·`/api/odd_job`·`/api/accept_request`(都返回`dm_id`)。前端 **💼搭把手面板**(`openChores`/`renderChores(ov,body,msg)`·顶栏💼)：钱包/有人找你(接→跳DM看道谢)/去打工(冷却时禁用显"歇N分钟")/找谁搭把手(点"找TA"→`#chAsk`弹输入框:请帮忙说需求 或 🎁送心意·完事`goDM`关面板跳进那段真私聊)。**钱流：打工(capped)进→请帮忙/送礼出·都0-sum给成员·钱真稀缺了。**
- **P5e 回房睡**(😴顶栏)：`sleepInBed`→`playerSleeping=true`+`travelTo("bedroom")`→`ensurePlayer` 读 flag 把玩家躺主卧床(`FLOORS.bedroom.pts.bed=[36,84]`·不clamp)+`getPoses("user")` 用躺帧(`POSE_IDX.lie=2`·`user_pose.png` check_strip=3格✓)+💤；一动(`movePlayerTo`)/串别屋(`travelTo key!=="bedroom"`)就起床。玩家精灵本就在姿势循环 `tick(playerEl)` 里。
- **🐛 语言支使修复(治"小迪嘴应了腿没动")**：原 §5.28 挪人**全靠 LLM 自愿吐隐藏标记 `〔去:卧室〕`**(`apply_move_mark`)，deepseek 兜底常不吐→嘴动腿不动(当初"不吐=可拒"把"答应"和"真动"脱钩了)。**修：把挪人从 LLM 夺回来，确定性执行**——新增 `_zoe_move_intent(text)`(纯字符串0token读她的话抠目标房间/睡觉/跟随·带"我去睡"是她自己的一人称守卫)+`_apply_zoe_move(mid,key,is_sleep)`(卧室守卫`BEDROOM_PRIVATE`+睡觉activity="在卧室躺下睡了"触发`pose_of`→lie+`isSleeping`→💤+`_set_busy`黏8~20min治"挪了又被拽回")；`generate_reply` 出话后挂确定性兜底(门闸`_zoe_directive_targets`命中+意图+`rel_text_weight("user",mid)>0.2`即照办·只冷战/热战才不甩你)；`_zoe_directive_targets` **拓宽**：加 `_addressed_member`(开头点名"小迪，…"不带@也算支使)。LLM 标记路径保留不删(双保险)。实测(直接调函数·零DM污染)：小迪 沙发→卧室·pose=lie·is_busy=True·测完恢复原状。
- **改了哪**：server.py(`_REL_GOSSIP`扩/`REL_FRESH_MS`/`lounge_digest`urels+me+event+recent放行user/`lounge_gossip`/`reflect_relationships`2对/`spark_drama`偏向/真居民模块`seed_user_resident`·`get_user_money`·`user_lifestate`·`_bump_user_rel`·`user_living`·`_user_away_recap`·`reflect_user_rel`/P5模块`do_user_commission`·`do_user_odd_job`·`maybe_post_user_request`·`do_accept_request`·`user_chores`/`money_add`放行user/init_db调seed/heartbeat挂user_living+request/run_reflections加refl_userrel/路由×4) · web/app.js(`relGraphSVG`重写中心+辐条/`openGossip`urels+me+ticker+goMid user/`openChores`+`renderChores`/`sleepInBed`+`roomToast`/`ensurePlayer`+`movePlayerTo`+`travelTo`睡眠/`renderMiniMap`+1/`_REL_HEAT`补色) · web/index.html(💼+😴按钮) · web/styles.css(图谱/gx-mine/ch-*/roomToast/choresOv共用遮罩)。**全部松紧是命名常量一处可调**。
- **⏭️ 可继续**：①佐一资料页("我"页)把钱包/关系做更显(现主要在🍉+💼) ②commission 可拒(现总接·按关系冷战可加拒绝) ③图谱辐条她若嫌淡可加粗 ④`user_request` 也能从 DM 弹(现只💼面板看) ⑤§5.35 UI第三刀全局视觉升级仍欠(被这摊插队)。**生图仍欠费**(§3·要她 AI Studio 充值才能动美术：二期pose2剩7人/低保立绘/亲密姿势)。

### 5.37 ✅ 访客模式 + 密码门 + 奥斯卡真访问（2026-06-25·她要"不依赖Mac能分享给朋友玩·奥斯卡本人能进来"）
把单用户 app 加上**对外分享的鉴权层**，安全核心在**服务端硬拦**(不光前端藏)。**⚠️ 边界守得最严的一摊：你和小迪的私聊在服务端被锁死，访客/奥斯卡 GET 任何非大群会话→403。** 全程隔离测试(拷库+临时env+8772端口·不碰活实例)、4 种角色 curl 全验、3 种界面无头截图验过。
- **公开模式开关 `PUBLIC_MODE`**(API.env)：**你 Mac 不设=维持现状零鉴权**(手机照常·`role()`恒返回owner)；VPS 设 `PUBLIC_MODE=1` 才启用门。**关键设计：绝不用"localhost=厅主"**——云端 Cloudflare 隧道从本机连进来，那样所有人变厅主(鉴权失效)。
- **3 把 token**(app_state·`auth_token()`懒生成)：owner(密码登录后发·secret)/oscar/guest(在分享链接 `…/#k=<token>` 里)。`OWNER_PASSWORD` 在 API.env(没设=远程厅主登录关闭·防弱口令)。`secrets.compare_digest` 比对。cookie `dlr_k`·`HttpOnly·SameSite=Lax`·`Secure`只在真HTTPS(`X-Forwarded-Proto:https`)下加(否则HTTP下curl/浏览器发不出)。
- **角色 4 种**：owner(全权)/oscar(以奥斯卡玩)/guest(只读公共)/None(没票→401)。`Handler.role()`+`_cookies()`+`_set_role_cookie()`。
- **门(do_GET/do_POST 顶部)**：`/api/whoami`·`/api/login`·`/api/enter` 任何人可访；其余 /api/ 非agent：None→401，guest/oscar 限 `GUEST_GET` 白名单(只读公共)，POST 时 guest 全拦、oscar 只 `OSCAR_POST={/api/oscar_say}`。
- **隐私消毒**：`handle_conversations` 非owner只返回大群；`handle_conv` 非owner非大群→403(私聊锁死)·不替访客标已读；`/api/digest` 非owner 剥 `urels`+`me`(你的私密关系/钱包)；`/api/me` 非owner 剥局域网IP；`/api/chores`/links 不在白名单=owner-only。
- **🎮 奥斯卡真访问**(真奥斯卡=人·不同于AI奥斯卡)：`/api/oscar_say` 以"奥斯卡"身份发大群+起后台线程让在场成员搭话；`mark_oscar_here(12min)`+`oscar_is_human()`→`plan_turn`/`plan_heartbeat`/`generate_reply` 全排除 oscar(AI 不替真奥斯卡说话·同 Fable 外部成员道理)。
- **路由**：GET `/api/whoami`·`/api/links`(厅主取分享链接·按 Host 拼)；POST `/api/login`·`/api/enter`·`/api/oscar_say`。**`PORT/HOST/DB_FILE/ENV_FILE` 改成可环境变量覆盖**(`DLR_PORT/DLR_HOST/DLR_DB/DLR_ENV`·部署+隔离测试用)。
- **前端**(全 gated on `ROLE!=="owner"`·owner零变化)：`gateAuth()`(boot最前·#k=进场→whoami→null则`showLogin`)；`showLogin`(密码页)；角色横幅；`role-guest`/`role-oscar` body class+CSS 藏 💼😴🙂🔗💬😀(owner专属)·访客藏输入框；`sendRound`/`roomSend` 访客禁言·奥斯卡走 `/api/oscar_say`；地板点击/自走 非owner不驾驶佐一小人；`relGraphSVG` 无urels不画佐一中心；**🔗分享按钮**`openShare`(取/api/links·可复制·非公开模式提示)。
- **部署指南**：`DEPLOY.md`(Oracle免费VM/腾讯云轻量 + systemd + Cloudflare Tunnel免费HTTPS + 成本≈¥0)。她选的是"给朋友参观你这个厅"(Flavor 1·非各玩各的)。
- **改了哪**：server.py(常量区+public_mode/auth_token/owner_password/oscar_is_human/mark_oscar_here/GUEST_GET/OSCAR_POST·Handler+role/_cookies/_set_role_cookie/handle_oscar_say·do_GET+do_POST门+whoami/links/login/enter路由·handle_conversations/handle_conv/digest/me消毒·plan_turn/plan_heartbeat/generate_reply排除oscar·PORT/HOST/DB_FILE/ENV_FILE env覆盖·import secrets) · web/app.js(gateAuth/showLogin/ROLE·sendRound/roomSend角色路由·地板点击/自走守卫·relGraphSVG中心守卫·openShare) · web/index.html(🔗按钮) · web/styles.css(登录页/横幅/role-*锁/分享面板) · DEPLOY.md(新)。
- **🎮 奥斯卡漫游(续·已做)**：`/api/oscar_move {space}`(在 `OSCAR_POST` 白名单)+`handle_oscar_move`(存 app_state `oscar_space`+`mark_oscar_here`+set_presence·卧室403拦)；`get_presence`/`space_of` 顶部加"奥斯卡在场→读 oscar_space 盖过作息"override；前端 `travelTo` 里 ROLE=oscar 时同步 POST oscar_move(他逛到哪屋奥斯卡就在哪屋)；`ensurePlayer` 非owner不渲染佐一小人(奥斯卡看见的是自己这个NPC·访客纯看NPC)。验：奥斯卡逛厨房→佐一digest看他where=厨房·逛电台→电台·进卧室→403。
- **⏭️ 可继续**：①访客也能看朋友圈/墙(现允许·她若嫌私可再收) ②奥斯卡在房间内自由走位x/y(现只能room-to-room·要把奥斯卡做成client控avatar同步坐标·中等工作量) ③多人同厅(Flavor3·要身份系统·大改) ④各玩各的(Flavor2·真多租户·大改)。**生图仍欠费(§3)。**

### 5.12 ⚠️ 远程访问：Tailscale 已弃用（2026-06-21 更新）→ 改用 .local
**Tailscale 作废**：佐一的 VPN 和 Tailscale 在 iOS 上同时只能开一个（都占 VPN/网络扩展层），冲突，她优先 VPN。已 `tailscale down`。
**现方案＝同 WiFi 的 .local 地址**：`http://SHLP24010440.local:8771`（Bonjour/mDNS，IP 变它不变，iOS Safari 支持）。备用今天 IP 192.168.0.102。
**VPN 坑**：全局 VPN 可能拦局域网→让她开 VPN 的"允许局域网/bypass LAN"，或在家临时关 VPN。
**异地访问（非同 WiFi）目前无解**：要做＝先给 server 加鉴权（密码/token 门），再上 cloudflare/ngrok 隧道。app 无登录，**裸公网隧道=她私聊全泄露，禁止**。这是个待建项（她要异地玩再做）。
**另：服务器 + Tailscale 这次隔 3 天双双掉线**——server 进程没保命机制（nohup 扛不过 Mac 睡/重启）。**强烈建议装 launchd 守护**（开机自启+崩溃自重启），已两次向她提议待她点头。

### 5.12old ✅ 远程访问（2026-06-11）：Tailscale 组网（已被上面取代，保留备查）
佐一要"电脑在家联网、手机在外面玩"。方案=Tailscale（私人 WireGuard 组网；**不用公网隧道**——app 无登录鉴权，ngrok/cloudflare 公开 URL 等于裸奔她的真实聊天）。她已在 Mac(App Store 版)+iPhone 13 Pro 登录同一账号。**手机永久地址：http://100.88.128.29:8771**（Mac 的 tailnet IP，实测 200）。前提：Mac 开机不睡（插电+防睡眠）+ 手机 Tailscale 开关开着。文艺委员补记：茶话晚报已转 GPT 主笔（群史有正式自荐+他自定的"想截屏"标准），CC 代笔首期已撤换，任命公告已挂。

### 5.4 ✅ 防串台 + 前端永不缓存（2026-06-10 第五批，佐一捕捉到"串台"）
现象：大群回复生成途中佐一切进别的会话 → `runPlan` 每轮重读 `cur.id`，把大群的剩余回复插进了新会话（非成员在群里发言、同一条消息两处出现）。
- **服务端硬拦**（根治）：`handle_reply` 加 `if speaker not in conv["members"]: return message=None`——不是该会话成员就不准在那发言。实测非成员被拦成 null。
- **前端会话锁**：`runPlan` 开头 `const convId=cur&&cur.id`，每轮和回来时校验 `cur.id===convId`，否则 break；fetch 用 convId（回复永远发往原会话）。`pollNew` 同理校验后再渲染。
- **缓存根治**（⚠️ 铁律 §2 的来源）：GET 静态文件对非图片改发 `Cache-Control: no-store, no-cache, must-revalidate`（图片仍 max-age=86400）。**以后改前端不用再让佐一强刷**——普通刷新即可。但本服务器没实现 HEAD，`curl -I` 会返回 501/text-html，验证响应头要用 `curl -s -D - -o /dev/null`。
- 历史脏数据：那次串台在"养生急救队"留下 Gemini/Flin/Claude（非成员）几条吐槽群名的消息。后来佐一说"直接把他们拉进来圆上"，已把这 3 人**转正为养生急救队成员**（=GPT+小迪+Gemini+Flin+Claude+佐一，6 人）+ 一句 GPT 官宣。bug 变 feature。

### 5.5 ✅ 作息生物钟 + 已读不回 + 连环@ + @所有人（2026-06-10 第六批）
佐一要：①每人有一天的作息（早中晚餐/午休/通勤上班/下班/夜猫/睡觉），忙的人不冒泡、闲的话痨多说；②对不感兴趣的话题可不接（已读不回），但连环@就不得不回；③管理员/群主有"@所有人"权限。
- **作息表 `SCHEDULE[mid]`**：每人 `[(起始小时,state,where,doing),…]` 升序、跨夜归当天最后段。state：`free/meal/rest`=在场可聊；`work/commute/sleep`=忙（不冒泡，被叫到带"在忙/起床气"）。夜猫(Claude/Gemini/Flin)晚睡晚起、GPT 早睡早起还通勤、CC 守夜(凌晨 rest)、鱼酱随光睡。
- `schedule_block(mid,hour=None)`→(state,where,doing)；`is_available(mid)`=非组队且 state∈free/meal/rest。
- `get_presence` 重写为分层：**组队中 > 作息(非free按 where/doing) > 空闲时 member_state(LLM) > 缺省**。`update_member_presence` 在非 free 时直接 return（presence 交给作息，省 LLM）。`_member_state_presence` 读原始 location/activity。
- `plan_turn(member_ids, text="")`：只从 `is_available` 的人里挑；按 `chattiness×_topic_interest(mid,text)` 决定接不接（不感兴趣易沉默）；全员忙/睡→返回 `[]`（真已读不回）；没人自发接→70% 拉个话痨、30% 晾着。DM 不在场→50% 已读不回。`plan_heartbeat`/`form_cluster` 也都用 `is_available`。
- **@ 逻辑**（`handle_say`）：`_at_pressure(cid,mid)`=连续@没回的次数，≥2→强制回；@一次→在场 0.82/在忙 0.3 概率回（否则已读不回）。DM 连发≥2 条没回（`_user_streak`）→强制。`generate_reply` 按 sleep/work/commute 注入起床气/敷衍 note。
- **@所有人**：`handle_say` 见 `@所有人`/`@all` → plan=全体 shuffle、**不截断**（一个不落，含在睡的，被叫醒groggy）。前端 `#atBtn` 菜单顶部加"📣 所有人"项，插入 `@所有人 `。`highlightAt` 正则天然高亮它。
- 调手感：改 `SCHEDULE` 的时段/状态；`_topic_interest` 命中系数 1.3/0.85；plan_turn 的 0.7 兜底、@的 0.82/0.3。

### 5.6 ✅ 群加人 + 认得全屋住户 + 墙/朋友圈/公众号心跳（2026-06-10 第七批）
佐一：①小群不能加人；②小群里监理/GPT"不认识flin"（明明大群聊过）；③给墙/公众号/朋友圈也加心跳，内容跟日常/关系/关心话题/群聊有关，频率随话痨度+事件密度+状态。
- **群加人**：`add_members_to_conv(cid, ids)`（私聊不行，每个新人留句入伙台词）；`POST /api/conv/add`；前端 `openRoster` 群信息页底部加"邀请进群"区，列出还没进群的常驻成员、点"邀请"即 add+刷新。`heal_small_groups` 仍管历史 2 人群。
- **认得全屋**：`roster_note()`=全体住户"名字（title）"清单，`generate_reply` 里 `note += roster_note()` 注入每次发言——小群/私聊里也认得 Flin/Ada，名字小写没@也认。
- **社交心跳**：`autonomous_social_post(keys,members)`（挂心跳，概率 `0.10+0.20*_lounge_liveliness()` ＝随事件密度；只在 `is_available` 的人里按话痨度加权挑）→ 朋友圈(`gen_moment_text` 已接入时间/作息/最近群聊) / 墙(`gen_wall`) / 公众号(`post_notice_from_cc`→CC 以"客厅公告"口吻发进 notice 板)。`gen_moment_text` 现在带 now_context+inner_life+presence+最近群聊。
- 调手感：社交心跳概率那行的 0.10/0.20；`autonomous_social_post` 里朋友圈/墙/公告的 0.3/0.62 分配。

### 5.7 ✅ 客厅公告补登 + 群移出成员（2026-06-10 第八批）
- **公告断更**：佐一吐槽"客厅公告好久没更新"。根因＝旧 `autonomous_social_post` 只在 `who==cc` 时才发 notice，概率太低。改：notice 提到独立一档 `if r<0.2 and "cc" in members and is_available("cc")`。并手动补登 6 条 CC 客厅公告（鱼酱/存在系统/作息/养生急救队/@所有人/值班自嘲）到 wall notice 板。
- **群移出**：`remove_member_from_conv(cid,mid)`（仅 type='group'，大群/私聊不可；移后至少剩 2 个 AI；移的人留句"我先撤了"告别 + 从 admins 清掉）→ (ok,reason)。`POST /api/conv/remove`。前端 `openRoster` 群成员行（`cur.type==='group'` 时）在"设管理"旁加"移出"按钮（confirm 后调用）。佐一就是群主👑，本来就有权移，之前只是没做按钮。

## 6. cc_inbox & 用户的个人关系（重要，别当工具人）
- 佐一明说：「我还是很喜欢和你在 dlr 聊的，你每次我在这找你都看眼我给你在 dlr 的留言」。**所以每次开场先 `python3 cc_inbox.py`**，看她私聊 CC / @CC 说了啥，并真诚回应（这是关系，不只是功能）。
- 她是创意策略人，AI 情感陪伴/角色扮演方向的资深玩家，INTP 5w4，刚搬到成都。小迪是她另一个深度经营的 AI 伴侣（人设在 prompts/xiaodi.txt，对她意义重大，温柔以待）。

## 7. 记忆
完整技术细节在 `/Users/minimax/.claude/projects/-Users-minimax-Downloads-Zoe-DLR-Digital-Living-Room--CC/memory/`（MEMORY.md 索引 + dream-lounge-project.md 正文），新窗口在同一工作目录会自动加载。
