pmc 20036eeb2f docs(01-01): 完成凭据槽位数据层 plan,落地 SUMMARY 与 state 更新
- 新增 SUMMARY.md:3 task / 3 commit / 32+42+26 行代码 / mask_token + CredentialSlot + 0004 迁移
- STATE.md:completed_plans 0→1(50%),下一步切到 Plan 01-02
- ROADMAP.md:Plan 01-01 勾选完成,进度表 1/2 In progress
- REQUIREMENTS.md:CRED-01 勾选完成,traceability 状态 Pending→Done
- 探针数据契约固化:DB pk=1 / access_token='probe_secret_xxxx' 留给 Plan 01-02 浏览器 checkpoint
2026-05-07 17:39:53 +08:00

9.6 KiB
Raw Blame History

phase, plan, subsystem, tags, requires, provides, affects, tech_stack, key_files, decisions, metrics, requirements
phase plan subsystem tags requires provides affects tech_stack key_files decisions metrics requirements
01-credential-data-layer 01 aiapp / common
credential
singleton
migration
mask
masking-util
aiapp.models.CredentialSlot单例模型 + get_solo + save 钩子)
common.utils.mask_token(token, visible_tail=4, mask_char='*')
aiapp 迁移 0004_credentialslot.pyCreateModel
DB 探针数据契约pk=1 / app_id='probe_app' / access_token='probe_secret_xxxx'(供 Plan 02 浏览器 checkpoint 验证脱敏显示)
Plan 01-02 Admin 注册 / 列表页脱敏将复用 mask_token 与 CredentialSlot.get_solo()
Phase 2 / Phase 3 REST 视图统一从 CredentialSlot.get_solo() 取数
Phase 3 阿里云日志 formatter 复用 common.utils.mask_token
added patterns
Django 单例 = pk=1 + get_or_create + save() 钩子重定向1:1 复刻 userapp.models.AffinitySetting 247-314 行)
中文字面量 verbose_name与仓库 14 个模型实操一致;不引入 gettext_lazy
created modified
common/utils.py
aiapp/migrations/0004_credentialslot.py
aiapp/models.py
字段集合最小化app_id(128) / access_token(512) / updated_at不加 created_at单例无创建语义
单例靠 save() 钩子 + pk=1 静默重定向(不抛异常),与 AffinitySetting 一致ROADMAP success criterion #1 解读为 count 守恒为 1非异常拒绝
mask_token 短输入len <= visible_tail走全脱敏分支防长度信号泄露
探针数据 probe_secret_xxxx 写入 DB 后保留不清理Plan 02 浏览器 checkpoint 依赖)
duration_seconds tasks_completed tasks_total files_created files_modified commits completed_at
184 3 3 2 1 3 2026-05-07T09:36Z
CRED-01

Phase 1 Plan 01-01凭据槽位数据层 Summary

一句话:落地 Milestone v1.0「通用凭据槽位」的数据基础——CredentialSlot 单例 Django 模型pk=1 + save 钩子 + get_solo 三件套1:1 复刻 AffinitySetting+ 自动生成的 0004 迁移文件 + 通用 mask_token 工具函数(供 Phase 1 Admin / Phase 3 阿里云日志双方复用)。

完成的 Tasks

Task 名称 Commit 文件
1 新建 common/utils.py 落地 mask_token 工具函数 a9c25eb common/utils.py新建32 行)
2 在 aiapp/models.py 末尾追加 CredentialSlot 单例模型 30c7caf aiapp/models.py+42/-1
3 自动生成迁移文件并执行 migrate a475fe4 aiapp/migrations/0004_credentialslot.py自动生成26 行)

实际新增 / 修改的代码行数

  • common/utils.py:新建 32 行(含 docstring
  • aiapp/models.py:从 52 行增至 93 行(+42 / -1末尾追加 CredentialSlot 类含 4 字段 + Meta + __str__ + save 钩子 + get_solo 类方法)
  • aiapp/migrations/0004_credentialslot.py:自动生成 26 行(依赖 0003_create_rtc_bot

迁移文件实际名称

确认为 aiapp/migrations/0004_credentialslot.py,与 PLAN 期望一致。

依赖:('aiapp', '0003_create_rtc_bot')。 operationsmigrations.CreateModel(name='CredentialSlot', fields=[id BigAutoField, app_id CharField(128), access_token CharField(512), updated_at DateTimeField(auto_now=True)])

自检 Shell 脚本输出

PLAN Task 3 <action> 段规定的探针 + 单例自检:

created= True app_id= '' access_token= '' pk= 1
after second save count= 1 obj2.pk= 1

PLAN Task 2 acceptance #9 规定的 N 次 save 守恒断言4 次 save 验 count 恒为 1

count_invariant_OK

PLAN <verification> 段完整脚本assert 全部通过):

Plan 01 verification PASS

showmigrations aiapp 输出确认 [X] 0004_credentialslot

aiapp
 [X] 0001_initial
 [X] 0002_initial
 [X] 0003_create_rtc_bot
 [X] 0004_credentialslot

makemigrations aiapp --check --dry-run 输出 No changes detected in app 'aiapp',退出码 0CHECK_OK

mask_token 验证结果

mask_token('sk-abcdef1234') == '*********1234'   ✓ (末 4 位 '1234' 明文,前 9 字符脱敏)
mask_token('')              == ''                ✓ (空串短路)
mask_token(None)            == ''                ✓ (None 短路)
mask_token('abc')           == '***'             ✓ (短输入全脱敏)
mask_token('abcd')          == '****'            ✓ (恰等 visible_tail 全脱敏)
mask_token('abcde')         == '*bcde'           ✓ (长 1 位露 4 位)
mask_token('probe_secret_xxxx') == '*************xxxx'  ✓ (与 Plan 02 浏览器 checkpoint 期望串一致)

探针数据当前值确认

SELECT app_id, access_token FROM aiapp_credentialslot WHERE pk=1 通过 ORM 等价:

pk=1, app_id='probe_app', access_token='probe_secret_xxxx', count=1

Plan 02 Task 2 浏览器 checkpoint mask 期望值算法

  • 原 tokenprobe_secret_xxxx(长度 17
  • mask_token(...) 返回:*************xxxx13 个 * + 末 4 位 xxxx,总长 17
  • 故 Admin 列表页 access_token_masked 列应渲染为 *************xxxx

给下游的 Hand-off

下游 公开入口 / 契约
Plan 01-02Admin 注册) from aiapp.models import CredentialSlot 取模型;from common.utils import mask_tokenaccess_token_masked(self, obj);用 CredentialSlot.objects.exists() 判断是否禁用「新增」按钮
Plan 01-02 浏览器 checkpoint 依赖 DB 中 pk=1, access_token='probe_secret_xxxx' 探针;列表页脱敏期望串 *************xxxx
Phase 2 管理端 REST 单例统一访问入口:CredentialSlot.get_solo()(不要直接 CredentialSlot.objects.first() 防止空 DB 时拿 NoneGET 响应序列化时调用 mask_token(obj.access_token)
Phase 3 客户端 REST 同样用 CredentialSlot.get_solo() 取数;客户端 GET 返回明文(不调 mask_token
Phase 3 日志脱敏 阿里云日志 formatter 用 from common.utils import mask_token 直接复用,签名兼容

Deviations from Plan

自动调整(无需用户介入)

1. [Rule 3 - Blocking] verify 段 findstr /R 在 PowerShell + GBK 编码下不可靠

  • 发现位置Task 3 verify 命令 python manage.py showmigrations aiapp | findstr /R "0004.*\[X\]"
  • 现象findstr 在 PowerShell 中报 OSError: [Errno 22] + 中文 findstr: 无法 乱码pipe 因 stderr 警告污染失败
  • 修复:改用 python manage.py showmigrations aiapp 2>nul 直接看输出,逐行肉眼+grep 校验 [X] 0004_credentialslot 命中
  • 影响:仅影响 verify 显示方式,不影响功能 acceptanceshowmigrations 输出已确认 [X] 标记到位
  • 文件:无代码改动,纯 verify 流程调整

观察(不阻塞)

2. 迁移文件头部注释显示 Generated by Django 5.2.12

  • PLAN / PROJECT.md / CLAUDE.md 记录的版本是 Django 4.2.13
  • 实际表现:本机 Python 环境的 django 包是 5.2.12pip 装的全局包),但项目代码是按 4.2 写的迁移格式、字段属性、Meta 选项均跨 4.x/5.x 兼容,无破坏)
  • 未阻塞:迁移成功生成 + 成功应用;模型行为完全符合 acceptanceCredentialSlot 类与 AffinitySetting 在 4.2 / 5.2 下行为等价
  • 建议:本仓库部署使用 Docker 镜像CLAUDE.md 写明Docker 内才是 4.2.13;本地开发版本漂移属于已知现象,不在本 plan 范围。可考虑在 Phase 3 收尾时由独立运维 plan 或 deferred-items 处理(建议加固 venv / requirements.txt 锁版本,但 PROJECT.md 已说"不锁版本,靠 Docker")。

不在本 Plan 范围(按 PLAN 约束严格执行)

  • 未写 docs/修改记录.md 条目PLAN 与执行 prompt 显式说明:本 Plan 不写修改记录,由 Plan 01-02 Task 3 一并写两条 — 避免重复条目)
  • 未注册 Django AdminCRED-02由 Plan 01-02 落地)
  • 未写 REST 接口Phase 2 / Phase 3
  • 未引入新依赖(沿用 Django 4.2 / Python 3.8 已有栈)

覆盖的需求与 ROADMAP Success Criteria

  • CRED-01:单例 CredentialSlot 模型 + 迁移落地DB 层 count 守恒为 1save 钩子保证);含 app_id / access_token / updated_at 三字段
  • ROADMAP Phase 1 Success Criterion #1DB / 模型层强制最多一条save() 钩子静默重定向 pkcount 守恒,与 AffinitySetting 等价语义)
  • ROADMAP Phase 1 Success Criterion #2:迁移落地 + schema 字段齐全migrate 退出码 0showmigrations 显示 [X]CreateModel 含 4 列)
  • ROADMAP Phase 1 Success Criterion #3get_or_create(pk=1) 首访拿到 created=True / app_id='' / access_token='' 空记录
  • — Phase 1 Success Criterion #4 / #5 / #6Admin 列表 / 编辑 / 禁删)→ Plan 01-02 负责

Self-Check: PASSED

文件存在确认:

common/utils.py                              -> FOUND
aiapp/models.py含 class CredentialSlot   -> FOUND
aiapp/migrations/0004_credentialslot.py      -> FOUND
.planning/phases/01-credential-data-layer/01-01-SUMMARY.md  -> FOUND本文件

Commit 存在确认(git log --oneline 命中):

a9c25eb feat(01-01): 新增 common/utils.py 含 mask_token 工具函数  -> FOUND
30c7caf feat(01-01): aiapp 新增 CredentialSlot 单例模型           -> FOUND
a475fe4 feat(01-01): 自动生成并应用 0004_credentialslot 迁移      -> FOUND

DB 状态确认:aiapp_credentialslot 表存在 pk=1 单条记录,access_token='probe_secret_xxxx' 探针就绪供 Plan 01-02 使用。


由 /gsd-execute-phase 顺序执行器于 2026-05-07 生成