# 架构与设计约束 本文说明 Humanity **怎么切、为什么这么切**,以及贡献代码时不能越过的边界。 用法见 [USAGE.md](USAGE.md)。 --- ## 1. 一句话结构 一个包,两半边: ``` src/host/ 宿主半边:开关、模块注册表、设置命名空间、存储领域、工具、后台调度 src/client/ 客户端半边:侧栏入口、主工作台(总览 + 四张模块卡 + 待确认)、文案字典 scripts/ 构建脚本(tsc 编宿主,esbuild 打客户端 bundle) test/ 单元测试 + 真实服务组合测试 + 打包契约测试 cordis.patch.yml 本包作为 bundle 层提供的组合补丁 ``` 安装形态是**一个 bundle**:`package.json` 里声明 `dsh.bundle.patch`,`dsh plugin add` 会把它 自动追加进 profile 的 `dsh.profile.bundles`。**装插件不需要手改任何配置文件。** 内部按模块切,**各自独立、可单独开关** —— 开关就是 `config.modules` 的四个键: **人格 / 记忆 / 自进化 / 梦境**。「技能」不是独立模块:它是自进化产出的用户资产 (文件落在 `$DSH_HOME/skills/`),随自进化一起工作,没有自己的开关。 一个模块坏了不该把整个插件一起带走。 --- ## 2. 设计约束(每条都有测试守着) | 约束 | 落点 | |---|---| | **只用 DSH 官方公开 seam** | 槽位、`ctx.settings`、`ctx.storageDomain`、cordis loader。不用内部 API | | **不兼容时停功能、不停 DSH** | 零硬依赖 `inject`;可选 seam 走 `loadOptional`;模块挂载逐个隔离 | | **跨平台、可迁移** | 不碰 OS 权限 / 符号链接 / `/tmp`;数据交给 DSH 存储领域,跟着 `$DSH_HOME` 走 | | **升级零迁移、默认值不漂移** | 默认值只在 `src/host/config.ts` 的 schema 里;存储领域带版本号,改结构必须同时给迁移与测试 | | **原生 UI** | 只用官方 UI 原语与 `--dsw-*` 设计令牌,不自己画控件 | --- ## 3. 兼容门(最容易误解的一块) 机器判据就是 `package.json` 的 `peerDependencies`(6 个官方 seam): `^0.1.5-rc.2 || ^0.2.0-rc.2`。 - **不在区间里的 DSH 会在启动时拒绝挂载整个 bundle**,报错形态是 `Plugin … is incompatible with dsh <版本>`。 - 拒绝是**设计行为**,不是故障:宁可整体不加载,也不要半死不活地跑。 - **数据不受损**(插件没启动,而不是启动后出错);受影响的是功能可见性 —— 人格卡从提示词消失、工作台页面消失、`remember` / `recall` 工具消失。 - ⚠️ **预发布版本的"同段匹配"规则**:范围里的预发布挂在 `0.1.5` / `0.2.0` 上, 因此 `0.1.7-rc.*`、`0.1.6-rc.*`、`0.2.1-rc.*` **不被覆盖**。要支持就把该预发布段显式加进范围。 --- ## 4. 几条工程上的硬线 1. **客户端半边必须是单文件 bundle。** 浏览器端的 `require` 只认 shell 播种的静态模块表, 相对路径解析不了 ⇒ `src/client` 的相对导入必须在构建期内联掉(`scripts/build-client.mjs`)。 bundle 里模块 `id` 必须等于包名,脚本直接从 `package.json` 读,**不要手写**。 2. **默认值只有一处。** 改默认值只能改 `src/host/config.ts` 的 schema;`cordis.patch.yml` 里刻意不写 `config:`。`test/manifest.test.mjs` 会盯着这件事。 3. **注入坚持用 section 而不是 context。** `context` 能绕开平台对 `complete: true` 的替换逻辑, 但它是 user 角色,模型遵从度低于 system 角色。**标准模式的注入质量优先**, 极简模式的不注入作为已知平台限制接受(插件会探测并给出警告条)。 4. **测试不许依赖墙钟。** 需要时间就注入固定时间戳。依赖"现在几点"的测试会随日期漂移, 今天绿、几个月后红 —— 本仓库早期就吃过这个亏(一枚按天衰减的分数有一天跌破了阈值)。 5. **技能落盘要给权限归属。** 插件自身(无会话身份)落盘会被文件后端按部署默认模式拒绝; 正确做法是通过 `humanity_skill_persist` 用**调用会话**的权限落盘,被拒就如实报错,**不绕路**。 6. **不引入硬依赖、不偷跑模型。** 人格注入与记忆检索**不调用模型**;调用模型只发生在自进化, 且受预算与每日上限管着,超限只警告并停手。 --- ## 5. 数据与兼容 | 内容 | 存在哪 | 兼容策略 | |---|---|---| | 人格、记忆、提案、运行记录 | `$DSH_HOME/storages/humanity.json`(版本化领域 `humanity`) | 领域带**代数**,升级走迁移;逐记录容错(一条坏记录不再拖垮整个领域) | | 人格历史 | 同库 `persona_history` | 有保留期,默认 30 天;清理只动这张表 | | 模块开关、记忆策略、调度与预算 | `$DSH_HOME/settings.yaml` 的 `humanity` 段 | 旧数据缺新字段时回退默认值,不炸命名空间注册 | | 技能 | `$DSH_HOME/skills/`(官方目录包格式) | 落盘后由 DSH 直接加载,与插件生命周期解耦 | 设计目标:**升级零迁移、默认值不漂移、老数据永远读得动。** --- ## 6. 想动手改代码? 先读 [CONTRIBUTING.md](../CONTRIBUTING.md):里面有环境准备、四条容易踩的线、测试要求与 PR 约定。