libs 解耦:终审残留事项与后续投入建议
位置变更(2026-11-02):本文自仓库根 docs/plans/ 迁入 docs 站(U 批文档收口)。状态:【活跃】。
2026-11-01 更新(W1-W4 修复批后)| 三切面终审(libs 分层 / 组合根与 HTTP 面 / 文档与打包 CI) 的问题已分批收口:P1 全部关闭;P2 中 health(W2-B)、任务归属(W2-A)、openapi CI 门禁(W4)、 分层盲区四项(W3)、actionlint 豁免(W4)已落地;本文保留仍未修的条目与决策记录。 各节标题后标注状态。
总体结论(三切面终审汇总)
- 组合根「薄且守得住」:apps/api 的 30 条运行时
@/互引全部是组合/HTTP 面引用,无 lib → app; 11 个 APP_GUARD 顺序实测一致;openapi.json 全程零 diff(md5 不变)。 - 依赖方向干净:34 lib / 937 条 import 边,唯一跨层例外是文档化的
exceptions → blpath; madge 零循环;19 个注入端口定义/绑定/消费/implements 四方全对齐。 - 分层门禁已机械守:ESLint 八 rank + 五处注册面 drift(含 ESLint 名单对账)+ 资产断言, 负向探针全部验证过。
P1(✅ 已全部关闭,见各节 W 批标注)
1. 分层门禁的四个已知盲区 ✅ 已修(W3)
packages/tooling/eslint-config/nest.ts的全局 ignores 仍豁免**/*.d.ts(25 个 ambient 文件不在分层覆盖内)——需明确 .d.ts 的类型依赖是否也守;no-restricted-imports不覆盖相对路径跨 lib 与动态import()两条路径(实测均不报错);- drift ⑤ 面只对账「名单」,不对账
layer()的 files/禁用集合接线——删一条禁用项是静默削弱; - 基建层内存在
utils ↔ config ↔ const同层互引 SCC(当前无文件级环,因子路径叶子导入)。 改进方向:用一份有序LAYERS数组派生 files/禁用集合(单一真源),补接线对账 spec、 相对/动态导入规则,并把 env 判定下沉独立叶子消除 SCC。
2. 迁移账本判据(本地已修于 V5;生产库待核)【需在生产环境执行】
DataScopePhase1 类名是 28 字符(DataScopePhase1 + 1793336000000),本地库账本行名曾少一个 0 (已 UPDATE 修正,migration:show 现为 6/6 applied)。判据:生产/预发库 migrations.name 必须是 28 字符同款;若为 27 字符执行同一 UPDATE。不要改类名——TypeORM 用类名末 13 位解析排序, 27 字符会让空库首跑顺序崩坏(已用集成测试实证)。
3. Windows 本地 staging 不可移植【文档已补(W4);执行仍需 WSL/Linux 环境】
build/image/server/node_modules 的 pnpm 符号链接指向本机绝对路径、.bin 是 Windows shim—— 本机 staging 直接 docker build Linux 镜像会得到悬空依赖。可信路径:Linux CI 或 WSL 内重跑 pnpm deploy。deploy/README.md 的本地构建节缺 OS 提示,补一句即可。
P2
4. 健康检查升级 ✅ 已修(W2-B:/health/live + /health/ready + compose + nginx 挡板 + Redis healthcheck)
compose healthcheck 探静态图片路由 = 仅 liveness(不触 PG/Redis);且异常过滤器恒返回 HTTP 200 (错误在 body code),任何走 controller 的健康检查(含 /app/health/*,还需 ROOT/DEVELOPER 角色)都会失去探测意义。建议:新增不鉴权、绕过全局响应包裹的 readiness 端点(依赖挂时真 503), healthcheck 改探它并对 body code 断言;同时给 Redis 补 healthcheck(当前 backend 只等 service_started)。
5. bootstrap 任务归属显式化 ✅ 已修(W2-A:AppBootstrapTaskModule 归属 + rotate 事务内重判 + cron 单实例锁;V5 修复批补抢锁超时与原子释放)
WORKER_STANDALONE 只让位 @Cron,六个 onModuleInit 一次性任务不受控——api + worker 双启动时 rotateAppKey(非幂等)等仍各跑一次(撞 uq_key 被 catch 吞掉)。本轮已给 openapi:export 加 WALNUT_ADMIN_BOOTSTRAP_TASKS=off 隔离;根治方向是把 bootstrap 任务的角色归属(api 专属 / worker 专属 / 双方幂等)逐个显式化。
6. openapi 顺序依赖的第三处未登记
tech.module.ts 的 import 顺序(health 在 task 前)+ task.module.ts 的 AppKeyModule 共同决定 /app/health/*、/app/key/* 先于 /app/monitor/user——重排会改 openapi 字节。ADR 0022 只登记了 monitor/user 与 deleted 两处。另外整个 AppModule.imports 顺序都是 openapi 字节形态真源, 目前只有 prepush 的 openapi diff 兜底。
7. openapi 门禁不在 CI ✅ 已修(W4:ci.yml 新增 openapi job——PG/Redis service + migration:run + openapi:check;gate-wiring 豁免已删)
gate-wiring.test.ts:151 把 openapi:check 豁免给本地 prepush/发版电池;CI 与 release workflow 都没有该段。tag 由本地创建时 openapi 漂移无 CI 拦截。若要收口:CI 加 PG/Redis service 后跑 openapi:check,或约定「动 DTO 的 PR 必须附 openapi.json/schema.d.ts 变更」。
8. 端口三方一致性无机械 spec【未修;19/19 当前人工对账全对齐,spec 待补】
19 个 token 当前定义/绑定/消费/implements 全对齐(人工对账表见 V5b 报告),但新增 token 漏绑定 只会到启动期才暴露。建议补一条 spec:grep 收集 token 定义 → 断言每个都有 provide: 绑定点。
9. actionlint ✅ 本机已装(W4 起 workflow-lint 真跑;service command 键的 schema 滞后已用 .github/actionlint.yaml 窄豁免,actionlint 升级后可删)
prepush 的「workflow 校验」段在本机是 SKIP(✓ 但 ≠ 通过);权威闸在 CI 的 workflow-lint.yml。 本机装 actionlint(或 WSL)后该段会真跑。另:workflow-lint.yml 已补 setup-node@24(V5)。
10. mailer 视图路径的字符串耦合【未修;verify-build-assets 只断言存在不断言解析,待补】
libs/mailer/src/mailer.config.service.ts 用 join(__dirname, '../../../apps/api/src/views/...') 回溯 app 视图;verify-build-assets 只断言资产存在、不证明这条相对路径解析正确(ESLint 也看不见 路径字符串)。改进:把模板根做成注入配置,或给 verify-build-assets 加一条「从 dist/libs/mailer/src 按同款相对路径解析必须命中」的断言。
P3(记录即可)
worker 复用整 AppModule:无 HTTP 的 worker 实例化全部 38 个 controller 与 socket gateway; 在意内存/启动面时拆
AppCoreModule/AppHttpModule(ADR 0022 已声明这是当前取舍)。SocketModule 引 monitor-user-http 的路由顺序装配:与交错点 A 同源,脆但被 openapi diff 兜底。
命名漂移清单(device-geo.dto 已改,W4;其余不影响行为,找机会顺手改):原
libs/list/src/shared.dto.ts实为设备/经纬度 DTO;libs/crud/src/operate-log/operate-log.decorator.ts实为拦截器;SecurityRsa*前缀属技术层;SysDeletedEntity真身在 libs/repository(system 侧无);SysRoleEntity真身在 identity (system 是 re-export);blpath命名不自解释。24 个文件的中文注释含 U+FFFD 乱码✅ 已清(W4,215 行按上下文重写;ASCII 半截乱码另行清理 9 处)。ADR 0022 待补两句✅ 已补(W4:进程级 provider 归属判据 + 与 ADR 0013 的关系)。Dockerfile 加非 root
USER node(当前 root 运行);无.dockerignore(staging 已手工清理, 本地构建依赖 Dockerfile rm 兜底)。docker-bake.hcl nginx dockerfile 路径笔误✅ 已修(W4);~ docker-bake.hcl:nginx target 的dockerfile路径疑似 context-relative 笔误—— 按 bake 语义会解析为下面这个不存在的路径(主流程走docker build未触发;:38注释的 staging 路径过时):textdeploy/nginx/deploy/nginx/Dockerfilelib-registration ④ 不比值:tsconfig alias 的值断言已补(W4);worker/cli 两份 tsconfig 的
include与各tsconfig.lib.jsonoutDir 仍不在守卫面(当前值均正确,新增 lib 时按 drift ⑤ 的名单对账兜底)。ADR 0022:87 「当前全部 19 个 token」口径:另有
WALNUT_ADMIN_OTP_STRATEGY、WALNUT_ADMIN_PERMANENT_BLACKLIST_PATHS等 JWT/OAuth/strategy 组注入常量不在清单(清单本身 19/19 全部真实存在)。
U 批(packages 上收)新增留档
【需产品确认】敏感操作验证方式的三套词汇
- server 授权真源:
password / sms / mfa / email(VerifyMethod,sensitive.const) - MFA 登录:
totp / webauthn(SysUserMfaType,已上收 contract/mfa) - OTP 发送:
email / sms(otpType,按 ADR 决定留 server) - 前端 VerifyAuthMethodType:
sms / email / totp / webauthn(缺 password,已改由 contract/mfa + 本地联合) - 归一方向需产品语义映射确认后再上收
libs/security/src/sensitive/sensitive.const.ts。
【既有风险,未修】签名序列化的 undefined 字段边界
admin 在 JSON.stringify 前签名:{ a: undefined } 签出 a=undefined,而服务端收到的 body 已丢弃该字段 → 此类请求 401。共享函数两端一致,差异在序列化管线。现有表单多用 null,无线上证据。建议后续在请求边界剔除 undefined 或改为「序列化后签名」。 (同族:demo 页 _timestamp 参数先签名后追加会 401,仅 demo 使用。)
【记录】文档数字口径
packages 上收后 contract 123 常量、utils-core 7 测试文件等计数以门禁对账为准 (route-catalog.drift.spec / contract 快照 / entries 门禁),不在文档维护计数。
已验证的基线(W4 后终点状态)
pnpm lint:0 error / 0 warning(含 infra/scripts,历史 2 条 warning 已根因修复)pnpm test(根):14/14 任务;server 37 文件 / 281 例pnpm test:e2e1/1;pnpm test:integration63 文件 / 560 例两轮全绿;TEST_DATA=real5 例(dump 30 表 665,121 行)pnpm build --force:10/10 任务真跑,资产断言 4 项通过,dist 无*spec.jspnpm prepush:23 段全绿(actionlint 段本机 SKIP,见 P3)pnpm smoke:dist:真实 Chrome 冒烟有结论(无后端错误屏 = 设计内)pnpm lint:dist:1473 文件零密钥命中openapi.json/schema.d.ts:全程零 diff(md5 不变)- staging(build/image/*):无 env-local / env-encrypted / .data / .tmp / logs / dist-stage
migration:show:6/6 applied(账本行名已修)
生产/预发部署前清单(判据)
- 核对
migrations.name的 Phase1 行为 28 字符DataScopePhase11793336000000(见 P1-2)。 - 预建
walnut_app角色(NOSUPERUSER NOBYPASSRLS)并设置强密码,更新 env-encrypted 的DATABASE_PG_APP_PASS→pnpm setup-env——本地 L5 防呆当前拦截 prod 启动即因此。 - 镜像构建在 Linux 环境(staging 由
pnpm deploy现做,勿复用 Windows 本地 staging)。 - 启用 worker 时:api 与 worker 两个进程都要设
WORKER_STANDALONE=true(见 apps/worker/README.md;漏 api 侧会 cron 双跑)。