gabriel / musehub public
test_agent_model_staleness.py python
188 lines 6.8 KB
Raw
sha256:fc04e4cae9e1774d6a21b65c45daeed0e6787eb581d13aa1b03bfe9384a34226 Merge branch 'fix/two-column-scroll-layout' into dev Human 9 days ago
1 """TDD — agent_model must reflect the most recently used model, not a
2 frozen genesis value (musehub#132).
3
4 Covers:
5 AM_01 latest_agent_model_id returns the newest commit's model_id
6 AM_02 latest_agent_model_id returns None when the agent has no commits
7 AM_03 _resolve_identity (registered MusehubIdentity row) returns the live
8 model, not the stale cached identity.agent_model column
9 AM_04 _resolve_identity (registered row, agent has no commits yet) falls
10 back to the stored identity.agent_model
11 AM_05 _resolve_identity (derived agent, no identity row) returns the
12 newest commit's model, not the oldest
13 AM_06 build_profile_manifest returns the live model for agent identities
14 """
15 from __future__ import annotations
16
17 from datetime import datetime, timedelta, timezone
18
19 import pytest
20 from sqlalchemy.ext.asyncio import AsyncSession
21
22 from musehub.db.musehub_identity_models import MusehubIdentity
23 from musehub.db.musehub_repo_models import MusehubCommit
24 from muse.core.types import blob_id, long_id
25
26
27 def _utc(offset_days: int = 0) -> datetime:
28 return datetime.now(tz=timezone.utc) + timedelta(days=offset_days)
29
30
31 async def _add_commit(
32 session: AsyncSession,
33 seed: str,
34 *,
35 agent_id: str,
36 model_id: str,
37 timestamp: datetime,
38 ) -> None:
39 session.add(MusehubCommit(
40 commit_id=blob_id(f"commit-{seed}".encode()),
41 branch="dev",
42 parent_ids=[],
43 message=f"feat: {seed}",
44 author="gabriel",
45 timestamp=timestamp,
46 agent_id=agent_id,
47 model_id=model_id,
48 ))
49 await session.commit()
50
51
52 # ---------------------------------------------------------------------------
53 # latest_agent_model_id
54 # ---------------------------------------------------------------------------
55
56
57 @pytest.mark.asyncio
58 async def test_latest_agent_model_id_returns_newest_commit(db_session: AsyncSession) -> None:
59 from musehub.services.derived_agent_provisioner import latest_agent_model_id
60
61 await _add_commit(db_session, "old", agent_id="claude-code", model_id="claude-sonnet-4-6", timestamp=_utc(-10))
62 await _add_commit(db_session, "new", agent_id="claude-code", model_id="claude-sonnet-5", timestamp=_utc(-1))
63
64 result = await latest_agent_model_id(db_session, "claude-code")
65
66 assert result == "claude-sonnet-5"
67
68
69 @pytest.mark.asyncio
70 async def test_latest_agent_model_id_returns_none_without_commits(db_session: AsyncSession) -> None:
71 from musehub.services.derived_agent_provisioner import latest_agent_model_id
72
73 result = await latest_agent_model_id(db_session, "never-committed-agent")
74
75 assert result is None
76
77
78 # ---------------------------------------------------------------------------
79 # _resolve_identity — registered MusehubIdentity row (branch 1)
80 # ---------------------------------------------------------------------------
81
82
83 @pytest.mark.asyncio
84 async def test_resolve_identity_registered_agent_uses_live_model(db_session: AsyncSession) -> None:
85 from musehub.api.routes.musehub.ui_user_profile import _resolve_identity
86
87 db_session.add(MusehubIdentity(
88 identity_id=long_id("a" * 64),
89 handle="claude-code",
90 identity_type="agent",
91 agent_model="claude-sonnet-4-6", # stale seed from first-ever provisioning
92 created_at=_utc(-30),
93 ))
94 await db_session.commit()
95
96 await _add_commit(db_session, "old", agent_id="claude-code", model_id="claude-sonnet-4-6", timestamp=_utc(-10))
97 await _add_commit(db_session, "new", agent_id="claude-code", model_id="claude-sonnet-5", timestamp=_utc(-1))
98
99 identity = await _resolve_identity(db_session, "claude-code")
100
101 assert identity is not None
102 assert identity["agent_model"] == "claude-sonnet-5"
103
104
105 @pytest.mark.asyncio
106 async def test_resolve_identity_registered_agent_falls_back_when_no_commits(db_session: AsyncSession) -> None:
107 from musehub.api.routes.musehub.ui_user_profile import _resolve_identity
108
109 db_session.add(MusehubIdentity(
110 identity_id=long_id("b" * 64),
111 handle="fresh-agent",
112 identity_type="agent",
113 agent_model="claude-sonnet-5",
114 created_at=_utc(-1),
115 ))
116 await db_session.commit()
117
118 identity = await _resolve_identity(db_session, "fresh-agent")
119
120 assert identity is not None
121 assert identity["agent_model"] == "claude-sonnet-5"
122
123
124 @pytest.mark.asyncio
125 async def test_resolve_identity_human_unaffected(db_session: AsyncSession) -> None:
126 """Live-model recomputation must only apply to agent identities."""
127 from musehub.api.routes.musehub.ui_user_profile import _resolve_identity
128
129 db_session.add(MusehubIdentity(
130 identity_id=long_id("c" * 64),
131 handle="gabriel",
132 identity_type="human",
133 created_at=_utc(-30),
134 ))
135 await db_session.commit()
136
137 identity = await _resolve_identity(db_session, "gabriel")
138
139 assert identity is not None
140 assert identity["agent_model"] is None
141
142
143 # ---------------------------------------------------------------------------
144 # _resolve_identity — derived agent, no MusehubIdentity row (branch 4)
145 # ---------------------------------------------------------------------------
146
147
148 @pytest.mark.asyncio
149 async def test_resolve_identity_derived_agent_uses_newest_commit(db_session: AsyncSession) -> None:
150 from musehub.api.routes.musehub.ui_user_profile import _resolve_identity
151
152 await _add_commit(db_session, "old", agent_id="mix-engine-7", model_id="claude-opus-4-6", timestamp=_utc(-10))
153 await _add_commit(db_session, "new", agent_id="mix-engine-7", model_id="claude-opus-5", timestamp=_utc(-1))
154
155 identity = await _resolve_identity(db_session, "mix-engine-7")
156
157 assert identity is not None
158 assert identity["is_derived"] is True
159 assert identity["agent_model"] == "claude-opus-5"
160 # member_since must still reflect the earliest commit, not the latest
161 assert identity["member_since"] == _utc(-10).replace(microsecond=identity["member_since"].microsecond)
162
163
164 # ---------------------------------------------------------------------------
165 # build_profile_manifest — agent identities
166 # ---------------------------------------------------------------------------
167
168
169 @pytest.mark.asyncio
170 async def test_build_profile_manifest_uses_live_model_for_agent(db_session: AsyncSession) -> None:
171 from musehub.services.musehub_profile import build_profile_manifest
172
173 db_session.add(MusehubIdentity(
174 identity_id=long_id("d" * 64),
175 handle="claude-code",
176 identity_type="agent",
177 agent_model="claude-sonnet-4-6",
178 created_at=_utc(-30),
179 ))
180 await db_session.commit()
181
182 await _add_commit(db_session, "old", agent_id="claude-code", model_id="claude-sonnet-4-6", timestamp=_utc(-10))
183 await _add_commit(db_session, "new", agent_id="claude-code", model_id="claude-sonnet-5", timestamp=_utc(-1))
184
185 manifest = await build_profile_manifest(db_session, "claude-code")
186
187 assert manifest is not None
188 assert manifest.agent_model == "claude-sonnet-5"
File History 1 commit
sha256:fc04e4cae9e1774d6a21b65c45daeed0e6787eb581d13aa1b03bfe9384a34226 Merge branch 'fix/two-column-scroll-layout' into dev Human 9 days ago