|
1 | 1 | { |
2 | 2 | "project": "Orchestra", |
3 | | - "branchName": "feat/769-deep-agent-backend-sandbox-patterns", |
4 | | - "description": "Extensible Sandbox/Backend Selection - Re-introduce user-configurable sandbox selection using enum + registry dict + factory function pattern so adding new backends requires ~3 lines of code", |
| 3 | + "branchName": "feat/772-schedule-model-dropdown", |
| 4 | + "description": "Replace manual model text input with ModelSelect dropdown on schedule create/edit pages", |
5 | 5 | "userStories": [ |
6 | 6 | { |
7 | 7 | "id": "US-001", |
8 | | - "title": "Add SandboxType enum and schema fields", |
9 | | - "description": "As a developer, I need a SandboxType enum and schema fields so the system has a typed, validated representation of sandbox choices.", |
| 8 | + "title": "Decouple SelectModel from ChatContext", |
| 9 | + "description": "As a developer, I need SelectModel to accept model state via props so it can be reused outside of ChatContext (e.g., in schedule forms).", |
10 | 10 | "acceptanceCriteria": [ |
11 | | - "SandboxType(str, Enum) added to backend/src/schemas/entities/settings.py with values 'auto', 'daytona', 'state'", |
12 | | - "default_sandbox: Optional[str] = None field added to UserSettings entity", |
13 | | - "default_sandbox: Optional[str] = None field added to UserSettingsResponse model", |
14 | | - "UpdateDefaultSandboxRequest(BaseModel) added with sandbox: Optional[str] field", |
15 | | - "Typecheck passes (make format)" |
| 11 | + "SelectModel accepts optional props: value (string), onChange (callback), modelsList (array)", |
| 12 | + "When props are provided, SelectModel uses them instead of useChatContext()", |
| 13 | + "When props are NOT provided, SelectModel falls back to useChatContext() (backward compatible)", |
| 14 | + "Existing usages of SelectModel in chat pages continue to work unchanged", |
| 15 | + "Typecheck passes" |
16 | 16 | ], |
17 | 17 | "priority": 1, |
18 | 18 | "passes": true, |
19 | | - "notes": "" |
| 19 | + "notes": "SelectModel currently imports useChatContext for model/setModel/models and useModelVisibility for filtering. Add optional controlled props with fallback to context." |
20 | 20 | }, |
21 | 21 | { |
22 | 22 | "id": "US-002", |
23 | | - "title": "Add set_default_sandbox repo method with unit tests", |
24 | | - "description": "As a developer, I need a repository method to persist the user's sandbox preference so it can be read back on every agent invocation.", |
| 23 | + "title": "Add standalone models list fetcher", |
| 24 | + "description": "As a developer, I need a way to fetch available models outside of ChatContext so the schedule form can populate the SelectModel dropdown.", |
25 | 25 | "acceptanceCriteria": [ |
26 | | - "set_default_sandbox(sandbox) method added to UserSettingsRepo in backend/src/repos/user_settings_repo.py, mirroring set_default_model()", |
27 | | - "Method validates input against SandboxType enum values; raises ValueError for invalid input", |
28 | | - "None input clears the setting (same as clearing default model)", |
29 | | - "Unit test added: test_set_default_sandbox stores and returns the value", |
30 | | - "Unit test added: test_clear_default_sandbox null clears the value", |
31 | | - "Unit test added: test_set_invalid_sandbox_raises rejects invalid values", |
32 | | - "make test passes" |
| 26 | + "A hook or utility exists to fetch the models list from the /llm/models API endpoint", |
| 27 | + "Returns the same shape as ChatContext's models array", |
| 28 | + "Can be used independently of ChatContext", |
| 29 | + "Typecheck passes" |
33 | 30 | ], |
34 | 31 | "priority": 2, |
35 | 32 | "passes": true, |
36 | | - "notes": "" |
| 33 | + "notes": "Check if ChatContext already fetches from an endpoint that can be extracted into a standalone hook. The /llm/models endpoint likely already exists." |
37 | 34 | }, |
38 | 35 | { |
39 | 36 | "id": "US-003", |
40 | | - "title": "Add PUT /settings/default-sandbox endpoint with route tests", |
41 | | - "description": "As a frontend client, I need an API endpoint to update the user's default sandbox setting.", |
| 37 | + "title": "Replace text input with SelectModel in AgentScheduleForm", |
| 38 | + "description": "As a user creating a schedule, I want to select a model from a dropdown instead of typing the model key name manually.", |
42 | 39 | "acceptanceCriteria": [ |
43 | | - "PUT /settings/default-sandbox endpoint added to backend/src/routes/v0/settings.py, mirroring update_default_model", |
44 | | - "Endpoint accepts UpdateDefaultSandboxRequest body", |
45 | | - "ValueError from repo is caught and returns HTTP 400", |
46 | | - "All existing settings endpoint responses updated to include default_sandbox field", |
47 | | - "Route test added: test_get_settings_includes_default_sandbox verifies field in GET response", |
48 | | - "Route test added: test_set_default_sandbox verifies PUT stores and returns value", |
49 | | - "Route test added: test_set_invalid_sandbox_returns_400 verifies invalid input returns 400", |
50 | | - "make test passes" |
| 40 | + "The Model field in AgentScheduleForm uses SelectModel component instead of text Input", |
| 41 | + "The dropdown shows the same models available in the chat model selector", |
| 42 | + "Selected model value is correctly written to the form state (customModel field)", |
| 43 | + "The agent's current model is shown as placeholder/default when inheriting", |
| 44 | + "Model selection persists correctly on form submit", |
| 45 | + "Typecheck passes", |
| 46 | + "Verify in browser using agent-browser skill" |
51 | 47 | ], |
52 | 48 | "priority": 3, |
53 | 49 | "passes": true, |
54 | | - "notes": "" |
| 50 | + "notes": "File: frontend/src/components/forms/AgentScheduleForm.tsx. The Input at line ~506 with placeholder={agent.model} needs to become a controlled SelectModel. The form uses react-hook-form with register('customModel')." |
55 | 51 | }, |
56 | 52 | { |
57 | 53 | "id": "US-004", |
58 | | - "title": "Implement registry pattern in resolve_sandbox_backend with dispatch tests", |
59 | | - "description": "As a developer, I need resolve_sandbox_backend() to accept a sandbox_type parameter and dispatch to the correct factory, so the function is extensible without modifying its core logic.", |
60 | | - "acceptanceCriteria": [ |
61 | | - "_create_daytona_backend_checked(runtime) factory extracted in backend/src/agents/__init__.py wrapping existing Daytona creation logic", |
62 | | - "_create_state_backend(runtime) factory extracted creating StateBackend", |
63 | | - "_SANDBOX_FACTORIES dict maps SandboxType values to factory callables", |
64 | | - "resolve_sandbox_backend(runtime, sandbox_type=None) dispatches: None/'auto' tries Daytona then falls back to State; 'state' uses State directly; 'daytona' tries Daytona then falls back; unknown treats as auto", |
65 | | - "Unit test added: test_auto_uses_daytona_when_available (sandbox_type=None uses Daytona)", |
66 | | - "Unit test added: test_auto_falls_back_to_state (sandbox_type=None falls back when unavailable)", |
67 | | - "Unit test added: test_explicit_state_skips_daytona (sandbox_type='state' never calls create_daytona_backend)", |
68 | | - "Unit test added: test_explicit_daytona_falls_back_gracefully (sandbox_type='daytona' falls back if unavailable)", |
69 | | - "make test passes" |
70 | | - ], |
71 | | - "priority": 4, |
72 | | - "passes": true, |
73 | | - "notes": "" |
74 | | - }, |
75 | | - { |
76 | | - "id": "US-005", |
77 | | - "title": "Wire sandbox_type through LLMController", |
78 | | - "description": "As a user, I want my sandbox preference applied when I invoke or stream an agent, so my setting is respected in all execution paths.", |
79 | | - "acceptanceCriteria": [ |
80 | | - "_resolve_user_settings() in backend/src/controllers/llm.py returns 3-tuple (model, api_key, default_sandbox) instead of 2-tuple", |
81 | | - "default_sandbox read from the already-fetched settings object", |
82 | | - "sandbox_type passed to resolve_sandbox_backend() in llm_invoke()", |
83 | | - "sandbox_type passed through to stream_generator() in llm_stream()", |
84 | | - "make test passes" |
85 | | - ], |
86 | | - "priority": 5, |
87 | | - "passes": true, |
88 | | - "notes": "" |
89 | | - }, |
90 | | - { |
91 | | - "id": "US-006", |
92 | | - "title": "Wire sandbox_type through stream_generator", |
93 | | - "description": "As a developer, I need stream_generator() to accept and forward the sandbox preference so streaming execution uses the correct backend.", |
94 | | - "acceptanceCriteria": [ |
95 | | - "sandbox_type: str | None = None parameter added to stream_generator() in backend/src/utils/stream.py", |
96 | | - "Parameter passed to resolve_sandbox_backend(runtime, sandbox_type)", |
97 | | - "make test passes" |
98 | | - ], |
99 | | - "priority": 6, |
100 | | - "passes": true, |
101 | | - "notes": "" |
102 | | - }, |
103 | | - { |
104 | | - "id": "US-007", |
105 | | - "title": "Wire sandbox_type through worker tasks", |
106 | | - "description": "As a developer, I need _execute_agent_stream() in the distributed worker to read and forward the sandbox preference from user settings.", |
| 54 | + "title": "Verify schedule edit loads saved model in dropdown", |
| 55 | + "description": "As a user editing an existing schedule, I want the previously selected model to appear pre-selected in the dropdown.", |
107 | 56 | "acceptanceCriteria": [ |
108 | | - "settings.default_sandbox read from the already-fetched settings object in _execute_agent_stream() in backend/src/workers/tasks.py", |
109 | | - "Value passed to resolve_sandbox_backend(runtime, sandbox_type)", |
110 | | - "make test passes" |
111 | | - ], |
112 | | - "priority": 7, |
113 | | - "passes": true, |
114 | | - "notes": "" |
115 | | - }, |
116 | | - { |
117 | | - "id": "US-008", |
118 | | - "title": "Frontend service types and API call", |
119 | | - "description": "As a frontend developer, I need TypeScript types and an API function to interact with the new sandbox settings endpoint.", |
120 | | - "acceptanceCriteria": [ |
121 | | - "SandboxType type exported in frontend/src/lib/services/userSettingsService.ts: 'auto' | 'daytona' | 'state'", |
122 | | - "default_sandbox: string | null field added to UserSettingsResponse interface", |
123 | | - "updateDefaultSandbox(sandbox: string | null) function added that PUTs to /settings/default-sandbox", |
124 | | - "npm run test passes", |
125 | | - "npm run build passes" |
126 | | - ], |
127 | | - "priority": 8, |
128 | | - "passes": true, |
129 | | - "notes": "" |
130 | | - }, |
131 | | - { |
132 | | - "id": "US-009", |
133 | | - "title": "SandboxSettings frontend component", |
134 | | - "description": "As a user, I want a dropdown in the Settings page to choose my default sandbox backend.", |
135 | | - "acceptanceCriteria": [ |
136 | | - "New file frontend/src/components/settings/SandboxSettings.tsx created", |
137 | | - "Follows DefaultModelSettings.tsx pattern (Card + Select dropdown)", |
138 | | - "3 options displayed: Auto (Recommended), Daytona, Local", |
139 | | - "Current value fetched from getSettings() on mount", |
140 | | - "Selection calls updateDefaultSandbox() and shows toast on success/error", |
| 57 | + "When editing a schedule, the SelectModel dropdown shows the saved model as selected", |
| 58 | + "Changing the model and saving persists the new selection", |
| 59 | + "Switching between Inherit from agent and custom correctly toggles the dropdown state", |
141 | 60 | "Typecheck passes", |
142 | 61 | "Verify in browser using agent-browser skill" |
143 | 62 | ], |
144 | | - "priority": 9, |
145 | | - "passes": true, |
146 | | - "notes": "" |
147 | | - }, |
148 | | - { |
149 | | - "id": "US-010", |
150 | | - "title": "Wire SandboxSettings into Settings page", |
151 | | - "description": "As a user, I want the sandbox selector visible on the Settings page between existing settings sections.", |
152 | | - "acceptanceCriteria": [ |
153 | | - "SandboxSettings imported in frontend/src/pages/settings/index.tsx", |
154 | | - "Rendered between <DefaultModelSettings /> and <UserApiKeysSettings />", |
155 | | - "npm run test passes", |
156 | | - "npm run build passes", |
157 | | - "Verify in browser using agent-browser skill" |
158 | | - ], |
159 | | - "priority": 10, |
| 63 | + "priority": 4, |
160 | 64 | "passes": true, |
161 | | - "notes": "" |
| 65 | + "notes": "initialData?.customModel is set at line 64 of AgentScheduleForm.tsx. This value needs to be passed as the controlled value to SelectModel." |
162 | 66 | } |
163 | 67 | ] |
164 | 68 | } |
0 commit comments