fix(planning): the plan gate joins step text through embedding_text (#4079)

test_nothing_else_builds_the_embedding_document_itself caught start_planning
building f"{title}\n{body}" inline for the plan gate's candidate text. That is
the embedded-document shape; plan_candidate_text now takes (title, body) pairs
and calls embedding_text, so the candidate moves with the corpus it is ranked
against (#2486).

Also carries the create_task / create_records milestone_id docstring lines from
step 5 (#4080), which share the file.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01821k5B3Ysecp9fNYs92Kuy
This commit is contained in:
2026-09-15 13:45:52 -04:00
co-authored by Claude Opus 5
parent 59407728e6
commit 2811fc9025
3 changed files with 13 additions and 6 deletions
+4 -3
View File
@@ -767,16 +767,17 @@ async def get_plan_match_threshold(user_id: int) -> float:
def plan_candidate_text(
description: str | None = None,
body: str | None = None,
step_texts: list[str] | None = None,
steps: list[tuple[str | None, str | None]] | None = None,
) -> str:
"""What a plan that doesn't exist yet says about itself, for the gate.
The steps belong in it: a plan passed with steps and no design is still
recognisable by them, and what its steps say is most of what makes two
plans the same plan.
plans the same plan. Each step is (title, body), joined by embedding_text
like every other record that becomes embedded text (#2486).
"""
parts = [(description or "").strip(), (body or "").strip()]
parts += [t.strip() for t in (step_texts or [])]
parts += [embeddings_svc.embedding_text(t, b) for t, b in (steps or [])]
return "\n\n".join(p for p in parts if p)