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
60 lines
2.0 KiB
Python
60 lines
2.0 KiB
Python
"""milestone_embeddings — a plan becomes findable by meaning (milestone 415)
|
|
|
|
Revision ID: 0102
|
|
Revises: 0101
|
|
Create Date: 2026-09-15
|
|
|
|
`search` covered notes, tasks and rules, and a milestone — the record a plan
|
|
lives in — could not be found at all. So "is there already a plan for this?"
|
|
had no tool, and a project whose roadmap was written as milestones had every
|
|
later plan opened as a new milestone beside the one that already described it.
|
|
|
|
The sibling of rule_embeddings (0089), for the reasons its model docstring and
|
|
note 3163 give. The vectors are DERIVED: nothing is backfilled here, the startup
|
|
backfill writes them.
|
|
"""
|
|
import sqlalchemy as sa
|
|
from alembic import op
|
|
|
|
revision = "0102"
|
|
down_revision = "0101"
|
|
branch_labels = None
|
|
depends_on = None
|
|
|
|
# Matches note_embeddings and rule_embeddings — bge-small-en-v1.5, 384-dim.
|
|
_EMBEDDING_DIM = 384
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.create_table(
|
|
"milestone_embeddings",
|
|
sa.Column(
|
|
"milestone_id", sa.Integer(),
|
|
sa.ForeignKey("milestones.id", ondelete="CASCADE"), primary_key=True,
|
|
),
|
|
sa.Column("chunk_index", sa.Integer(), primary_key=True),
|
|
sa.Column("chunk_text", sa.Text(), nullable=False),
|
|
sa.Column("chunker_version", sa.Integer(), nullable=False),
|
|
sa.Column(
|
|
"updated_at", sa.DateTime(timezone=True), nullable=False,
|
|
server_default=sa.text("now()"),
|
|
),
|
|
)
|
|
# Raw DDL for the vector column, as 0067 and 0089 do: the type comes from
|
|
# the pgvector extension, not SQLAlchemy's type system.
|
|
op.execute(
|
|
f"ALTER TABLE milestone_embeddings ADD COLUMN embedding vector({_EMBEDDING_DIM}) NOT NULL"
|
|
)
|
|
op.execute(
|
|
"""
|
|
CREATE INDEX ix_milestone_embeddings_embedding_hnsw
|
|
ON milestone_embeddings
|
|
USING hnsw (embedding vector_cosine_ops)
|
|
"""
|
|
)
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.execute("DROP INDEX IF EXISTS ix_milestone_embeddings_embedding_hnsw")
|
|
op.drop_table("milestone_embeddings")
|