姚章浩
eea0a0ec3b
test(release): 整库发布独立oracle与历史兼容矩阵
...
build / build (push) Successful in 1m25s
十表全形状夹具补独立 oracle:逐列内容、全部引用、索引正文/向量、对象字节与
零 Embedding 调用;期望由夹具声明推导,不调用被测映射器生成。Wiki 日志本地号
映射断言双射;正文/向量/引用/映射对真实目标变异后 oracle 必须红、改回必须绿。
补历史兼容矩阵:旧 manifest、V1 台账、空作业/目标身份等缺证明组合在正式入口
被拒绝,拒绝前后 PG/索引/对象版本/序列哈希不变;不做 V1→V2 迁移。
旧版对照:0.1.258(6fe98c9)在同一隔离库/桶/索引以相同输入复现
wiki_log_entries_pkey/23505;当前代码同一输入发布成功并分配本地号。
2026-11-02 02:58:50 +08:00
姚章浩
99055a582e
test(library): 补孤儿校验锚点失败与介质失效的真实隔离用例
...
孤儿校验:缺 knowledge_id 的索引文档被拒绝、未知 knowledge_id 如实报孤儿、
数据库层中途失败(真实 statement timeout,SQLSTATE 57014)冒泡为错误而不是
"没有孤儿"。
锚点:真实 PG 约束拒绝写版本锚点时,作业在任何阶段写入前失败、目标零写入、
版本表不留本次行;去掉故障后同一请求写满四阶段。
介质失效:归档 part 缺失/摘要不符时恢复停在内容层,保持 held + RestoreBlocked,
服务与入口都不恢复、校验不再跑。
2026-11-01 17:21:42 +08:00
姚章浩
8df19f9368
test(library): 补软删除共享资源与索引 oracle 隔离用例
...
真实本机 PG/ES/MinIO 上补五组场景:7 张带 deleted_at 表的软删除 before-image 与
恢复逐列复原;同 _id 属其它知识库时外部文档 _source/seq_no/term 逐字节不变;
正文/向量/metadata 任一篡改都让独立 oracle 变红;晚阶段失败后既有对象精确版本
不变;同 ID 共享资源在 handle/定位/字节三类差异下写前拒绝。另在复用用例里补
"no-op 复用不改写对象版本"的断言。
需隔离服务的用例继续在 test-scope.json 显式声明:普通 CI 跳过不算通过。
2026-11-01 12:08:55 +08:00
姚章浩
8febe12d7d
test(library): 隔离数据契约与 r11 身份自然键矩阵
...
真实本机 PG/ES/MinIO:v071 目录镜像 10 模式、结构引用/游标 15 模式、
8 表×2 外部范围身份矩阵、13 模式自然键(含软删除与合法同 parent 正例)、
备份期间三注入、收尾故障与授权撤销两个真实治理 PG 用例。
需隔离服务的用例在 test-scope.json 显式声明:普通 CI 跳过不算通过。
2026-11-01 11:35:09 +08:00
姚章浩
ee9d078794
fix(release): 分配目标分块序列并保持原值恢复
build / build (push) Successful in 1m26s
2026-10-31 06:08:05 +08:00
姚章浩
7ab2eb1ccf
fix(library): 按目标身份隔离版本授权与执行历史
build / build (push) Successful in 1m47s
2026-10-31 02:15:15 +08:00
姚章浩
06c2db19fc
fix(library): 删除 handoff 路径残留的无条件 stage 写
...
build / build (push) Successful in 1m31s
BeginLibraryHandoff 事务里已经把 handoff 阶段推进为 running,函数返回后又补了一次
无条件 UpdateLibraryStage:既重复事务里的写,又在 Begin 返回与这次写之间给交接留出
竞态窗口(此时所有权可能已被 release 拿走)。删掉这次调用,handoff 路径的
running/succeeded/failed 三类阶段写只发生在 Begin/Complete/Fail 三个持有 job 行锁的
事务方法里。
回归:新增源码契约测试(library_handoff.go 里不得再出现 store.UpdateLibraryStage,
且必须走三个事务方法),以及行为测试(所有权不属于本次 handoff 时零阶段行、作业零改动;
用真实 handoff ID 重放则幂等成功且同样零写入)。既有事务行为测试保持不变。
验证:隔离 fixture PostgreSQL + 本 SHA libraryctl 实跑 cmd/worker 全包通过;完整门禁见提交日志。
未部署、未推送;云 SSH 与 UI 仍未验证。
2026-10-30 19:42:13 +08:00
姚章浩
8a68ed1819
fix(library): 交接所有权 CAS、回传游标跨轮与快照原产地
...
父审查确认四项是原 finding 未闭环,本包按真实调用链修完(先 RED 再修)。
R4a 正常流程必现阻断:API 先入 handoff 队列作业并关联,交接却只接受"未关联",
每次正常发布都被自己的所有权检查挡住。新增 governance.LibraryHandoffQueueJobID
(jobs.id 为 varchar(36),原实现拼后缀超长,插入即失败);EnqueueAndAttachLibraryJob
增加"声明的前驱队列作业号",只允许当前无人持有或正是该 handoff 时替换,任意替换拒绝。
测试从 API 真实前驱(已关联)开始,覆盖正常交接、错误前驱拒绝、交接后重放、
以及 release 与 handoff 两条队列行都存在。
R4b 迟到 handoff 污染已交接作业:失败出口(含 Push 错误)改为所有权 CAS
(FailLibraryHandoffIfOwner),只在业务作业仍由本次 handoff 持有时写 state/stage/recovery;
已交接/已终态零修改。所有权判定改为只看 library_jobs.queue_job_id(阶段行是否存在
与所有权无关,原实现会误判)。阶段写入也带所有权。用测试屏障确定性地复现"失败出口
到来时所有权已切给 release",验证 release running 与 succeeded 两种状态下零修改。
R2 回传游标每轮重置:改为跨轮保留(单轮仍 20 页预算),读到不足一页才回到起点;
新增 pullCursor 与跨轮覆盖测试(2100 条引用、每轮 5 页仍全部覆盖,不重复不漏取)。
R3 原产地误判:封存成功后会再登记 production_effective(同一快照),原实现把
sealed+target 判成冲突,正常再发布/历史恢复被误拒。现在原产地只由捕获事件决定
(test_sealed=源 / production_prebackup=目标),effective/recovery_verified 只是引用;
二者冲突或都没有捕获事件仍拒绝。测试覆盖正常成功链、真冲突、无捕获事件三种。
验证:隔离 fixture PostgreSQL 实跑 cmd/worker 全包通过(含上述四条真实调用链);
libraryhandoff/librarysnapshot/httpapi/governance/platform-postgres/libraryctl 全绿;
完整 ci_checks 见提交时日志。未部署、未推送、未构建镜像;真实跨主机 SSH 链路仍未验证。
2026-10-30 19:22:48 +08:00
姚章浩
9300865d23
fix(library): 搬运复用前复验字节、回传游标分页与发布队列事务交接
...
父独立审查退回的四个 finding,逐项修在本包内,不改架构。
1 复用路径没复验字节:remoteEvidence 只读清单+摘要就宣布可用。现在新增
libraryctl snapshot-verify(整版逐部件重算)并在复用前调用;blob 缺失/篡改必须
在入发布队列之前失败。新增真实 local 跨进程用例:清单不动、删 blob / 改 blob 后
再 Push 必须拒绝。
2 回传枚举被固定 LIMIT 50 截断:LibraryJobSnapshots 只取成功作业且按摘要字典序。
改为 LibrarySnapshotCandidates:合并 library_versions、library_jobs.snapshot_id、
library_jobs.target_snapshot_id 三处引用,按 (created_at, snapshot_id) 游标分页;
回传循环翻页到不足一页为止。失败/待恢复作业留下的预备份、第 51 条以后、历史恢复
输入快照都能被枚举。另修"本地只有清单、blob 已损坏就永远跳过":改用整版 Load 判定,
并给 Unpack 增加显式 RepairCorruptBlobs(只在权威云端读回时开启)。
3 身份映射错误:搬运拿目标 tenant/KB 去核对封存快照,而封存清单写的是**源**身份
(云测试验收目标 a8e9c8ad/2e980686 与源 KB 必然不同)。现在 expectedSnapshotIdentity
按版本台账判定:test_sealed → 用部署注入的源绑定(LIBRARY_SOURCE_TENANT_ID /
LIBRARY_SOURCE_KB_*);预备份/生效/恢复版本 → 用目标绑定;没有登记或两类冲突一律拒绝。
身份永远来自独立绑定与台账,不接受包自证。
4 两步入队有竞态:Enqueue 成功后云执行器可能立刻领取并开始维护,Attach 失败却把作业
标失败并声称"未维护"。改为 EnqueueAndAttachLibraryJob 单事务(锁行 + 插入 + 关联 +
状态守卫),要么都成功要么都不发生;重复执行幂等,已关联别的队列作业则拒绝。
另修终态重放:AdvanceLibraryJob 无状态守卫,迟到的失败出口会把已成功作业改写;
新增 AdvanceLibraryJobIfActive(仅 pending/running 可推进),拿不到就明确记录"未覆盖"。
测试:libraryhandoff 用例覆盖跨租户/错误 scope/清单在而字节坏/本地损坏自愈;
隔离 fixture 库覆盖"校验通过才入队、失败不入队"、>50 条分页与失败作业预备份、
幂等重放不重复投递、迟到失败不改写终态。
未部署、未推送、未构建镜像;真实跨主机 SSH 链路仍未验证。
2026-10-30 19:10:02 +08:00
姚章浩
6400fd1bbe
feat(library): 快照搬运先于维护、双向校验与云端版本回传
...
为什么改:云端 release-executor 与目标同机后,本机封存的快照必须"先到、先验证"
才允许开维护窗口。此前只有 pack/unpack 两条人工命令:UI 点发布时执行器读到的是
本机归档路径,云端根本没有这份快照,作业只能在装配阶段失败;而预备份落在云端,
本机页面也读不到。用户已批准补齐这条链路。
搬运(新包 internal/libraryhandoff):本机 Load 逐 blob 重算 → Pack → SSH 流式写入
云端暂存 → 原子改名 → 云端 snapshot-unpack 再逐 blob 校验 → 读回版本清单摘要自证 →
scope/tenant/知识库身份与治理库作业一致。只有全部通过才投递 library.sync|restore,
失败时发布作业根本不存在(目标不进维护、不写知识)。远端命令是固定 argv,容器名与
路径只来自部署配置;同 ID 异内容拒绝;同一快照本机 flock 串行化;暂存 bundle 用后即删,
归档本体永不自动清理。
回传:capture 角色按已成功作业的 snapshot_id 扫描,把本机缺失的云端快照 Fetch 回来
(云端 pack → SSH 流回 → 本机 Unpack 逐 blob 校验)。回传成功后 API/页面既有的列表、
详情、差异、存储与历史恢复路径读到的就是真实字节;失败则版本不出现在本机,不会被误报成可恢复。
角色:搬运与回传只注册在 capture;若接线到 full(同时消费发布作业)启动即失败,
避免与云端执行器抢同一批作业。release-executor 仍不拥有治理库 DDL,也不消费
QA/邮件/来源扫描。
验证:internal/libraryhandoff 全绿(跨进程 push/fetch、身份不符拒绝、幂等复用、
并发串行、篡改与未知快照不落版本、路径穿越拒绝、远端错误不当作"没有");
cmd/worker 在隔离 fixture 库(独立测试 PG)上验证"校验通过才入队发布、失败不入队";
httpapi 队列决策单测;相关包 go test 全绿;go vet 全仓通过。
未部署、未推送、未构建镜像:云端接线与验收仍由父执行。真实云链路(含 SSH 通道)
本轮没有云侧写操作,只有本机跨进程与隔离 fixture 证据。
2026-10-30 18:58:34 +08:00
姚章浩
d0310761b6
fix(worker): 接管复用预备份不再抹掉写集合证据
...
BackupPrepared 每轮都新造一份证据,而版本登记是 evidence=EXCLUDED.evidence
整份替换。接管复用时 BackupRef 只带快照 id(没有字节数)、写集合预检又只在
首轮执行,于是已登记的 writeset_digest/writeset_rows 被抹掉、backup_bytes
记成 0;同一作业第二次中断后再接管就取不回写集合,恢复链彻底断开
(2026-10-30T00:58Z 演练 …-1793321916382871758-retry,摘要 fddd98cf70… → 空)。
现在证据按「同一作业 + 同一份预备份」合并进已持久化那一行,身份不符一律失败;
接管路径用 prebackupBytesOf 从首次登记的证据取回真实字节,缺失即拒绝(不再写 0);
生效版本证据统一由 effectiveEvidence 构造;同一作业的写集合只允许落定一次。
验证:真实 PG(一次性库 127.0.0.1:25471)上先复现再修
- 修前红:cmd/worker 两条新集成用例分别停在「第 1 次接管后写集合丢失」
与「复用未登记的预备份竟然成功」
- 修后绿:两轮中断+接管+第三次完整恢复后 writeset_digest/backup_bytes(4096)/
job/backup_snapshot/backup_reused 逐项存活,created_at 仍是首次登记时刻
- 反向自检(各自单独变异后复跑,均红):去掉合并、去掉身份核对、
去掉摘要冲突判定、去掉复用字节硬门禁
- make verify:16 项全部 success(含 tests-go 真跑集成用例)
2026-10-30 09:14:10 +08:00
姚章浩
780ab988c4
fix(worker): 历史快照恢复接管按作业身份与新授权判定
...
build / build (push) Successful in 1m20s
真实现场 2026-10-30T00:19Z:新恢复作业恢复历史快照 fc595511(已被 0d982f76
顶替),写入 database 后被 SIGKILL;重启接管时 classifyTakeover 只要看到
该快照有 SupersededAt 就判 superseded,没有判这行历史版本属于哪个作业,
把历史那次发布被顶替当成这次作业过期,作业 failed/prebackup,而恢复文案
又写"无需恢复",目标半写且维护锁未释放。
修法:被顶替分支先分清归属——本作业 finalize 成功或版本行属于本作业 ⇒ 仍
拒绝;历史行属于别的作业时,仅当本次是 restore、授权签发于顶替之后、scope
与 instance 两把锁仍由本作业独占且指纹一致、预备份登记属于本作业且清单字节
仍等于摘要,才按 fresh 继续重跑整条链路。任一证据缺失/读不到仍拒绝。
同时把续作早退统一走 failLibraryJobOrTakeover:有持久化预备份时保留
needs_restore 并点名 backup,不再写"无需恢复"。
验证:
- 先写失败回归(历史行+新授权 ⇒ 期望 fresh,实得 superseded),修后绿;
- 真实 PostgreSQL 正负例:授权撤销/实例锁释放/预备份归属被改/清单字节被改
均回到 superseded;续作早退保留备份与 needs_restore;
- 本地 make verify 16 项必需检查全绿(identity 本地档案跳过)。
2026-10-30 08:51:07 +08:00
姚章浩
85b3596e0e
fix(postgres): 复用预备份时保留首次登记时刻
...
崩溃接管会复用崩溃前那份预备份;BackupPrepared 会再登记一次版本行,而
`created_at = EXCLUDED.created_at` 把"备份什么时候拍的"洗成了接管时刻 ——
事后无法区分"复用"和"崩溃后重做了备份",而后者正好把半写状态当成可回滚基线。
- 只对 `production_prebackup` 取 LEAST(首次登记时刻只早不晚);
- 生效版本行仍写当前时刻(重新激活一个旧快照后,页面要显示最近一次生效);
- 真实 PG 用例两条负例:撤掉保留规则 ⇒ 红;规则漏到所有 kind ⇒ 红。
verify: 一次性库 kg_takeover_reuse_test 上 PASS;
负例 1(撤掉 CASE)→ "复用后登记时刻被改写";负例 2(CASE 去掉 kind 条件)→
"生效版本重新激活后时刻";make verify overall success。
2026-10-30 07:56:12 +08:00
姚章浩
d544728001
test(worker): 真实PG断言拒绝接管的落库效果
...
build / build (push) Successful in 1m17s
拒绝接管不只要"分类对",还要三处落库都对:library_jobs 终态 failed + 保留预备份
基线 + 恢复指引里带得上那个基线;library_maintenance 的 released_at 仍为 NULL、
state=needs_restore(目标可能半写,释放等于谎称已恢复)。
用一次性库 kg_takeover_foreign_test 实跑:
- TestFailLibraryTakeoverKeepsMaintenanceAndNamesBackup PASS (1.35s)
- 负例:把恢复指引改成"无需恢复" ⇒ 同一用例红(library_takeover_integration_test.go:117)
- TestTakeoverRefusesSameSnapshotRegisteredByAnotherJob / 超出历史窗口那条仍 PASS
2026-10-30 03:50:49 +08:00
姚章浩
ba852f3dc2
fix(worker): 接管判据绑作业身份,同快照别作业登记不算完成
...
2026-10-29T19:16Z 真实现场:作业只写完 database 阶段(resources/index/wiki/verify
仍 pending),worker 被杀、人工拉起后被判 succeeded,阶段写"已登记生效版本(仅收尾,
未重写生产)",并把生效版本归属改写成这个作业。
根因:classifyTakeover 只看"当前生效版本的目标快照是不是我的目标快照"。版本行主键
由 (kind, scope, 快照) 决定(LibraryVersionID),登记是 ON CONFLICT (id) DO UPDATE、
会改写 source_job_id —— 快照相等这一维里没有作业身份。
改法:effectiveOwnedBy(version, jobID, snapshotID) 一份实现,接管与基线共用;
归属别的作业 ⇒ 新的 takeoverForeignEffective,走 failLibraryTakeover:不写目标、
不释放维护、终态失败并指明预备份(目标可能半写,释放等于谎称已恢复)。
本作业自己的 finalize 阶段成功仍是独立证据,不被归属改写推翻。
验证(已跑):
- go test ./cmd/worker -run TestClassifyTakeover → PASS(含 4 条新用例)
- 负例:撤掉身份判定后同两条用例红(classify="pending_final", want "foreign_effective")
- 真实 PG(一次性库 kg_takeover_foreign_test):
TestTakeoverRefusesSameSnapshotRegisteredByAnotherJob PASS(1.41s),
负例同一用例红(library_takeover_integration_test.go:53)
- 同一库 TestTakeoverClassificationUsesExactCurrentVersionBeyondHistoryWindow PASS
- make verify:overall success(15 项必需全绿)
2026-10-30 03:40:13 +08:00
姚章浩
465cae80bd
docs: 记录"窗口当索引"陷阱与本次改动
...
build / build (push) Successful in 1m26s
trap-17:分页窗口当索引 —— "当前生效版本"不能按最近 N 条找。它比 trap-14 更
隐蔽:读成"没有"不报错,只是让错误的选择看起来合理(重写已上线的目标、给已有
权威版本的目标再建基线)。写清判据(250 条历史 + 窗口里看不到 + 精确查询仍命中)
与修法(身份/当前状态一律精确查,窗口只用于列表)。AGENTS.md 的陷阱索引同步加
一行;CHANGELOG 记录当前生效版本查询修复与固定同步屏障;test-scope 声明新增的
真实 PG 用例为"可选未运行"并写清理由(未声明 skip 判红)。
2026-10-30 03:09:37 +08:00
姚章浩
67743f2285
test(library): 真实PG验证超出历史窗口仍能取回写集合
...
build / build (push) Successful in 1m27s
fake 版本库不实现 Store 的 1..100 上限,正是它让"限 out of range"一路绿到线上。
这组用例跑真实 PostgreSQL:真实 Run → contentTrackedTarget → 真实
libraryStageRecorder.WriteSetPlanned → 真实 store,断言台账在第一笔写入之前可读、
原备份证据字段与 writeset_digest 原子共存,并把记录推到 250 条更新版本之后
(窗口里已看不到它)仍能按身份取回。
store 级契约用例覆盖:命中、真缺失 found=false、别的作业=冲突、同身份两行=拒绝挑选、
跨 scope/跨 kind 不命中、不完整身份拒绝。变异成"列最近 N 条再筛"时四条全红。
隔离 schema 逻辑收敛到 pgtest 一份,postgres 侧改为委托。
2026-10-30 02:32:26 +08:00
姚章浩
e0768f2caa
fix(library): 写集合台账与跨范围冲突预检
...
build / build (push) Successful in 1m34s
失败恢复会在写完之后按"目标租户里没人引用"清理资源行,于是历史孤儿、同租户其它
知识库暂未绑定的资源、被共享的资源都可能被删掉;而预备份里没有这些行,删了不可
恢复,随后整实例指纹才发现不一致,只能停在 rollback_failed。恢复校验依赖的整实例
证据又只存在内存里,worker 重启后只按快照 id 恢复,等于静默跳过比对。
改法:写第一笔之前把"本次明确写集合 + 写前存在状态 + 预备份时刻整实例内容证据"
算出来并按内容寻址落盘(文件名与治理库里的引用都是 sha256),摘要登记在预备份
版本证据里;恢复只删"写前不存在 ∧ 由本次写入 ∧ 不在恢复内容里"的精确 ID,并对
资源额外要求仍无绑定/正文引用;缺记录、记录被改、绑定对不上都直接失败、维护保持。
resources 的 upsert 补租户限定,跨范围同 ID 在写入之前就被预检拒绝。
整实例指纹从"身份集合"改成"逐行内容摘要",别的知识库同 ID 行被覆盖写坏也会暴露。
本地验证(已跑):
- make verify 15 项全 success(identity 因本地档案 skipped)
- go test ./internal/releaseadapter/v071/ -run Isolated(真实 PG + 真实 ES +
真实 MinIO 对象存储,写失败按定位符注入):目标非空/同租户其它知识库/共享资源/
历史孤儿/跨租户数据在失败恢复后逐行字节不变、本次残留 0;缺写集合拒绝恢复;
跨范围 ID 冲突在写入前拒绝;wiki 阶段半写回滚干净
- SIGKILL 子进程后按盘上写集合接管恢复,目标原有数据逐行不变
- go test ./internal/libraryrelease/:写集合先于第一笔、规划失败/登记失败都不写目标、
接管不重新规划
2026-10-29 23:55:13 +08:00
姚章浩
8da4666e76
test(library): 端到端编排在本机真实 PG/ES 上跑通
...
build / build (push) Successful in 1m14s
为什么:只验证单阶段写入还不够,完整链路(授权→预备份→四类写入→校验)也要在真实服务上跑。
本机 WeKnora 测试栈一次性库与随机索引已通过,CI 无这些服务故在 test-scope 声明为可选。
2026-10-28 15:58:06 +08:00
姚章浩
281699a431
feat(library): 生产整库真实读写/校验/恢复与本机真实服务验收
...
build / build (push) Successful in 1m21s
为什么:生产目标此前是"无条件失败"的占位,编排再完整也不能落地;本轮把它做成真实实现,
并在本机 WeKnora 测试栈的真实 PG/ES 上跑通导入、撤销、预备份与故障恢复。
- ProductionLibraryTarget:Current 读行数+身份摘要;Apply 按阶段真实写入(database/wiki
同事务先删本库再插、resources 传对象、index 搬运原始文档与向量);Verify 比对身份摘要;
Restore 按预备份快照回放。
- 撤销真正生效:索引阶段写入后 PruneScope 删除快照里没有的文档。
- ProductionBackupStore:导出前独立完整预备份,导出前后行数不一致即失败。
- worker 用注入配置装配目标与预备份;缺项把变量名写进阶段并失败,不再有占位对象。
- capacity 安全加法防溢出;保留策略组合语义明确(各维度都超限才候选)。
- 新增本机真实服务集成测试(一次性 kg_library_test_* 库 + 随机 ES 索引),
覆盖导入/校验/撤销/预备份/故障注入/恢复。
2026-10-28 15:54:01 +08:00
姚章浩
864a1632e0
test(snapshot): 增加真实测试源只读诊断并记录孤立索引阻塞
build / build (push) Successful in 1m11s
2026-10-28 08:32:23 +08:00
姚章浩
9f990158b9
test(snapshot): 联合验证真实隔离存储与暂停恢复链
build / build (push) Successful in 1m8s
2026-10-28 08:22:21 +08:00
姚章浩
9105f8316c
feat(snapshot): 增加整库独立快照及测试停写恢复基础
build / build (push) Successful in 1m20s
2026-10-27 23:44:12 +08:00
hermes
43b1e423fe
fix(qa): 规范化邮件调度测试并声明其外部依赖
...
build / build (push) Successful in 1m11s
gofmt 修正;该集成测试需要一次性库与测试密钥,按仓库约定在 test-scope.json 声明为可选未运行。
2026-10-26 10:01:14 +08:00
姚章浩
533db535f1
feat(qaops): 接入真实模型补缺任务与总表看板
...
build / build (push) Successful in 1m12s
按已确认需求提供独立安全模型配置、真实探测、正式字典字段评估与原问题译中,持久批次支持只补空白、暂停恢复、有限重试、幂等和逐字段依据。覆盖固定规则、本地语言、组织关联与主表有效看板共用治理侧读模型,保留源生产和冻结历史。
验证:完整 make verify 退出0,702项Go与329项前端通过;默认118项外部/隔离集成明确跳过,另跑完整镜像及组织隔离PG竞态。12并发、输入漂移、过期租约、暂停/恢复及有限重试通过。用户授权的百炼 deepseek-v4.1-flash 完成真实后台与浏览器评估/翻译;552行、16列、11块看板及完整PNG、独立配置页测试/保存、重开恢复和零JS异常通过。仅非业务样本外发,自动处理关闭,原payload不变。
上线前治理库已做导出快照原生备份并真实恢复,60表行数与17个QA表内容哈希一致;不修改DobotAI生产或隧道。本提交时尚未部署,线上验收须独立完成。
2026-10-19 14:29:49 +08:00
姚章浩
3e7fcce0b1
feat(qa-ops): 持久本地问答副本与安全答案预览
...
build / build (push) Successful in 1m11s
新增迁移50与每5分钟/手动持久同步,API只读本地;同步失败保留、权限阻断、租约与质量失效原子激活。
保留全量稳定分页/固定列,已存质量批量读取,元数据不反复解压长答案;详情安全Markdown与原文。
已运行整仓 make verify、源与副本隔离PG -race、真实只读源及隔离浏览器。
实际同步2525条;固定窗口640条本地中位1.46秒,对照直接源33.10秒。
停worker、缺源凭据、重启API后仍可查询;未更新8088,未修改生产原问答。
2026-10-18 19:10:47 +08:00
姚章浩
4b3aaff321
feat(qa-ops): 完成复检留档与人工复核闭环
...
治理侧保存输入观测版本、自动复检及人工复核历史,输入变化失效、版本冲突和幂等重放不覆盖人工记录。批量质量数量使用完整筛选元数据,未采集原文不伪造快照,延迟旧扫描不回退新输入。
验证:全仓 make verify 退出码0,Go656及前端253通过;五包竞态、源/组织/质量隔离PG、真实只读源和隔离网页读回复核通过。默认91个可选集成跳过不计通过。未接AI模型,不写DobotAI源库,未推送、未合并、未部署。
2026-10-17 22:37:26 +08:00
姚章浩
56b8cabed9
feat(qaops): 接入问答组织补全与区域使用统计
2026-10-17 19:02:13 +08:00
姚章浩
cc4906b6a9
feat(qaops): 接通网页通讯录持久化同步与后台接管
2026-10-17 17:36:07 +08:00
姚章浩
dab74b706b
feat(qaops): 增加版本化部门区域归属配置与预览
2026-10-17 11:39:38 +08:00
姚章浩
fd04bf7886
feat(qaops): 接入独立只读问答查询与治理覆盖复检
2026-10-17 09:04:19 +08:00
姚章浩
0974d8dfa2
feat(version-history): 「版本记录」页 —— 追踪远端 main 全部提交(只读 + 管理员手动同步)
...
build / build (push) Successful in 57s
按已确认的契约实现(口径见 docs/VERSION_HISTORY.md):
- **版本号口径(只给有证据的)**:注释 tag + tag 指向该提交 + 该提交内 CHANGELOG 顶部三者一致 ⇒ 正式版本;
tag 之后的提交按 `<主>.<次>.<自该 tag 的提交数>` 显示开发号(与 scripts/version.py **同规则**);
证据不足 ⇒ 「历史提交 · 短SHA」+ 原因码,**不补造版本号**。完整 SHA 是唯一身份、版本号不是唯一键
(tag 之后两条并行分支各自的第 1 个提交都算 0.1.1 ⇒ 两条都保留,用短 SHA 区分)。
- **迁移 000046**:version_history_commits(提交记录,主键 repo_key+commit_sha)+ version_history_sync_runs
(同步状态)。快照 = 一次同步:抓完并与 X-Total-Count 对账通过才切换;失败不切换快照、不清空历史、
last_success_at 不动(页面显示"上次同步失败(原因)· 仍展示上次成功数据");「同步成功」≠「历史完整」。
- **只读客户端(匿名,不使用任何凭据)**:白名单 origin + 固定仓库、拒绝任意 URL 与跨主机重定向、
响应上限 5MB / 超时 15s、仅 5xx/429 退避重试(最多 3 次);逐页抓到短页为止;每次全量扫 tag
⇒ 旧提交后补的 tag 会更新既有记录(不新增第二行)。
- **接口**(三个都登记进 api/openapi.yaml):GET /api/v1/build-info(viewer)、
GET /api/v1/version-history(viewer;游标绑定快照,过期返回 409 cursor_expired;不返回伪造 total)、
POST /api/v1/version-history/sync(admin;并发同步复用同一作业,靠 IdempotencyKey)。
- **「当前运行」只认精确匹配**:产物注入的完整 SHA 命中某条记录才标该条;开发构建显式标注"开发构建";
没有可用标识 ⇒ "当前运行版本未知";**不拿最新 main / 最新 tag 顶替**,CI 成功不等于已部署。
- **前端**:一级菜单第 5 项(版本记录)+ 时间线页面(全部/正式/开发/历史四段筛选、正文展开、
加载更多、760px 单列无横向溢出、提交正文与变更记录按纯文本渲染);无打 tag / 发布 / 部署入口。
新增配置(留空即未接线,页面降级):VERSION_HISTORY_GITEA_BASE_URL / _REPO / _ALLOWED_ORIGINS /
_HTTP_TIMEOUT / _MAX_PAGES。
验证:`make verify` 15/15 success(tests-go 578 通过 / 0 失败;tests-web 209 通过 / 0 失败);
新增 Go 用例 30(版本口径与图遍历 12、客户端守卫 6、同步编排 5、接口 7);前端新增 18(组件 10 + 客户端 8);
真实仓库匿名快照拉取(`VERSION_HISTORY_LIVE_BASE_URL=… go test -run TestLiveSyncAgainstRealRepository`):
抓到 113 条与 X-Total-Count 一致、v0.1.0 是注释 tag 且目标与 `git rev-parse v0.1.0^{commit}` 一致、
开发号逐个等于 `git rev-list --count v0.1.0..<sha>`(正式 1 / 开发 22 / 历史 90)。
2026-10-13 19:40:19 +08:00
姚章浩
7a9d51cc70
fix(version): 正式构建核证据 + 预检分"建议/就绪" + CI 就绪按必需 context
...
按要求补三项(本轮不推、不打真实 tag、不部署;全部用临时仓库与假对象验):
1) 正式构建身份不能只认 --release:
- 新增 release_evidence():核①完整 40 位 SHA ②**指向该提交的注释 tag** v<版本号>
③该提交里 CHANGELOG 顶部版本一致。缺任一条 ⇒ rc=2,身份退回 development 并把
release_blocked 的原因写进 JSON(给机器读,也给人看)。
- version.py --self-test:六条负例(有版本无 tag / unknown SHA / 短 SHA / tag 指错提交 /
轻量 tag / 版本不一致)+ 证据齐正例,**tag 全部在 mktemp 临时仓库里创建**。
- Go 侧新增 TestReleaseBuildRequiresEvidence:release 构建必须 CommitSHA 完整且
v<ReleaseVersion> 是**注释 tag** 并指向该提交(独立于 Python 的二次复核)。
2) 发布预检区分"准备建议"与"发布就绪":
- 新增纯函数 readiness(facts) → (是否就绪, 阻塞项, 建议);输出分两段。
- **就绪要求候选版本已写进目标提交**(version_in_commit:查 git show <sha>:CHANGELOG.md,
工作区写了没提交不算);首发按**显式指定版本**处理,**不把"记录里有这个版本"当已发布**
(已发布的证据是 tag,不是记录条目)。
- 预检**不执行、也不在输出里推荐自动发布路径**(正式发版由人按 docs/RELEASE_RUNBOOK.md)。
3) CI 就绪判据:必需 context 集合来自 .gitea/required-contexts.txt(缺失/为空 ⇒ 没有策略 ⇒
阻断,不用观察到的状态顶替);**全部 success 才通过**,失败/缺项/pending/查询异常一律阻断。
Release 查询加 release_state():**只有 HTTP 404 才算不存在**,403/5xx/超时算查询异常 ⇒ 阻断。
验证:version.py --self-test 绿;release_preflight --self-test 绿(就绪判定 19 个分支 +
临时仓库验证 git show 语义 + 五个 HTTP 码语义);go test 三态(未注入 SKIP / 开发 PASS /
自称正式 RED);反向夹具 83/0(含临时仓库 go test 六例、预检"记录未进提交"两态);
make verify 15/15 success。
2026-10-13 15:26:49 +08:00
姚章浩
05144309c3
feat(version): 构建身份(release_version/build_kind/display_version),无基线也放行
...
解决"开发构建沿用已发布版本号,分不清开发版与正式版":
- 新增 `scripts/version.py --identity`:release_version(无基线即空)/ build_kind /
commit_sha(完整 40 位)/ display_version / built_at。显示规则:发布 `X.Y.Z`、
开发 `X.Y.Z-dev+g<短SHA>`、无基线 `dev+g<短SHA>`、什么都拿不到 `dev`。
- `build_kind=release` 只认**显式发布上下文**(`BUILD_KIND=release` / `--release`);
声明发布构建却没有已发布版本 ⇒ 拒绝(rc=2)。普通 main 构建永远是 development,
**不许只显示上一正式版本号、不许自称正式版**。
- Go 侧 `internal/version` 加 ReleaseVersion/BuildKind/DisplayVersion()/IsRelease()/Info(),
原 Number/CommitSHA/BuildTime 语义不变(向后兼容);契约测试改成:发布构建必须等于
CHANGELOG 顶部、开发构建必须是 dev 形状;`make verify` 的 contracts 项新增
**负例**「自称正式版 ⇒ 必须红」(只有正例的契约会被静默掏空)。
- 构建接线:Makefile/Dockerfile/ci_checks `--internal-build` 统一走 identity;
Makefile 的 `make version` 打印身份 JSON;Dockerfile 新增 RELEASE_VERSION/BUILD_KIND。
- `tests-go` 的"按设计跳过"声明补上 `TestDevBuildDoesNotClaimRelease`(它的强制点在 contracts)。
验证:go test 三态实测(未注入 SKIP、开发身份 PASS、自称正式版 FAIL);
--internal-build 产出并打印 dev+g<sha>;--internal-identity 契约项 rc=0;
make verify 15/15 success;反向夹具 80/0。
2026-10-13 14:59:36 +08:00
姚章浩
88da069e1f
ci(checks): 必需与可选测试按声明区分,未声明就跳过的用例判红
...
test-scope.json 提供声明:package/test 至少给一个、必须给 reason 与 owner、package 级要写 expected_skips(跳过数涨了要重新声明)、review_by 过期即失效。文件缺失或解析不了 = 声明不出来 ⇒ 不许默认放行。
2026-10-12 11:15:18 +08:00