为什么:撤销文档后 Wiki 变化此前只有"快照保存"证据;页面级差异能给出增/改/撤, 但必须写明它不等于实体、概念、边与引用的语义重算。 - overview 差异新增 wiki 字段:读两份快照的 wiki_pages 行数组比较,缺部件时 unknown。 - 发布管理"本次变更"显示 Wiki 新增/更新/撤销页数与页面级声明。 - 真实归档验证:对内 vs 对外 = 新增 31 / 撤销 1,反向镜像一致。
This commit is contained in:
@@ -14,6 +14,11 @@
|
||||
- worker `library.sync` 接执行编排:先闸口、再授权,写入阶段落库;生产目标适配器未配置时明确失败。
|
||||
- 页面"生产保护与恢复"显示发布授权状态与有效期;系统设置→存储管理显示容量门禁、受保护版本与清理预览。
|
||||
|
||||
### Added(Wiki 页面级差异)
|
||||
|
||||
- `GET /library/overview` 的差异结果新增 `wiki`:两份快照 `wiki_pages` 的新增/更新/撤销页数与样例,
|
||||
明确标注页面级、不代表实体/边/引用重算;缺部件时如实 unknown。
|
||||
|
||||
### Changed
|
||||
|
||||
- 启动同步的缺项包含"没有与目标快照匹配的有效发布授权"与容量预算;作业只有在编排返回成功时才标成功。
|
||||
|
||||
@@ -19,7 +19,7 @@ A06(内容、问答、引用图片效果、性能报告)已被用户取消
|
||||
| --- | --- | --- | --- | --- |
|
||||
| V01 | 只有对内/对外两个目标,身份来自配置映射 | `internal/httpapi/library_*.go`(scope 只接受 internal/external);`web/src/ReleaseTargets.tsx` | 已实现·已验证 | overview 对非法 scope 返回 400;真实归档里 internal/external 各自解析到唯一 KB(0c84a098… / 对外 KB) |
|
||||
| V02 | 全部解析/索引/Wiki 就绪,验收绑定本版 | `internal/releaseadapter/v071/library_source_settlement.go`;页面准备清单 | 已实现·未验证 | 结清检查(settlement)在封存前执行且缺项阻断;但对"实时测试库此刻就绪"仍需一次真实封存复核;页面已注明依据的是哪次快照 |
|
||||
| V03 | 删除文档后 Wiki 共享正文/实体/边/引用真实变化 | — | 未实现 | 现在只能证明 Wiki 完整快照被保存;删除贡献的语义重算与证据未实现,界面不声称已满足 |
|
||||
| V03 | 删除文档后 Wiki 共享正文/实体/边/引用真实变化 | `wikiDiffOf`(页面级差异) | 部分实现 | 真实归档验证:对内 vs 对外 = 新增 31 页 / 撤销 1 页,反向镜像一致。**页面级不等于**实体/概念/边/引用的语义重算,后者仍需 WeKnora 内部证据,未实现也不声称 |
|
||||
| V04 | 完整固定版本:文档集合+处理结果+资源+索引+完整 Wiki | `internal/librarysnapshot/*`(10 表 + 索引 + 资源 + Wiki 部件);`artifacts` 清单 | 已实现·已验证 | 真实快照 f992d64e…:4 文档/38 分块/122 索引/21 资源/31 Wiki 页/55,309,044 字节;部件摘要逐项校验 |
|
||||
| V05 | 导出中源变更则拒绝或隔离 | `internal/libraryfreeze/*`(持久 journal、源锁、暂停/恢复);`ProjectController` | 已实现·未验证 | 冻结窗口内会重新比对兼容契约;跨"项目外写入者"的证明仍缺,需一次带并发写入的隔离演练 |
|
||||
| V06 | 只同步知识数据,不含账号/会话/凭据 | 导出白名单(10 张知识表 + 知识域索引 + 资源 + Wiki) | 已实现·已验证 | 部件清单里没有 users/sessions/api_keys;真实归档部件名可逐项核对 |
|
||||
@@ -54,6 +54,7 @@ A06(内容、问答、引用图片效果、性能报告)已被用户取消
|
||||
比较两份归档快照的 `knowledges` 行数组,输出新增/更新/撤销。真实归档上验证:
|
||||
对内快照 4 篇对空集为"新增 4",反向为"撤销 4",缺失快照返回原因而不是空差异。
|
||||
生产权威版本建立后即可直接给出生产→测试差异;在生产版本未知时页面保持"未接入"。
|
||||
Wiki 页面级差异单独返回(`diff.wiki`),明确标注"不代表实体/边/引用重算"。
|
||||
|
||||
## 未通过项如何阻断
|
||||
|
||||
|
||||
@@ -54,6 +54,85 @@ type libraryDiffView struct {
|
||||
// 避免把某次历史差异当成"当前拟发布"。
|
||||
BaseSnapshot string `json:"base_snapshot,omitempty"`
|
||||
TargetSnapshot string `json:"target_snapshot,omitempty"`
|
||||
// Wiki 是**页面级**差异:只能证明页面被增/改/撤,不能证明共享正文、实体、
|
||||
// 概念或边被重新计算——后者需要 WeKnora 内部语义,本平台不做也不声称。
|
||||
Wiki *libraryWikiDiffView `json:"wiki,omitempty"`
|
||||
}
|
||||
|
||||
type libraryWikiDiffView struct {
|
||||
State string `json:"state"` // computed | unknown
|
||||
Reason string `json:"reason,omitempty"`
|
||||
Added int `json:"added"`
|
||||
Updated int `json:"updated"`
|
||||
Removed int `json:"removed"`
|
||||
// Sample 只列前若干页,避免把整库 Wiki 列表塞进接口。
|
||||
Sample []string `json:"sample"`
|
||||
Note string `json:"note"`
|
||||
}
|
||||
|
||||
// wikiDiffOf 比较两份快照的 wiki_pages 行数组(页面级)。
|
||||
// 它不能替代"删除文档后共享正文/实体/边/引用如何变化"的语义证据。
|
||||
func wikiDiffOf(archive *librarysnapshot.Archive, baseID, targetID string) *libraryWikiDiffView {
|
||||
unknown := func(reason string) *libraryWikiDiffView {
|
||||
return &libraryWikiDiffView{State: "unknown", Reason: reason, Sample: []string{},
|
||||
Note: "页面级比对缺失时不能声称 Wiki 已同步"}
|
||||
}
|
||||
rows := func(id string) ([]map[string]any, string) {
|
||||
manifest, err := archive.Load(id)
|
||||
if err != nil {
|
||||
return nil, "快照不可读"
|
||||
}
|
||||
for _, part := range manifest.Parts {
|
||||
if part.Kind != "wiki" || part.Name != "wiki_pages" {
|
||||
continue
|
||||
}
|
||||
reader, err := archive.OpenPart(part)
|
||||
if err != nil {
|
||||
return nil, "Wiki 正文不可读"
|
||||
}
|
||||
defer reader.Close()
|
||||
parsed, err := librarydiff.ReadDocuments(reader)
|
||||
if err != nil {
|
||||
return nil, "Wiki 行格式不符"
|
||||
}
|
||||
return parsed, ""
|
||||
}
|
||||
return nil, "快照里没有 wiki_pages 部件"
|
||||
}
|
||||
base, reason := rows(baseID)
|
||||
if reason != "" {
|
||||
return unknown("生产版本:" + reason)
|
||||
}
|
||||
target, reason := rows(targetID)
|
||||
if reason != "" {
|
||||
return unknown("测试版本:" + reason)
|
||||
}
|
||||
result, err := librarydiff.Compare(base, target)
|
||||
if err != nil {
|
||||
return unknown("Wiki 页面比对失败:" + err.Error())
|
||||
}
|
||||
view := &libraryWikiDiffView{State: "computed", Added: len(result.Added), Updated: len(result.Updated),
|
||||
Removed: len(result.Removed), Sample: []string{},
|
||||
Note: "页面级差异:只说明页面被增/改/撤,不代表实体、概念、边或引用已重算"}
|
||||
for _, item := range result.Added {
|
||||
if len(view.Sample) >= 20 {
|
||||
break
|
||||
}
|
||||
view.Sample = append(view.Sample, "新增 "+item.Title)
|
||||
}
|
||||
for _, item := range result.Updated {
|
||||
if len(view.Sample) >= 20 {
|
||||
break
|
||||
}
|
||||
view.Sample = append(view.Sample, "更新 "+item.Title)
|
||||
}
|
||||
for _, item := range result.Removed {
|
||||
if len(view.Sample) >= 20 {
|
||||
break
|
||||
}
|
||||
view.Sample = append(view.Sample, "撤销 "+item.Title)
|
||||
}
|
||||
return view
|
||||
}
|
||||
|
||||
// computeLibraryDiff 用归档里的 knowledges 行数组比较两个快照:
|
||||
@@ -115,6 +194,7 @@ func computeLibraryDiff(root, baseID, targetID string) (libraryDiffView, bool) {
|
||||
}
|
||||
view := libraryDiffView{State: "computed", Added: []any{}, Updated: []any{}, Removed: []any{},
|
||||
BaseSnapshot: baseID, TargetSnapshot: targetID}
|
||||
view.Wiki = wikiDiffOf(archive, baseID, targetID)
|
||||
for _, item := range result.Added {
|
||||
view.Added = append(view.Added, item)
|
||||
}
|
||||
|
||||
@@ -209,6 +209,14 @@ describe('ReleaseTargetsView(确认稿布局)', () => {
|
||||
removed: [{ id: 'doc-del-0003', title: '撤销的说明', digest: 'c'.repeat(64) }],
|
||||
base_snapshot: 'prod00000000',
|
||||
target_snapshot: 'test00000000',
|
||||
wiki: {
|
||||
state: 'computed',
|
||||
added: 2,
|
||||
updated: 1,
|
||||
removed: 3,
|
||||
sample: [],
|
||||
note: '页面级差异',
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -218,6 +226,8 @@ describe('ReleaseTargetsView(确认稿布局)', () => {
|
||||
expect(html).toContain('撤销的说明 · doc-del-')
|
||||
expect(html).toContain('生产 prod00000000 → 测试 test00000000')
|
||||
expect(html).not.toContain('变更清单来自生产与测试版本对比')
|
||||
expect(html).toContain('Wiki 页面:新增 2 · 更新 1 · 撤销 3')
|
||||
expect(html).toContain('不代表实体/边/引用重算')
|
||||
})
|
||||
|
||||
it('准备信息注明快照时间与范围,不声称实时测试库已就绪', () => {
|
||||
|
||||
@@ -311,6 +311,12 @@ function ChangeTable({ diff }: { diff: LibraryOverview['diff'] }) {
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
{diff.wiki && diff.wiki.state === 'computed' ? (
|
||||
<p className="rel-scope-note">
|
||||
Wiki 页面:新增 {diff.wiki.added} · 更新 {diff.wiki.updated} · 撤销 {diff.wiki.removed}
|
||||
(页面级,不代表实体/边/引用重算)
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -250,6 +250,16 @@ export interface LibraryDiff {
|
||||
/** 这次比对用的两个整库版本(生产 → 测试),避免把历史差异当成当前。 */
|
||||
base_snapshot?: string
|
||||
target_snapshot?: string
|
||||
/** 页面级 Wiki 差异;不代表实体、概念、边或引用被重算。 */
|
||||
wiki?: {
|
||||
state: 'computed' | 'unknown'
|
||||
reason?: string
|
||||
added: number
|
||||
updated: number
|
||||
removed: number
|
||||
sample: string[]
|
||||
note: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface LibraryStageRow {
|
||||
|
||||
Reference in New Issue
Block a user