Re-architect document ingest from the shared NATS-glued document-processor to a per-tenant, in-process model owned by nextcloud-mcp-server (Deck #183). The MCP server now owns both sides of ingest: - Producer (api role): the scanner defers one job per changed document into the app's Postgres via procrastinate (queueing_lock dedup; no execution lock, so a crashed worker can't deadlock a doc — Qdrant upserts are idempotent). - Consumer (worker role): `nextcloud-mcp-server worker` drains the queue and runs the existing process_document pipeline; a periodic task reclaims jobs orphaned in `doing` by a crash. INGEST_QUEUE selects the transport (auto: postgres when DATABASE_URL is Postgres, else the in-process anyio queue for SQLite/dev). procrastinate manages its own tables (applied on a fresh DB at startup and by `db upgrade`). The vector-sync status surface reads job counts from Postgres in postgres mode. procrastinate + psycopg3 ship in the [postgres] extra; the app's own engine still uses asyncpg (driver unification is a follow-up handled in the rendered Helm chart). NATS JetStream, the Postgres-queue stub, the bus status subscriber, and nats-py are removed. BREAKING CHANGE: the external-NATS-ingest env vars are removed (INGEST_MODE, STATUS_BACKEND, INGEST_BUS_URL, INGEST_BUS_NUM_REPLICAS, FACT_EVENT_EMITTER). Use INGEST_QUEUE (memory|postgres) and the `worker` command instead. TENANT_ID is retained (no longer NATS-subject-charset-validated). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
26 lines
879 B
Python
26 lines
879 B
Python
"""Canonical JSON encoding shared across cross-implementation hashes.
|
|
|
|
The Astrolabe Cloud decomposition (design §2.3) fixes a single canonical JSON
|
|
encoding so hashes computed here match those computed independently by the
|
|
external embedding-gateway service. Any drift in separators, key ordering, or
|
|
unicode handling would break Qdrant point-ID idempotency and ACL-hash
|
|
compatibility.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from typing import Any
|
|
|
|
|
|
def canonical_json(obj: Any) -> bytes:
|
|
"""Encode ``obj`` to canonical JSON bytes.
|
|
|
|
Deterministic across implementations: sorted keys, no inter-token
|
|
whitespace, non-ASCII preserved (UTF-8). Consumers: Qdrant point IDs
|
|
(vector/payload_keys.py) and ACL hashes (acl_hash.py).
|
|
"""
|
|
return json.dumps(
|
|
obj, sort_keys=True, separators=(",", ":"), ensure_ascii=False
|
|
).encode("utf-8")
|