Skip to content

fix(kg): 修复全局问答 Map 阶段 LLM 失败的诊断缺口与模型解析链路; - #678

Merged
ThreeFish-AI merged 2 commits into
feature/1.x.xfrom
ThreeFish-AI/fix-global-qa-map-failure
May 27, 2026
Merged

fix(kg): 修复全局问答 Map 阶段 LLM 失败的诊断缺口与模型解析链路;#678
ThreeFish-AI merged 2 commits into
feature/1.x.xfrom
ThreeFish-AI/fix-global-qa-map-failure

Conversation

@ThreeFish-AI

@ThreeFish-AI ThreeFish-AI commented May 27, 2026

Copy link
Copy Markdown
Owner

背景

  • 知识图谱全局问答(GraphRAG Global Search)提交查询后,所有 Map 阶段 LLM 调用均失败(401 AuthenticationError),前端仅收到泛化错误提示,运维无法定位根因
  • Task Registry 中缺少全局问答任务槽位,用户无法为全局问答独立配置模型,只能依赖 Corpus 级绑定(若该凭证失效则全局问答完全不可用)

核心变更

  • 注册 knowledge.kg.global_search 任务槽位,支持 UI 为全局问答独立配置模型
  • 引入 resolve_llm_config_for_task 任务级解析,优先级:任务级 > Corpus 绑定 > 全局默认,当 Corpus 级凭证失效时可自动回退到全局默认
  • 添加 _map_diagnostics 诊断累加器,在 LLM 调用失败时捕获模型名与掩码端点,解决原先 Map 阶段个体失败在服务日志中完全静默的问题
  • 将 all-map-failed 日志级别从 WARNING 提升为 ERROR,附带 model/api_base_masked/map_failures 字段,运维可立即定位失败的模型和端点
  • 错误响应消息嵌入具体模型名和端点信息,替代原先泛化的"请检查后端 LLM 模型配置"提示

风险与回滚

  • 主要风险:_call_llm 新增 resolve_llm_config_for_task 解析路径,若 task_model_settings 表中该 task_key 无映射则自动回退到全局默认(与原有行为一致)
  • 回滚方式:git revert 即可

验证证据

  • 单元测试:tests/unit_tests/knowledge/test_global_search.py 9 passed
  • E2E/Workflow:浏览器实机验证 — 全局问答「该 Corpus 的核心主题是什么?」成功返回 3/9 社区证据,Reduce 阶段正确聚合为最终中文回答
  • 覆盖率/关键截图:后端日志确认 task_model_resolved task_key=knowledge.kg.global_search source=defaultglobal_search_done candidates=9 evidence=3

影响范围

  • 前端:知识图谱页面侧边栏新增「KG 全局问答」任务模型下拉选择器
  • 后端:global_search.py(核心流水线)、task_registry.py(任务注册)、graph.py(API 路由)
  • GitHub Actions / 文档:无影响

Next Best Action

  • 检查 Corpus 绑定的 LLM 凭证(openai/gpt-5-nanolocalhost:3392)是否需要更新,当前全局问答通过 task-aware 回退到默认模型 openai/gpt-5-mini 工作

🤖 Generated with Claude Code, CodeX, Gemini
Co-Authored-By: Aurelius Huangthreefish.ai@gmail.com

- 注册 knowledge.kg.global_search 任务槽位,支持 UI 为全局问答独立配置模型
- 引入 resolve_llm_config_for_task 任务级解析,优先级:任务级 > Corpus 绑定 > 全局默认
- 添加 _map_diagnostics 诊断累加器,在 LLM 调用失败时捕获模型名与掩码端点
- 将 all-map-failed 日志级别从 WARNING 提升为 ERROR,附带 model/api_base_masked/map_failures 字段
- 错误响应消息嵌入具体模型名和端点信息,替代原先泛化的配置检查提示

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>
@ThreeFish-AI ThreeFish-AI changed the title fix(kg): 修复全局问答 Map 阶段 LLM 失败的诊断缺口与模型解析链路 fix(kg): 修复全局问答 Map 阶段 LLM 失败的诊断缺口与模型解析链路; May 27, 2026
1. _call_llm 中 call_llm_with_retry 异常时同步记录 _map_diagnostics,
   确保认证错误/模型不可用等异常场景的诊断信息不丢失;
2. resolve_llm_config_for_task 新增 fallback_config_id 参数,
   在回退链中插入语料库绑定配置(corpus llm_config_id),
   防止 task 配置未命中时直接跳到全局默认导致模型/凭证不匹配;

🤖 Generated with [Claude Code](https://github.com/Claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>
@ThreeFish-AI
ThreeFish-AI merged commit 886609f into feature/1.x.x May 27, 2026
3 checks passed
@ThreeFish-AI
ThreeFish-AI deleted the ThreeFish-AI/fix-global-qa-map-failure branch May 27, 2026 06:46
ThreeFish-AI added a commit that referenced this pull request May 29, 2026
…718)

* fix(Composer): 修复 @ Mention 弹层与模型选择器下拉定位问题

- @ Mention 弹层:改为向上展开并水平锚定 @ 符号位置(canvas measureText 测量)
- 模型选择器:将原生 select 替换为自定义 Portal 下拉组件,向上展开完整显示模型清单
- 新增 useClickOutside hook,复用于模型选择器的点击外部关闭

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(Composer): 修复 Mention 弹层水平定位折行偏差与下拉高度不同步问题;

- Composer: 以镜像 div 替代 canvas measureText,正确处理 textarea 多行折行场景下 @ 符号的水平像素定位
- LlmModelSelect: measuredTop 改为基于约束后实际高度(effectiveH)计算,消除 maxHeight 缩小时下拉框与触发器间的视觉空隙
- 移除未被引用的 use-click-outside.ts 死代码

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* chore(security): 修复 Dependabot 报告的 69 个依赖漏洞 + cognizes-ui/travel-agent-ui 收敛到 pnpm (#651)

* chore(negentropy): 锁定 urllib3>=2.7.0、新增 idna>=3.15 修复 4 项依赖漏洞;

- constraint-dependencies 中将 urllib3 上调至 ≥2.7.0,缓解
  GHSA-mf9v-mfxr-j63jGHSA-qccp-gfcp-xxvc 解压缩炸弹/无限链解压缩高危漏洞
- 新增 idna ≥3.15 约束,修复 GHSA-65pc-fj4g-8rjx(idna.encode()
  特殊构造输入可绕过 CVE-2024-3651 修复,medium)
- 重新执行 uv lock --upgrade-package 局部 relock,避免无关包漂移
  lockfile 中 urllib3 2.6.3→2.7.0、idna 3.11→3.16

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* chore(perceives): override-dependencies 追加 authlib>=1.7.1 修复 OIDC Open Redirect;

- 在 [tool.uv].override-dependencies 中新增 authlib ≥1.7.1,缓解
  GHSA-r95x-qfjj-fjj2(OIDC Implicit/Hybrid 授权流程开放重定向漏洞,medium)
- authlib 为 requests/openid 链路上的传递依赖,原 lockfile 锁死 1.7.0
  受影响;本次通过 override-dependencies 与 negentropy 对齐
- uv lock --upgrade-package authlib 局部 relock:authlib 1.7.0→1.7.2

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* refactor(workspace): cognizes-ui/travel-agent-ui 收敛到 pnpm 工作区,自动消化 ~46 项 JS 依赖漏洞;

将 cognizes-ui 从 yarn 迁移至 pnpm,并把 e2e_travel_agent/travel-agent-ui
纳入根 pnpm-workspace.yaml;两者借助根 overrides(dompurify/lodash-es/
picomatch/follow-redirects/postcss/uuid/prismjs 等)自动吸收安全补丁,
约 46 项 JS 传递依赖告警由此一并消化,同时回归项目「JS/TS 必须用 pnpm」
统一约定,消除两条违规链路。

== cognizes-ui (yarn → pnpm) ==

- 删除 apps/cognizes-ui/{yarn.lock,.yarnrc.yml};package.json 中
  test:e2e/test:e2e:ui/test:visual 去掉 yarn flat node_modules 兼容用的
  NODE_PATH=./node_modules 前缀(pnpm 严格 hoist 下不需要)
- README 安装/启动改为 `pnpm install`、`pnpm --filter @negentropy/cognizes-ui dev`
- 删除 .github/actions/setup-yarn-ui/(无其他引用),workflow 全量改为
  setup-pnpm-ui,yarn lint|test:coverage|build|test:e2e 改写为
  pnpm lint|test:coverage|build|test:e2e;tsc 改为 pnpm exec tsc --noEmit
- cognizes-ui-tests.yml path filter 同步从 setup-yarn-ui/** 切到
  setup-pnpm-ui/**,并补充 pnpm-lock.yaml/pnpm-workspace.yaml/package.json
  触发以反映 monorepo 共享依赖联动

== travel-agent-ui 纳入 pnpm workspace ==

- pnpm-workspace.yaml packages 追加 apps/cognizes/.../travel-agent-ui,
  删除其 package-lock.json,pnpm install 一次性吸收 next/hono/mermaid/
  CopilotKit 等子图并统一应用全局 overrides
- 新增 prismjs ≥1.30.0 override(refractor@3.6.0 锁死 prismjs 1.27.0,
  GHSA-x7hr-w5r2-h6wg DOM clobbering,强制覆盖至 1.30.0)
- @scarf/scarf 设为 false:匿名使用统计上报工具,禁用其 postinstall

== 验证 ==

- pnpm --filter @negentropy/cognizes-ui lint/build/test 全绿
  (typecheck 退出码 0;vitest 28 passed | 10 skipped)
- pnpm --filter travel-agent-ui build 通过(Next.js 16.2.6 静态生成 6 路由)
- pnpm why 抽检:axios@1.16.1、postcss@8.5.14、picomatch@4.0.4、
  follow-redirects@1.16.0、dompurify@3.4.3、lodash-es@4.18.1、uuid@14.0.0、
  hono@4.12.22、mermaid@11.15.0、prismjs 已统一 1.30.0、next@16.2.6

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* chore(cognizes): 重新 resolve uv.lock 修复 18 项 Python 漏洞 + 删除孤儿子级 lockfile;

cognizes 工作区主 uv.lock 长期未跟进 pyproject.toml 的 google-adk≥2.0.0
等约束变更,导致 lockfile 中存在大量 stale 包与 18 项 Dependabot 告警。
本次对 vulnerable 包执行 surgical relock(uv lock --upgrade-package 逐项指定
不使用 --upgrade 全图 re-resolve,避免无关包漂移),并删除 e2e_travel_agent
子目录下的孤儿 uv.lock(uv workspace 统一在工作区根维护单一 lockfile)。

升级到位的关键安全补丁:
- urllib3 2.7.0、idna 3.16、cryptography 48.0.0、requests 2.34.2、
  pillow 12.2.0、authlib 1.7.2、python-multipart 0.0.29、
  python-dotenv 1.2.2、pytest 9.0.3、streamlit 1.57.0、
  pygments 2.20.0、gitpython 3.1.50、pyasn1 0.6.3、black 26.5.1、
  protobuf 5.29.6
- tornado/mako/pyjwt 等通过 google-adk 2.x 重构自然移除(不再传递引入),
  对应告警将自动关闭

孤儿 lockfile 处理:
- apps/cognizes/src/cognizes/examples/e2e_travel_agent/uv.lock 删除
- e2e_travel_agent 在 [tool.uv.workspace] members 中,其依赖解析统一
  归口至 apps/cognizes/uv.lock,子目录 lockfile 为历史遗留
- 验证:cd e2e_travel_agent && uv sync --frozen → Checked 146 packages

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(negentropy-wiki): 修复 Next.js 16 下 next lint 命令失效问题,迁移至 ESLint flat config; (#653)

- 新增 eslint.config.mjs(基于 eslint-config-next 的 flat config 格式)
- 将 package.json lint 脚本从 next lint 迁移为 eslint .
- 新增 @eslint/js、typescript-eslint、eslint-plugin-react 等依赖
- 预存代码的 react-hooks/refs 等高阶规则降级为 warning 以避免阻塞 CI

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)

* feat(wiki-ui): 按设计图重构 Wiki 页面布局、暗色主题与交互组件; (#652)

* feat(wiki-ui): 按 Wiki 设计图重构页面布局与暗色主题;

- 暗色主题 token 微调:背景 #2D2D2D、侧边栏 #1E1E1E、强调色 #4ade80
- Header 新增搜索框 (WikiSearchBox) 和操作区 (WikiHeaderActions: 登录/GitHub/设置)
- 文档详情页集成面包屑导航 (WikiBreadcrumb + buildBreadcrumbPath DFS 辅助函数)
- 新增标签栏组件 (WikiTagBar) 和评论区 UI 壳 (WikiCommentSection)
- 导航树选中项增加左侧 2px 绿色边框高亮
- TOC 默认态改为 collapsed (rail 模式),尊重设计图无固定右侧面板的布局
- 新增 CSS 变量:--wiki-header-bg、--wiki-accent-green、--wiki-input-bg

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(wiki-ui): 修复 Code Review 反馈的三项问题:注释与代码矛盾、死代码清理、占位按钮交互一致性;

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(cli): 降级 start_service 已在运行日志为 info 级别; (#654)

Phase 4 为 wiki SSG 构建提前启动 perceives/backend,Phase 5 再次遍历时
命中 is_running 检查输出 log_warn(黄色),但这是预期行为而非异常,
降级为 log_info(蓝色)以消除视觉噪音与语义误导。

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)

* refactor(wiki-ui): 统一首页与内容页布局设计语言,整合双轨设计系统; (#655)

* refactor(wiki-ui): 统一首页与内容页布局设计语言,整合双轨设计系统;

- 整合 --home-* 和 --wiki-* 双轨 Design Token 为单一体系,新增 --wiki-card-radius/shadow、--wiki-home-accent
- 扩展 WikiHeader 支持 homeLinks 首页模式,淘汰独立的深色 WikiHomeNavbar 组件
- WikiLayoutShell 新增 variant="home" 全宽单栏变体 + footer 属性,首页复用统一布局壳
- 首页卡片系统改为数据驱动,动态渲染 publication,移除硬编码 CARD_SLOTS
- CardIcons 新增 IconGeneric fallback + getPublicationIcon 映射函数
- MarkdownRenderer 添加 rehypeSlug 支持,内容页切换到增强渲染(锚点标题、代码块、响应式表格)
- 清理 home-navbar.css、home.css 等遗留文件,净减 328 行代码

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(wiki-ui): 首页导航链接改用 Next.js Link 组件,移除未使用的 HomeCardEmpty;

- WikiHeader homeLinks 分支从 <a> 改为 <Link>,恢复客户端路由体验
- 移除死代码 HomeCardEmpty(全项目无引用)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(wiki-ui): 首页 Hero 条幅改造为液态流动渐变动画背景; (#656)

* feat(wiki-ui): 首页 Hero 条幅改造为液态流动渐变动画背景;

参考 reactbits.dev Aurora 组件的三色流动渐变模式,以纯 CSS 实现零依赖液态动画:
- 三层 radial-gradient 斑点 + filter:blur(60px) 有机柔化
- 14s/18s/22s 不同周期的 @Keyframes 对角线漂移
- 基于 --wiki-home-accent 衍生三色渐变(#20B2AA / #2DD4BF / #0D9488)
- 完整的 prefers-reduced-motion 无障碍适配与移动端 blur 降级
- 新增 home-hero-bg 背景层 + home-hero-content 内容层分层结构

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(wiki-ui): 提取 Hero 液态渐变硬编码色值为设计令牌

将 ::after 伪元素中的硬编码 #34D399 提取为 --wiki-hero-blob-4
自定义属性,统一至 tokens.css 维护,保持设计令牌一致性。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(perceives): 修复 PDF→Markdown standalone 算法块对学术正文的误判

对未携带 ``Algorithm N`` 显式标题的 standalone 路径,新增 ``_has_pseudocode_skeleton`` 守卫:
仅在出现 ``Require/Ensure/Input/Output`` 结构化头部或 ≥3 行 ``N: step`` 编号步骤时才允许识别。

根因:Context Engineering 2.0 论文 Section 2.1 正文段落仅靠 if/for/then 等英文高频词命中关键字 +5、
``∈/≤/∧`` 数学 Unicode 字符命中特殊字符 +2,恰好凑齐 standalone 阈值 7 分,
导致整段被误包装为 ``\`\`\`algorithm`` 代码块,引入数百行重复 PDF 文本。

新增 3 个回归测试:
- standalone 学术段落(数学 Unicode + 关键字)不再被误判
- standalone 含 ≥3 行 ``N:`` 编号步骤的真伪代码仍被识别
- standalone 含 Require/Ensure 头部的真伪代码仍被识别

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(perceives): 过滤 PDF 装饰性小光栅图(bbox ≤ 24pt)

在 ``FitzImageExtractor`` raster 路径上对 ``bbox`` 维度 ≤ 24pt 的光栅图做
二次拦截,与矢量 figure 渲染分支(< 20pt)形成梯度防御。

根因:``EnhancedPDFProcessor.extract_images_from_pdf_page`` 按渲染像素
``< 50px`` 过滤,对原图本身 ≥ 50px 而 PDF 显示尺寸 ≤ 24pt 的装饰图标
(章节图标、脚注上标、SII 装饰元素)无能为力。Context Engineering 2.0
论文中典型 SII 章节图标 bbox 即为 20×22 pt,逃过 50px 像素过滤后被当作
正常图片输出,与正文交织产生视觉噪声(每页 1-2 张 20×22 像素小图)。

阈值取 24pt 而非 20pt:留出 ±2pt 栅格化抖动余量,覆盖 PDF 排版常见的
小图标尺寸区间(typical 20-24pt),同时保留 ≥ 25pt 的真正有意义元素。

新增 2 个回归测试:
- bbox 在 20×22 pt 的装饰图被过滤;> 24pt 的正常 figure 保留
- 无 bbox(``position is None``)的图不参与维度过滤

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(perceives): 孤儿图判定识别 HTML <img src> 引用避免重复追加

``_append_orphan_images`` 仅扫描 ``![alt](path)`` markdown 语法判定引用关系,
但 assembly 阶段为承载 PDF 原始显示尺寸会把所有图渲染为 ``<img src="...">``
HTML 形式(带 width/height 属性)。这导致主体已用 HTML 引用过的图全部被
判为孤儿,在文档末尾整段重复追加,破坏 1:1 还原。

实测:Context Engineering 2.0 论文 markdown 末尾出现一整段 ``<!-- orphan
images appended -->`` 块,重复引用 56 张已在正文中以 HTML img 形式渲染的图,
其中包括论文标题页 SII logo、章节图标、Figure 1-7 等,与正文位置完全重叠。

修复:扩展引用集识别 ``<img src="...">`` 标签,按 basename 匹配;同时支持
``src`` 含 ``/api/documents/<id>/assets/<basename>`` 后端重写路径的形式。

新增 3 个回归测试:
- HTML ``<img>`` 标签引用计入已引用集合(不再误判为孤儿)
- HTML + markdown 图引用混用时不重复追加,真正未引用的孤儿仍能追加
- HTML ``src`` 为 ``/api/.../assets/<file>`` 绝对路径时按 basename 匹配

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(perceives): 合并 PDF span 拼接误拆的复合章节编号

PyMuPDF ``page.get_text("dict")`` 经常把章节复合编号 ``3.1.1`` 拆为
多个 span(``3.`` + ``1.1``),``FitzTextExtractor`` 块拼接采用
``" ".join`` 后输出 ``3. 1.1 Technological Landscape``,进而被 Markdown
解析为有序列表项(``3.`` 起手),完全丢失原 heading 语义。

在拼接后增加一条无损正则后处理:``\d+\.\s+\d+\.\d+(?:\.\d+)?`` 模式
合并为紧凑形态。该模式在自然语言中极罕见(不会出现 ``某数. 某数.某数``
的句法结构),不会误伤普通有序列表、年份句号、小数表达等正文。

实测:Context Engineering 2.0 论文中 ``3.1.1`` / ``3.1.2`` / ``3.1.3``
/ ``5.3.1`` / ``5.3.2`` 等 5 处子章节标题原本输出为 ``3. 1.1`` 形态,
修复后正确还原为 ``3.1.1``。

新增 11 个回归测试覆盖:
- 三段 / 两段 / 四段复合编号合并
- 正文中普通有序列表、小数、年份不被误改
- 一行中多个复合编号同时合并

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(perceives): 重组 PDF 拆解的间隔变音符号(Pokémon / Baltrušaitis 类)

PyMuPDF 把 ``Pokémon`` / ``Baltrušaitis`` / ``Westhäußer`` / ``Perdigão``
等含组合变音字符(acute / caron / diaeresis / tilde / grave / circumflex)
的词在 PDF 中拆为 ``base + 独立间隔符号 + 后续字母``,``" ".join`` 拼回
``Pok ´ emon`` / ``Westh ¨ außer`` / ``Perdig ˜ ao`` 形态,严重破坏
非英语作者名 / 专有名词 / 参考文献的可读性与可检索性。

在 ``_typography_inner`` 前置加入 ``_rejoin_split_diacritics``:识别
``<letter><space>?<spacing-diacritic><space>?<letter>`` 模式,把组合
字符贴到 **后续字母** 上(PDF 视觉语义),再通过 ``unicodedata.normalize(
"NFC", ...)`` 收敛为预组合 codepoint。覆盖 6 类常见变音符号:

- ´ (U+00B4) → COMBINING ACUTE        → é á í ó ú
- ` (U+0060) → COMBINING GRAVE        → è à
- ˆ (U+02C6) → COMBINING CIRCUMFLEX   → ê â
- ¨ (U+00A8) → COMBINING DIAERESIS    → ä ö ü
- ˜ (U+02DC) → COMBINING TILDE        → ã õ ñ
- ˇ (U+02C7) → COMBINING CARON        → š č ž

实测 Context Engineering 2.0 论文中 ``Pok ´ emon`` / ``Baltru ˇ saitis`` /
``Westh ¨ außer`` / ``Perdig ˜ ao`` / ``Perdig˜ao`` / ``Hervé J ´ egou`` 等
8+ 处全部还原。

新增 16 个回归测试覆盖:
- 6 类变音符号的预期合并
- 紧贴 / 单侧空格 / 双侧空格三种间距形态
- 已是预组合形式的文本不被破坏
- 一行中多处变音符号同时合并

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* feat(wiki-ui): Wiki 首页标题与侧边栏映射为一级 Catalog 导航 (#657)

* feat(wiki-ui): Wiki 首页标题与侧边栏映射为一级 Catalog 导航;

将 Publication 根页的 h1 标题与侧边栏标题从 publication.name 替换为
一级 Catalog 名称(如 Harness Engineering),点击后导航到该 Catalog
下首篇文档而非 Wiki 产品首页。Entry 详情页侧边栏 back link 同步适配。

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* refactor(wiki-ui): 将 WikiSidebar 的 activeItem prop 替换为已派生的 catalogTargetSlug 与 catalogName,消除重复计算;

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(perceives): list formatter 不再拆裂复合章节编号

``MarkdownFormatter._format_lists`` 把 ``^(\d+)[\.\)]\s*(.+)$`` 当作有序
列表项 normalize,正则把 ``3.1.1 Foo`` 解析为 ``\1='3', \3='1.1 Foo'``,
强行输出 ``3. 1.1 Foo``,撕裂 ``FitzTextExtractor`` 已合并的复合章节编号。

实测:上游 ``FitzTextExtractor`` 已正确输出 ``3.1.1 Technological Landscape``
(PyMuPDF span 拼接 + 复合编号合并双重保障),但 ``MarkdownFormatter._format_lists``
在管线末端强制把首字符为数字的行视为有序列表项,重新插入空格,导致
``3.1.1 / 3.1.2 / 3.1.3 / 5.3.1 / 5.3.2`` 等 5 处子章节标题在最终
markdown 输出中退化为 ``3. 1.1`` / ``5. 3.1`` 形态,被 markdown 解析器
当作 ``Ordered list item #3``,丢失 section heading 语义。

修复:在 list-item 识别正则中追加 negative lookahead ``(?!\d+\.\d)``,
仅当数字 + 点后接的内容**不以** ``\d+\.\d`` 起手时才视为列表项。常规
``1. item`` / ``10. item`` 仍正常 normalize,``3.1.1 heading`` 保持紧凑。

新增 8 个回归测试覆盖:
- 三段 / 两段 / 四段复合编号被保留
- 普通单数字 + 单点 list 仍被规范化(数字后插空格)
- ``1)`` paren-style list 也被规范化
- 多位数 ``10.Item`` 也被规范化

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* docs(issue-094): 续写第四轮迭代 — Context Engineering 2.0 unseen 样本 6 类根因

第三轮以 71 页双栏 LaTeX 论文为基线 1:1 还原后,本期切换到 28 页混合双栏 +
大量图表 + 多语言作者名(中文 / 葡萄牙文 / 德文)的 unseen sample
``Context Engineering 2.0: The Context of Context Engineering.pdf`` 端到端
验证,再次定位 6 类与前三轮正交的独立根因,全部修复后字符数从 131369 →
113979(-13.2%),图片引用从 93 → 11(孤儿重复 + 装饰小图全部清空,仅
保留 8 张真实 figure + 3 张真孤儿)。

记录内容:
- 表因 / 根因 / 处理方式 三段式覆盖 6 类根因(algorithm 误判 / 装饰小图
  / 孤儿重复 / 复合编号 span 断裂 / 多语言变音字符 / list formatter 二次
  拆裂)
- 量化效果(before-after diff)
- 四轮防范要点(5 条新增经验)
- 四轮同类问题影响(cherry-pick 指引 + inline 公式与 PDF-specific 噪声
  两个未修问题的说明)

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(perceives): 修正变音符号映射与 list formatter 守卫的回归隐患;

针对 PR review 的两条阻塞反馈做收口修复,避免本轮 PDF→Markdown 1:1
还原修复反过来破坏 webpage / 两段章节这类已有正常路径:

1. ``_DIACRITIC_MAP`` 剔除 U+0060 ASCII BACKTICK,改用 U+02CB
   ``ˋ`` MODIFIER LETTER GRAVE。ASCII backtick 与 Markdown inline
   code 定界符 ```code``` 同形,``_apply_typography_fixes`` 会把
   ``Use the \`getline\` function`` 误拼为 ``Use theg̀etlinef̀unction``,
   破坏 webpage / PDF 全流程的 inline code 引用;``_protect_code_blocks``
   仅护栏围栏代码块,对 inline 反引号无效。U+02CB 是 PDF 真实编码的
   grave-accent 间隔符,安全收录。

2. ``_format_lists`` negative lookahead 扩展覆盖 **两段编号 + 大写
   起始标题** 形态(``3.1 Introduction`` / ``5.3 Memory``)。原本
   ``(?!\d+\.\d)`` 仅守护三段及以上复合编号(``3.1.1``),两段
   subsection 仍被退化为 ``3. 1 Introduction``,与三轮修复目标背离。
   新增 ``(?!\d+(?:\.\d+)*\s+[A-Z])`` 双层守卫,覆盖任意层级编号 +
   大写标题词的章节形态;自然语言列表项不会以"数字 + 单空格 + 大写词"
   紧贴前缀,回归风险极低。

新增 / 调整回归用例:
- ``test_ascii_backtick_preserved_as_inline_code``:ASCII 反引号
  inline code 不被当 grave-accent 拼合。
- ``test_a_grave``:迁移到 U+02CB ``ˋ`` 形态。
- ``test_two_segment_capitalized_heading_preserved`` /
  ``test_two_segment_capitalized_heading_mid_doc_preserved``:
  ``3.1 Introduction`` / ``5.3 Memory Layer`` 两段标题保持紧凑。

实测 28 个 formatter 复合编号 + 变音重组用例全部通过,182 个
formatter/markdown/typography 周边用例全绿。

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* chore(agents): 迁移 Agent 文档至 docs/.agents/ 隐藏目录并更新引用链接;

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* docs(guide): 重命名用户手册为 docs/README.md;

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* feat(wiki): 重构 Wiki 站点视觉设计并对齐 Markdown 渲染引擎 (#659)

* build(wiki-deps): 新增 Markdown 渲染增强与动画依赖;

添加 rehype-highlight + highlight.js(语法高亮)、remark-math + rehype-katex + katex(数学公式)、
rehype-raw + rehype-sanitize(raw HTML 支持)、mermaid(图表渲染)、motion(ReactBits 动画运行时)

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* feat(wiki-markdown): 对齐 Markdown 渲染引擎,支持语法高亮、数学公式与 Mermaid 图表;

扩展 MarkdownRenderer 插件链(remark-math、rehype-raw、rehype-sanitize、rehype-katex、rehype-highlight),
新增 MermaidDiagram 客户端组件与 highlight.js 双模式主题,排版与样式对齐 negentropy-ui DocumentMarkdownRenderer

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* feat(wiki-ui): 重构 Wiki 站点首页与内容页视觉设计,参考 Docusaurus 风格;

设计令牌从 Notion 蓝调转向 Docusaurus 绿调(#2e8555 / #25c2a0 暗色模式);
Header 新增 CSS-only 下拉菜单映射 catalog 一级项,60px 高度 + box-shadow;
Footer 全站化深色主题(#30384a)四列布局;首页 Hero 增大字号,卡片四列网格 + Spotlight hover;
侧边栏 active 项主色高亮 + scrollIntoView;面包屑使用 › 分隔符;移动端抽屉动画迁入 CSS

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(wiki-deps): 移除未使用的 motion 依赖并将 Mermaid 安全级别收紧为 strict;

移除 package.json 中未使用的 motion 包以减少安装体积;
将 MermaidDiagram 的 securityLevel 从 loose 改为 strict 以防止 XSS 风险。

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(auth): OAuth 重登录不再覆盖 DB 管理的角色 (#660)

* fix(auth): OAuth 重登录不再覆盖 DB 管理的角色;

_upsert_user_state 在已有用户重登录时保留 DB 中的 roles 而非用 admin_emails
配置派生值无条件覆盖;仅对新用户首次登录使用配置派生角色;返回 AuthUser
携带 DB 权威角色使 JWT session token 立即反映正确角色。

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* test(auth): 新增 OAuth 重登录角色持久化测试;

覆盖 6 个场景:新用户 config 派生角色、已有用户保留 DB 角色、config 提升
不覆盖 DB 角色、损坏数据回退、相同角色实例复用。

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* feat(wiki): 支持归属文档显示名编辑以修正源名称传播瑕疵 (#661)

* feat(wiki): 新增 knowledge_documents.display_name 列以支持 Wiki 显示名编辑;

- Alembic 迁移 0040:ADD COLUMN display_name VARCHAR(255) NULL;
- ORM 模型 KnowledgeDocument 新增 display_name 字段;

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* feat(wiki): 后端 PATCH 端点与存储层支持文档 display_name 更新;

- DocumentStorageService 新增 update_document_display_name 方法;
- DocumentResponse schema 新增 display_name 字段;
- _build_document_response 序列化器携带 display_name;
- 新增 PATCH /base/{corpus_id}/documents/{document_id} 端点;

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* feat(wiki): Wiki 发布管线 display_name 优先级链对齐;

- wiki_service 新增 _resolve_doc_display_title 辅助函数;
- sync_publication 使用 display_name → metadata_.title → original_filename 优先级;
- wiki_dao.get_entry_content SELECT 与 title 计算同步对齐;

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* feat(wiki): 前端 KnowledgeDocument 类型新增 display_name 字段与 updateDocument API;

- KnowledgeDocument 接口新增 display_name?: string | null;
- 新增 updateDocument(corpusId, docId, patch, params) → PATCH 客户端函数;
- features/knowledge/index.ts 导出 updateDocument;

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* feat(wiki): 归属文档区双名展示与行内编辑 display_name;

- 主行显示 display_name || original_filename,副行展示源名称与状态;
- 行内 input 编辑 + Enter 提交 / Esc 取消 + 保存成功 toast 提示;
- 新增 Check / X 图标组件;

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(wiki): 前端 effectiveDisplayName 补齐 metadata.title 回退以对齐后端优先级链;

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(ui): 修复 LLM/Embedding Model 下拉框选项冗余显示 vendor/model 信息; (#662)

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)

* fix(perceives): PDF→Markdown figure 全宽还原 + 公式/矢量/样式多维度修复 (ISSUE-094 R5-R8) (#663)

* fix(perceives): inline 公式恢复 + 编号借入 + KaTeX 语法兼容 (ISSUE-094 R5)

为 Context Engineering 2.0 论文端到端 1:1 还原修复 5 类正交缺陷:
1. `_orphan_block_formulas` → `_orphan_formulas` 共池兜底,inline 公式不再被 assembly 静默丢弃;
2. 新增 `_extract_formula_eq_number` 三模式编号识别(`\tag{N}` / `\quad (N)` / 尾部 `(N)`);
3. `_formula_eq_nums` 集合同时采集 inline `$...$` 与 block `$$...$$` 编号;
4. 新增 2.4.5 段"邻接文本段编号借入"清除 eq 6 重复 PyMuPDF 字符流副本;
5. 新增 2.5 段 inline 公式 promotion 把孤立"数学符号 + (N) 编号 + 短小段"包裹为 `$...$`,KaTeX 兼容 `\quad (N)` 写法(绕过 `\tag` ParseError)。

实测效果:inline `$...$` 计数 0 → 2(eq 3, 4),等式 6 重复段消除,char 113973 → 113917;新增 29 例单测,1538 例既有单测无回归;浏览器 7 个 KaTeX 实例零 ParseError。

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(perceives): Figure 矢量 overlay 标签抑制 + caption 例外保留 (ISSUE-094 R6)

R5 合入后双 Tab 对比浮现:PDF Figure 1 是矢量 + 位图复合图形(顶部 4 列 "Context 1.0..4.0" 标题 / 底部 "Context Input"、"Intelligence Level" 分类标签 + 4 个角色名),但 markdown 中只渲染中间位图,矢量标签因落在位图 bbox 之外被作为独立 text block 散落到图下方破坏阅读流。

修复(assembly 单文件聚焦):
1. `special_regions` 构造扩展消费 `input_data.layout.regions` 中 `region_type in ("figure", "picture")` 的 bbox,覆盖完整 Figure 视觉框含矢量标签;
2. 新增 `_is_figure_or_table_caption_text` + `_FIGURE_TABLE_CAPTION_RE`:`Figure N:` / `Fig. N:` / `Table N:` / `Tab N -` 等 caption 即便落入 figure region 也例外保留为段落;
3. `_block_overlaps_special` 命中后对 caption 例外加入 elements。

实测:Figure 1 散落 overlay 标签("Context Input"、"Passive Executor"、"Initiative Agent"、"Reliable Collaborator"、"Considerate Master"、"Intelligence Level"、"More Intelligence...")全部消失,caption 完整保留;char 113917 → 113806(-111);新增 10 例单测(7 caption 正样本 + 7 overlay 标签负样本);既有 1567 例单测无回归。

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(perceives): layout figure region 整图渲染 + PDF pt → CSS px 比例修复 (ISSUE-094 R7)

R6 后双 Tab 实测发现两类正交缺陷:
1. Figure 矢量信息丢失:PDF Figure 1 演进图(4 列 Context 1.0..4.0 标题 + 分类标签)在 markdown 中只剩中间嵌入位图,矢量绘图层全部丢失;
2. Figure 显示宽度仅占容器 1/3:PDF 点(72pt/in)直接当作 CSS 像素(96px/in)输出,缺失 4/3 比例换算。

修复(双文件聚焦):

image_extraction.py:
- `_OVERLAP_THRESHOLD = 0.5` 改为 `_FIGURE_CONTAINS_RASTER_THRESHOLD = 0.8` 并反转去重方向(从「figure 让位 raster」到「figure 整图渲染替代被包含的 raster」);
- `_render_figure_regions` 返回签名变为 `Tuple[List, Set[int]]`,新增 `raster_drop_indices`;
- stage 主流程消费 `drop_indices` 在合并前剔除被替代的 raster;
- `region_type` 接受范围从 `"figure"` 扩展到 `("figure", "picture")`。

assembly.py:
- 新增 `_PDF_PT_TO_CSS_PX = 96.0 / 72.0` 常量;
- `_image_to_markdown` 中 bbox 宽高按 4/3 换算后输出,与 web 96 DPI 渲染对齐。

实测:Figure 1-7 全部从嵌入位图 → 完整 layout region 渲染产物(含矢量绘图层 + 标签 + caption),与 PDF 原版 1:1;Figure 1 显示宽度 373px → 497px(A4 全宽 figure 接近 793px);char 113806 → 113108(-698);md 孤儿 img 3 → 0;新增 10 例单测(含 full-width / 真实场景断言);既有 1577 例单测无回归。

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(perceives): docling 公式 bbox 透传 + PyMuPDF 公式残片清理 (ISSUE-094 R8)

R7 后双 Tab 对照 Section 2.1 发现两类正交缺陷:
1. 公式顺序倒置:Definition 1 后应是 Eq(1) Char:E→P(F),但 markdown 输出 Eq(2) C=⋃Char(e) 在前;
2. `C = [` 残片单独成段,破坏阅读流。

修复(三文件 + 二新单测):

pdf/engines/docling.py:
- `DoclingFormula` 加 `bbox` 字段;
- `_extract_formulas` 优先 `doc.iterate_items()` 拿 `label='formula'` item 的 prov[0].bbox(剥离 $$/$ 包裹 + 同 latex 去重),iterate_items 不可用时降级 markdown 正则。

formula_extraction.py:
- `DoclingFormulaExtractor` 透传 `bbox` 字段,与 mineru 适配器契约对齐。

assembly.py:
- 新增 2.5.5 段 `_FORMULA_FRAGMENT_RE = r"^\s*[A-Za-z]\w*\s*=\s*[\[\(\{]\s*$"` 残片正则;text element ≤ 15 字符且紧邻下一个公式 element 时剔除。

实测:Section 2.1 阅读顺序与 PDF 1:1(Definition 1 → Eq(1) → Definition 2 → Definition 3 → Eq(2) → Definition 4 → Eq(3) → Eq(4) → ...);`C = [` 残片消失;Definition 1 段落完整保留(R7 时因 docling 公式无 bbox 导致 sort 错乱被误删);block math 7 块全部按视觉顺序;char 113108 → 114815;新增 17 例单测(6 docling bbox + 11 残片正则),既有 1587 例无回归。

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(perceives): figure region bbox 视觉扩展 + wiki img width/style 透传 (ISSUE-094 R8)

R7 后双 Tab 实测发现:Context Engineering 2.0 Figure 1 在 Wiki 渲染中仅占容器
~44% 宽度,但原 PDF 占满正文栏全宽(~515pt / A4 正文栏 ~451pt)。

根因:Docling/MinerU 给出的 figure region bbox 仅覆盖嵌入光栅图本体
(~299pt),未含 figure 完整视觉上下文(列标题/子标签/装饰线/caption)。
R7 修复了 pt→px 比例换算(96/72),但若种子 bbox 仅占正文栏 50%,
markdown 的 <img width> 仍远小于 PDF 原版视觉占用。

修复(双维度正交分解):

image_extraction.py:
- 新增 _expand_figure_bbox() 纯函数:以 layout figure bbox 为种子,
  吸纳邻近矢量 drawings(get_drawings)与短文本标签(get_text("dict")),
  得到与 PDF 视觉对齐的扩展 bbox(算法参考 PDFFigures 2.0
  [Clark & Divvala, JCDL'16] 三层合并思路);
- 四步策略:矢量吸纳→文本吸纳→横向截断到正文栏→退化保护(4x 面积上限);
- 在 _render_page_figures 中集成调用,页级 drawings/text_dict 缓存复用;
- 扩展后 bbox 同时用于反向去重计算(更准确的 raster 覆盖率)。

MarkdownRenderer.tsx(Wiki 前端):
- img 组件解构 width/height/style 并透传到 <img> 元素;
- sanitize schema 的 attributes.img 追加 "style"(rehype-sanitize 内置
  CSS 白名单防 expression()/url(javascript:) 等 XSS);
- 与后台 DocumentMarkdownRenderer.tsx 语义对齐。

实测:Context Engineering 2.0 Figure 1 种子 299pt→扩展 ~515pt→markdown
<img width="~687">→Wiki 容器占比从 ~44% 提升至 ~90%;1485 例既有单测无回归;
新增 8 例 figure bbox 扩展单测 + 3 例前端 MarkdownRenderer 透传单测。

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(perceives): formatter style 保护 + wiki 大图 width:100% 填充 (ISSUE-094 R8)

根因 1:formatter._basic_cleanup 中 re.sub(r'\s+style="[^"]*"')
无差别清除所有 style 属性,包括 _image_to_markdown 输出的
max-width:100%;height:auto 响应式样式。新增 _strip_orphan_styles
模块级函数,先用占位符保护 <img> 标签再执行清除。

根因 2:wiki MarkdownRenderer 中 max-width:100% 仅限制图片不超出
容器,不会主动填满。对于 width≥400px 的大图(PDF figure region),
追加 width:100%;max-width:100%;height:auto 使其填满容器,
与 PDF 原版 figure 占满正文栏全宽的视觉等价。小图保持原尺寸不变。

Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(perceives): 公式残片清理注释修正,与代码实际行为对齐 (ISSUE-094 R8)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(wiki): 页面评论 + 文本选中注解 + Google SSO 认证 (#664)

* feat(wiki): 新增页面评论与文本注解后端支持;

- 新增 WikiEntryComment / WikiEntryAnnotation ORM 模型与 Alembic 迁移 0041
- 新增 Comment + Annotation Pydantic schemas(Create/Update/Response/List)
- 新增 DAO 方法:create/list/update/soft-delete,JOIN user_states 解析用户 profile
- 新增 8 个 API 端点:GET(公开)/ POST/PATCH/DELETE(Depends(get_current_user) 鉴权)

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* feat(wiki): 新增 Google SSO 认证、页面评论与文本注解前端实现;

- 新增 Auth BFF 代理路由(login/callback/me/logout),复用后端 Google OAuth
- 新增 WikiAuthProvider + useWikiAuth hook,包裹 root layout
- 重构 WikiCommentSection 为可工作的评论面板(登录检测、提交、删除)
- 新增文本注解系统:TextSelectionHandler(浮动按钮)+ AnnotationComposer(输入浮层)
  + AnnotationHighlightLayer(常驻高亮)+ AnnotationTooltip(hover 提示)
- 新增 W3C TextQuoteSelector 锚定计算/解析 hook
- 新增注解与评论 CSS 样式(高亮、Tooltip、FAB、评论卡片)

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(wiki): 修复 Review 发现的 4 个问题;

1. rangesOverlap 改用标准区间重叠判定,修复部分重叠未检测导致高亮失败
2. /api/auth/login 添加同源校验,防范开放重定向攻击
3. computeAnchor 用 TreeWalker 精确计算选区 offset,修复重复文本定位错误
4. callback route 改用 getSetCookie() 正确处理多个 Set-Cookie

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(wiki): 修复 login route 中 URL 对象误用 headers 属性导致构建失败 (#665)

将 incomingUrl.headers 改为 request.headers,从原始 Request 对象读取 referer 头而非 URL 实例。

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)

* fix(wiki): 修复 CLI start 两处构建失败 (#666)

* fix(wiki): 修复 login route 中 URL 对象误用 headers 属性导致构建失败

将 incomingUrl.headers 改为 request.headers,从原始 Request 对象读取 referer 头而非 URL 实例。

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(wiki): 修复 computeXPath 中 Node 类型与 ChildNode[] 不匹配导致构建失败;

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(auth): 配置级 Admin 角色不可被 DB 状态自动降级 (#667)

* fix(auth): 配置级 Admin 角色不可被 DB 状态自动降级;

- admin_emails 加入 cm.huang@aftership,确保配置级始终授予 Admin
- _upsert_user_state() 增加 Admin 保护断言:admin_emails 中的用户,
  其 Admin 身份不可被任何 DB 状态(记录丢失、角色损坏、降级写入)覆盖
- 补充 3 个测试用例覆盖 DB 降级、记录丢失、角色损坏场景

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(auth): config 级 admin 角色不可被 DB 降级;补充 admin_emails 配置

在 _upsert_user_state 中增加保护守卫:当 config admin_emails 判定为 admin 但 DB 中角色非 admin 时,
强制以 config 为准,防止 DB 状态降级 config 级管理员。同步更新测试用例覆盖此逻辑。

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(knowledge): 补全文档 PATCH 代理路由,修复 wiki 页面修改文档名称报 405 Method Not Allowed; (#668)

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)

* fix(wiki): 隐藏头部导航栏 tabs 区域的水平滚动条; (#669)

将 scrollbar-width: thin 改为 none 并添加 ::-webkit-scrollbar { display: none } 规则,
在 Chrome/Safari/Firefox 中均隐藏滚动条视觉表现,同时保留横向滚动能力。

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)

* feat(pipeline): Pipeline Run ID 使用文档名称替代 UUID 后缀,支持一眼识别对应文档; (#670)

- 后端:新增 _extract_source_label / _sanitize_label 辅助函数,按优先级从 filename > url > source_uri 提取人类可读标签
- 后端:修改 create_pipeline() 生成语义化 run_id(如 ingest_file-report-abcd),保留 4 位 hex 后缀保障唯一性
- 前端:调整 truncateRunId 智能截断逻辑,优先展示 operation + label 部分
- 测试:新增 26 个单元测试覆盖各输入模式与优先级

🤖 Generated with [Claude Code](https://github.com/anthropic.com), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)

* fix(wiki): 修复发布下拉框默认选中及文档条目数统计逻辑; (#671)

* fix(wiki): 修复发布下拉框默认选中及文档条目数统计逻辑;

- Wiki 发布下拉框加载后默认选中第一个发布对象,而非显示"请选择"占位符;
- 文档条目数 (entries_count) 改为只统计 DOCUMENT 类型(实际文档篇数),
  排除 CONTAINER 类型(目录文件夹节点),修正如 5 篇文档 + 3 个文件夹
  错误显示为 8 个条目的问题;
- 新增 WikiDao.count_document_entries 方法以 SQL COUNT 高效查询文档数。

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(wiki): 修复 get_wiki_publication 端点 entries_count 统计逻辑与列表接口一致;

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* feat(wiki): 启用 Google 登录功能 (#672)

* feat(wiki): 启用 Google 登录,将登录按钮接入 WikiAuthProvider;

将 WikiHeaderActions 从 Server Component 转为 Client Component,
接入 useWikiAuth() hook 实现三态 UI(loading/unauthenticated/authenticated):
- 未登录时显示可点击的登录按钮,触发 Google OAuth 流程
- 已登录时显示用户头像 + 下拉菜单(用户名、邮箱、退出登录)
- 新增用户下拉菜单 CSS 样式(wiki-user-dropdown 系列),复用 Wiki CSS 变量确保深色模式兼容

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(wiki): 补全用户下拉菜单的键盘交互与无障碍属性,增加头像加载失败 fallback;

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(wiki): 修复文档内容页文本注解功能无法触发的问题 (#673)

* fix(wiki): 修复文档内容页文本注解功能无法触发的问题;

TextSelectionHandler 中 range.startContainer 在用户选中文本时返回
Text 节点,而 Text 节点没有 closest() 方法,导致容器查找始终失败,
FAB 注解按钮永远不渲染。通过 parentElement 上溯到元素节点后再
调用 closest() 修复此问题;同时补全创建注解时的用户 profile 信息。

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(wiki): 对齐 update_entry_annotation 端点的用户 profile 解析,与 create 端点保持一致;

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(auth): 添加启动时 admin_emails 诊断日志,防止配置层覆盖导致 Admin 角色丢失; (#674)

cm.huang@aftership.com 始终无法获得 Admin 角色的根因是 ~/.negentropy/config.yaml
中 admin_emails 缺失该邮箱,用户级配置优先级高于 config.default.yaml 导致覆盖。
在 bootstrap 启动日志中输出解析后的 admin_emails,便于快速定位同类配置不匹配问题。

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)

* fix(perceives): 修复 webpage pipeline 实现 1:1 还原 Anthropic 博客等 Next.js 站点 (#675)

* build(perceives): 补全 webpage pipeline S4/S10 必要依赖;

新增 trafilatura、readability-lxml、html2text、lxml-html-clean 四个 webpage 主内容提取与 HTML→Markdown 转换依赖:

- S4 main_content_extraction 默认配置启用 trafilatura(rank=1)/readability(rank=2)/bs_heuristic(rank=3) 三引擎竞争,但 trafilatura 与 readability 此前未声明在 dependencies 中,运行时强制降级到 bs_heuristic 兜底,正文质量退化;
- S10 markdown_conversion 默认启用 markitdown(rank=1)/html2text(rank=2) 二引擎竞争,html2text 同样缺失;
- lxml-html-clean 是 lxml>=5 拆分出的子模块,justext / readability 依赖此清洗器,缺失会导致 readability 初始化抛 ImportError;

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(perceives): 修复 webpage pipeline 多处 stage 退化问题,实现 1:1 还原;

针对 Anthropic Engineering blog 等 Next.js 静态生成站点 1:1 还原过程中发现的多处 stage 质量问题,做最小干预修复:

S3 anti_detection 短路:
- 此前 S3 配置注释说"仅在 S2 失败时触发",但 orchestrator 实际无条件按顺序执行 sequential stage,S2 成功后 S3 仍尝试启动 playwright_stealth / undetected_chromedriver,遇到驱动错误直接终止整条管线;
- 让 S3 的两个工具在 ctx.raw_html 已存在(≥1KB)时短路返回 success(skip),保持 fallback 语义并避免无谓终止。

S4 main_content_extraction 多处加固:
- _ensure_h1_title:readability / trafilatura 把 H1 当 title 抽走,summary 不含 H1;注入 ctx.title 为 <h1> 时必须放在 <body> 内,否则 MarkItDown 把 <html> 之外的内容视为噪声丢弃;
- _extract_hero_metadata:从 raw_html 启发式识别含 hero / metadata / summary / lead / published 关键字的容器,提取其中的 <p> 序列(发布日期 + lead 简介),紧随 H1 注入到 body;
- _normalize_redirect_urls:把 anchor href 为 ``<host>/redirect/<tracking-id>`` 且 anchor 文本看起来像域名的链接,规范化为 ``https://<anchor-text>/``;
- trafilatura 兜底阈值收紧:
  · 图片丢失阈值由 3 调至 1(哪怕原文只有 1 张正文图,trafilatura 丢失也应让位);
  · 新增"结构退化"兜底:原文 H2+H3 ≥ 3 但 trafilatura 输出 H2+H3 ≤ 1 时视为正文压平,主动失败让 readability / bs_heuristic 接管。

S11 markdown_formatting 两处修复:
- _protect_code_blocks 扩展:此前仅保护"带语言标签"的代码块,未标注语言的纯 fence 在 typography pass 中会被 ``re.sub(r"--", "\\u2014")`` 把代码内 ``--oneline`` 类参数误转为 em-dash。改为保护所有 fenced 代码块(含未标注语言的);
- _dedupe_image_caption:当 ``![alt](url)`` 紧跟一个内容与 alt 完全相同的段落时(HTML <figcaption> 经 MarkItDown 输出为独立段落),删除该重复段,避免 UI 渲染时 figcaption 与正文段同时出现。

回归:apps/negentropy-perceives 内 webpage / main_content / formatter / anti_detection 相关 112 个单元测试全部通过,无既有用例退化。

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(formatter): 修复代码块语言检测因管线顺序错误而失效的问题;

将 _format_code_blocks 调用从 _protect_code_blocks 之后移至之前,
使语言检测正则能匹配实际的 ``` 围栏标记而非 %%CODEBLOCK_%% 占位符。

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(main_content_extraction): 使用 html.escape() 替代手动转义以完整覆盖 HTML 特殊字符;

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(wiki): 修复 Agent 对话框 user_id mismatch 及 session 未创建导致的对话失败问题; (#676)

- ChatDrawer 从 WikiAuthProvider 获取 userId 并透传至 useChatAgent,在 URL 中携带 user_id 参数,解决 auth middleware 的 user_id mismatch 403 错误
- 将 AgentChatMount 移入 WikiAuthProvider 内部,确保 ChatDrawer 能访问认证上下文
- 新增 ensureBackendSession 在首次发送前调用 BFF 创建 ADK session,解决 Session not found 错误
- session 创建结果缓存至 sessionStorage,避免重复创建

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)

* fix(wiki): 修复 Agent 对话框 user_id mismatch 及 session 未创建导致的对话失败问题 (#677)

* fix(wiki): 修复 Agent 对话框 user_id mismatch 及 session 未创建导致的对话失败问题;

- ChatDrawer 从 WikiAuthProvider 获取 userId 并透传至 useChatAgent,在 URL 中携带 user_id 参数,解决 auth middleware 的 user_id mismatch 403 错误
- 将 AgentChatMount 移入 WikiAuthProvider 内部,确保 ChatDrawer 能访问认证上下文
- 新增 ensureBackendSession 在首次发送前调用 BFF 创建 ADK session,解决 Session not found 错误
- session 创建结果缓存至 sessionStorage,避免重复创建

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(wiki): 修复 send 与 reset 之间的 threadId 竞态条件;

在 ensureBackendSession 异步间隙期间若用户触发 reset,doSend 仍会
使用过期的 threadId 发送消息,导致状态不一致。增加 stale 检查丢弃
过期发送。

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(knowledge-graph): 修复 Wiki 知识图谱同步失败,补齐 Mention document_id 关联; 全宽布局与深色主题适配; (#679)

* fix(knowledge-graph): 修复 Wiki 知识图谱同步失败,Mention 创建时补齐 document_id 关联;

KG Build Pipeline 创建 KgEntityMention 时未填充 document_id,
导致 Wiki 图谱切片查询(依赖 kg_entity_mentions.document_id)无法关联任何实体,图谱始终为空。

修复链路:
- routes/graph.py: chunk 数据中提取 document_id 到顶层字段
- service.py: process_chunk 返回 document_id,chunk 循环中构建 entity→documents 映射
- entity_service.py: mention 创建时填充 document_id,已存在实体为多 document 创建幂等 mention

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* refactor(wiki-graph): 知识图谱页改为全宽布局,Canvas 适配深色/浅色模式;

- 图谱页使用 variant="home" 全宽布局,移除 sidebar,最大化可视化空间
- WikiForceGraphCanvas 和 WikiGraphCanvas 的边/标签/箭头颜色适配主题
- CSS 使用 --wiki-border / --wiki-bg-secondary 变量替代硬编码色值

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(kg): 修复全局问答 Map 阶段 LLM 失败的诊断缺口与模型解析链路; (#678)

* fix(kg): 修复全局问答 Map 阶段 LLM 失败的诊断缺口与模型解析链路;

- 注册 knowledge.kg.global_search 任务槽位,支持 UI 为全局问答独立配置模型
- 引入 resolve_llm_config_for_task 任务级解析,优先级:任务级 > Corpus 绑定 > 全局默认
- 添加 _map_diagnostics 诊断累加器,在 LLM 调用失败时捕获模型名与掩码端点
- 将 all-map-failed 日志级别从 WARNING 提升为 ERROR,附带 model/api_base_masked/map_failures 字段
- 错误响应消息嵌入具体模型名和端点信息,替代原先泛化的配置检查提示

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(kg): 修复全局问答 Map 阶段诊断缺口与模型解析回退链断裂;

1. _call_llm 中 call_llm_with_retry 异常时同步记录 _map_diagnostics,
   确保认证错误/模型不可用等异常场景的诊断信息不丢失;
2. resolve_llm_config_for_task 新增 fallback_config_id 参数,
   在回退链中插入语料库绑定配置(corpus llm_config_id),
   防止 task 配置未命中时直接跳到全局默认导致模型/凭证不匹配;

🤖 Generated with [Claude Code](https://github.com/Claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* refactor(knowledge-graph): 将右侧栏重构为独立折叠按钮 + 浮动面板,实体详情改由 Tooltip 显示; (#680) (#680)

* fix(knowledge-graph): 修复 lint 错误:usePanelState setState-in-effect 及未使用的 NodeTooltip import;

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(knowledge-graph): 修复 FloatingPanel 空挂载、定位缺失、死代码及 D3 tooltip 裁剪问题;

- FloatingPanel 关闭时不渲染 children,避免未激活面板触发 API 请求;
- 为 FloatingPanel 的 positioned 父级容器补加 relative;
- 移除未使用的 NodeHoverInfo 接口与 onNodeHover prop;
- D3 inline tooltip foreignObject 高度从 60 调整为 70,防止内容裁剪;

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(perceives): 修复 webpage pipeline Hero 区域元数据丢失 (#681)

* fix(perceives): 修复 webpage pipeline 丢失 Hero 区域元数据(标题、日期、引述、品牌图片);

根因:S4 主内容提取的 hero 元数据注入(标题、发布日期、引述、品牌图片)仅 readability 引擎调用,
trafilatura(rank=1)成功时 hero 提取函数不执行,导致 Anthropic 等博客的头部关键信息丢失。

修复:
- 扩展 _extract_hero_metadata() 同时提取 <img> 和 <time> 元素(按文档顺序)
- 新增 _extract_og_metadata() 从 Open Graph meta 标签提取后备元数据
- 新增 _clean_title() 清洗 <title> 中的站点名后缀(优先使用 og:title)
- 创建 _apply_common_post_processing() 统一封装 H1 注入 + hero 注入 + OG 后备
- 所有 3 个引擎(trafilatura、readability、bs_heuristic)统一调用共享后处理函数
- 去重保护:已存在于正文中的段落不会重复注入

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(perceives): 修复 hero 去重子串误匹配与标题清洗启发式缺陷;

1. hero 去重:将 `text in main_html` 子串匹配改为基于 BS4 提取已有 <p> 文本的
   精确集合匹配,避免短文本(如 "AI")在 HTML 源码中误匹配导致 hero 段落被跳过;
2. 标题清洗:对两段式标题(如 "Title | Site"),当长度相近(短段 ≥ 长段 50%)
   时优先取第一段,覆盖最常见的 CMS 标题格式;长度差异大时仍取最长段。

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(perceives): 修复 Bandit B110 安全扫描告警,hero 去重异常处理替换裸 pass;

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(perceives): PDF→Markdown 1:1 还原 R9 六维修复(公式编号 / KaTeX / 元数据 / caption / 全宽图) (#682)

* fix(perceives): PDF→Markdown 公式 / 元数据 / 装饰图三维修复 (ISSUE-094 R9 round 1);

修复 R8 后 Context Engineering 2.0 PDF 端到端实机暴露的 3 类失真:

- D-5 KaTeX ParseError:``_sanitize_latex`` 新增第 4 策略,剥离非 ``align``
  / ``aligned`` / ``array`` / ``matrix`` / ``cases`` 等对齐环境下的裸 ``&``
  分隔符(Docling 抽出 Eq (2) 携带 ``C & = \bigcup ... & ( 2 )`` 触发
  ``Misplaced &`` ParseError),同环境内对齐符与 ``\&`` 转义符不剥离。

- D-1b 封面元数据残留:``_RUNNING_HEADER_FOOTER_PATTERNS`` 扩展两类模式 ——
  ``§ <SingleWord>`` 链接锚(``Github`` / ``Code`` / ``Project`` 等白名单)
  与 ``<ACRONYM> <ProjectDescriptor>`` 项目 banner(``SII Context`` 类,
  仅命中 2-5 ALL-CAPS + 项目描述词白名单避免误吞 ``MIT Press`` / ``LLM
  Reasoning`` 类正常组合);章节引用 ``§ 2.1`` 等正文不受影响。

- D-1a 装饰光栅图过滤决策修订:抽取 ``_is_decorative_raster_bbox``
  helper 便于单测,决策仅保留 R8 既有「短边 ≤ 24pt」最小必要过滤,
  不引入「cover header zone 小图标」激进规则 —— 实机验证发现激进规则
  会误删 PDF 原版机构 / 项目徽章(如 ASI / SII-GAIR),与「1:1 还原
  PDF 所有内容(含高清原图、图片显示尺寸)」目标冲突。

新增 36 单元测试覆盖三类修复回归不变量:

- ``test_assembly_sanitize_latex.py`` (9)
- ``test_assembly_cover_metadata_residue.py`` (15)
- ``test_image_decorative_filter.py`` (12)

量化签名(Context Engineering 2.0,28 页):
- ``katex_block_with_bare_ampersand`` 1 → 0
- ``residue_section_github`` 1 → 0
- ``residue_sii_context`` 1 → 0
- ``image_html_imgs`` 8(含 ASI 徽章保留)

既有 1521 单元测试 0 退化(仅环境差异 ``test_config`` 跳过)。

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>

* fix(perceives): PDF→Markdown 公式编号推断 + 图片 caption 邻接注入 (ISSUE-094 R9 round 2);

修复 R9 round 1 后 Context Engineering 2.0 PDF 端到端暴露的 2 类失真:

- D-2/D-3/D-4 公式编号推断回填:新增 ``_infer_missing_formula_numbers``
  函数,利用学术论文连续编号习惯,按相邻有编号公式之间的 gap 与未编号
  公式数是否一致(gap-consistency)来保守填补缺失的 ``\quad (N)`` 编号。
  集成到 assembly 2.5.6 段,Eq (3) / Eq (4) / Eq (6) 三处缺号全部恢复,
  ``formula_quad_numbered`` 从 3 → 6。Gap 不一致时拒绝填补避免误编号。

- D-6 图片 caption 邻接段注入:新增 ``_figure_caption_to_inject`` 函数,
  当 image_extraction 阶段未关联到图片 caption 且文档顺序下一个 text
  element 匹配 ``Figure N:`` / ``Fig. N:`` / ``Table N:`` ca…
ThreeFish-AI added a commit that referenced this pull request Sep 6, 2026
沉淀存量 alert 清零过程中的四项实测结论,其中两项推翻既有记载:

- setuptools #555 由 dismiss 改判为升级:记录 pip-audit 双配置对照(保留/剔除
  ignore 匹配数同为 8)作为「该 ignore 已死」的判据,以及「有零成本修复路径时
  消因优于记账」这一对上一轮最小干预判断的修正
- torch #507:旧记「四方联动约束」证伪(constraint 解析下三个 PDF 引擎零位移),
  真实阻断是 torchvision 0.29 与 transformers 4.57.6 在 docling worker 的不兼容
- transformers #678/#499:pillow<11 冲突经 uv lock exit 1 与上游 METADATA 双重证实
- 附带订正:marker-pdf 1.10.2(当前锁定版)本身也声明 Pillow<11,现状已由 override
  压过;准确表述应为「已验证组合换未验证组合」,而非「升 marker 会把 pillow 降级」

两条方法论增补(本轮踩坑所得):
- override-dependencies 会静默压过上游声明,故不能用它验证「某约束是否可行」,
  必须用 constraint-dependencies(冲突即报错)——本轮曾因此得到 transformers 5.x
  「能解析」的假象
- 验证必须走本项目真实调用路径:DocumentConverter 直调在 torch 2.14 下成功,
  只有经 perceives engine worker 才失败,只测上游库会得出完全相反的结论

另记 GitHub 侧收口:dismiss #678/#499/#507,并刷新 #492/#343 两条历史 dismiss 的
陈旧 comment(原载已证伪的 transformers.onnx 口径)。

🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist)
Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant