Skip to content

Utopia reads: search a knowledge base and read a pushed item back by its external id - #2791

Merged
gedaiu merged 1 commit into
mainfrom
utopia/read-client
Oct 16, 2026
Merged

gedaiu merged 1 commit into
mainfrom
utopia/read-client

Conversation

@gedaiu

@gedaiu gedaiu commented Oct 16, 2026

Copy link
Copy Markdown
Collaborator

The first read path from Lore into Utopia, for the side-by-side experiment (#2787): a small client for the two reads the first Utopia context arm needs, against the routes now live in Utopia 0.1.0-re.7 (#2789).

What changed

  • outbound/utopia/utopia-reads.ts: utopiaReadsFrom(env) (null when UTOPIA_API_URL or UTOPIA_TOKEN is unset) reads a repo's knowledge base as the Lore person.
    • search(kbId, q, {topK, offset}) → POST /kbs/{kb}/search, scored hits with source and external_id.
    • documentByExternalId(kbId, sourceId, externalId) → GET …/documents/by-external-id; a 404 is null.
    • The shared requester retries only 429/5xx, so a 422 (page_too_deep, empty_query) fails at once.
  • domain/utopia/read-shapes.ts (pure, Zod): parseSearchPage, parseStoredDocument, parseStatementsChunk (null for a chunk Utopia cut mid-item), statementTexts, and pushedPathOf, which turns a pushed id such as stmt:specs/…/spec.md#0003.2 back into kind, path and statement ordinal.
  • Fixtures copied from re.7's Rust structs (ScoredChunk, by_external_id, a pushed statements item).
  • utopia-client.ts: utopiaRequester builds the /api/v1 base and retry for both clients; UtopiaPushError becomes UtopiaError, since reads throw it too.

Nothing calls the client yet; the Utopia arm for the repo and adrs context sections comes next.

specs/utopia-graph-mirror: new "Reads" section FR11.1–FR11.3; FR3.3 names UtopiaError.

Tests

read-shapes.test.ts and utopia-reads.test.ts (the HTTP side against a local node:http server answering the fixtures): the search page with null ranks kept, pushedPathOf for stmt/doc/unknown ids, statement texts from a pretty-printed chunk and a cut chunk, the stored document, the request shape and PAT, 404 → null, 422 not retried, unset env → null.

tsc clean in libs/shared; vitest src/domain/utopia + src/outbound/utopia 51 passed; eslint clean on changed files and specs adrs.

Generated with Claude Code

…its external id

utopiaReadsFrom reads a repository's knowledge base as the Lore person
(UTOPIA_TOKEN): POST /kbs/{kb}/search with q, top_k and offset, and
GET .../documents/by-external-id, a 404 reading as null. The pure
read-shapes module parses the scored search page, the stored document
and a statements chunk, and pushedPathOf turns a pushed external id
back into its kind, path and statement ordinal. Fixtures are copied
from Utopia v0.1.0-re.7. UtopiaPushError becomes UtopiaError, since
reads throw it too; a 422 such as page_too_deep is not retried.
Spec: utopia-graph-mirror FR11.1-11.3.
@lore-agent

lore-agent Bot commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

Lore is reviewing this PR — 37804ac8-790b-418d-bd71-5058de29f838.

Submit a review that requests changes to have Lore push a fix for it. Comment @lore review to re-run the review.

@github-actions

Copy link
Copy Markdown

🔍 Lore Spec Impact — advisory

This PR touches 12 statement(s) across 1 spec(s), and 5 of them have validating tests this PR does not change.

Feature Specification: The spec-to-code chain mirrored in Utopia · 6 statement(s)

specs/utopia-graph-mirror/spec.md

The push
✓ this PR also changes the tests that validate it

FR3.3 — A 429 or 5xx is retried once after a short wait; a second failure, or any other refusal, throws a `UtopiaPushEr…

validated by libs/shared/src/outbound/utopia/utopia-client.test.ts:130

The ingest
⚠ the tests that validate it are not touched by this PR

only its test links changed — the statement text itself is unchanged

FR4.3 — After the delta route has projected a specs or adrs delta into the graph, each file is mirrored as a spec o…

validated by apps/lore-api/src/transport/routes/ingest/ingest-delta.test.ts:711

The ingest
⚠ the tests that validate it are not touched by this PR

only its test links changed — the statement text itself is unchanged

FR4.4 — A mirror that fails is logged as [utopia-mirror] <repo> <path>: <message>, counted on `lore_utopia_mirror_fai…

validated by apps/lore-api/src/transport/routes/ingest/ingest-delta.test.ts:711, libs/shared/src/outbound/otel/metrics.test.ts:109

Test runs
⚠ the tests that validate it are not touched by this PR

only its test links changed — the statement text itself is unchanged

After the delta route has projected a default-branch report into the graph, lore-api pushes them through the mirror of …

validated by apps/lore-api/src/transport/routes/ingest/ingest-delta.test.ts:711

Test runs
⚠ the tests that validate it are not touched by this PR

only its test links changed — the statement text itself is unchanged

FR9.5 — A deleted test file is a test:<path> tombstone, pushed after the delta route has pruned it from the graph.

validated by apps/lore-api/src/transport/routes/ingest/ingest-delta.test.ts:711, libs/shared/src/domain/utopia/test-report-statements.test.ts:239, libs/shared/src/work/utopia/mirror-test-report.test.ts:53

Test runs
⚠ the tests that validate it are not touched by this PR

only its test links changed — the statement text itself is unchanged

FR9.7 — A report for any branch but the default one goes to that branch's overlay in the graph and is not mirrored: a s…

validated by apps/lore-api/src/transport/routes/ingest/ingest-delta.test.ts:711

Weaker signals (6) — linked by a spec, not proven by a test run

Feature Specification: The spec-to-code chain mirrored in Utopia · 6 statement(s)

specs/utopia-graph-mirror/spec.md

The push
✓ this PR also changes the tests that validate it

FR3.1 — An item is posted to POST /api/v1/sources/{source}/statements under the source's own key, as external_id, `…

validated by libs/shared/src/outbound/utopia/utopia-client.test.ts:130
via changed file libs/shared/src/outbound/utopia/utopia-client.test.ts

The push
✓ this PR also changes the tests that validate it

FR3.2 — A tombstone is external_id plus deleted: true, answered marked_missing.

validated by libs/shared/src/outbound/utopia/utopia-client.test.ts:130
via changed file libs/shared/src/outbound/utopia/utopia-client.test.ts

The ingest
✓ this PR also changes the tests that validate it

FR4.1 — The deployment reaches Utopia through UTOPIA_API_URL (utopia.enabled in the lore-api chart, from `enable_ut…

validated by libs/shared/src/outbound/utopia/utopia-client.test.ts:130
via changed file libs/shared/src/outbound/utopia/utopia-client.test.ts

Setup
✓ this PR also changes the tests that validate it

FR5.1 — POST /api/repos/{owner}/{repo}/utopia/setup (admin scope) takes a person's Utopia session token and a workspa…

validated by libs/shared/src/outbound/utopia/utopia-client.test.ts:130
via changed file libs/shared/src/outbound/utopia/utopia-client.test.ts

Setup
✓ this PR also changes the tests that validate it

FR5.5 — The setup calls carry any bearer Utopia accepts as a person: a session JWT or a personal access token with `wri…

validated by libs/shared/src/outbound/utopia/utopia-client.test.ts:130
via changed file libs/shared/src/outbound/utopia/utopia-client.test.ts

Setup on first ingest
✓ this PR also changes the tests that validate it

FR6.1 — A deployment sets repos up itself when it has UTOPIA_API_URL, UTOPIA_TOKEN (the write token of the Utopia p…

validated by libs/shared/src/outbound/utopia/utopia-client.test.ts:130
via changed file libs/shared/src/outbound/utopia/utopia-client.test.ts

2 changed statement(s) had no validating test, so no coverage broke. 22 new statement(s) have no test link yet.

Deterministic · graph @ 54e00aa (projected 2026-10-07) · 4 file(s) skipped: changed since the graph last saw them, so their line numbers no longer line up · no tests run by this check

@lore-agent lore-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lore review — Approved

Clean, well-tested Utopia read client and response parsers matching FR11 and ADR-052.

Must fix (0) · Consider (0) · Nits (0)

Reviewed by gemini-4-argon

Submit a review that requests changes to have Lore push a fix for it. Comment @lore review to re-run the review.

Posted by floor, visit d66e1b6e-a269-4124-ac1b-c5410ace1b2a.

@gedaiu
gedaiu merged commit fb53a29 into main Oct 16, 2026
30 checks passed
@gedaiu
gedaiu deleted the utopia/read-client branch October 16, 2026 20:23
gedaiu added a commit that referenced this pull request Oct 16, 2026
…e Utopia mirror (#2794)

The first real Utopia arm for the Lore-vs-Utopia experiment (#2787,
#2792): under backend `utopia`, and in the Utopia half of `both`, the
context's **Conventions** (`repo`) and **Architecture Decisions**
(`adrs`) sections come from Utopia instead of answering "no Utopia
reader yet". It needs no new writes: it reads the spec and ADR
statements the mirror (ADR-052) already pushed, through the read client
from #2791.

## What changed

- **One shared section pipeline.** `context-assembly-pipeline.ts` (moved
out of `context-assembly.ts`): budget, fetching per section through a
reader, packing, the debug trace. Lore and Utopia both go through it, so
an eval's `found` grades Utopia's assembly the same way as Lore's. The
Lore path is unchanged; the existing context-assembly tests pass without
edits.
- **`context-assembly-utopia.ts`.**
- One search per assembly (`topK` 50), a second page only when a section
is short and the first page says `hasMore`.
- Keeps statements-source hits whose pushed id is a `doc:` or `stmt:`
item (other sources are dropped, since search has no source filter yet),
sorts each path into `repo` or `adrs` with Lore's `classifyFile`, and
groups by path in first-rank order.
- Each item is the document's title then its statement texts in order,
under the Lore path, with Utopia's score; same item limits as Lore.
- Every other section still answers "no Utopia reader yet", as all do
when the deployment has no reader or the repo has no `settings.utopia`.
- **Wiring.** `repoContextSettings` also reads `settings.utopia`
{`kb_id`, `source_id`} in the same query. `/api/context` and the evals
pass `utopiaReadsFrom(env)` and those ids. Under `both`, the two halves
now assemble in parallel; the debug trace stays Lore's.

Caveats, recorded in the ADR-052 amendment: a tombstoned document can
still match until Utopia's search can exclude missing documents;
post-filtering by source id until search takes a source filter; code
items carry no text yet; typed graph reads wait for the release that
types Lore's relations.

Spec: `specs/utopia-graph-mirror` "The Utopia arm" FR12.1–FR12.6,
`specs/context-assembly` FR-1.5. (FR12 may need renumbering if #2790
lands first.)

## Tests

Pure and reader tests (libs/shared): one item per spec from three
statement chunks in order; another source's hits dropped; a second page
only when short; code has no reader. End-to-end with the real templates
(server-core): the ADR under Architecture Decisions with its statement
texts; the trace lists the kept paths; no settings falls back to
no-reader sections. Fixtures are built from the real `docStatementItems`
output with a hand-written fake `UtopiaReads`. `repoContextSettings`
reads the mirror ids.

`tsc` clean in libs/shared, server-core, lore-api; vitest libs/shared
knowledge + utopia 143 passed (2 skipped), server-core context 27,
lore-api context + context-evals + utopia 95; eslint clean on changed
files and `specs adrs`.

Generated with [Claude Code](https://claude.com/claude-code)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant