Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## Unreleased

- Label pages and links written by the local curator as `local-curator`.

## 0.13.0 - 2026-09-29

- Added optional `wikibricks-curator propose` for local backlog curation. It
Expand Down
24 changes: 18 additions & 6 deletions src/wikibricks/curation/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ def _apply_patch(
patch: dict[str, Any],
*,
force: bool,
created_by: str,
) -> UUID | None:
operation = patch["operation"]
proposal = patch["proposal"]
Expand All @@ -172,7 +173,7 @@ def _apply_patch(
proposal["title"],
proposal["content"],
page_type=proposal["page_type"],
created_by="remote-curator",
created_by=created_by,
tags=proposal["tags"],
source_ids=proposal["source_ids"],
parent_id=proposal["parent_id"],
Expand All @@ -187,7 +188,7 @@ def _apply_patch(
proposal["title"],
proposal["content"],
page_type=proposal["page_type"],
created_by="remote-curator",
created_by=created_by,
tags=proposal["tags"],
source_ids=proposal["source_ids"],
parent_id=proposal["parent_id"],
Expand All @@ -210,7 +211,7 @@ def _apply_patch(
proposal["title"],
proposal["content"],
page_type=proposal["page_type"],
created_by="remote-curator",
created_by=created_by,
tags=proposal["tags"],
source_ids=proposal["source_ids"],
parent_id=proposal["parent_id"],
Expand All @@ -234,13 +235,14 @@ def _apply_patch(
conn.execute(
"INSERT INTO links "
"(link_id, source_page_id, target_page_id, link_type, origin, metadata, created_at) "
"VALUES (?, ?, ?, ?, 'remote-curator', ?, ?) "
"VALUES (?, ?, ?, ?, ?, ?, ?) "
"ON CONFLICT (source_page_id, target_page_id, link_type) DO NOTHING",
(
str(uuid4()),
source["page_id"],
target["page_id"],
proposal["link_type"],
created_by,
json.dumps(metadata, separators=(",", ":"), sort_keys=True),
datetime.now(timezone.utc).isoformat(),
),
Expand All @@ -251,13 +253,14 @@ def _apply_patch(
conn.execute(
"INSERT INTO links "
"(link_id, source_page_id, target_page_id, link_type, origin, metadata) "
"VALUES (%s, %s, %s, %s, 'remote-curator', %s) "
"VALUES (%s, %s, %s, %s, %s, %s) "
"ON CONFLICT (source_page_id, target_page_id, link_type) DO NOTHING",
(
uuid4(),
UUID(source["page_id"]),
UUID(target["page_id"]),
proposal["link_type"],
created_by,
Jsonb(metadata),
),
)
Expand Down Expand Up @@ -333,6 +336,7 @@ def _apply_group(
*,
force: bool,
failpoint: Callable[[str], None],
created_by: str = "remote-curator",
) -> tuple[str, list[dict[str, Any]]]:
processed = receipt_count(conn, patches)
if processed == len(patches):
Expand Down Expand Up @@ -386,7 +390,13 @@ def _apply_group(
result_version_id=result_id,
)
else:
result_id = _apply_patch(store, conn, patch, force=force)
result_id = _apply_patch(
store,
conn,
patch,
force=force,
created_by=created_by,
)
insert_receipt(
conn,
run_id,
Expand All @@ -404,6 +414,7 @@ def apply_run(
*,
policy: str = "safe",
failpoint: Callable[[str], None] | None = None,
created_by: str = "remote-curator",
) -> dict[str, Any]:
if policy not in {"safe", "all"}:
raise ValueError("curation policy must be 'safe' or 'all'")
Expand Down Expand Up @@ -432,6 +443,7 @@ def apply_run(
items,
force=False,
failpoint=trigger,
created_by=created_by,
)
mark_run_applied(conn, run_id)
results.append(
Expand Down
7 changes: 6 additions & 1 deletion src/wikibricks_curator/curator.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,12 @@ def run_curator(
store_manifest(store, manifest)
result["run_id"] = str(run_id)
if apply:
outcome = apply_run(store, run_id, policy="safe")
outcome = apply_run(
store,
run_id,
policy="safe",
created_by="local-curator",
)
result["counts"] = outcome["counts"]
result["status"] = (
"applied"
Expand Down
76 changes: 76 additions & 0 deletions tests/test_local_curator.py
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,82 @@ def chat(_prompt: str, request: dict, _schema: dict) -> dict:
}


def test_run_curator_labels_created_page_and_applied_link(tmp_path: Path):
from wikibricks_curator.curator import run_curator

store = _store(tmp_path)
store.write_page(
"topics/related-target",
"Related Target",
{"summary": "Label project target", "body": "The label project target already exists."},
)
with store.connection(write=True) as conn:
conn.execute(
"UPDATE pages SET updated_at = ? WHERE path = 'topics/related-target'",
(_recent_timestamp(4),),
)
_backlog_project(store, project="label-project")

calls = []

def chat(_prompt: str, request: dict, _schema: dict) -> dict:
calls.append(request)
if len(calls) == 1:
return {"proposals": [_proposal(
operation="create_page",
path=request["living_page"],
evidence_id=request["evidence"][0]["evidence_id"],
)]}
return {
"proposals": [{
"group": "main",
"operation": "add_link",
"path": request["living_page"],
"target_path": "topics/related-target",
"link_type": "related",
"evidence_ids": [request["evidence"][0]["evidence_id"]],
"reason": "The pages cover related concepts.",
"risk_class": "low",
}]
}

result = run_curator(store.database_path, chat=chat, projects=1)
assert result["projects"][0]["status"] == "applied"

with store.connection(write=True) as conn:
conn.execute(
"UPDATE pages SET updated_at = ? WHERE path = ?",
(_recent_timestamp(3), "topics/label-project"),
)

_ingest(
store,
"label-project-second",
"/Users/u/work/label-project",
_recent_timestamp(1),
[SessionEvent("user-2", "user", "another durable fact")],
)

result = run_curator(store.database_path, chat=chat, projects=1)
assert result["projects"][0]["status"] == "applied"
assert result["projects"][0]["counts"] == {"applied": 1}
with store.connection() as conn:
version = conn.execute(
"SELECT created_by FROM page_versions "
"WHERE version_id = (SELECT current_version_id FROM pages WHERE path = ?)",
("topics/label-project",),
).fetchone()
link = conn.execute(
"SELECT origin FROM links WHERE source_page_id = "
"(SELECT page_id FROM pages WHERE path = ?) "
"AND target_page_id = (SELECT page_id FROM pages WHERE path = ?)",
("topics/label-project", "topics/related-target"),
).fetchone()

assert tuple(version) == ("local-curator",)
assert tuple(link) == ("local-curator",)


def test_shrink_guard_leaves_page_for_review(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
Expand Down
Loading