姚章浩
7ab2eb1ccf
fix(library): 按目标身份隔离版本授权与执行历史
build / build (push) Successful in 1m47s
2026-10-31 02:15:15 +08:00
姚章浩
a7d643635a
fix(library): 守卫按行取作业身份、同作业多代次、原作业续作与执行域准入
...
build / build (push) Successful in 1m27s
R1 维护/发布守卫硬编码 j.id=$1:维护类入口的 $1 是 scope/instance,作业号在 $2,
导致 update/release/ingress 六个入口正常路径全部匹配不到作业。改为按被更新行自己的
owner_job_id 生成条件,并同时要求该代次在 library_attempts 里仍活跃。
R2 认领只改 jobs.attempt_epoch 不改 attempts.epoch,第二次接管即错位;唯一索引
(job_id) 与"每次接管新 attempt 且历史不可变"矛盾。改为同作业多代次:锁作业行与
operation 行、同 owner 幂等续租、活跃未过期者让路、过期/本作业旧代次标 abandoned、
新代次 epoch=max+1,并清同 operation 其它作业行租约。迟到 write/renew/finish 一律
ErrLibraryEpochStale(finish 补了"只能在活跃态进来一次")。
R3 续作不再新建 -resume 作业:PrepareLibraryJobResume 让原业务作业回 pending 并显式
结束上一代,维护窗口/journal/预备份/写集合仍按原作业 id 归属,代次由 worker 起手递增。
R4 执行域准入改为注册 handler 前读事实:目标实况(KB/租户/schema/索引)、旧 release
路由绑定的 KB、未释放维护窗口持有者;未知或重叠即拒绝执行(不做维护动作、不停既有服务)。
同时自纠 5 项设计偏离:current 次序键取意图创建时间(旧意图迟到事实被拒)、首次锚点
改事务 FOR UPDATE + 回读且 NULL≠0、版本事件冲突只做加法且 xmax=0 才算新事实、
恢复作为新 kind=recover 意图推进 current、隔离测试加测试库护栏并清理残留 schema。
测试:真实 PG(kg-pgtest-writeset,一用例一 schema)新增 8 条集成用例覆盖上述正负例;
go test ./... 全绿(50 包)、go vet/build rc=0、web tsc + vitest 485 通过、make verify 16 项通过。
2026-10-30 17:20:40 +08:00
姚章浩
98294b9583
fix(library): 统一执行身份、终态收尾、准入与数据身份
...
build / build (push) Failing after 24s
why:审计 19 项(12 P1 / 7 P2)来自同一批根因,逐点试错会把协议撕成十几份:
· 意图与执行混为一谈(F01/F15):`library.sync:<scope>:<快照>` 是终身幂等键,
A→B→A 的第三次发布拿到第一次的历史成功作业,什么都不做;
· 版本行既是实体又是事件(F02/F09):主键只由 (kind,scope,快照) 决定、写入是
upsert,第二个作业覆盖 `source_job_id`/`evidence`/`created_at`;
· 接管没有代次(F13):两个 worker 都能认领同一 pending 作业,失租的旧执行还能
写业务状态、释放别人的维护锁;
· 早退与终态没有统一政策(F03/F04/F05/F06):半写窗口的前置检查失败会重新开门、
服务起不来也照样开入口、journal 终态后收尾不可重建、恢复成功不回写 current;
· 数据身份与准入各写一套(F07/F08/F14/F17):接管丢了复用语义、全库搬运不改
location_hash、通用入口可造 library.* 作业、目标模型从没被问过;
· 存储/容量/UI/CI 判据各自为政(F10/F11/F12/F16/F18/F19)。
what:按批准的一套协议集中实施(详见 handoffs/release-consolidated-implementation/
current-contract.md):
· operation(一次明确意图)/ attempt(epoch 代次)/ 不可变版本事件 / current 指针;
治理库迁移 76,只动治理库,不碰 WeKnora Schema/源码/镜像;
· 所有业务状态、阶段、版本事件、维护锁写入带 epoch 守卫(失租代次写不进去);
心跳失租立刻取消 handler;
· 半写窗口早退保持关闭;服务未全部健康绝不开入口;journal 终态可从持久事实重建
收尾;恢复成功登记 recovery 事件并推进 current;
· worker 在动维护前核对业务行/kind/scope/队列关联;通用入口拒绝 library.*;
· 全库搬运重算 location_hash 并与文档级导入共用实现;接管消费写集合的复用决定;
发布前核目标实时模型契约、容量门禁前置;
· 存储保护闭包改为全量依赖、类型只认显式登记、保护未知则不出可删候选;
· 页面核 HTTP 状态与结构、恢复纳入轮询、Wiki 差异覆盖标题/摘要/父级。
how:`make verify` rc=0(16 项必需检查全绿,含 go test 全量与前端 485 例);
`go vet ./...` rc=0;`npx tsc -p tsconfig.json --noEmit` rc=0;逐项负例见各包测试
(F03/F04/F08/F12/F13/F14/F16/F18/F19 都有先红后绿记录)。
未执行:治理库迁移到任何已有库、真实 PG/ES/S3/WeKnora 集成、故障注入、部署。
2026-10-30 16:14:54 +08:00
姚章浩
517d5f862d
feat(library): 首次发布接上目标权威基线与整库空判定
...
目标非空又没有生效版本时,首次发布以前只能停在"权威版本未知"。现在有了
真实入口:管理员发起 baseline 作业,worker 在维护隔离下把目标整库捕获成
归档快照,再用同一套 Verify 证明目标与快照逐类一致,最后登记
production_effective(证据写明 origin=target_baseline、快照 ID、逐表行数、
目标索引映射)。不写目标内容、不清空目标、不手写版本。
同时修两处会误导人的判定:
- productionScopeEmpty 原来只数 knowledges,"文档删了但分块/Wiki/资源还在"
的目标会被当成空库;改用适配层同一份表清单与范围过滤逐表计数。
- 概览里的"测试封存候选"原来取该 scope 最新一份归档,而归档里还有生产
预备份与目标基线快照;改为只认版本登记里 kind=test_sealed 的那一份。
迁移 075 放开 kind='baseline' 并把它并入 sync/restore 的写互斥索引;
worker 重启时用同一作业 id 接管未完成的基线窗口。维护接口补 capability,
页面才能区分"未运行维护"与"维护能力没接线"。
验证:go build ./... / go vet ./... 通过;新增用例覆盖管理员校验、已有生效
版本拒绝、目标读不到时零作业零版本、候选来自登记、缺项文案指向基线入口、
capability 逐项缺失;反向自检(把候选改回按最新归档取、把文案改回旧句、
去掉管理员判断)三项均按预期变红后复原。
2026-10-29 18:16:21 +08:00
姚章浩
47eb23cf68
feat(library): 封存执行器可用性与还原条件来自真实观测
...
页面上的"可发起封存"原来只判 LIBRARY_AGENT_SOCKET 非空:正式栈的 API 根本没挂
那个 socket,而 agent 曾顶着正式单元名冻结候选实例——配置非空、页面可点、真发起
却封错库。
现在:agent 新增 health 动作(不占封存操作锁、不跑子进程,只回答身份/降级/忙),
worker 每 30 秒探测并写入迁移 074 的 library_executor_status;overview、快照列表与
POST /library/captures 共用同一份判定(过期、不可达、实例不符、降级、正忙都拒绝并
给原因)。封存结果由 agent 附上自报身份,worker 在停写入者之前再探测一次、执行后
用结果复核,不一致就不登记测试封存——心跳只能证明"刚才"。
期望身份用 LIBRARY_SOURCE_*(封存源),不是 LIBRARY_TARGET_*(同步目标),否则
正确的执行器会被判成不匹配。另加 GET /library/restore-availability:恢复能否执行由
双闸、该快照的恢复授权与实例维护状态决定,页面不再写死 disabled。
真跑(候选隔离栈,两处 socket 隔离不变):overview 返回 executor
{kgmaint-weknora/kgmaint-app};停 agent 后转不可达并 503 带原因;重启后恢复。
2026-10-29 13:01:37 +08:00
姚章浩
c2402947a9
feat(library): 死信按精确指纹解除,保留上游记录
...
build / build (push) Successful in 1m26s
上游 task_dead_letters 没有解决标记,purge/DELETE 会销毁"失败过"的唯一证据。
治理侧新增解除记录(迁移 073):绑定实时行逐字节指纹、重放引用、源侧观测摘要与
操作人;行被改过、重放后又出现新死信、源侧未结清都不命中,封存照旧阻断。
指纹先压紧空白再摘要,避免 PG to_jsonb::text 与 json.Marshal 的空白差异。
试过:go test ./internal/{librarydeadletter,libraryagent,httpapi,releaseadapter/v071,
platform/postgres},make verify 15 项全绿。
2026-10-29 06:55:13 +08:00
姚章浩
3643e9bf82
fix(library): 维护收尾必须按预备份恢复并校验
...
build / build (push) Successful in 1m20s
删掉会自己开入口、只看服务健康的 FinalizeAfterOperatorRecovery。
新的 RecoverWindow 绑定原作业/目标指纹/持久化预备份,在维护关闭下
执行恢复与逐类校验,通过才恢复服务与入口;失败继续维护。
预备份身份与内容风险在第一笔写入前一起落盘;后台任务必须静默后才停写者;
写入者发布端口即拒绝开窗口;维护占用改为按实例互斥(000072)。
2026-10-29 00:50:32 +08:00
姚章浩
6fe98c9a30
feat(library): 同步/恢复改为实例级维护窗口
...
build / build (push) Successful in 1m26s
只锁治理库 scope 挡不住 WeKnora 直连和后台写入。把封存链路的
维护机制接到发布路径:持久化 owner/目标身份/原服务状态,关入口、
停写入者、验证排空与入口不可达,再写目标;写完先恢复原运行状态
与入口才释放占用。内容风险一旦落盘,重启恢复不再自动启动写入者。
新增 000071 迁移记录 level/指纹/服务/入口状态;未接线的目标直接
拒绝写,不用库级锁冒充系统维护。
2026-10-28 23:01:29 +08:00
姚章浩
288e01a01a
feat(library): 目标维护窗口(持久所有权/独占/release-on-verify)+ known-broken 记录
...
build / build (push) Failing after 16s
为什么:用户确认维护窗口方案,需要真实占有目标而不是 503 页面。
- 迁移 000070 library_maintenance:scope 唯一持有者、作业/目标身份、服务原状态、阶段。
- worker 在写目标前 acquire,验证+登记成功才 release;回滚失败/未验证保持 needs_restore。
- API GET /library/maintenance + 同步入口在他人持有时 409。
- 已知缺陷如实记录:候选实例该路由 404;kill 取证脚本竞态导致三项断言为 false。
2026-10-28 20:45:02 +08:00
姚章浩
ac3b395102
fix(library): 对齐真实 WeKnora Schema + 入口级真实服务验收
...
build / build (push) Successful in 1m21s
为什么:候选环境改用真实 WeKnora 表结构后,暴露出多处"按猜的列名/形状"的假设,
以及若干 varchar 长度、对象定位符、空索引基线问题,导致真实链路跑不通。
- knowledge_tag_relations 无 tenant_id/KB 列 → 归属走父表;resources 作用域=绑定+chunk 引用。
- 写入顺序按真实外键;无代理键表用内容摘要核对。
- 队列作业 ID / 版本记录 ID 改固定长度摘要(列宽 varchar(36)/varchar(64))。
- 对象定位符拆 storage://<backend>/oss://<bucket>/<key>;provider 可配置。
- 空索引基线全链路一致(Current/Export/Backup/Archive),非空仍严格比对映射。
- finalize 阶段:版本登记失败不报成功;崩溃接管复用已持久化预备份。
- 迁移 000069:同一目标的 sync/restore 跨 kind 互斥。
2026-10-28 17:53:43 +08:00
姚章浩
a2d051f6b3
feat(library): 按版本恢复入口与失败作业续作
...
build / build (push) Successful in 1m25s
为什么:恢复此前只有"失败自动回滚",没有手动按版本恢复;失败作业也只能重发封存。
- 迁移 000068 允许 kind=restore;POST /library/restores(管理员 + 该快照恢复授权 + 闸口)。
- POST /library/jobs/{id}/resume 对失败/取消的同步/恢复作业按原参数续作。
- worker 统一 runLibraryRelease,同步与恢复共用链路;成功后登记 production_effective 版本。
2026-10-28 16:27:46 +08:00
姚章浩
7b8cc0384b
feat(library): 生产发布授权与同步/恢复编排,worker 接线
...
build / build (push) Successful in 1m19s
为什么:同步入口此前只会拒绝;授权、预备份、四类写入、验证与失败恢复都没有代码。
本轮把编排与授权实现出来,真实生产执行仍按授权闸口关闭。
- internal/libraryrelease:预备份 → 数据库/资源/索引/Wiki → 验证 → 失败恢复;
授权过期/目标不符/快照不符/参数变化在动生产前拒绝;恢复失败单独上报。
- 迁移 000067 library_release_authorizations(目标+快照+参数摘要+有效期,可撤销)。
- POST/GET /api/v1/library/authorizations 与 /{id}/revoke,仅管理员签发,5–1440 分钟。
- worker library.sync 接编排:先闸口再授权,阶段落库,只有编排成功才标成功。
- 生产目标适配器未配置时显式失败(不返回零值状态),预备份存储同。
- overview 返回授权状态,页面"生产保护与恢复"显示签发人与有效期。
- 验收矩阵区分"已实现·隔离测试通过"与"真实执行未授权"。
2026-10-28 15:11:27 +08:00
姚章浩
dc9e827656
feat(library): 整库版本/作业持久化与状态读取契约,生产同步默认拒绝
...
build / build (push) Failing after 17s
- 新增 library_versions:测试封存、生产生效、生产预备份三类分开登记,
生产生效版本唯一(旧版本必须先 supersede),测试快照不能顶替生产版本
- 新增 library_jobs + library_job_stages:业务作业身份、阶段、错误与恢复说明,
分项计数用 NULL 表示"总量未知";互斥与幂等由数据库唯一索引保证
- 新增 GET /api/v1/library/overview、/jobs、/jobs/{id};POST /syncs 闸口未齐
返回 503 缺项且不建作业
- 封存作业落库:先建业务作业再入队,worker 推进 export 阶段并登记 test_sealed
- 发布管理读真实状态:生产未知不再顶替、差异 unknown、启动同步按缺项禁用、
同步明细/四类分项来自持久阶段;阶段勾选只认当前作业证据
- 准备信息标注封存时间与范围,不声称实时测试库已就绪
2026-10-28 13:58:51 +08:00
姚章浩
7448d130ec
feat(settings): 汇总全部数据源配置操作审计
build / build (push) Successful in 1m13s
2026-10-27 10:42:30 +08:00
hermes
f87d3fad9b
fix(qa): 授权码按写入版本解密,改其它设置不再失效
...
build / build (push) Successful in 1m13s
只调整收件人或时间也会递增设置版本,导致已保存的授权码无法解密、测试发送报存储不可用。
新增 password_revision 记录加密时的版本,迁移回填现有行。
2026-10-26 16:09:00 +08:00
hermes
23b19350cc
feat(qa): 周报邮件新增测试/审核邮箱
...
build / build (push) Successful in 1m13s
测试发送只投递到该地址,不打扰主送与抄送名单;未填时退回主送首位。
2026-10-26 16:06:17 +08:00
hermes
822fb652ff
feat(qa): 周报邮件区分主送抄送并附统计看板图片
...
build / build (push) Successful in 1m21s
设置新增抄送列表,MIME 增加 Cc 头、envelope 同时投递主送与抄送,同一地址不得同时出现在两边。
确认后可保存统计看板 PNG(multipart/related + cid),发送时放在正文末尾内联显示;正文改动使确认失效后不再使用旧图。
2026-10-26 15:59:06 +08:00
hermes
6816289552
feat(qa): 周报确认后按设定时段自动发邮件
...
build / build (push) Successful in 1m23s
周报页新增自动发送区:管理员配置 SMTP/收件人/发送星期与小时,授权码用主密钥加密保存。
确认绑定当前草稿版本与正文哈希,正文再改则确认失效;worker 每分钟仅在启用、到点、周已结束且未发送过时发一次。
邮件同时提供渲染后的 HTML 与可读纯文本;SMTP 仅允许 TLS,拒绝明文。
迁移 000060 新增设置、确认与发送记录三张表,唯一键防重复发送。
2026-10-26 09:49:23 +08:00
姚章浩
2c0bb32183
fix(qaops): 点踩问题统一判为不正确
...
build / build (push) Successful in 1m11s
通过持久来源关联读取最新本地反馈,强制总表有效正确性为否,保留原问答和人工证据。统一列表、详情、指标和两阶段读取的反馈快照,拒绝冲突选择并保留其他字段编辑。已通过完整本地门禁、隔离PG竞态和真实恢复库52条点踩逐条读回、桌面及手机验收;未调用模型或连接源生产库。
2026-10-20 08:18:03 +08:00
姚章浩
87f97a2f9b
feat(qaops): 完成详情下拉与每周自动保存草稿
...
build / build (push) Successful in 1m7s
详情使用共享主表字典,稀疏修改所选字段并保留原始问答及旧复核审计。
月周统计提供文本和下拉双入口;每租户每周一份持久草稿,按技能六节双语模板真实预填。
加入幂等自动保存、冲突保留、未知响应重试、切周及离开保护和schema58非破坏降级保护。
验证:完整make verify、隔离PostgreSQL全mirror竞态回归、真实恢复库浏览器验收通过。
浏览器实测字段精确读回、月周双输入、模板预填、MD/CSV、自动保存、冲突、失败重试与API重启恢复。
未连接或改写DobotAI生产源库,未运行付费模型;提交时尚未部署。
2026-10-20 05:44:02 +08:00
姚章浩
a9870beade
feat(qaops): 补充独立历史覆盖率与正确率趋势
...
build / build (push) Successful in 1m10s
用户提供的历史百分比独立持久化,按ISO自然周和截止范围读取,不伪造分子分母、记录数或冻结时间。现有两张趋势图与PNG共用来源分色/分段及两位小数;缺失周留空,关键词筛选不借用无明细历史,当前指标与原冻结证据不变。
验证:完整make verify退出0;镜像全量隔离PG race退出0,新增来源/精度/关键词/租户/截止/不可变和安全down回归先失败后通过。前端输入/来源及缺失周回归先红后绿。治理库上线前原生备份与真实恢复已完成,71表行数和28张QA表内容哈希一致,独立主密钥同时受限备份。
当前仅完成开发验证,真实数据候选浏览器验收、正式25周导入与应用上线需继续完成。不修改源生产数据库、原问答、已有模型自动开关或隧道;用户原始报表和图像只保存于私有证据目录,不入Git。
2026-10-19 17:55:15 +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
姚章浩
f5e0f8d20f
feat(qa-ops): 建立独立正式主表并保真导入历史问答
...
build / build (push) Successful in 1m7s
将可维护总表与可替换2.0镜像分离,稳定来源关联和原始事件键保留;
源端更新/删除不覆盖主表或人工复核。首次收录保留原评估审计,
主表输入/复核独立持久化;历史原标注不冒充新AI或冻结证据。
历史导入绑定源哈希、逐行批准清单与真实恢复备份;三字段精确去重,
批次及原字段留档,重复执行不覆盖后续修改。仅操作本地治理库。
验证:完整make verify退出0;镜像与区域两组隔离PG race退出0;
真实恢复数据隔离导入12522、跳过792、暂缓1,总表15050;
重放保留模拟修改,结构down拒绝丢历史;真实浏览器第41周551条完整,
历史原标注和独立主表复核读回通过,移动布局及JS异常0。
2026-10-19 08:49:18 +08:00
姚章浩
6e67034fbd
feat(qa-ops): 交付十一项统计看板与完整图片导出
...
build / build (push) Successful in 1m10s
以治理库单一只读事务分开聚合累计、自然周、截止月及当前通讯录使用分母;浏览器不再读取全量长答案来构建看板。
新增迁移52与普通worker周质量冻结,保留真实冻结时点、规则和标量贡献;缺少当时快照留空。提交后冻结头与贡献不可修改、删除或追加;只读接口保留管理员、固定租户、严格JSON与输入冲突保护。
已验证:make verify退出0,686个Go用例和305个前端用例通过,必需跳过0;镜像与组织隔离PostgreSQL竞态回归退出0。真实治理副本浏览器核对第41周累计1885与新增471、十一项和完整PNG下载,390px无外溢、JS异常0;明确模拟503后不伪零且重试恢复。没有修改DobotAI原始问答、真实区域规则或生产发布门禁。
2026-10-18 23:26:55 +08:00
姚章浩
76671e0784
feat(qa-ops): 支持自定义区域与分级部门批量归属
build / build (push) Successful in 1m8s
2026-10-18 21:50:29 +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
姚章浩
dab74b706b
feat(qaops): 增加版本化部门区域归属配置与预览
2026-10-17 11:39:38 +08:00
姚章浩
2c7265efc4
feat(sources): 数据源配置收敛成 dws 导入 + PP 导入;「去掉来源」= 归档下架(不删数据)
...
build / build (push) Successful in 58s
主人要求:去掉入口,统一成 dws 导入与 PP 导入;其他来源连数据一起下架,之后用 dws 重新获取。
界面(web):
- 「数据源配置」只剩两块:**dws 导入(钉钉)**(按当前钉钉账号列出可访问的知识库与钉盘空间)
与 **PP 导入**(PP 资料下载中心)。上一步加的「显示 AList/钉钉知识库来源」折叠开关删除。
- 来源卡片新增管理员可见的「去掉来源」(带确认弹窗,说明是归档下架、数据保留可恢复)。
- 侧栏文案改成"只留两个入口";发现面板标题改为「dws 导入(钉钉)」。
后端(归档语义,可逆):
- 迁移 000047:source_connections 增 archived_at / archived_by + 只覆盖未归档行的部分索引。
- store:ArchiveSourceConnection / RestoreSourceConnection(幂等;归档同时停用并清空
next_refresh_at,避免被手工改回后立刻带着旧调度跑)。列表、目录、计划刷新(claimDue)、
手动扫描(triggerBlock/inspect)四类查询一律排除归档行。
- 注册路径(dws 重新获取):RegisterDingtalkSpace 的 ON CONFLICT 清掉归档标记 ——
重新登记同一个空间**原地复活同一行**,不产生重复,历史快照仍属于同一行。
- HTTP:POST /api/v1/source-connections/{id}/archive|restore(管理员专属,写意图头
source-archive / source-restore + Idempotency-Key;404 区分"不存在"与"已是目标状态");
openapi 同步两条契约。
验证:
- 集成测试(真 postgres):归档从列表消失但 source_items 一行不少、重复归档不漂移归档时间与人、
未知 ID 报 ErrSourceConnectionNotFound、归档来源不能再被扫描、恢复后回到列表但保持停用;
dws 重新登记同一空间复活同一行。
- httpapi:写意图/幂等键门禁、200/403/404/500、错误体不回显内部细节、classifyRole 管理员专属。
- 迁移护栏:000047 结构 + 四类 SQL 必须含 archived_at is null(漏一条就是入口没去掉)。
- make verify 15/15。
2026-10-14 12:36:32 +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
姚章浩
3f603aa293
feat(backup): 系统设置里的存储与备份——定时时间与储存天数可配、下发到目标主机 cron
...
需求:整库逻辑备份做定时,但要在「系统设置 → 存储与备份」里设置定时时间与储存天数。
后端
- migration 000045 backup_schedule(单行:enabled/schedule_time/retention_days/retention_months/
last_applied_*),store + 领域校验(HH:MM、1~365 天、0~24 月,非法值 400 且不落库不下发)
- internal/backuphost:通过 ssh 在目标主机上读回/下发计划并立即备份(未接线时 Available=false,
页面降级提示;主机侧错误带输出回传)。最近一次只认有 MANIFEST.json 的完整目录,跳 *.incomplete
- 接口 GET/PUT /api/v1/backup-schedule、GET …/host、POST …/apply、POST …/run;
写操作经 classifyRole 限管理员;run 为异步 202 + 在途单飞(http 写超时 30s,备份约 35s)
- openapi.yaml 补齐 5 条路由(契约测试要求每条都写 400/500)
主机侧
- scripts/install_backup_schedule.sh(幂等):写 /etc/knowledge-governance/backup-schedule.env 与
/etc/cron.d/kb-logical-backup,参数校验,--show/--disable,最后一行 SCHEDULE_APPLIED 供平台核对
- logical-backup.sh 从该文件读保留策略;失败或被中断把目录改名 *.incomplete,不留'像成功'的半成品
- install --show 只回显计划行(原先把 SHELL=/PATH= 当计划显示)
前端
- web/src/backupSchedule.ts(严格解析:缺计划字段即报错,不把默认值伪装成设置)
- BackupScheduleSection:容器 + 纯视图(管理员表单/只读、已一致/待下发、在途轮询)
运行前提(已落实):运行镜像加 openssh-client,且容器 uid 1000 在 /etc/passwd 有名字
(否则 ssh 报 No user exists for uid 1000);凭据只读挂载 secrets/backup-host(git 忽略)
实测:主机 cron 每天 03:30 / 保留 7 天 / 每月 3 份;立即备份一次 34 秒 / 742.7 MiB / 失败 0;
Go 与前端测试全绿(170 vitest)。
2026-10-09 13:50:23 +08:00
姚章浩
6440e3f679
feat(release): 发布目标知识库可选(源库 → 目标库映射)
...
- 迁移 000044:production_kb_bindings + release_item_executions.target_knowledge_base_id
- 系统设置新增「生产知识库绑定(对内/对外)」:从生产 WeKnora 实际列表选库,
保存时真校验(不存在拒收;生产端不可达只提示并如实回报),仅管理员可改
- 执行计划按绑定解析目标库并冻结进 release_item_executions;空目标直接拒收;
已发布过的条目保留原目标(重试/回滚回到同一处)
- DocumentBundle.RemapKnowledgeBase:knowledges/chunks/knowledge_tags/wiki_pages/
wiki_folders 的 knowledge_base_id 与 ES 文档字段改写为目标库,重算表/文档摘要并重新封装
- 契约比对改为「声明的映射对」(KnowledgeBaseMapping),其余字段继续 fail-closed
- 契约检查器 / 目标身份 / 验证器全部走映射后的目标库;对象通道无需改写
- Compose 拆分:数据面接线 compose.release-sim.yaml + 两道门禁 compose.release-gates.yaml
- 测试:RemapKnowledgeBase 全通道/无操作/fail-closed、映射契约、绑定接口、RBAC、真实 PG 集成
2026-10-08 15:41:13 +08:00
姚章浩
1db21fb899
feat(settings): 服务配置四项化并保存时真校验
...
- 生产/测试 WeKnora 地址、租户 ID、知识库 ID 由静态 env 收敛为版本化 UI 设置,保留修订号与操作人审计
- 新增 serviceverify:保存前真调目标端点探测,端点无效才拒绝;网络抖动或无凭据仅告警
- 迁移 000042(生产 WeKnora 端点)/000043(服务目标);补齐设置候选与校验契约用例
2026-09-30 10:54:07 +08:00
姚章浩
5037c05e93
feat(monitor): 取消失败发布并修正危险按钮对比度
...
- Parse-monitor: the batch-retry button rendered red-on-red because
weknoraMonitor.css overrode the global .danger style with text color
only; make .danger a filled red button with proper hover/disabled states.
- Add operator cancel: 批量取消所选 / per-task 不再处理 buttons move
failed/stalled publications to a new 'cancelled' state in one audited
batch operation (POST /api/v1/test-publications/cancel-batch, idempotent,
migration 000041 widens the CHECK constraints). Queued upload jobs are
cancelled, open dead letters abandoned; source records and spool files
stay intact so re-marking in the catalog can retry later.
- Queue status filter and labels learn 已取消; OpenAPI contract updated.
2026-09-30 10:54:07 +08:00
姚章浩
0ef41594c2
fix(catalog): 快照节点绑定物化条目以显示导入进度
2026-09-30 10:54:07 +08:00
姚章浩
c84f848849
perf(catalog): resolve filename filters once per query, not per row
...
The 000036 evaluator probes the rules table and runs two regexes for every
catalog file row; the first-level page of the 8k-file education source took
2.5-5s per query and the browser hung on 'reading first-level directory'.
- source_catalog_nodes gains a STORED generated ext column (000038)
- catalog_filter_context() resolves the connection's source-level allow/
exclude sets once; hot queries match node.ext against arrays (000039)
- filename_is_filtered stays the exact fallback, used only when the
connection has directory-scoped rules
- ListCatalogSources, ListCatalogNodes (current/subtree/total/items) and
RouteCatalogFiles fan-out adopt the fast path
Education root page: 4770ms -> 12ms; all-source counts: 54ms.
2026-09-30 10:54:07 +08:00
姚章浩
e6ef5a4693
feat(filters): allowlist mode for filename filters
...
- filter rules gain mode ('exclude'|'allow'); nearest allow rule governs a
directory subtree, extensions outside it are filtered before fetch
- filename_extension() now requires a well-formed suffix, so dated names like
'CRA_V3.1手册_2024-07-31_en' no longer fake an extension; extensionless
files are never filtered
- API accepts {mode, extensions}; UI editor becomes grouped format chips with
keep/exclude modes (keep-list default: docs/text/scripts/images)
- migration 000036 + integration tests for allow, nesting and restore
2026-09-30 10:54:07 +08:00
姚章浩
b3c9331966
feat(ingest): 源与目录文件名过滤及钉钉文档导出
...
- filename_filter_rules table (000035) with source-root and per-directory rows
- additive inheritance: source default + every ancestor directory contributes
- filename_is_filtered() SQL predicate applied to catalog browsing, subtree
counts, change-set diffing, routing fan-out and artifact fetch manifests
- fetch_state 'filtered' (CHECK extended); reconcile cancels/restores fetch
jobs, retires planned publications and blocks items when rules change
- GET/PUT/DELETE /api/v1/filename-filters (Idempotency-Key, normalized
extensions) + Settings and Catalog filter editors
- failJobSQL no longer swallows the claimed row when the failure projection
upserts (fixes spurious 'lease lost' retries)
- DingTalk live fetch: adoc/axls now use the export-job pipeline (HTTP 403
from file/download fixed), header-signature downloads supported
2026-09-30 10:54:07 +08:00
姚章浩
021e91eae4
feat(settings): bind internal/external knowledge bases from the UI
...
Routing targets move from env-only to a UI-managed binding (migration
000034 routing_kb_bindings, single active row):
- GET/PUT /api/v1/routing-bindings + /options: env defaults act as the
bootstrap fallback; a UI binding wins and takes effect immediately
(per-request resolution, no restart), and clearing falls back to env
- PUT validates against the connected WeKnora instance (real knowledge
base list via the scoped API key) and rejects same-KB bindings;
saves require the admin role
- route/catalog capabilities now resolve through the binding so both
annotation paths share one source of truth
- system settings > 服务配置 gains a 知识库绑定 panel: dropdown picker
(or raw ID when WeKnora is unreachable) showing bound name, origin
(UI vs env default) and last author
- contract tests: OpenAPI, handler matrix (fallback, validation,
same-KB rejection, capability resolution), frontend fetch/save layer
2026-09-30 10:54:07 +08:00
姚章浩
63f3b2c503
feat(governance): policy-driven ingestion from catalog annotations
...
Annotation in 来源与目录 is now the only import decision surface:
- migration 000033: catalog_import_intents (durable, rescan-proof per-file
intent with locator snapshot + fetch lifecycle), source_items.policy_state,
planned publication state, ui_setting_revisions.ingestion_concurrency
- marking fans a folder decision out to per-file intents, materializes
versioned items, reuses already-verified local bytes (compare, don't pull),
and plans one publication per routed KB; blocked retires planned rows
while live publications stay untouched (需下架 shows in the KB diff)
- artifact.fetch worker lane (scripts/artifact_fetch.py): DingTalk export
tree/live box API, AList /api/fs/get direct download with SSO-redirect
fail-closed, PP ledger via locator hash; content-level dedupe on sha256
- 开始处理 promotes planned->pending inside the claim-time concurrency budget
from system settings; claims enforce it in SQL
- monitor overview rebuilt as knowledge-base diff: planned/needs_removal per
KB + fetch pipeline summary; frontend lanes 待入库/需下架 replace the
deprecated snapshot change-set confirmation
- spool: images (png/jpg/gif/bmp/webp) accepted, ppt/pptx rejected in favour
of PDF twins; DingTalk PPT resolves its converted PDF pair
2026-09-30 10:54:07 +08:00
zheromiku
fbe80ad784
feat(auth): accounts, sessions, RBAC and server-stamped actors
...
上线账号权限管理(后端):
- 迁移 000032:users + user_sessions(token 只存 sha256,服务端可即时撤销)
- bcrypt 密码、8 次失败锁 15 分钟、统一 401 错误防用户名枚举
- 三角色 viewer/engineer/admin;HttpOnly+SameSite=Lax 会话 cookie,滑动续期
- 强制改密锁定:must_change_password 会话只能走 auth 端点;首登 dobotai
- bootstrap:users 表为空时创建 admin(初始密码 dobotai,强制改密)
- 生产双闸授权/回滚/失败重试收敛为 admin 角色;AuthorizedBy 用会话身份
- 全部业务写端点删除手填 actor 字段(operator_ref/reviewer_ref/created_by/
sealed_by/confirmed_by),服务端从会话注入;auth 关闭的开发 socket 回退 system
- 成员管理 API:列表/创建/改角色/启停/重置密码,最后一名 active admin 保护
- RBAC 矩阵纯函数契约测试 + 中间件行为测试(登录/锁定/角色/审计戳)
- 附带修复:GET /api/v1/users 曾被 GET=viewer 规则误放行(测试抓到)
2026-09-30 10:54:07 +08:00
zheromiku
dcdc8d1ddd
feat: 知识治理平台(含已验证发布/回滚契约)
...
全栈知识治理平台首次入库(P1-P10 + 发布链路模拟验证闭环):
- 治理核心:源目录/变更集/人工审核/发布批次/发布执行(fail-closed 闸口)
- 测试通道:WeKnora 测试 KB 上传/轮询/验收;poll 完成时写入
parser_generation/index_generation,修复 manual/seed 路径发布闸口永久拦截
- 发布适配器 v071(本地 source=测试角色 / 云端 target=生产角色 模拟验证通过):
* ES 导出/导入 mapping 自适应(.keyword 子字段、可选 tenant_id)
* OSS locator 支持嵌套 scheme;chunks.seq_id 由目标序列分配
* bundle 归档同身份重试原子替换;verifier 按 bundle chunk_type 计数
* 回滚 job payload 支持 prior_execution_id(strict 解码)
- 三通道发布+回滚演练实测:publish 4/4 verified,检索命中,
rollback 4/4 rolled_back,目标端 PG is_enabled=false、ES is_enabled=false
- 前端 A·Data-console 风格全站统一,120 用例通过
- 门禁:go vet / go test 全包通过,gofmt 干净
2026-09-30 10:54:06 +08:00