feat(mcp): enter_project becomes a small primer: goal, recent work, open work, vocabulary (#4045)
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / integration (push) Successful in 46s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / Python tests (push) Successful in 1m44s
CI & Build / Build & push image (push) Successful in 23s
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / integration (push) Successful in 46s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / Python tests (push) Successful in 1m44s
CI & Build / Build & push image (push) Successful in 23s
The handshake carried the whole project record, every milestone's plan, full rule text, the notes most recently edited and ~9k of design guidance. For project 2 that was ~222k characters, past what an MCP client accepts as a tool result. Each category was walked through with the operator and sized to what a session needs on arrival; each names the call that has the rest. - project: id, title, status and the full goal (session start's "full goal" pointer still lands here). get_project keeps the whole record. - milestone_summary: the 5 most recently touched milestones, any status, most recent first, without plans. Summaries gain last_touched_at: the later of the milestone's own edit and its newest step update, from the query that already counts steps. milestone_summary_omitted counts the rest and points to list_milestones. get_project and list_milestones list every milestone, also without plans. - open_tasks: the 10 most recently touched open tasks, with or without a milestone, each naming its milestone. list_notes gains sort="touched" (the later of updated_at and the newest work-log), because a log doesn't bump updated_at. - recent_notes: dropped. Retrieval surfaces notes by relevance, and get_recent covers recency. - systems: id and name. - design_system: summary plus guidance_call. get_design_system gains resolved_guidance, the chain-merged prose; its own guidance field is only the departures, so session start's old pointer to it led to a fragment. The session start pointer and using-scribe's "Building UI" section now name resolved_guidance. - rules: rules_payload(brief=True) gives project_rules as id and title plus subscribed_rulebooks, and records only what it shows. Retrieval delivers rules in full and ignores subscriptions (#4052). Other callers unchanged. - pattern_coverage, inception and systems_bootstrap: unchanged. Clients: the plugin's using-scribe skill, the compaction notice and session start are updated here; the REST project summary only gains last_touched_at. Plugin version minted. Tests: a size ceiling on the handshake for a large project; milestone and task selection and naming; brief rules; resolved_guidance; the session start pointer; and a real-Postgres test that a work-log touches its task and a step update touches its milestone. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01821k5B3Ysecp9fNYs92Kuy
This commit is contained in:
@@ -318,6 +318,9 @@ async def list_notes(
|
||||
degraded but never empty. Superseded records are not demoted here: the
|
||||
penalty reorders a top-k, and reordering a paginated, counted list would
|
||||
make page boundaries lie. The search surfaces carry the demotion.
|
||||
|
||||
`sort` is a Note column name, or "touched": the later of updated_at and
|
||||
the record's newest work-log.
|
||||
"""
|
||||
from scribe.services.access import notes_visibility_clause
|
||||
|
||||
@@ -442,6 +445,19 @@ async def list_notes(
|
||||
if semantic_order is not None:
|
||||
# A query is a relevance claim — see the docstring.
|
||||
query = query.order_by(semantic_order)
|
||||
elif sort == "touched":
|
||||
# Last touched: the later of the task's own edit and its newest
|
||||
# work-log. Logging doesn't bump updated_at, so a task worked
|
||||
# through its logs would otherwise sort as untouched (#4045).
|
||||
# GREATEST skips a NULL, so a task with no logs sorts by its edit.
|
||||
from scribe.models.task_log import TaskLog
|
||||
last_log = (
|
||||
select(func.max(TaskLog.created_at))
|
||||
.where(TaskLog.task_id == Note.id)
|
||||
.scalar_subquery()
|
||||
)
|
||||
touched = func.greatest(Note.updated_at, last_log)
|
||||
query = query.order_by(touched.asc() if order == "asc" else touched.desc())
|
||||
else:
|
||||
sort_col = getattr(Note, sort, Note.updated_at)
|
||||
if order == "asc":
|
||||
|
||||
Reference in New Issue
Block a user