diff --git a/apps/shared/src/gateway-contract.generated.ts b/apps/shared/src/gateway-contract.generated.ts index d95c4f8f94..b8449d245e 100644 --- a/apps/shared/src/gateway-contract.generated.ts +++ b/apps/shared/src/gateway-contract.generated.ts @@ -1473,6 +1473,7 @@ export interface ProfileRow { description?: string display_name?: string skill_count?: number + previous_names?: string[] last_session?: ProfileSessionPreview | null worker_session?: ProfileWorkerSession | null canonical_session?: ProfileCanonicalSession | null diff --git a/apps/shared/src/gateway-contract.openrpc.json b/apps/shared/src/gateway-contract.openrpc.json index 926d6c92c1..37dfeac885 100644 --- a/apps/shared/src/gateway-contract.openrpc.json +++ b/apps/shared/src/gateway-contract.openrpc.json @@ -19390,6 +19390,13 @@ "title": "Skill Count", "type": "integer" }, + "previous_names": { + "items": { + "type": "string" + }, + "title": "Previous Names", + "type": "array" + }, "last_session": { "anyOf": [ { diff --git a/tests/tui_gateway/contracts/test_profile_rename_history.py b/tests/tui_gateway/contracts/test_profile_rename_history.py new file mode 100644 index 0000000000..209f06c110 --- /dev/null +++ b/tests/tui_gateway/contracts/test_profile_rename_history.py @@ -0,0 +1,24 @@ +"""Regression for #117696: roster rename history belongs to the wire contract.""" + +import pytest +from pydantic import ValidationError + +from tui_gateway.contracts.profiles_vault_complete_foreign_subagents import ProfilesListResult +from tui_gateway.contracts.registry import METHODS, check_result + + +def test_profile_rename_history_survives_result_validation(): + for names in ([], ["old-bot", "older-bot"]): + payload = {"profiles": [{"name": "bot", "path": "/profiles/bot", "previous_names": names}]} + check_result(METHODS["profiles.list"], payload) + result = ProfilesListResult.model_validate(payload) + assert result.profiles[0].previous_names == names + assert ProfilesListResult.model_validate({"profiles": []}).profiles == [] + legacy = ProfilesListResult.model_validate({"profiles": [{"name": "bot", "path": "/profiles/bot"}]}) + assert legacy.profiles[0].previous_names == [] + + +def test_profile_rename_history_keeps_strict_result_validation(): + for extra in ({"previous_names": [None]}, {"previous_names": "old-bot"}, {"unknown_field": []}): + with pytest.raises(ValidationError): + ProfilesListResult.model_validate({"profiles": [{"name": "bot", "path": "/profiles/bot", **extra}]}) diff --git a/tui_gateway/contracts/profiles_vault_complete_foreign_subagents.py b/tui_gateway/contracts/profiles_vault_complete_foreign_subagents.py index 0e8cc1f748..b00e3fb9e7 100644 --- a/tui_gateway/contracts/profiles_vault_complete_foreign_subagents.py +++ b/tui_gateway/contracts/profiles_vault_complete_foreign_subagents.py @@ -155,6 +155,7 @@ class ProfileRow(Result): description: str = "" display_name: str = "" skill_count: int = 0 + previous_names: list[str] = Field(default_factory=list) last_session: ProfileSessionPreview | None = None worker_session: ProfileWorkerSession | None = None canonical_session: ProfileCanonicalSession | None = None