de9a5baf45
Slice-6 of the worldtree-sdk cutover: migrate the two admin routes off the
hand-rolled paths onto the `ratatoskr.wt` adapter over `client.admin.*`, and delete
the retired code. Both are web-only (the coverage-map's `tui.py` rows were stale —
corrected to `web/server.py`).
Adapter (`wt.py`): `get_session_bifrost` → `client.admin.sessions.bifrost` (open-world
dict verbatim, any error → SessionApiFailed default); `stream_admin_events` →
`client.admin.stream_events`, re-wrapping the SDK's `AdminEvent` → ratatoskr's at the
boundary.
Decisions (contract § slice-6 notes):
- Admin auth moves from a per-call `Authorization` header override to the client's
`admin_auth` (`_wt_client(admin_key=…)`, extended this slice) — the SDK's admin.*
routes use the provider, not a header.
- `AdminEvent` re-wrap (chosen over yield-through): the SDK's `admin_id`(nan)/None-able
`type`/`data` diverge from ratatoskr's `id`/`type`/`data` that the web filter reads;
re-wrapping (nan→0, None→""/{}) degrades the open-world None/nan ONCE at the adapter
and keeps the web endpoint + `_admin_event_matches_web` + the `AdminEvent` domain type
unchanged (preserves the web surface). Rejected: yield SDK events + rewire the web
filter (heavier churn, scattered hardening).
- Admin-stream error map: a NON-200 open raises `ApiError("admin_stream_failed")`
(NOT `ConnectFailed`) → SseConnectFailed; `ConnectionDropped` (connect-time OR
mid-stream/resumable-EOF) → SseConnectionDropped. The web integration test caught the
ApiError-not-ConnectFailed gotcha the unit fake couldn't.
Web (`web/server.py`): both admin endpoints build the wt client with admin_key and call
`wt.*`; the bifrost endpoint gains ConnectFailed→502 handling (cutover foot-gun); the
admin-events endpoint closes the injected transport (INV-CUT-1), never the wt client.
Deleted the hand-rolled `sessions.get_session_bifrost` + `sse_client.stream_admin_events`
(+ orphaned httpx/httpx_sse/json/AsyncIterator imports); the ratatoskr `AdminEvent`
dataclass stays in `sse_client.py` (re-wrap target, imported by wt + web) until slice-7.
Retired `test_sse_client.py` entirely (its last test was the admin stream) and the
`test_sessions.py` `TestGetSessionBifrost`; added the slice-6 adapter tests.
LIVE SMOKE (:8081, readonly-admin key) — INV-CUT-5 / DEC-4 cleared: the web bifrost
endpoint returned an admin-authed clean 404 envelope (auth + route + mapping proven);
a real `session.created` admin event (id=32) re-wrapped cleanly on live wire (driven by
a session-create, throwaway session cleaned up).
Suite 490 green; ruff clean; mypy net-improved on web/server.py (16→12 pre-existing, no
new). Patch bump 0.21.18 → 0.21.19 (the cutover MINOR is deferred to slice-7, DEC-6).
98 lines
4.3 KiB
TOML
98 lines
4.3 KiB
TOML
[build-system]
|
|
requires = ["hatchling"]
|
|
build-backend = "hatchling.build"
|
|
|
|
[project]
|
|
name = "ratatoskr"
|
|
version = "0.21.19"
|
|
description = "Worldtree Conversation API debug console (web + headless CLI) — multi-pane observability"
|
|
readme = "README.md"
|
|
requires-python = ">=3.12"
|
|
license = { file = "LICENSE" }
|
|
authors = [{ name = "Vuong Hoang" }]
|
|
keywords = ["worldtree", "debug", "sse", "web", "observability"]
|
|
|
|
# Network + SSE consumer.
|
|
# See docs/design-brief.md §3 (httpx-sse).
|
|
dependencies = [
|
|
"httpx>=0.27",
|
|
"httpx-sse>=0.4", # #20 slice-7 teardown drops this once the SDK owns SSE parsing
|
|
"worldtree-sdk==1.0.0", # #20 cutover: the consumer client layer (gitea PyPI); slices retire the hand-rolled wrappers behind ratatoskr.wt
|
|
]
|
|
|
|
[project.optional-dependencies]
|
|
web = [
|
|
"starlette>=0.40",
|
|
"uvicorn[standard]>=0.30",
|
|
]
|
|
# Tier-3 Bifrost consumer: the durable memory.* + affect.* persistence
|
|
# provider Worldtree writes into. Opt-in extra — distinct deployment surface
|
|
# from the debug TUI. Recipe: bifrost/docs/implementing-a-consumer.md.
|
|
provider = [
|
|
"ratatoskr[web]", # reuse the starlette + uvicorn ASGI stack
|
|
"bifrost==1.1.4", # consumer engines + library. 1.1.4 = hasattr-gate backstop for the maintenance verbs (mark_superseded/mark_invalid/patch_many/delete_many/upsert_edges/get_edges_for → unimplemented verb degrades to unsupported_capability 400, never AttributeError/500/retry-storm; we surfaced it via WT #364) + 1.1.3 scan/cursor conformance harness + 1.1.2 frozen-v0.6 fix. 1.1.1 = frozen-wire serialization fix (ADR-0008): additive capability fields are gated on the NEGOTIATED wire, so a v0.6-negotiated describe_store handshake stays v0.6-clean. 1.1.0 leaked the v0.7-additive `sortable_chunk_fields` into v0.6 StoreCapabilities → a strict v0.6 client (additionalProperties:false) rejects our server's handshake. Wire schemas + pins UNCHANGED (serialization-correctness only); our v0.7 handshake with Worldtree b47 is unaffected. (1.1.0 = wire v0.7 additive: memory.scan sort + sortable_chunk_fields; 1.0.0 = first STABLE, wire v0.6 FROZEN; 0.8.0/v0.6 scope_all/scope_any #11; 0.7.0/v0.5 agent_self)
|
|
"jsonschema>=4", # bifrost runtime dep — envelope validation
|
|
"sqlite-vec>=0.1.6", # vector index for the memory plane (vec0 virtual table)
|
|
]
|
|
dev = [
|
|
"pytest>=8",
|
|
"pytest-asyncio>=0.24",
|
|
"respx>=0.21", # httpx mocking for SSE-recorded snapshot tests
|
|
"ruff>=0.6",
|
|
"mypy>=1.11",
|
|
"pyyaml>=6", # used by docs/contracts/contract_parser.py and scripts/contract_drift_check.py
|
|
"ratatoskr[web]", # web extras included in dev so test_web_* can import starlette
|
|
]
|
|
|
|
[project.scripts]
|
|
ratatoskr = "ratatoskr.cli:main"
|
|
ratatoskr-web = "ratatoskr.web.entrypoint:main"
|
|
ratatoskr-provider = "ratatoskr.provider.serve:main"
|
|
ratatoskr-memory-provider = "ratatoskr.provider.serve_memory:main"
|
|
ratatoskr-combined-provider = "ratatoskr.provider.serve_combined:main"
|
|
|
|
[project.urls]
|
|
Repository = "https://gitea.phasefinal.com/vh/ratatoskr"
|
|
"Design Brief" = "https://gitea.phasefinal.com/vh/brokkr-smithy/src/branch/main/docs/ratatoskr-design-brief.md"
|
|
|
|
# Worldtree spec pin — see docs/SPEC-PIN.md for the full bump procedure.
|
|
# Ratatoskr is built against Worldtree at this commit; the vendored
|
|
# spec snapshot in docs/ reflects that SHA.
|
|
[tool.ratatoskr.spec-pin]
|
|
worldtree-spec-rev = "c9e59ec"
|
|
worldtree-version = "v1.0.0b22"
|
|
pinned-on = "2026-07-06"
|
|
|
|
# Bifrost lives on the auth-gated gitea PyPI index (not public PyPI).
|
|
# uv reads the credential from UV_INDEX_GITEA_USERNAME / _PASSWORD or ~/.netrc.
|
|
[[tool.uv.index]]
|
|
name = "gitea"
|
|
url = "https://gitea.phasefinal.com/api/packages/vh/pypi/simple/"
|
|
|
|
[tool.uv.sources]
|
|
bifrost = { index = "gitea" }
|
|
worldtree-sdk = { index = "gitea" }
|
|
|
|
[tool.hatch.build.targets.wheel]
|
|
packages = ["src/ratatoskr"]
|
|
|
|
# Issue #16: ship the web companion's static HTML in the wheel so
|
|
# importlib.resources can locate it post-install.
|
|
[tool.hatch.build.targets.wheel.force-include]
|
|
"src/ratatoskr/web/static" = "ratatoskr/web/static"
|
|
|
|
[tool.pytest.ini_options]
|
|
asyncio_mode = "auto"
|
|
testpaths = ["tests"]
|
|
|
|
[tool.ruff]
|
|
line-length = 100
|
|
target-version = "py312"
|
|
|
|
[tool.ruff.lint]
|
|
select = ["E", "F", "I", "B", "UP", "RUF"]
|
|
|
|
[tool.mypy]
|
|
python_version = "3.12"
|
|
strict = true
|