feat(search): milestones are searchable by meaning — "is there already a plan for this?" (#4078)
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 11s
CI & Build / TypeScript typecheck (push) Successful in 52s
CI & Build / integration (push) Successful in 56s
CI & Build / Python tests (push) Successful in 1m37s
CI & Build / Build & push image (push) Successful in 28s
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 11s
CI & Build / TypeScript typecheck (push) Successful in 52s
CI & Build / integration (push) Successful in 56s
CI & Build / Python tests (push) Successful in 1m37s
CI & Build / Build & push image (push) Successful in 28s
`search` covered notes, tasks and rules, and a milestone — the record a plan lives in — could not be found. A project whose roadmap was written as milestones had every later plan opened beside the one that already described it, because nothing could have told the session it existed. - milestone_embeddings (migration 0102): the third sibling of note_ and rule_embeddings, for note 3163's reason — the search is milestone-specific. The document is title — description, then description and the plan body, so a roadmap milestone with no description is still found by its design. - Written on create, on a title/description/body update, and for a plan made through start_planning / create_records, fire-and-forget with the parent-row claim (#3262); a startup backfill covers every existing milestone. Derived, so it joins _NOT_INCLUDED beside the other embeddings. - semantic_search_milestones: a project's milestones when the caller can read it (access.can_read_project), otherwise the caller's own; optional status. - search(content_type="milestone"): id, title, description, status, project and progress. Its own shape, and not part of "all", whose results are note-shaped. The docstring says what it is for: ask before start_planning. - Integration test on real Postgres: found in its project and not another, status narrows, an unreadable project returns nothing. Milestone 415 step 3. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01821k5B3Ysecp9fNYs92Kuy
This commit is contained in:
@@ -11,6 +11,30 @@ from scribe.models.note import Note
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def embed_milestone(milestone: Milestone) -> None:
|
||||
"""Refresh a milestone's vectors, fire-and-forget (milestone 415).
|
||||
|
||||
At the service, so every path that writes a milestone gets it — the lesson
|
||||
embed_note records (#2056): a record written through a door that forgot the
|
||||
call stays out of search until a restart. Exceptions are swallowed because
|
||||
a milestone that saved must not fail on its index refresh; no running loop
|
||||
(a script, a unit test) is ordinary. A delete racing the refresh wins: the
|
||||
upsert claims the milestone's row first (#3262).
|
||||
"""
|
||||
try:
|
||||
import asyncio
|
||||
|
||||
from scribe.services.embeddings import upsert_milestone_embedding
|
||||
|
||||
asyncio.create_task(upsert_milestone_embedding(
|
||||
milestone.id, milestone.title, milestone.description, milestone.body,
|
||||
))
|
||||
except RuntimeError:
|
||||
pass
|
||||
except Exception: # noqa: BLE001 - never let indexing break a write
|
||||
logger.exception("embedding refresh failed for milestone %s", milestone.id)
|
||||
|
||||
|
||||
async def create_milestone(
|
||||
user_id: int,
|
||||
project_id: int,
|
||||
@@ -33,6 +57,7 @@ async def create_milestone(
|
||||
session.add(milestone)
|
||||
await session.commit()
|
||||
await session.refresh(milestone)
|
||||
embed_milestone(milestone)
|
||||
return milestone
|
||||
|
||||
|
||||
@@ -125,6 +150,8 @@ async def update_milestone(user_id: int, milestone_id: int, **fields: object) ->
|
||||
milestone.updated_at = datetime.now(timezone.utc)
|
||||
await session.commit()
|
||||
await session.refresh(milestone)
|
||||
if {"title", "description", "body"} & set(fields):
|
||||
embed_milestone(milestone)
|
||||
return milestone
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user