feat(family): family canon reaches the session - entry readout, retrieval reach, skill, report cue (milestone 463 step 6, #4992)
CI & Build / Plugin hooks (push) Successful in 15s
CI & Build / Python lint (push) Successful in 2s
CI & Build / TypeScript typecheck (push) Successful in 54s
CI & Build / integration (push) Failing after 1m53s
CI & Build / Python tests (push) Successful in 2m39s
CI & Build / Build & push image (push) Skipped
CI & Build / Plugin hooks (push) Successful in 15s
CI & Build / Python lint (push) Successful in 2s
CI & Build / TypeScript typecheck (push) Successful in 54s
CI & Build / integration (push) Failing after 1m53s
CI & Build / Python tests (push) Successful in 2m39s
CI & Build / Build & push image (push) Skipped
- enter_project carries a `family` key, but only when the project has something to answer: counts of unassessed, owed and to-recheck answers, each with the list_family_adoptions call that lists it. It shows on every entry, never by platform touch: entry is when work is chosen, and an unanswered idea is otherwise invisible. - Retrieval: a widened project search (include_global_kinds) now also reaches the canon ideas on the project's platforms. It also reaches their references in the project's languages, or all of them when none matches. An off-platform project gets none, and the plain project filter (the duplicate gate) is unchanged. - Closing a task returns `family_owed`, the owed answers filed while it was open, and the report cue asks for them to be named. - New plugin skill family-canon (moment work.record) covers when to evaluate a promotion, answering in order, what counts as a reason, the precedent reflex and the conflict order. _INSTRUCTIONS, create_note, create_snippet, classify_shapes and reporting-back point at it. The plugin version is minted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
"""Tests for fable_*_project tools."""
|
||||
import contextlib
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
@@ -48,6 +49,16 @@ def _no_coverage():
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _no_family():
|
||||
"""enter_project reads the family-canon readout (milestone 463 step 6) —
|
||||
no database here, so the common case is stubbed: nothing to answer. The
|
||||
populated key is asserted in its own test below."""
|
||||
with patch("scribe.mcp.tools.projects.family_adoption_svc.family_readout",
|
||||
AsyncMock(return_value=None)) as mock:
|
||||
yield mock
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _no_background_seed():
|
||||
"""enter_project now fire-and-forgets a coverage self-seed (#2802). These
|
||||
@@ -483,3 +494,33 @@ def test_inception_routes_and_tool_are_registered():
|
||||
# Rules left inception with subscriptions (milestone 414).
|
||||
assert "subscribe_rulebooks" not in tool.parameters.get("properties", {})
|
||||
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_enter_project_carries_the_family_line_only_when_it_asks_something(_no_family):
|
||||
"""The `family` key is attached when the project has family canon to
|
||||
answer, and absent otherwise — a key that usually says null trains
|
||||
readers to skip it (#2483)."""
|
||||
line = {"line": "family canon on this project's platforms: 2 unassessed",
|
||||
"unassessed": 2, "owed": 0, "needs_recheck": 0,
|
||||
"calls": {"unassessed": 'list_family_adoptions(project_id=5, status="unassessed")'}}
|
||||
|
||||
async def enter():
|
||||
with contextlib.ExitStack() as stack:
|
||||
for cm in _enter_project_stubs(fake_project(id=5)):
|
||||
stack.enter_context(cm)
|
||||
return await enter_project(project_id=5)
|
||||
|
||||
assert "family" not in await enter()
|
||||
_no_family.return_value = line
|
||||
assert (await enter())["family"] == line
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_a_failing_family_readout_never_fails_the_handshake(_no_family):
|
||||
_no_family.side_effect = RuntimeError("database down")
|
||||
with contextlib.ExitStack() as stack:
|
||||
for cm in _enter_project_stubs(fake_project(id=5)):
|
||||
stack.enter_context(cm)
|
||||
out = await enter_project(project_id=5)
|
||||
assert "family" not in out and out["project"]["id"] == 5
|
||||
|
||||
Reference in New Issue
Block a user