Skip to content
Open
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ All notable changes to vouch are documented here. Format follows
KB under `eval/fixture-kb/`, and an `eval` workflow gating retrieval changes
(#226).
### Fixed
- `review-ui` health checks now stay alive when a pending proposal YAML file is
corrupt or contains invalid control characters, so desktop launchers do not
time out waiting for `/healthz`.
- `parse_since` (the `--since` parser behind `vouch metrics`/`vouch audit`) now raises a clean `MetricsError` for a duration too large to represent (e.g. `--since 1000000000000d`), instead of letting an uncaught `OverflowError` traceback escape — restoring the documented "clean error, not a traceback" contract.
- `sync_apply` now loads the sync source exactly once and passes the same `_SyncSource` instance into `sync_check`, closing a TOCTOU window where a bundle replaced on disk between the two `_load_source` calls could cause the validation and write phases to operate on different snapshots. Also eliminates redundant directory walks (KB sources) and triple tarball opens (bundle sources). Fixes #217.
- `vault_to_kb` now passes `slug_hint=page_id` to `propose_page` so vault edit proposals target the existing page id from frontmatter instead of a slugified copy of the title (fixes #219).
Expand Down
7 changes: 6 additions & 1 deletion src/vouch/web/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,11 @@ def _pending_page(store: KBStore, page: int, page_size: int
return proposals, page, pages, total


def _pending_count(store: KBStore) -> int:
proposed_dir = store.kb_dir / "proposed"
return len(list(proposed_dir.glob("*.yaml"))) if proposed_dir.is_dir() else 0


# --- auth -----------------------------------------------------------------


Expand Down Expand Up @@ -369,7 +374,7 @@ def healthz() -> dict[str, Any]:
return {
"ok": True,
"kb": str(store.root),
"pending": len(store.list_proposals(ProposalStatus.PENDING)),
"pending": _pending_count(store),
"auth": auth.enabled,
"clients": hub.client_count,
}
Expand Down
16 changes: 16 additions & 0 deletions tests/test_web.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,22 @@ def test_healthz(client: TestClient, store: KBStore) -> None:
assert body["pending"] == 1


def test_healthz_survives_unreadable_pending_yaml(
client: TestClient, store: KBStore,
) -> None:
(store.kb_dir / "proposed" / "bad.yaml").write_text(
"id: bad\npayload: '\x80'\n",
encoding="utf-8",
)

r = client.get("/healthz")

assert r.status_code == 200
body = r.json()
assert body["ok"] is True
assert body["pending"] == 1


# --- bring-up errors ------------------------------------------------------


Expand Down
Loading