Skip to content

Commit 5346945

Browse files
committed
feat: add serial mutation recovery
1 parent 8d40458 commit 5346945

9 files changed

Lines changed: 1433 additions & 218 deletions

File tree

openkb/agent/compiler.py

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
resolve_entity_types,
3737
)
3838
from openkb.lint import list_existing_wiki_targets, strip_ghost_wikilinks
39+
from openkb.locks import atomic_write_text
3940
from openkb.schema import INDEX_SEED, get_agents_md
4041

4142
logger = logging.getLogger(__name__)
@@ -779,7 +780,7 @@ def _write_summary(wiki_dir: Path, doc_name: str, summary: str,
779780
fm_lines.append(f"doc_type: {doc_type}")
780781
fm_lines.append(_yaml_kv_line("full_text", f"sources/{doc_name}.{ext}"))
781782
fm_block = "---\n" + "\n".join(fm_lines) + "\n---\n\n"
782-
(summaries_dir / f"{doc_name}.md").write_text(fm_block + summary, encoding="utf-8")
783+
atomic_write_text(summaries_dir / f"{doc_name}.md", fm_block + summary)
783784

784785

785786
_SAFE_NAME_RE = re.compile(r'[^\w\-]')
@@ -839,7 +840,7 @@ def _write_concept(wiki_dir: Path, name: str, content: str, source_file: str, is
839840
if brief:
840841
fm_lines.append(_yaml_kv_line("description", brief))
841842
existing = frontmatter.block(fm_lines) + clean
842-
path.write_text(existing, encoding="utf-8")
843+
atomic_write_text(path, existing)
843844
return
844845
# Guarantee type + refresh description on update; remove legacy brief:.
845846
ex_parts2 = frontmatter.split(existing)
@@ -851,7 +852,7 @@ def _write_concept(wiki_dir: Path, name: str, content: str, source_file: str, is
851852
# Drop legacy brief: lines (migrated to description:).
852853
fm_block = frontmatter.drop_line(fm_block, "brief")
853854
existing = fm_block + body
854-
path.write_text(existing, encoding="utf-8")
855+
atomic_write_text(path, existing)
855856
else:
856857
clean_parts = frontmatter.split(content)
857858
if clean_parts is not None:
@@ -863,7 +864,7 @@ def _write_concept(wiki_dir: Path, name: str, content: str, source_file: str, is
863864
if brief:
864865
fm_lines.append(_yaml_kv_line("description", brief))
865866
fm_block = "---\n" + "\n".join(fm_lines) + "\n---\n\n"
866-
path.write_text(fm_block + content, encoding="utf-8")
867+
atomic_write_text(path, fm_block + content)
867868

868869

869870
def _write_entity(
@@ -927,10 +928,10 @@ def _build_entity_frontmatter(sources: list[str]) -> str:
927928
break
928929
merged = [source_file] + [s for s in recovered if s != source_file]
929930
existing = _build_entity_frontmatter(merged) + clean
930-
path.write_text(existing, encoding="utf-8")
931+
atomic_write_text(path, existing)
931932
return
932933

933-
path.write_text(_build_entity_frontmatter([source_file]) + clean, encoding="utf-8")
934+
atomic_write_text(path, _build_entity_frontmatter([source_file]) + clean)
934935

935936

936937
_set_fm_line = frontmatter.set_line
@@ -1041,7 +1042,7 @@ def _add_related_link(
10411042
text = _prepend_source_to_frontmatter(text, source_file)
10421043

10431044
text += f"\n\nSee also: {link}"
1044-
path.write_text(text, encoding="utf-8")
1045+
atomic_write_text(path, text)
10451046
return True
10461047

10471048

@@ -1068,7 +1069,7 @@ def _backlink_summary_pages(
10681069
_ensure_h2_section(lines, section, quiet=True)
10691070
for slug in reversed(missing):
10701071
_insert_section_entry(lines, section, f"- [[{page_dir}/{slug}]]")
1071-
summary_path.write_text("\n".join(lines), encoding="utf-8")
1072+
atomic_write_text(summary_path, "\n".join(lines))
10721073

10731074

10741075
def _backlink_pages(
@@ -1089,7 +1090,7 @@ def _backlink_pages(
10891090
lines = text.split("\n")
10901091
_ensure_h2_section(lines, "## Related Documents", quiet=True)
10911092
_insert_section_entry(lines, "## Related Documents", f"- {link}")
1092-
path.write_text("\n".join(lines), encoding="utf-8")
1093+
atomic_write_text(path, "\n".join(lines))
10931094

10941095

10951096
def _backlink_summary(wiki_dir: Path, doc_name: str, concept_slugs: list[str]) -> None:
@@ -1195,7 +1196,7 @@ def _remove_doc_from_pages(
11951196
path.unlink()
11961197
deleted.append(path.stem)
11971198
elif new_text != text:
1198-
path.write_text(new_text, encoding="utf-8")
1199+
atomic_write_text(path, new_text)
11991200
modified.append(path.stem)
12001201

12011202
return {"modified": modified, "deleted": deleted}
@@ -1291,7 +1292,7 @@ def remove_doc_from_index(wiki_dir: Path, doc_name: str, concept_slugs_deleted:
12911292
while _remove_section_entry(lines, "## Entities", entity_link):
12921293
pass
12931294

1294-
index_path.write_text("\n".join(lines), encoding="utf-8")
1295+
atomic_write_text(index_path, "\n".join(lines))
12951296

12961297

12971298
def _update_index(
@@ -1315,7 +1316,7 @@ def _update_index(
13151316

13161317
index_path = wiki_dir / "index.md"
13171318
if not index_path.exists():
1318-
index_path.write_text(INDEX_SEED, encoding="utf-8")
1319+
atomic_write_text(index_path, INDEX_SEED)
13191320

13201321
lines = index_path.read_text(encoding="utf-8").split("\n")
13211322

@@ -1361,7 +1362,7 @@ def _update_index(
13611362
else:
13621363
_insert_section_entry(lines, "## Entities", entry)
13631364

1364-
index_path.write_text("\n".join(lines), encoding="utf-8")
1365+
atomic_write_text(index_path, "\n".join(lines))
13651366

13661367

13671368
# ---------------------------------------------------------------------------
@@ -2035,7 +2036,7 @@ async def compile_long_doc(
20352036
updated = fm_block + body
20362037
if updated != summary_content:
20372038
summary_content = updated
2038-
summary_path.write_text(summary_content, encoding="utf-8")
2039+
atomic_write_text(summary_path, summary_content)
20392040

20402041
# Base context A. cache_control marker on the doc message creates a
20412042
# cache breakpoint covering (system + doc) for every concept call.

0 commit comments

Comments
 (0)