feat(observations): enumerate + filter + visualize observation scopes - #2149
Merged
Conversation
Add an exact (set-equality) tag match mode, a list_observation_scopes engine method + GET /observations/scopes endpoint, and a scope filter in the control-plane Observations tab (list + graph views). A scope is the exact tag set an observation was consolidated under; the empty set is the global/untagged scope. Regenerated OpenAPI + clients + docs skill.
invalidate-memory-dialog.tsx references memoryDetailPanel.curationInvalidateTitle/ Explain/ReasonPlaceholder/Cancel/Invalidate, but these keys were never added to any locale (the parity test passed because all 10 locales lacked them equally), so the invalidate dialog logged IntlError: MISSING_MESSAGE and rendered raw key names. Add all five strings across the 10 locales. Pre-existing gap, unrelated to scopes.
…ti tags The scope dropdown trigger relied on SelectValue, which clones the selected item's wrapping pill layout; a multi-tag or long-tag scope (e.g. [session:2, user:nicolo]) wrapped to two lines and overflowed the fixed-height control. Render a compact, single-line, truncating summary in the trigger instead, keeping the full pills only in the open dropdown list.
… detail dialog observation_scopes passed at retain time was only persisted per source fact (memory_units), never on the document, so the document detail dialog couldn't show which scoping was requested. Capture it into documents.retain_params in _build_retain_params (alongside context/event_date/metadata) and surface it as a top-level field on the get_document response. The control-plane document detail dialog now shows an 'Observation scopes' row (mode badge or scope chips). New-documents-only by design: existing docs have no captured value and show nothing. Note: this also clarifies that all_combinations on 2 tags correctly creates 3 scopes — the transient '2' is async consolidation still in flight.
…e constellation Two UX improvements to the observations view: 1. Live refresh while consolidating. The 'In Sync' badge previously read a one-shot, up-to-60s-cached stat, so it could show green while observations were still materializing (each scope is a separate consolidation pass). The view now polls every 4s while pending_consolidation > 0, silently refreshing the observations, scope list, and badge in place until consolidation settles. 2. Group-by-scope clustering on the Constellation. A new 'Group by scope' toggle lays observations out around per-scope centroids (instead of the id-hash ring), colors each scope distinctly, and wraps each scope's nodes in a translucent, labeled convex-hull blob — so overlapping tag scopes read as visual clusters. Adds clusterKeyFn/clusterColorFn/clusterLabelFn props to Constellation and an inline monotone-chain convex hull; suppresses the heat legend while clustering.
With many scopes the scope filter dropdown grew past the bottom of the screen. Cap its height at min(60vh, --radix-select-content-available-height) so it fits the space below the trigger and scrolls for the rest, instead of overflowing.
Replace the plain Select with a Popover + Command (cmdk) combobox so scopes can be searched by typing — matching the tag filter's search UX — which matters once a bank has many scopes. Uses a substring filter over each scope's tags (not cmdk's fuzzy default, which over-matches scattered letters). Keeps the compact, single-line, height-capped trigger; selection still applies exact-scope filtering.
nicoloboschi
force-pushed
the
feat/observation-scopes-ui
branch
from
June 11, 2026 16:16
9358baa to
d62e10a
Compare
The new scope-enumeration endpoint powers the control-plane scope filter/clusters and isn't a useful end-user CLI command, so add it to the [skip] list (matches the other UI-only endpoints) to satisfy check-cli-coverage.
#2135 removed the TokenUsage re-export from llm_wrapper, but test_load_large_batch and test_retain still imported it from there, breaking test collection across the API test jobs. Import it from response_models (where it's defined), matching every other test.
nicoloboschi
force-pushed
the
feat/observation-scopes-ui
branch
from
June 11, 2026 17:00
7317b92 to
7833863
Compare
This was referenced Jun 12, 2026
nicoloboschi
added a commit
that referenced
this pull request
Jun 16, 2026
…nts (#2230) The `exact` set-equality match mode landed in the API + generated clients in #2149 but was never exposed in the control plane, documented, or added to the hand-maintained SDK wrappers. This completes the feature. Control plane: add `exact` to the TagsMatch type/unions and to the tags_match dropdowns in think-view, search-debug-view, and both mental-model trigger forms; add translated labels to all 10 locales. Docs: document `exact` in the recall tags_match table + tag_groups, the reflect tags value list, and the observations scope-listing guide; regenerate the docs skill mirror. Clients: add `exact` to the hand-maintained Python and TypeScript wrapper Literals/unions and docstrings (generated clients already had it; Rust is generated from openapi.json at build time). Supersedes #2159.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Observations live under a scope — the exact tag set they were consolidated with — but until now there was no way to see which scopes exist or filter by them. This adds scope enumeration, filtering, document-level surfacing, a live consolidation indicator, and a constellation visualization.
Backend (
hindsight-api-slim)exacttag-match mode (engine/search/tags.py) — order-independent set equality (@> AND <@), distinct fromall_strict's contains semantics. Needed so scope[a]doesn't match[a, b].GET /observations/scopes—list_observation_scopesenumerates distinct observation scopes (normalized tag sets) with counts;[]= global/untagged scope.tags_match=exactwith no tags filters to untagged observations.observation_scopescaptured intodocuments.retain_paramsat retain time and surfaced onget_document(new-documents-only by design).Control plane (
hindsight-control-plane)observation_scopes("Observation scopes" row).pending_consolidation > 0, the observations view polls every 4s and silently refreshes observations/scopes/badge in place (the "In Sync" badge previously read a one-shot, up-to-60s-cached stat).Tests
exactSQL clause + Python filter.observation_scopesround-trip through retain_params.Notes
memoryDetailPanel.curation*i18n keys were referenced in code but missing from all locales.🤖 Generated with Claude Code