feat(lessons): the lesson kind, and one join for every trigger title (#3729)
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 10s
CI & Build / integration (push) Successful in 50s
CI & Build / TypeScript typecheck (push) Successful in 54s
CI & Build / Python tests (push) Failing after 1m5s
CI & Build / Build & push image (push) Skipped
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 10s
CI & Build / integration (push) Successful in 50s
CI & Build / TypeScript typecheck (push) Successful in 54s
CI & Build / Python tests (push) Failing after 1m5s
CI & Build / Build & push image (push) Skipped
Milestone 385 step 2, implementing decision #4157 from the step-1 spike. The kind: `note_type='lesson'`, a note findable by WHEN IT APPLIES rather than by what it is about. The trigger lives in `notes.data.when_to_apply`, mirrored into the title and the head of the body — the shape snippets already use, and the reason nothing re-embeds: chunk_document is untouched, so CHUNKER_VERSION does not move. NO MIGRATION, and the step assumed there would be one. `note_type` carries no CHECK — only `task_kind` does (0056, 0065). Migration 0036 added it as plain Text with a server default and nothing has gated it since, so rule 36 has no whitelist to expand and #3128's failure mode (a value the database refuses) cannot arise for this column. The vocabulary that actually decides what a reader can reach is services.knowledge._FACETS, which since #3161 is one table feeding the door's validation, the counts and both dialects of the type filter — so the kind lands there in a single edit. ONE JOIN, not a fourth copy. `{subject} — {trigger}` had three implementations: rule_document, snippets.compose_title, and this step needed another. #3207 records what that costs, so the join moves to embeddings.trigger_title beside embedding_text and all three delegate. Behaviour is unchanged for rules and snippets; the guard calls each through its own public name, so a re-implementation fails it. The #3163 bill is stated in the service docstring rather than left to be inferred: versions, supersession, trash, the share ACL, tags, project and System tagging, chunked embeddings and the duplicate gate are all inherited; status/task_kind/milestone_id and recurrence are not, and verify_with/expires_when are available but outside the kind's contract. The status cell is the one that matters — `is_task` IS `status is not None`, so a lesson that acquired one would become a task. The integration guard asserts the WRITE rather than the constraint: it holds whether or not note_type is ever gated, and goes red only if it is gated without this value. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01821k5B3Ysecp9fNYs92Kuy
This commit is contained in:
@@ -55,10 +55,15 @@ UNSET: object = object()
|
||||
# --- serialize: structured fields -> note (title/body/tags) ------------------
|
||||
|
||||
def compose_title(name: str, when_to_use: str = "") -> str:
|
||||
"""`name — when to use` (or just `name` when no usage note is given)."""
|
||||
name = (name or "").strip()
|
||||
when = (when_to_use or "").strip()
|
||||
return f"{name} — {when}" if when else name
|
||||
"""`name — when to use` (or just `name` when no usage note is given).
|
||||
|
||||
The join itself lives in `embeddings.trigger_title`, which rules and
|
||||
lessons build their titles from too. Kept as a named function here because
|
||||
it is this module's public vocabulary and callers say `compose_title`.
|
||||
"""
|
||||
from scribe.services.embeddings import trigger_title
|
||||
|
||||
return trigger_title(name, when_to_use)
|
||||
|
||||
|
||||
def compose_tags(language: str = "", tags: list[str] | None = None) -> list[str]:
|
||||
|
||||
Reference in New Issue
Block a user