Files
OpenOPC/tests/test_prompt_assembly_dedup.py
T
2026-07-01 17:56:31 +08:00

684 lines
31 KiB
Python

"""Tests for the prompt-assembly deduplication refactor.
These tests pin down the structural decisions made in the refactor:
1. Work-item identity (the global_intent_summary / your_responsibility /
inputs / deliverables / acceptance_criteria / out_of_scope block)
is rendered exactly ONCE in any per-turn prompt for a company-mode
role. The runtime plan and ownership contract no longer
re-render those fields.
2. Static collaboration boilerplate (Company Work Item Contract, the
Work Item Turn: ... headers, the Blocking collaboration (rare)
block, the "When to send / When to reply" prose previously baked
into the comms inbox section) no longer appears in any
ContextAssembler output. It now lives in the
``collaboration-playbook`` skill, injected per-turn through the
skills channel for company_mode runs only.
3. ``SkillLibrary.build_skills_summary`` honors the per-skill
``modes`` frontmatter field: a skill restricted to company_mode
is fully hidden — body AND description — when the call is made
without a matching ``execution_mode``.
4. ``render_inbox_section`` no longer emits the "When to send" /
"When to reply" prose. ``render_meetings_section`` returns "" when
no meetings are open instead of emitting "(no open meetings)".
The first two tests are the load-bearing ones: if a future refactor
introduces a new builder that re-renders work-item identity or
re-introduces a static collaboration paragraph into the per-turn
prompt, these tests should fail and tell the author to either move
the content into the playbook skill or pick a single canonical
section to own it.
"""
from __future__ import annotations
import tempfile
import unittest
from pathlib import Path
from types import SimpleNamespace
from unittest.mock import AsyncMock
from opc.core.models import SessionMessageRecord, SessionPartRecord
from opc.core.models import Task
from opc.layer1_perception.context_assembler import ContextAssembler
from opc.layer5_memory.memory_manager import MemoryManager
from opc.layer2_organization import comms as _comms
from opc.layer2_organization.collaboration_policy import render_ownership_contract
from opc.layer5_memory.skill_library import Skill, SkillLibrary
# ----------------------------------------------------------------------
# Helpers
# ----------------------------------------------------------------------
_STATIC_PHRASES_THAT_BELONG_IN_THE_PLAYBOOK = (
# Removed from context_assembler._build_work_item_collaboration_guidelines
"## Company Work Item Contract",
"## Work Item Turn: Plan / Intake / Dispatch",
"## Work Item Turn: Execute",
"## Work Item Turn: Review",
"## Work Item Turn: Aggregate / Deliver",
# Removed from build_external_runtime_mailbox_context
"## Blocking collaboration (rare)",
# Removed from comms.render_inbox_section
"### When to send a message",
"### When to reply to a message you received",
# Removed from comms.render_meetings_section empty case
"(no open meetings)",
)
def _work_item_assignment_task(*, role: str = "ceo") -> Task:
"""Build a task with a representative work_item_assignment payload."""
return Task(
id=f"task-{role}",
title="CEO Intake",
description="legacy description",
assigned_to=role,
metadata={
"execution_mode": "company_mode",
"work_item_projection_title": "CEO Intake",
"work_item_turn_type": "intake",
"work_item_assignment": {
"global_intent_summary": "Ship the migration to the new auth provider this quarter.",
"your_responsibility": "Frame the company-wide intake brief and route slices to the C-suite.",
"out_of_scope": [
"Do not implement code in this work item.",
"Do not validate gate evidence in this work item.",
],
"inputs": [
"Use the original user request as the mission baseline.",
],
"deliverables": [
"deliverables/ceo_intake.md with Mission Summary, Scope, Risks, Routing, Acceptance",
],
"acceptance_criteria": [
"Brief covers every required section and stays inside the work-item boundary.",
],
},
"work_item_runtime_plan": {
"turn_type": "intake",
"summary": "Frame the company mission and route slices.",
# The next four are intentionally duplicates of the
# work_item_assignment fields above. After the refactor
# they MUST NOT be rendered by _render_work_item_runtime_plan.
"inputs": ["Use the original user request as the mission baseline."],
"deliverables": ["deliverables/ceo_intake.md ..."],
"acceptance_criteria": ["Brief covers every required section ..."],
"out_of_scope": ["Do not implement code in this work item."],
# Work-item-unique field that the runtime plan IS allowed
# to carry. Used to verify the runtime plan still
# surfaces non-duplicate content.
"collaboration_expectations": [
"Direct messages for clarifications; meetings only for cross-role decisions."
],
"verification_required": False,
},
"ownership_contract": {
# The summary and expected_artifacts fields are
# intentionally duplicates of the work_item_assignment
# responsibility/deliverables. After the refactor
# render_ownership_contract MUST NOT render them.
"summary": "Frame the company-wide intake brief and route slices to the C-suite.",
"expected_artifacts": ["deliverables/ceo_intake.md ..."],
# The boundary fields below SHOULD still be rendered.
"write_scope": "/tmp/ceo-intake-workspace",
"allowed_collaboration_targets": ["cto"],
"downstream_consumer": ["cto"],
},
},
)
# ----------------------------------------------------------------------
# Bucket 1: work-item identity is rendered exactly once
# ----------------------------------------------------------------------
class WorkItemIdentityDeduplicationTests(unittest.TestCase):
def test_work_item_runtime_plan_does_not_re_render_work_item_assignment_fields(self) -> None:
assembler = ContextAssembler(memory=SimpleNamespace())
task = _work_item_assignment_task()
rendered = assembler._render_work_item_runtime_plan(task.metadata["work_item_runtime_plan"])
# Headings that used to come out of work_item_runtime_plan are
# now ONLY produced by company_mode._build_work_item_description /
# ContextAssembler.build_task_brief. The runtime plan
# renderer must not re-emit them.
self.assertNotIn("Inputs:", rendered)
self.assertNotIn("Deliverables:", rendered)
self.assertNotIn("Acceptance Criteria:", rendered)
self.assertNotIn("Out of Scope:", rendered)
# Work-item-unique fields are still rendered.
self.assertIn("Collaboration Expectations:", rendered)
self.assertIn("Direct messages for clarifications", rendered)
self.assertIn("Work item turn type: intake", rendered)
self.assertIn("No automatic verification pass is required", rendered)
def test_render_ownership_contract_omits_summary_and_expected_artifacts(self) -> None:
task = _work_item_assignment_task()
rendered = render_ownership_contract(task)
# The boundary header still appears...
self.assertIn("## Ownership Contract", rendered)
self.assertIn("Write scope: /tmp/ceo-intake-workspace", rendered)
self.assertIn("Allowed collaboration targets:", rendered)
self.assertIn("Downstream consumers:", rendered)
# ...but the summary and expected_artifacts strings (which
# are duplicates of work-item identity) MUST NOT.
self.assertNotIn("Frame the company-wide intake brief", rendered)
self.assertNotIn("Expected artifacts:", rendered)
self.assertNotIn("deliverables/ceo_intake.md ...", rendered)
def test_build_task_brief_renders_projection_identity_once(self) -> None:
assembler = ContextAssembler(memory=SimpleNamespace())
task = _work_item_assignment_task()
brief = assembler.build_task_brief(task)
# Work-item identity headers appear exactly once.
self.assertEqual(brief.count("## Global Intent Summary"), 1)
self.assertEqual(brief.count("## Your Responsibility"), 1)
self.assertEqual(brief.count("## Inputs"), 1)
self.assertEqual(brief.count("## Deliverables"), 1)
self.assertEqual(brief.count("## Acceptance Criteria"), 1)
self.assertEqual(brief.count("## Out of Scope"), 1)
# The literal responsibility text from work_item_assignment also
# appears exactly once across the entire brief.
self.assertEqual(
brief.count("Frame the company-wide intake brief and route slices to the C-suite."),
1,
)
# runtime role map is no longer included in build_task_brief
# — it was superseded by the unified Topology section in the
# collaboration context path. Pinning this down so a future
# refactor doesn't accidentally re-introduce it.
self.assertNotIn("## Runtime Role Map", brief)
# Ownership contract is also no longer included in
# build_task_brief — it lives only in the collaboration
# path now, so the brief and the system context don't
# double-render it for native agents.
self.assertNotIn("## Ownership Contract", brief)
# ----------------------------------------------------------------------
# Bootstrap work-item assignment input/acceptance deduplication
# ----------------------------------------------------------------------
class RuntimeWorkItemAcceptanceDedupTests(unittest.TestCase):
"""Pin down generic acceptance criteria deduplication."""
def _planner(self) -> Any:
from opc.layer2_organization.company_mode import CompanyRuntimeSpecBuilder
class _OrgEngine:
def get_agent(self, role_id: str):
return None
return CompanyRuntimeSpecBuilder(_OrgEngine())
def _projection_spec(self, *, dependencies: list[str]) -> Any:
from opc.layer2_organization.org_work_item_planner import WorkItemProjectionSpec
return WorkItemProjectionSpec(
projection_id="cto_synthesis",
turn_type="aggregate",
title="CTO Synthesis",
summary="Review child outputs and synthesize the engineering handoff.",
role_id="cto",
dependency_projection_ids=list(dependencies),
execution_strategy="auto",
)
def test_generic_acceptance_criteria_omit_main_deliverable_echo(self) -> None:
planner = self._planner()
projection_spec = self._projection_spec(dependencies=["senior_engineer__execute"])
deliverables = [
"A concrete output that fulfills this work item objective and can be handed off downstream.",
]
criteria = planner._infer_work_item_acceptance(projection_spec, deliverables)
self.assertFalse(
any(item.startswith("The main deliverable is present:") for item in criteria),
msg=f"Generic acceptance criteria still echo the first deliverable verbatim: {criteria}",
)
self.assertNotIn(deliverables[0], "\n".join(criteria))
# ----------------------------------------------------------------------
# Bucket 2: Self section consolidation
# ----------------------------------------------------------------------
class SelfSectionConsolidationTests(unittest.IsolatedAsyncioTestCase):
async def test_self_section_merges_identity_persona_and_delta(self) -> None:
memory = SimpleNamespace(
build_agent_memory_context=AsyncMock(return_value=""),
build_memory_context=AsyncMock(return_value=""),
)
assembler = ContextAssembler(memory=memory, store=SimpleNamespace())
task = Task(
id="self-section-task",
title="Engineering",
assigned_to="senior_engineer",
metadata={
"execution_mode": "company_mode",
"employee_assignment": {
"name": "Backend Architect",
"employee_id": "backend-architect",
"role_id": "senior_engineer",
"category": "engineering",
"domains": ["coding", "api"],
"experience_score": 5,
},
"employee_prompt_context": "Backend specialist focused on APIs.",
"employee_delta_context": "## Working Patterns\n- Leave reviewer-friendly artifacts.",
},
)
rendered = await assembler.build_role_reference_context(task, role_id="senior_engineer")
# Single canonical Self section with role, employee, and profile sub-blocks.
self.assertIn("## Self", rendered)
self.assertIn("### Role", rendered)
self.assertIn("- Role: senior_engineer", rendered)
self.assertIn("### Employee", rendered)
self.assertIn("- Employee: Backend Architect", rendered)
self.assertIn("### Employee Persona", rendered)
self.assertIn("Backend specialist focused on APIs.", rendered)
self.assertIn("### Learned Working Profile", rendered)
# The legacy independent headers must NOT be present.
self.assertNotIn("## Assigned Employee", rendered)
self.assertNotRegex(rendered, r"(?m)^## Employee Persona$")
self.assertNotIn("## Employee Delta Profile", rendered)
# ----------------------------------------------------------------------
# Static collaboration boilerplate is gone from per-turn prompt
# ----------------------------------------------------------------------
class StaticCollaborationBoilerplateRemovedTests(unittest.IsolatedAsyncioTestCase):
async def test_no_static_collaboration_phrases_in_external_context(self) -> None:
memory = SimpleNamespace(
build_focused_memory_context=AsyncMock(return_value=""),
build_memory_context=AsyncMock(return_value=""),
build_agent_memory_context=AsyncMock(return_value=""),
)
# We do not need a real CommunicationManager — just enough
# surface for build_collaboration_context to short-circuit.
communication = SimpleNamespace(
build_agent_context=AsyncMock(
return_value={
"inbox": [],
"annotations": [],
"pending_handoffs": [],
"allowed_contacts": [
{
"role_id": "cto",
"name": "CTO",
"relation": "downstream",
"responsibility": "Owns technical execution",
}
],
}
)
)
assembler = ContextAssembler(memory=memory, store=None, communication=communication)
task = _work_item_assignment_task()
external_ctx = await assembler.build_external_context(task, role_id="ceo")
for phrase in _STATIC_PHRASES_THAT_BELONG_IN_THE_PLAYBOOK:
self.assertNotIn(
phrase,
external_ctx,
msg=f"Static phrase {phrase!r} should now live in the collaboration-playbook skill, not in per-turn context",
)
async def test_task_mode_external_context_omits_collaboration_mailbox_and_comms_prompting(self) -> None:
memory = SimpleNamespace(
build_focused_memory_context=AsyncMock(return_value=""),
build_memory_context=AsyncMock(return_value=""),
build_agent_memory_context=AsyncMock(return_value=""),
)
assembler = ContextAssembler(
memory=memory,
store=None,
communication=SimpleNamespace(build_agent_context=AsyncMock(return_value={})),
)
task = Task(
id="task-mode-external",
title="Task Mode External",
description="Handle the task directly.",
assigned_to="task_generalist",
metadata={
"mode": "task",
"execution_mode": "task_mode",
"work_item_role_id": "task_generalist",
},
context_snapshot={},
)
external_ctx = await assembler.build_external_context(task, role_id="task_generalist")
self.assertNotIn("## Continue After Blocking Collaboration", external_ctx)
self.assertNotIn("## External Collaboration Actions", external_ctx)
self.assertNotIn("## Comms", external_ctx)
def test_render_inbox_section_omits_when_to_send_prose(self) -> None:
with tempfile.TemporaryDirectory() as tmpdir:
layout = _comms.resolve_layout(tmpdir, "proj1", "sess1")
_comms.ensure_layout(layout, ["ceo", "cto"])
rendered = _comms.render_inbox_section(layout, "ceo")
self.assertIn("### Mailbox", rendered)
self.assertIn("runtime-owned", rendered)
# The standing rules now live in the playbook skill.
self.assertNotIn("### When to send a message", rendered)
self.assertNotIn("### When to reply to a message you received", rendered)
self.assertNotIn("Send ONLY when", rendered)
self.assertNotIn("Reply ONLY when", rendered)
self.assertNotIn("call `read_inbox`", rendered)
def test_render_meetings_section_returns_empty_when_no_meetings(self) -> None:
with tempfile.TemporaryDirectory() as tmpdir:
layout = _comms.resolve_layout(tmpdir, "proj1", "sess1")
_comms.ensure_layout(layout, ["ceo"])
rendered = _comms.render_meetings_section(layout, "ceo")
self.assertEqual(rendered, "")
# ----------------------------------------------------------------------
# Current-turn replay is omitted from prompt history
# ----------------------------------------------------------------------
class PromptHistoryReplayTests(unittest.IsolatedAsyncioTestCase):
async def test_session_prompt_builders_skip_latest_user_turn_when_requested(self) -> None:
with tempfile.TemporaryDirectory() as tmpdir:
memory = MemoryManager(Path(tmpdir))
earlier_user = SessionMessageRecord(session_id="sess-1", role="user")
assistant = SessionMessageRecord(session_id="sess-1", role="assistant")
current_user = SessionMessageRecord(session_id="sess-1", role="user")
visible_items = [
{
"message": earlier_user,
"parts": [SessionPartRecord(message_id=earlier_user.message_id, session_id="sess-1", payload={"text": "first request"})],
},
{
"message": assistant,
"parts": [SessionPartRecord(message_id=assistant.message_id, session_id="sess-1", payload={"text": "assistant reply"})],
},
{
"message": current_user,
"parts": [SessionPartRecord(message_id=current_user.message_id, session_id="sess-1", payload={"text": "current request"})],
},
]
memory._get_visible_session_transcript = AsyncMock(return_value=visible_items) # type: ignore[method-assign]
memory.build_session_memory_context = AsyncMock(return_value="") # type: ignore[method-assign]
full_history = await memory.build_session_history_messages("sess-1")
trimmed_history = await memory.build_session_history_tail_messages(
"sess-1",
include_latest_user_turn=False,
)
prompt_context = await memory.build_session_prompt_context(
"sess-1",
include_latest_user_turn=False,
)
self.assertEqual(len(full_history), 3)
self.assertEqual(len(trimmed_history), 2)
self.assertIn("first request", trimmed_history[0]["content"])
self.assertIn("assistant reply", trimmed_history[1]["content"])
self.assertNotIn("current request", prompt_context)
self.assertIn("first request", prompt_context)
self.assertIn("assistant reply", prompt_context)
async def test_child_session_seed_message_is_omitted_from_prompt_history(self) -> None:
with tempfile.TemporaryDirectory() as tmpdir:
memory = MemoryManager(Path(tmpdir))
seed_user = SessionMessageRecord(
session_id="sess-2",
role="user",
metadata={"kind": "child_session_seed"},
)
assistant = SessionMessageRecord(session_id="sess-2", role="assistant")
followup_user = SessionMessageRecord(session_id="sess-2", role="user")
visible_items = [
{
"message": seed_user,
"parts": [
SessionPartRecord(
message_id=seed_user.message_id,
session_id="sess-2",
payload={
"text": "## Global Intent Summary\n做一个3d坦克大战游戏\n\n## Your Responsibility\nOwn the execution work item for role engineer."
},
)
],
},
{
"message": assistant,
"parts": [SessionPartRecord(message_id=assistant.message_id, session_id="sess-2", payload={"text": "working on it"})],
},
{
"message": followup_user,
"parts": [SessionPartRecord(message_id=followup_user.message_id, session_id="sess-2", payload={"text": "please keep the game browser-friendly"})],
},
]
memory._get_visible_session_transcript = AsyncMock(return_value=visible_items) # type: ignore[method-assign]
memory.build_session_memory_context = AsyncMock(return_value="") # type: ignore[method-assign]
prompt_context = await memory.build_session_prompt_context(
"sess-2",
include_latest_user_turn=False,
)
history_messages = await memory.build_session_history_messages(
"sess-2",
include_latest_user_turn=False,
)
self.assertNotIn("Your Responsibility", prompt_context)
self.assertNotIn("Global Intent Summary", prompt_context)
self.assertEqual(len(history_messages), 1)
self.assertIn("working on it", history_messages[0]["content"])
self.assertIn("working on it", prompt_context)
# ----------------------------------------------------------------------
# SkillLibrary mode filtering and the playbook skill
# ----------------------------------------------------------------------
class SkillModeFilteringTests(unittest.TestCase):
def _make_library(self, *, with_playbook: bool = True) -> SkillLibrary:
tmpdir = Path(tempfile.mkdtemp(prefix="opc-skill-test-"))
skills_dir = tmpdir / "skills"
skills_dir.mkdir()
if with_playbook:
playbook_dir = skills_dir / "collaboration-playbook"
playbook_dir.mkdir()
(playbook_dir / "SKILL.md").write_text(
"---\n"
"name: collaboration-playbook\n"
"description: Standing rules for company-mode collaboration.\n"
"always: true\n"
"modes:\n"
" - company_mode\n"
"---\n\n"
"# Collaboration Playbook\n\n"
"Stay inside your work-item boundary.\n"
)
memory_dir = skills_dir / "memory"
memory_dir.mkdir()
(memory_dir / "SKILL.md").write_text(
"---\n"
"name: memory\n"
"description: Read and edit canonical OpenOPC memory.\n"
"always: true\n"
"---\n\n"
"# Memory\n\n"
"Use `.opc/memory/global.md` and `.opc/memory/projects/<current_project_id>.md`.\n"
)
# Also create one ordinary skill that should always show.
ordinary_dir = skills_dir / "writing"
ordinary_dir.mkdir()
(ordinary_dir / "SKILL.md").write_text(
"---\n"
"name: writing\n"
"description: Helpful writing patterns.\n"
"---\n\n"
"# Writing\n"
)
library = SkillLibrary(tmpdir)
library.load_all()
return library
def test_company_mode_skill_is_visible_under_company_mode(self) -> None:
library = self._make_library()
summary = library.build_skills_summary(execution_mode="company_mode")
self.assertIn("## Skill: collaboration-playbook", summary)
self.assertIn("Stay inside your work-item boundary.", summary)
# Ordinary skill is also catalogued.
self.assertIn("**writing**", summary)
def test_company_mode_skill_is_completely_hidden_in_task_mode(self) -> None:
library = self._make_library()
summary = library.build_skills_summary(execution_mode="task_mode")
# Body must be gone.
self.assertNotIn("Stay inside your work-item boundary.", summary)
self.assertNotIn("## Skill: collaboration-playbook", summary)
# Description must ALSO be gone — even the catalog summary
# entry must not appear in task mode.
self.assertNotIn("collaboration-playbook", summary)
self.assertNotIn(
"Standing rules for company-mode collaboration.", summary
)
# The ordinary skill is unaffected.
self.assertIn("**writing**", summary)
self.assertIn("## Skill: memory", summary)
def test_company_mode_skill_is_hidden_when_execution_mode_unknown(self) -> None:
library = self._make_library()
summary = library.build_skills_summary(execution_mode=None)
self.assertNotIn("collaboration-playbook", summary)
self.assertIn("**writing**", summary)
self.assertNotIn("## Skill: memory", summary)
def test_memory_skill_visible_in_task_mode_for_all_agents(self) -> None:
library = self._make_library()
summary = library.build_skills_summary(
execution_mode="task_mode",
role_id="worker",
user_facing=False,
final_decider_role_id=None,
)
self.assertIn("## Skill: memory", summary)
def test_memory_skill_visible_only_to_user_facing_final_decider_in_company_mode(self) -> None:
library = self._make_library()
worker_summary = library.build_skills_summary(
execution_mode="company_mode",
role_id="worker",
user_facing=True,
final_decider_role_id="ceo",
)
agent_turn_summary = library.build_skills_summary(
execution_mode="company_mode",
role_id="ceo",
user_facing=False,
final_decider_role_id="ceo",
)
final_summary = library.build_skills_summary(
execution_mode="company_mode",
role_id="ceo",
user_facing=True,
final_decider_role_id="ceo",
)
self.assertNotIn("## Skill: memory", worker_summary)
self.assertNotIn("## Skill: memory", agent_turn_summary)
self.assertIn("## Skill: memory", final_summary)
def test_skill_visibility_helper_handles_modes_field_shapes(self) -> None:
# Backward compat: skill with no modes is visible everywhere.
skill_no_modes = Skill(name="x", modes=[])
self.assertTrue(SkillLibrary._skill_visible_in_mode(skill_no_modes, None))
self.assertTrue(SkillLibrary._skill_visible_in_mode(skill_no_modes, "company_mode"))
self.assertTrue(SkillLibrary._skill_visible_in_mode(skill_no_modes, "task_mode"))
# Restricted skill is hidden everywhere except declared modes.
skill_company = Skill(name="y", modes=["company_mode"])
self.assertFalse(SkillLibrary._skill_visible_in_mode(skill_company, None))
self.assertFalse(SkillLibrary._skill_visible_in_mode(skill_company, ""))
self.assertFalse(SkillLibrary._skill_visible_in_mode(skill_company, "task_mode"))
self.assertTrue(SkillLibrary._skill_visible_in_mode(skill_company, "company_mode"))
skill_memory = Skill(name="memory", modes=[])
self.assertTrue(SkillLibrary._skill_visible_in_mode(skill_memory, "task_mode"))
self.assertFalse(SkillLibrary._skill_visible_in_mode(skill_memory, "company_mode"))
self.assertTrue(
SkillLibrary._skill_visible_in_mode(
skill_memory,
"company_mode",
role_id="ceo",
user_facing=True,
final_decider_role_id="ceo",
)
)
# ----------------------------------------------------------------------
# Real bundled collaboration-playbook skill
# ----------------------------------------------------------------------
class BundledPlaybookSkillTests(unittest.TestCase):
"""Sanity-check the playbook shipped under .opc/skills/."""
@classmethod
def setUpClass(cls) -> None:
# tests/ -> repo root
cls.repo_root = Path(__file__).resolve().parents[1]
cls.skill_path = cls.repo_root / ".opc" / "skills" / "collaboration-playbook" / "SKILL.md"
def test_playbook_file_exists_with_company_mode_only_frontmatter(self) -> None:
self.assertTrue(
self.skill_path.exists(),
msg=f"Expected collaboration playbook at {self.skill_path}",
)
text = self.skill_path.read_text(encoding="utf-8")
self.assertIn("name: collaboration-playbook", text)
self.assertIn("always: true", text)
# The mode restriction is what makes the skill default-true
# in company mode and invisible in task mode.
self.assertIn("modes:", text)
self.assertIn("- company_mode", text)
def test_bundled_playbook_loads_through_skill_library(self) -> None:
library = SkillLibrary(self.repo_root / ".opc")
library.load_all()
skill = library.get("collaboration-playbook")
self.assertIsNotNone(skill)
assert skill is not None
self.assertTrue(skill.always)
self.assertEqual(skill.modes, ["company_mode"])
self.assertIn("Stay inside your work-item boundary.", skill.content)
company_summary = library.build_skills_summary(execution_mode="company_mode")
self.assertIn("## Skill: collaboration-playbook", company_summary)
task_summary = library.build_skills_summary(execution_mode="task_mode")
self.assertNotIn("collaboration-playbook", task_summary)
if __name__ == "__main__":
unittest.main()