把 agent_summary 写入对应 section 并保存 .outline.json。 经 load_or_build_outline 取**新鲜**大纲(盘上缓存过期时现场重建), 再原子写回——保证 annotate 的锚点视图与 `k.py outline` 展示的一致, 且写回后盘上缓存重新变为新鲜。
(md_path: Path, anchor: str, summary: str)
| 1776 | |
| 1777 | |
| 1778 | def annotate_section(md_path: Path, anchor: str, summary: str) -> dict: |
| 1779 | """把 agent_summary 写入对应 section 并保存 .outline.json。 |
| 1780 | |
| 1781 | 经 load_or_build_outline 取**新鲜**大纲(盘上缓存过期时现场重建), |
| 1782 | 再原子写回——保证 annotate 的锚点视图与 `k.py outline` 展示的一致, |
| 1783 | 且写回后盘上缓存重新变为新鲜。 |
| 1784 | """ |
| 1785 | outline = load_or_build_outline(md_path) |
| 1786 | outline_path = md_path.with_suffix(".outline.json") |
| 1787 | target = anchor.lstrip("^") |
| 1788 | |
| 1789 | def walk(secs): |
| 1790 | for s in secs: |
| 1791 | if s.get("anchor") == target: |
| 1792 | s["agent_summary"] = summary |
| 1793 | return s |
| 1794 | r = walk(s.get("children", [])) |
| 1795 | if r: |
| 1796 | return r |
| 1797 | return None |
| 1798 | |
| 1799 | sec = walk(outline["sections"]) |
| 1800 | if not sec: |
| 1801 | raise LookupError(f"outline 里没有 anchor: {anchor}") |
| 1802 | # 原子写:避免 web 端正在读 .outline.json 时拿到半截 JSON |
| 1803 | _atomic_write_text( |
| 1804 | outline_path, |
| 1805 | json.dumps(outline, ensure_ascii=False, indent=2), |
| 1806 | ) |
| 1807 | return { |
| 1808 | "path": _to_rel_posix(md_path), |
| 1809 | "anchor": target, |
| 1810 | "title": sec.get("title", ""), |
| 1811 | "agent_summary": summary, |
| 1812 | } |
| 1813 | |
| 1814 | |
| 1815 | def _collect_anchors_in_md(md_path: Path) -> set[str]: |
no test coverage detected