clanka 0.2.62 → 0.2.64
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Agent.d.ts.map +1 -1
- package/dist/Agent.js +1 -1
- package/dist/Agent.js.map +1 -1
- package/dist/AgentExecutor.d.ts +11 -11
- package/dist/CopilotAuth.test.js +1 -1
- package/dist/CopilotAuth.test.js.map +1 -1
- package/dist/ScriptPreprocessing.d.ts.map +1 -1
- package/dist/ScriptPreprocessing.js +29 -8
- package/dist/ScriptPreprocessing.js.map +1 -1
- package/dist/ScriptPreprocessing.test.js +1 -0
- package/dist/ScriptPreprocessing.test.js.map +1 -1
- package/package.json +14 -14
- package/src/Agent.ts +1 -3
- package/src/CopilotAuth.test.ts +1 -1
- package/src/ScriptPreprocessing.test.ts +1 -0
- package/src/ScriptPreprocessing.ts +42 -10
- package/src/fixtures/patch21-broken.txt +1016 -0
- package/src/fixtures/patch21-fixed.txt +1016 -0
|
@@ -0,0 +1,1016 @@
|
|
|
1
|
+
const specPath = '.specs/products-frontend-api-readiness.md';
|
|
2
|
+
const spec = `# Products Frontend API Readiness Specification
|
|
3
|
+
|
|
4
|
+
## Request
|
|
5
|
+
|
|
6
|
+
Build out the Effect/TypeScript products API so a frontend application can start being built. The target frontend is similar to Linear for task management, with AI agents as first-class users that can be assigned to tasks.
|
|
7
|
+
|
|
8
|
+
The requester stated that signup, login, task management, model provider, preset/provider configuration, and related foundations already exist, and asked to investigate what is missing from the API for frontend work.
|
|
9
|
+
|
|
10
|
+
Clarifications gathered from the user:
|
|
11
|
+
|
|
12
|
+
- The first frontend-ready iteration should focus on enabling a Linear-like workspace/task board experience.
|
|
13
|
+
- Defer richer collaboration features such as comments, labels, notifications, saved views, and realtime updates.
|
|
14
|
+
- Task querying only needs status filtering for now.
|
|
15
|
+
- Task assignees can continue to use `Task.assigneeId: UserId`; `User.userType` already distinguishes human users from agent users.
|
|
16
|
+
- Do not add a combined bootstrap endpoint in v1. The frontend should assemble its initial state from separate endpoints.
|
|
17
|
+
- Agent management HTTP endpoints are required in v1.
|
|
18
|
+
- Task sessions should be read-only in v1. A task session starts automatically when a task is assigned to an agent user and the task status is `Todo` or `InProgress`; the frontend should not need explicit start/cancel routes yet.
|
|
19
|
+
- Expose both task-scoped and id-scoped session reads: `GET /tasks/:taskId/sessions` and `GET /task-sessions/:id`.
|
|
20
|
+
|
|
21
|
+
This document is an implementation planning specification only. No product code should be implemented as part of creating this plan.
|
|
22
|
+
|
|
23
|
+
## Research summary
|
|
24
|
+
|
|
25
|
+
### Existing products API surface
|
|
26
|
+
|
|
27
|
+
The Effect/TypeScript products app lives under `apps/products`, with schema-first public API definitions in `packages/domain/src/**` and handlers in `apps/products/src/**`.
|
|
28
|
+
|
|
29
|
+
Current `RootApi` composes these groups:
|
|
30
|
+
|
|
31
|
+
- `OrganizationsApi`
|
|
32
|
+
- `ProductsApi`
|
|
33
|
+
- `GitRepositoriesApi`
|
|
34
|
+
- `TasksApi`
|
|
35
|
+
- `UsersApi`
|
|
36
|
+
- `ModelProviderInstancesApi`
|
|
37
|
+
|
|
38
|
+
Existing public endpoints discovered in the codebase include:
|
|
39
|
+
|
|
40
|
+
- Users:
|
|
41
|
+
- `POST /users/register/password`
|
|
42
|
+
- Organizations:
|
|
43
|
+
- `POST /organizations/`
|
|
44
|
+
- `GET /organizations/:organizationId`
|
|
45
|
+
- Products:
|
|
46
|
+
- `POST /products`
|
|
47
|
+
- `GET /organizations/:organizationId/products`
|
|
48
|
+
- Git repositories:
|
|
49
|
+
- `POST /git-repositories`
|
|
50
|
+
- `PUT /git-repositories/:id`
|
|
51
|
+
- `DELETE /git-repositories/:id`
|
|
52
|
+
- Tasks:
|
|
53
|
+
- `POST /tasks`
|
|
54
|
+
- `PUT /tasks/:id`
|
|
55
|
+
- `DELETE /tasks/:id`
|
|
56
|
+
- Model providers/provider instances:
|
|
57
|
+
- `GET /model-providers`
|
|
58
|
+
- `POST /model-provider-instances`
|
|
59
|
+
- `POST /model-provider-instances/:id/device-flow/start`
|
|
60
|
+
- `POST /model-provider-instances/:id/device-flow/poll`
|
|
61
|
+
- `POST /model-provider-instances/:id/device-flow/cancel`
|
|
62
|
+
|
|
63
|
+
There are domain/repository/service foundations that are not yet exposed as frontend-friendly read or management APIs:
|
|
64
|
+
|
|
65
|
+
- `User.userType` is already `human | agent`.
|
|
66
|
+
- `Agent` has a backing `User` row and stores `organizationId`, `modelProviderInstanceId`, `name`, `description`, `modelName`, `systemPrompt`, `modelConfig`, and `status`.
|
|
67
|
+
- `Agents` service already supports create/update/remove, and `AgentsRepo` supports insert/byId/list-by-organization/update/delete.
|
|
68
|
+
- `TaskSession` and `TaskSessionsRepo` exist, but there is no public API group for task sessions.
|
|
69
|
+
- `TasksRepo.forProductId(productId)` exists, but there is no public task list/detail API and no status-filtered query route.
|
|
70
|
+
- `GitRepositoriesRepo` supports active reads/lists internally, but current HTTP API only supports mutations.
|
|
71
|
+
- `ModelProviderInstancesRepo` supports reads/lists/update auth state internally, but current HTTP API only exposes provider catalog, create, and device-flow actions.
|
|
72
|
+
- `OrganizationMembershipsRepo.forUserId(userId)` exists, but there is no public endpoint for “my organizations”.
|
|
73
|
+
|
|
74
|
+
### Conventions to follow
|
|
75
|
+
|
|
76
|
+
- Effect guidance: `docs/effect.md` requires using existing patterns, reading `vendor/effect/LLMS.md` when writing Effect code, and validating with `pnpm check` and `pnpm test`.
|
|
77
|
+
- API definitions live in the domain package and use `effect/unstable/httpapi`.
|
|
78
|
+
- Handlers use `HttpApiBuilder.group(RootApi, <groupName>, ...)` and are provided in `apps/products/src/Api.ts`.
|
|
79
|
+
- Authenticated API groups use the domain `Authentication` middleware and the app `AuthenticationMiddleware` implementation.
|
|
80
|
+
- Authorization should follow existing policy composition patterns:
|
|
81
|
+
- organization-scoped reads use `OrganizationsPolicy.canRead(organizationId)`;
|
|
82
|
+
- product-scoped reads/mutations use `ProductsPolicy.canModify(productId)` or a resource policy that first loads the resource and then composes product authorization;
|
|
83
|
+
- id-only resource routes load the active row first so authorization is based on stored immutable ownership fields.
|
|
84
|
+
- Repositories filter soft-deleted rows with `deletedAt IS NULL`; frontend list/detail endpoints must not expose soft-deleted rows.
|
|
85
|
+
- Domain JSON schemas should avoid parallel payload types unless a route needs a frontend-specific response shape.
|
|
86
|
+
|
|
87
|
+
## Goals
|
|
88
|
+
|
|
89
|
+
1. Add the missing read APIs required for a frontend workspace/task board:
|
|
90
|
+
- current authenticated user;
|
|
91
|
+
- organizations available to the current user;
|
|
92
|
+
- product detail;
|
|
93
|
+
- organization assignable users;
|
|
94
|
+
- task list/detail with optional status filtering;
|
|
95
|
+
- git repository list/detail;
|
|
96
|
+
- model provider instance list/detail;
|
|
97
|
+
- task session list/detail.
|
|
98
|
+
2. Add public agent management APIs: list, detail, create, update, and soft-delete/remove.
|
|
99
|
+
3. Keep task assignment represented by `assigneeId: UserId`; the frontend distinguishes humans vs agents using `User.userType` from user/assignee list endpoints.
|
|
100
|
+
4. Keep task sessions read-only from the frontend in v1. Do not add manual session start/cancel routes.
|
|
101
|
+
5. Use separate endpoints rather than a combined bootstrap endpoint.
|
|
102
|
+
6. Preserve existing create/update/delete APIs and route shapes where already implemented.
|
|
103
|
+
7. Keep implementation tasks small, independently shippable, and validation-safe.
|
|
104
|
+
|
|
105
|
+
## Non-goals
|
|
106
|
+
|
|
107
|
+
- No frontend UI implementation.
|
|
108
|
+
- No combined `/bootstrap` endpoint.
|
|
109
|
+
- No comments, labels, priorities, due dates, project milestones, cycles, estimates, attachments, saved views, notifications, or realtime subscriptions.
|
|
110
|
+
- No task pagination, full-text search, multi-field sort, or filters other than status in v1.
|
|
111
|
+
- No manual frontend API for starting, cancelling, or retrying task sessions.
|
|
112
|
+
- No new task workflow transition rules beyond existing `TaskStatus` validation and the automatic session-start condition described below.
|
|
113
|
+
- No change to the `Task.assigneeId` data model.
|
|
114
|
+
- No new role/permission model beyond existing organization membership/policy checks.
|
|
115
|
+
- No provider token/API-key entry changes unless implementation discovers the current frontend cannot complete an already-planned provider auth flow.
|
|
116
|
+
- No OpenAPI client generation package is required by this plan, though the existing Scalar docs should reflect new routes automatically through `RootApi`.
|
|
117
|
+
|
|
118
|
+
## Frontend data model assumptions
|
|
119
|
+
|
|
120
|
+
### Users and assignees
|
|
121
|
+
|
|
122
|
+
- `User.userType = "human"` means a human account.
|
|
123
|
+
- `User.userType = "agent"` means an AI agent backing user.
|
|
124
|
+
- `Task.ownerId` and `Task.assigneeId` remain `UserId` fields.
|
|
125
|
+
- The frontend can display assignment options by calling organization-scoped user/assignee endpoints and checking `userType`.
|
|
126
|
+
- Agent management routes return `Agent` rows; assignable-user routes return `User` rows. The frontend can link an agent row to its backing user through `Agent.userId`.
|
|
127
|
+
|
|
128
|
+
### Task sessions
|
|
129
|
+
|
|
130
|
+
- Task sessions are a record of agent work on a task.
|
|
131
|
+
- A session starts automatically when all of these are true:
|
|
132
|
+
- the task has an assignee;
|
|
133
|
+
- the assigned user has `userType = "agent"`;
|
|
134
|
+
- the task status is `Todo` or `InProgress`.
|
|
135
|
+
- V1 frontend APIs only read sessions. If the automatic session creation/orchestration does not yet exist, implementation should add the minimal service hook needed to create a `TaskSession` in `Starting` state without exposing a manual route.
|
|
136
|
+
- Session creation must be idempotent for active runs: repeated task updates matching the start condition should not create duplicate active sessions for the same task/agent while an existing `Starting` or `Running` session exists.
|
|
137
|
+
|
|
138
|
+
## Required API additions
|
|
139
|
+
|
|
140
|
+
All new routes below require the existing `Authentication` middleware unless explicitly stated otherwise.
|
|
141
|
+
|
|
142
|
+
### Users API
|
|
143
|
+
|
|
144
|
+
Add current-user and organization-assignee reads.
|
|
145
|
+
|
|
146
|
+
#### `GET /users/me`
|
|
147
|
+
|
|
148
|
+
Purpose: allow the frontend to identify the authenticated actor after the login/session boundary has accepted the request.
|
|
149
|
+
|
|
150
|
+
Response:
|
|
151
|
+
|
|
152
|
+
- `User`
|
|
153
|
+
|
|
154
|
+
Authorization:
|
|
155
|
+
|
|
156
|
+
- Authenticated user required.
|
|
157
|
+
- Return the `CurrentActor` user when `_tag = "UserActor"`.
|
|
158
|
+
- Reject non-user actors with Unauthorized unless the existing auth model requires organization actors to be representable in the frontend.
|
|
159
|
+
|
|
160
|
+
Errors:
|
|
161
|
+
|
|
162
|
+
- Unauthorized from auth middleware or actor mismatch.
|
|
163
|
+
|
|
164
|
+
#### `GET /organizations/:organizationId/users`
|
|
165
|
+
|
|
166
|
+
Purpose: return assignable users for a workspace/organization, including human organization members and agent backing users.
|
|
167
|
+
|
|
168
|
+
Params:
|
|
169
|
+
|
|
170
|
+
- `organizationId: OrganizationId`
|
|
171
|
+
|
|
172
|
+
Response:
|
|
173
|
+
|
|
174
|
+
- `ReadonlyArray<User>` sorted deterministically by `userType`, display name/username, then id.
|
|
175
|
+
- Include both:
|
|
176
|
+
- human users with an active membership in the organization;
|
|
177
|
+
- agent users for active agents in the organization.
|
|
178
|
+
- Do not return soft-deleted users, memberships, or agents.
|
|
179
|
+
|
|
180
|
+
Authorization:
|
|
181
|
+
|
|
182
|
+
- `OrganizationsPolicy.canRead(organizationId)`.
|
|
183
|
+
|
|
184
|
+
Implementation notes:
|
|
185
|
+
|
|
186
|
+
- Add repository support in `UsersRepo` or a small query service for `forOrganizationAssignableUsers(organizationId)`.
|
|
187
|
+
- The query should not require agent users to have organization memberships if agent ownership is already represented by `agents.organizationId`.
|
|
188
|
+
- Deduplicate users by id if future data introduces overlap.
|
|
189
|
+
|
|
190
|
+
### Organizations API
|
|
191
|
+
|
|
192
|
+
#### `GET /organizations`
|
|
193
|
+
|
|
194
|
+
Purpose: list organizations available to the current authenticated user.
|
|
195
|
+
|
|
196
|
+
Response:
|
|
197
|
+
|
|
198
|
+
- `ReadonlyArray<Organization>` sorted deterministically by name then id.
|
|
199
|
+
|
|
200
|
+
Authorization:
|
|
201
|
+
|
|
202
|
+
- Authenticated user required.
|
|
203
|
+
- Use `OrganizationMembershipsRepo.forUserId(CurrentUser.id)` to discover active memberships, then load active organizations.
|
|
204
|
+
|
|
205
|
+
Errors:
|
|
206
|
+
|
|
207
|
+
- Unauthorized for non-user actors.
|
|
208
|
+
- Storage defects remain internal defects, not public route variants.
|
|
209
|
+
|
|
210
|
+
Implementation notes:
|
|
211
|
+
|
|
212
|
+
- Add service method `Organizations.listForCurrentUser()`.
|
|
213
|
+
- This is intentionally separate from `GET /users/me` and product list endpoints; no v1 bootstrap endpoint should be added.
|
|
214
|
+
|
|
215
|
+
### Products API
|
|
216
|
+
|
|
217
|
+
Existing:
|
|
218
|
+
|
|
219
|
+
- `POST /products`
|
|
220
|
+
- `GET /organizations/:organizationId/products`
|
|
221
|
+
|
|
222
|
+
Add:
|
|
223
|
+
|
|
224
|
+
#### `GET /products/:id`
|
|
225
|
+
|
|
226
|
+
Purpose: load product detail for product-scoped pages and refreshes.
|
|
227
|
+
|
|
228
|
+
Params:
|
|
229
|
+
|
|
230
|
+
- `id: ProductId`
|
|
231
|
+
|
|
232
|
+
Response:
|
|
233
|
+
|
|
234
|
+
- `Product`
|
|
235
|
+
|
|
236
|
+
Authorization:
|
|
237
|
+
|
|
238
|
+
- Load active product by id.
|
|
239
|
+
- Authorize through `ProductsPolicy.canRead(id)` if added, or equivalent composition via `OrganizationsPolicy.canRead(product.organizationId)`.
|
|
240
|
+
|
|
241
|
+
Errors:
|
|
242
|
+
|
|
243
|
+
- Not Found when no active product exists.
|
|
244
|
+
- Unauthorized when the actor cannot read the product organization.
|
|
245
|
+
|
|
246
|
+
Implementation notes:
|
|
247
|
+
|
|
248
|
+
- `ProductsPolicy` currently exposes `canModify`; add `canRead` if not already available.
|
|
249
|
+
- Keep `GET /organizations/:organizationId/products` as the list endpoint. No update/delete product API is required for v1 frontend readiness.
|
|
250
|
+
|
|
251
|
+
### Tasks API
|
|
252
|
+
|
|
253
|
+
Existing:
|
|
254
|
+
|
|
255
|
+
- `POST /tasks`
|
|
256
|
+
- `PUT /tasks/:id`
|
|
257
|
+
- `DELETE /tasks/:id`
|
|
258
|
+
|
|
259
|
+
Add:
|
|
260
|
+
|
|
261
|
+
#### `GET /products/:productId/tasks`
|
|
262
|
+
|
|
263
|
+
Purpose: list tasks for the board/backlog/task table.
|
|
264
|
+
|
|
265
|
+
Params:
|
|
266
|
+
|
|
267
|
+
- `productId: ProductId`
|
|
268
|
+
|
|
269
|
+
Query:
|
|
270
|
+
|
|
271
|
+
- `status?: TaskStatus`
|
|
272
|
+
|
|
273
|
+
Response:
|
|
274
|
+
|
|
275
|
+
- `ReadonlyArray<Task>` sorted deterministically by `createdAt ASC, id ASC` unless a repository-local existing order is already documented.
|
|
276
|
+
|
|
277
|
+
Authorization:
|
|
278
|
+
|
|
279
|
+
- `ProductsPolicy.canRead(productId)` or existing product access policy equivalent.
|
|
280
|
+
|
|
281
|
+
Behavior:
|
|
282
|
+
|
|
283
|
+
- Return only active tasks where `deletedAt IS NULL`.
|
|
284
|
+
- If `status` is present, return only tasks with that status.
|
|
285
|
+
- If `status` is absent, return all active tasks for the product.
|
|
286
|
+
|
|
287
|
+
Errors:
|
|
288
|
+
|
|
289
|
+
- Not Found if the product does not exist.
|
|
290
|
+
- Unauthorized if the actor cannot read the product.
|
|
291
|
+
|
|
292
|
+
Implementation notes:
|
|
293
|
+
|
|
294
|
+
- Extend `TasksRepo.forProductId` to accept an optional status filter, or add `forProductIdFiltered`.
|
|
295
|
+
- Do not add pagination in v1.
|
|
296
|
+
- Do not add additional board filters in v1.
|
|
297
|
+
|
|
298
|
+
#### `GET /tasks/:id`
|
|
299
|
+
|
|
300
|
+
Purpose: load task detail pages and refresh a single task after mutations.
|
|
301
|
+
|
|
302
|
+
Params:
|
|
303
|
+
|
|
304
|
+
- `id: TaskId`
|
|
305
|
+
|
|
306
|
+
Response:
|
|
307
|
+
|
|
308
|
+
- `Task`
|
|
309
|
+
|
|
310
|
+
Authorization:
|
|
311
|
+
|
|
312
|
+
- Load active task by id.
|
|
313
|
+
- Authorize through `TasksPolicy.canRead(id)` if added, or equivalent product-read composition.
|
|
314
|
+
|
|
315
|
+
Errors:
|
|
316
|
+
|
|
317
|
+
- Not Found when no active task exists.
|
|
318
|
+
- Unauthorized when the actor cannot read the task's product.
|
|
319
|
+
|
|
320
|
+
Implementation notes:
|
|
321
|
+
|
|
322
|
+
- Add a read policy separate from modify if needed. Read should not grant modify permission.
|
|
323
|
+
- Keep create/update/delete behavior unchanged except for the automatic task-session hook below.
|
|
324
|
+
|
|
325
|
+
### Task session API
|
|
326
|
+
|
|
327
|
+
Add a new domain API group, preferably `TaskSessionsApi`, and add it to `RootApi` only in the same implementation task that wires app handlers.
|
|
328
|
+
|
|
329
|
+
#### `GET /tasks/:taskId/sessions`
|
|
330
|
+
|
|
331
|
+
Purpose: show agent-run history or current run state on a task detail view.
|
|
332
|
+
|
|
333
|
+
Params:
|
|
334
|
+
|
|
335
|
+
- `taskId: TaskId`
|
|
336
|
+
|
|
337
|
+
Response:
|
|
338
|
+
|
|
339
|
+
- `ReadonlyArray<TaskSession>` sorted by `createdAt DESC, id DESC`.
|
|
340
|
+
|
|
341
|
+
Authorization:
|
|
342
|
+
|
|
343
|
+
- Load active task and authorize product read access.
|
|
344
|
+
|
|
345
|
+
Behavior:
|
|
346
|
+
|
|
347
|
+
- Return only active task sessions where `deletedAt IS NULL`.
|
|
348
|
+
- Include sessions for the task regardless of status (`Starting`, `Running`, `Completed`, `Cancelled`).
|
|
349
|
+
|
|
350
|
+
Errors:
|
|
351
|
+
|
|
352
|
+
- Not Found when no active task exists.
|
|
353
|
+
- Unauthorized when the actor cannot read the task's product.
|
|
354
|
+
|
|
355
|
+
#### `GET /task-sessions/:id`
|
|
356
|
+
|
|
357
|
+
Purpose: load a single session detail view or poll a current run.
|
|
358
|
+
|
|
359
|
+
Params:
|
|
360
|
+
|
|
361
|
+
- `id: TaskSessionId`
|
|
362
|
+
|
|
363
|
+
Response:
|
|
364
|
+
|
|
365
|
+
- `TaskSession`
|
|
366
|
+
|
|
367
|
+
Authorization:
|
|
368
|
+
|
|
369
|
+
- Load active task session by id.
|
|
370
|
+
- Load its active task.
|
|
371
|
+
- Authorize product read access for that task.
|
|
372
|
+
|
|
373
|
+
Errors:
|
|
374
|
+
|
|
375
|
+
- Not Found when no active session or parent task exists.
|
|
376
|
+
- Unauthorized when the actor cannot read the task's product.
|
|
377
|
+
|
|
378
|
+
Implementation notes:
|
|
379
|
+
|
|
380
|
+
- Add `TaskSessionsRepo.forTaskId(taskId)`.
|
|
381
|
+
- Consider adding `TaskSessionsPolicy.canRead(id)` and `TasksPolicy.canRead(taskId)` to avoid duplicating policy composition.
|
|
382
|
+
- Existing `TaskSessionsPolicy.canModify` should remain for future mutation routes; v1 public routes should use read policy.
|
|
383
|
+
|
|
384
|
+
### Automatic task-session creation hook
|
|
385
|
+
|
|
386
|
+
This is not a frontend route, but it is part of the v1 frontend contract because sessions are read-only and must appear after valid agent assignment.
|
|
387
|
+
|
|
388
|
+
Behavior:
|
|
389
|
+
|
|
390
|
+
- After task create/update persists a task, inspect the persisted task.
|
|
391
|
+
- If `assigneeId` is present, load the user.
|
|
392
|
+
- If the user has `userType = "agent"` and `status` is `Todo` or `InProgress`, ensure a task session exists for that task and agent.
|
|
393
|
+
- Resolve the `Agent` by backing `userId`; if no active agent exists, treat it as a recoverable business inconsistency and do not start a session, or return a typed Bad Request if assignment validation is implemented in the same task. The preferred v1 behavior is to prevent assigning inactive/nonexistent agent users through validation.
|
|
394
|
+
- Do not create a duplicate active session for the same `taskId` and `agentId` while an existing session is `Starting` or `Running`.
|
|
395
|
+
- Create new sessions with `status = "Starting"` and `completedAt = none/null`.
|
|
396
|
+
- Do not add start/cancel/retry HTTP endpoints in this spec.
|
|
397
|
+
|
|
398
|
+
Validation expectation:
|
|
399
|
+
|
|
400
|
+
- Add tests for human assignee no-op, agent assignee with eligible status creates one session, ineligible status no-op, and repeated matching updates remain idempotent.
|
|
401
|
+
|
|
402
|
+
If this hook is intentionally implemented by another worker/service outside `apps/products`, document that boundary in code comments/tests and leave the public task-session API read-only.
|
|
403
|
+
|
|
404
|
+
### Git repositories API
|
|
405
|
+
|
|
406
|
+
Existing:
|
|
407
|
+
|
|
408
|
+
- `POST /git-repositories`
|
|
409
|
+
- `PUT /git-repositories/:id`
|
|
410
|
+
- `DELETE /git-repositories/:id`
|
|
411
|
+
|
|
412
|
+
Add:
|
|
413
|
+
|
|
414
|
+
#### `GET /products/:productId/git-repositories`
|
|
415
|
+
|
|
416
|
+
Purpose: populate repository selectors and product settings screens.
|
|
417
|
+
|
|
418
|
+
Params:
|
|
419
|
+
|
|
420
|
+
- `productId: ProductId`
|
|
421
|
+
|
|
422
|
+
Response:
|
|
423
|
+
|
|
424
|
+
- `ReadonlyArray<GitRepository>` sorted by name then id.
|
|
425
|
+
|
|
426
|
+
Authorization:
|
|
427
|
+
|
|
428
|
+
- Product read access.
|
|
429
|
+
|
|
430
|
+
Behavior:
|
|
431
|
+
|
|
432
|
+
- Return only active repositories.
|
|
433
|
+
|
|
434
|
+
#### `GET /git-repositories/:id`
|
|
435
|
+
|
|
436
|
+
Purpose: load repository detail/edit screens.
|
|
437
|
+
|
|
438
|
+
Params:
|
|
439
|
+
|
|
440
|
+
- `id: GitRepositoryId`
|
|
441
|
+
|
|
442
|
+
Response:
|
|
443
|
+
|
|
444
|
+
- `GitRepository`
|
|
445
|
+
|
|
446
|
+
Authorization:
|
|
447
|
+
|
|
448
|
+
- Load active repository by id, then authorize product read access.
|
|
449
|
+
|
|
450
|
+
Errors:
|
|
451
|
+
|
|
452
|
+
- Not Found when no active repository exists.
|
|
453
|
+
|
|
454
|
+
### Model provider instances API
|
|
455
|
+
|
|
456
|
+
Existing:
|
|
457
|
+
|
|
458
|
+
- `GET /model-providers`
|
|
459
|
+
- `POST /model-provider-instances`
|
|
460
|
+
- device-flow start/poll/cancel routes
|
|
461
|
+
|
|
462
|
+
Add:
|
|
463
|
+
|
|
464
|
+
#### `GET /organizations/:organizationId/model-provider-instances`
|
|
465
|
+
|
|
466
|
+
Purpose: show configured provider instances for organization settings and agent setup.
|
|
467
|
+
|
|
468
|
+
Params:
|
|
469
|
+
|
|
470
|
+
- `organizationId: OrganizationId`
|
|
471
|
+
|
|
472
|
+
Response:
|
|
473
|
+
|
|
474
|
+
- `ReadonlyArray<ModelProviderInstance>` sorted by name then id.
|
|
475
|
+
|
|
476
|
+
Authorization:
|
|
477
|
+
|
|
478
|
+
- Organization read access.
|
|
479
|
+
|
|
480
|
+
Behavior:
|
|
481
|
+
|
|
482
|
+
- Return only active/non-soft-deleted rows.
|
|
483
|
+
- Do not return token/API key material.
|
|
484
|
+
|
|
485
|
+
#### `GET /model-provider-instances/:id`
|
|
486
|
+
|
|
487
|
+
Purpose: load detail/status for a provider instance.
|
|
488
|
+
|
|
489
|
+
Params:
|
|
490
|
+
|
|
491
|
+
- `id: ModelProviderInstanceId`
|
|
492
|
+
|
|
493
|
+
Response:
|
|
494
|
+
|
|
495
|
+
- `ModelProviderInstance`
|
|
496
|
+
|
|
497
|
+
Authorization:
|
|
498
|
+
|
|
499
|
+
- Load active instance by id, then authorize organization read access.
|
|
500
|
+
|
|
501
|
+
Errors:
|
|
502
|
+
|
|
503
|
+
- Not Found when no active instance exists.
|
|
504
|
+
|
|
505
|
+
#### Optional management routes for v1
|
|
506
|
+
|
|
507
|
+
If the frontend provider settings screen requires renaming/disabling instances immediately, add these in the same provider-management task:
|
|
508
|
+
|
|
509
|
+
- `PUT /model-provider-instances/:id` with a narrow payload for editable metadata (`name`, optionally `status` limited to `active | disabled`).
|
|
510
|
+
- `DELETE /model-provider-instances/:id` as soft-delete.
|
|
511
|
+
|
|
512
|
+
If not required by the first frontend milestone, keep provider-instance mutation limited to existing create/device-flow routes and defer these optional routes.
|
|
513
|
+
|
|
514
|
+
### Agents API
|
|
515
|
+
|
|
516
|
+
Add a new domain API group, preferably `AgentsApi`, and add it to `RootApi` only in the same implementation task that wires app handlers.
|
|
517
|
+
|
|
518
|
+
#### `GET /organizations/:organizationId/agents`
|
|
519
|
+
|
|
520
|
+
Purpose: list agents for organization settings and assignment-related views.
|
|
521
|
+
|
|
522
|
+
Params:
|
|
523
|
+
|
|
524
|
+
- `organizationId: OrganizationId`
|
|
525
|
+
|
|
526
|
+
Response:
|
|
527
|
+
|
|
528
|
+
- `ReadonlyArray<Agent>` sorted by name then id.
|
|
529
|
+
|
|
530
|
+
Authorization:
|
|
531
|
+
|
|
532
|
+
- Organization read access.
|
|
533
|
+
|
|
534
|
+
Behavior:
|
|
535
|
+
|
|
536
|
+
- Return only active/non-soft-deleted agents.
|
|
537
|
+
|
|
538
|
+
#### `GET /agents/:id`
|
|
539
|
+
|
|
540
|
+
Purpose: load agent detail/edit screens and allow frontend to resolve an assignee's agent configuration.
|
|
541
|
+
|
|
542
|
+
Params:
|
|
543
|
+
|
|
544
|
+
- `id: AgentId`
|
|
545
|
+
|
|
546
|
+
Response:
|
|
547
|
+
|
|
548
|
+
- `Agent`
|
|
549
|
+
|
|
550
|
+
Authorization:
|
|
551
|
+
|
|
552
|
+
- Load active agent by id, then authorize organization read access.
|
|
553
|
+
|
|
554
|
+
Errors:
|
|
555
|
+
|
|
556
|
+
- Not Found when no active agent exists.
|
|
557
|
+
|
|
558
|
+
#### `POST /agents`
|
|
559
|
+
|
|
560
|
+
Purpose: create an organization-scoped AI agent and its backing `User` row.
|
|
561
|
+
|
|
562
|
+
Payload:
|
|
563
|
+
|
|
564
|
+
- Use `Agent.jsonCreate` if it already represents the public create contract.
|
|
565
|
+
- Public create should include:
|
|
566
|
+
- `organizationId`
|
|
567
|
+
- `modelProviderInstanceId`
|
|
568
|
+
- `name`
|
|
569
|
+
- optional `description`
|
|
570
|
+
- `modelName`
|
|
571
|
+
- optional `systemPrompt`
|
|
572
|
+
- `modelConfig`
|
|
573
|
+
- `status`
|
|
574
|
+
- Public create must not include `userId`, lifecycle timestamps, or `deletedAt`.
|
|
575
|
+
|
|
576
|
+
Response:
|
|
577
|
+
|
|
578
|
+
- `Agent`
|
|
579
|
+
|
|
580
|
+
Authorization:
|
|
581
|
+
|
|
582
|
+
- Organization read/manage access. In the absence of a separate manage permission, use the same organization-read policy convention used by provider-instance creation, plus `policyRequire("Agent", "create")`.
|
|
583
|
+
- Validate or authorize that the referenced `modelProviderInstanceId` belongs to the same organization.
|
|
584
|
+
|
|
585
|
+
Behavior:
|
|
586
|
+
|
|
587
|
+
- Reuse existing `Agents.create` service behavior that creates a backing `User` with `userType = "agent"`.
|
|
588
|
+
- Return Conflict for duplicate active agent name in the organization.
|
|
589
|
+
- Return Bad Request or Conflict for cross-organization provider instance references; prefer Bad Request for invalid input and Unauthorized only for access denial.
|
|
590
|
+
|
|
591
|
+
#### `PUT /agents/:id`
|
|
592
|
+
|
|
593
|
+
Purpose: edit an agent's frontend-manageable metadata/config.
|
|
594
|
+
|
|
595
|
+
Params:
|
|
596
|
+
|
|
597
|
+
- `id: AgentId`
|
|
598
|
+
|
|
599
|
+
Payload:
|
|
600
|
+
|
|
601
|
+
- Use `Agent.jsonUpdate` if it is the correct public full-update contract.
|
|
602
|
+
- Editable fields should include `name`, `description`, `modelName`, `systemPrompt`, `modelConfig`, and `status`.
|
|
603
|
+
- Do not allow changing `organizationId`, `modelProviderInstanceId`, `userId`, lifecycle timestamps, or `deletedAt` in v1.
|
|
604
|
+
|
|
605
|
+
Response:
|
|
606
|
+
|
|
607
|
+
- `Agent`
|
|
608
|
+
|
|
609
|
+
Authorization:
|
|
610
|
+
|
|
611
|
+
- Load active agent by id, then authorize organization read/manage access using an `AgentsPolicy.canModify(id)` policy.
|
|
612
|
+
|
|
613
|
+
Behavior:
|
|
614
|
+
|
|
615
|
+
- Keep the backing user's display name in sync with the updated agent name, as existing `Agents.update` already does.
|
|
616
|
+
- Return Conflict for duplicate active agent name.
|
|
617
|
+
|
|
618
|
+
#### `DELETE /agents/:id`
|
|
619
|
+
|
|
620
|
+
Purpose: remove/deactivate an agent from frontend settings.
|
|
621
|
+
|
|
622
|
+
Params:
|
|
623
|
+
|
|
624
|
+
- `id: AgentId`
|
|
625
|
+
|
|
626
|
+
Response:
|
|
627
|
+
|
|
628
|
+
- No content.
|
|
629
|
+
|
|
630
|
+
Authorization:
|
|
631
|
+
|
|
632
|
+
- Load active agent by id, then authorize organization read/manage access using `AgentsPolicy.canModify(id)`.
|
|
633
|
+
|
|
634
|
+
Behavior:
|
|
635
|
+
|
|
636
|
+
- Soft-delete/remove the agent using existing repository semantics.
|
|
637
|
+
- The backing user must no longer be returned by `GET /organizations/:organizationId/users` after agent deletion.
|
|
638
|
+
- Do not hard-delete historical task/session rows.
|
|
639
|
+
|
|
640
|
+
## Response-shape guidance
|
|
641
|
+
|
|
642
|
+
V1 should prefer returning existing domain models to keep route implementation small and generated docs predictable:
|
|
643
|
+
|
|
644
|
+
- `User`
|
|
645
|
+
- `Organization`
|
|
646
|
+
- `Product`
|
|
647
|
+
- `Task`
|
|
648
|
+
- `TaskSession`
|
|
649
|
+
- `GitRepository`
|
|
650
|
+
- `ModelProviderInstance`
|
|
651
|
+
- `Agent`
|
|
652
|
+
|
|
653
|
+
Do not introduce expanded GraphQL-like view models in v1 unless implementation discovers a hard client blocker. The frontend can compose expanded views by calling separate endpoints, per the clarification to avoid a bootstrap endpoint.
|
|
654
|
+
|
|
655
|
+
If a route does need a response wrapper for query metadata, keep it minimal and document it before implementing. No pagination metadata is required in v1.
|
|
656
|
+
|
|
657
|
+
## Authorization matrix
|
|
658
|
+
|
|
659
|
+
| Route | Required access |
|
|
660
|
+
| --- | --- |
|
|
661
|
+
| `GET /users/me` | authenticated user actor |
|
|
662
|
+
| `GET /organizations` | authenticated user actor; memberships determine results |
|
|
663
|
+
| `GET /organizations/:organizationId/users` | organization read |
|
|
664
|
+
| `GET /products/:id` | product read via organization read |
|
|
665
|
+
| `GET /products/:productId/tasks` | product read |
|
|
666
|
+
| `GET /tasks/:id` | task read via product read |
|
|
667
|
+
| `GET /tasks/:taskId/sessions` | task read via product read |
|
|
668
|
+
| `GET /task-sessions/:id` | task-session read via parent task/product read |
|
|
669
|
+
| `GET /products/:productId/git-repositories` | product read |
|
|
670
|
+
| `GET /git-repositories/:id` | repository read via product read |
|
|
671
|
+
| `GET /organizations/:organizationId/model-provider-instances` | organization read |
|
|
672
|
+
| `GET /model-provider-instances/:id` | provider-instance read via organization read |
|
|
673
|
+
| `GET /organizations/:organizationId/agents` | organization read |
|
|
674
|
+
| `GET /agents/:id` | agent read via organization read |
|
|
675
|
+
| `POST /agents` | organization read/manage + agent create capability |
|
|
676
|
+
| `PUT /agents/:id` | agent modify via organization read/manage |
|
|
677
|
+
| `DELETE /agents/:id` | agent modify via organization read/manage |
|
|
678
|
+
|
|
679
|
+
Implementation can name read policies differently, but must keep read and modify permissions conceptually separate so read routes do not accidentally require edit access.
|
|
680
|
+
|
|
681
|
+
## Error contract guidance
|
|
682
|
+
|
|
683
|
+
- Missing active rows: return `HttpApiError.NotFoundNoContent`.
|
|
684
|
+
- Duplicate active names: return `HttpApiError.ConflictNoContent` or existing Conflict variant used by adjacent routes.
|
|
685
|
+
- Malformed payload/query/params: schema decode Bad Request from HttpApi.
|
|
686
|
+
- Cross-organization references on create/update: prefer Bad Request or Conflict when the actor can access both resources but the relationship is invalid; Unauthorized when access to a referenced resource is denied.
|
|
687
|
+
- Repository/SQL errors: map to existing domain repository errors internally and defect/die at service boundaries unless there is a user-actionable public error.
|
|
688
|
+
- Never expose provider tokens, API keys, raw provider auth responses, or redacted secret internals in public errors.
|
|
689
|
+
|
|
690
|
+
## Database and repository requirements
|
|
691
|
+
|
|
692
|
+
Expected repository additions or extensions:
|
|
693
|
+
|
|
694
|
+
- `UsersRepo`
|
|
695
|
+
- `current` is not required; use `CurrentActor`/auth context for `/users/me`.
|
|
696
|
+
- Add organization assignable users query, either directly or through a dedicated service.
|
|
697
|
+
- `OrganizationMembershipsRepo`
|
|
698
|
+
- Existing `forUserId` can drive `GET /organizations`.
|
|
699
|
+
- `OrganizationsRepo`
|
|
700
|
+
- Existing `byId` plus either batched lookup by ids or a join query for current user's organizations.
|
|
701
|
+
- `ProductsRepo`
|
|
702
|
+
- Existing `byId` can drive `GET /products/:id`.
|
|
703
|
+
- `TasksRepo`
|
|
704
|
+
- Extend product list with optional `status` filter.
|
|
705
|
+
- Existing `byId` can drive detail if it excludes soft-deleted rows.
|
|
706
|
+
- `TaskSessionsRepo`
|
|
707
|
+
- Add `forTaskId(taskId)`.
|
|
708
|
+
- Ensure `byId` excludes soft-deleted rows. If generic `repo.findById` behavior is not proven, replace with explicit active-row SQL.
|
|
709
|
+
- Add an idempotent active-session lookup for automatic session creation if the hook is implemented in products.
|
|
710
|
+
- `GitRepositoriesRepo`
|
|
711
|
+
- Use existing list/detail methods if present; otherwise add active `forProductId(productId)` and active `byId(id)`.
|
|
712
|
+
- `ModelProviderInstancesRepo`
|
|
713
|
+
- Use existing list/detail methods if present; otherwise add active `forOrganization(organizationId)` and active `byId(id)`.
|
|
714
|
+
- `AgentsRepo`
|
|
715
|
+
- Use existing `forOrganization`, `byId`, `insert`, `update`, and `delete` methods.
|
|
716
|
+
- Add `byUserId(userId)` or `activeByUserId(userId)` for automatic session creation and assignment validation.
|
|
717
|
+
|
|
718
|
+
Add SQL migrations only if required for new uniqueness/idempotency guarantees. Recommended if not already present:
|
|
719
|
+
|
|
720
|
+
- A partial unique index preventing more than one active `Starting`/`Running` task session for the same `taskId` and `agentId`:
|
|
721
|
+
- `UNIQUE ("taskId", "agentId") WHERE "deletedAt" IS NULL AND "status" IN ('Starting', 'Running')`
|
|
722
|
+
|
|
723
|
+
This index should be added in the same task as the automatic session-start hook if the hook is implemented in products.
|
|
724
|
+
|
|
725
|
+
## Testing requirements
|
|
726
|
+
|
|
727
|
+
Use existing Effect/TypeScript test conventions under `apps/products` and `packages/domain`.
|
|
728
|
+
|
|
729
|
+
Minimum focused coverage:
|
|
730
|
+
|
|
731
|
+
- Domain API schema tests for new route query/payload shapes where non-trivial, especially task status query and agent create/update schemas.
|
|
732
|
+
- Repository tests for:
|
|
733
|
+
- current user's organizations query or service;
|
|
734
|
+
- organization assignable users including human members and agent backing users;
|
|
735
|
+
- status-filtered task list;
|
|
736
|
+
- task sessions by task;
|
|
737
|
+
- active-session idempotency lookup if implemented;
|
|
738
|
+
- provider instance and git repository list/detail if missing.
|
|
739
|
+
- Handler tests for each new API group/route cluster:
|
|
740
|
+
- success path;
|
|
741
|
+
- Not Found for missing/soft-deleted resources;
|
|
742
|
+
- Unauthorized for inaccessible organization/product resources;
|
|
743
|
+
- status filter behavior for task list;
|
|
744
|
+
- agent create/update/delete conflict and authorization behavior;
|
|
745
|
+
- read-only task session routes do not expose mutation endpoints.
|
|
746
|
+
- Automatic task-session hook tests if implemented in products:
|
|
747
|
+
- human assignee does not create a session;
|
|
748
|
+
- agent assignee with `Todo` creates one `Starting` session;
|
|
749
|
+
- agent assignee with `InProgress` creates one `Starting` session;
|
|
750
|
+
- agent assignee with `Backlog`, `InReview`, or `Done` does not create a new session;
|
|
751
|
+
- repeated qualifying updates do not create duplicate active sessions.
|
|
752
|
+
|
|
753
|
+
Validation commands for implementation PRs:
|
|
754
|
+
|
|
755
|
+
- `pnpm check`
|
|
756
|
+
- Focused `pnpm test` commands for changed package/app tests.
|
|
757
|
+
- Full `pnpm test` before merging if multiple API groups or shared domain schemas are touched.
|
|
758
|
+
|
|
759
|
+
## Detailed implementation plan
|
|
760
|
+
|
|
761
|
+
Each task below should leave the repository typechecking and tests passing. When a domain API group is added to `RootApi`, the corresponding app handlers must be added in the same task to avoid incomplete handler-layer validation failures.
|
|
762
|
+
|
|
763
|
+
### Task 1 — Add read-policy foundations without public route expansion
|
|
764
|
+
|
|
765
|
+
Scope:
|
|
766
|
+
|
|
767
|
+
- Add read policies where currently only modify policies exist:
|
|
768
|
+
- `ProductsPolicy.canRead(productId)` if missing.
|
|
769
|
+
- `TasksPolicy.canRead(taskId)`.
|
|
770
|
+
- `GitRepositoriesPolicy.canRead(id)` if needed.
|
|
771
|
+
- `ModelProviderInstancesPolicy.canRead(id)` if needed.
|
|
772
|
+
- `AgentsPolicy.canRead/canModify(id)` if missing.
|
|
773
|
+
- `TaskSessionsPolicy.canRead(id)` for session detail routes.
|
|
774
|
+
- Keep existing modify policies and behavior unchanged.
|
|
775
|
+
- Add focused policy tests/mocks mirroring existing policy test patterns.
|
|
776
|
+
|
|
777
|
+
Validation:
|
|
778
|
+
|
|
779
|
+
- `pnpm check`
|
|
780
|
+
- Focused policy tests.
|
|
781
|
+
|
|
782
|
+
Why this is independent:
|
|
783
|
+
|
|
784
|
+
- It introduces no new public routes and should not affect frontend behavior yet.
|
|
785
|
+
- Later API tasks can rely on read policies without combining unrelated route work.
|
|
786
|
+
|
|
787
|
+
### Task 2 — Add current user and organization list endpoints
|
|
788
|
+
|
|
789
|
+
Scope:
|
|
790
|
+
|
|
791
|
+
- Extend `UsersApi`/handlers with `GET /users/me`.
|
|
792
|
+
- Extend `OrganizationsApi`/service/handlers with `GET /organizations` for current user's active memberships.
|
|
793
|
+
- Add repository/service helper to load organizations for current user if needed.
|
|
794
|
+
- Add handler tests for authenticated success and non-user/unauthorized behavior.
|
|
795
|
+
|
|
796
|
+
Validation:
|
|
797
|
+
|
|
798
|
+
- `pnpm check`
|
|
799
|
+
- Focused users/organizations API handler tests.
|
|
800
|
+
|
|
801
|
+
Why this is independent:
|
|
802
|
+
|
|
803
|
+
- These routes support app shell initialization and do not depend on task/agent APIs.
|
|
804
|
+
|
|
805
|
+
### Task 3 — Add organization assignable users endpoint
|
|
806
|
+
|
|
807
|
+
Scope:
|
|
808
|
+
|
|
809
|
+
- Add `GET /organizations/:organizationId/users` to `UsersApi` or a new organization users group, following existing route naming conventions.
|
|
810
|
+
- Implement query/service returning human organization members plus active agent backing users.
|
|
811
|
+
- Ensure deterministic sorting and deduplication.
|
|
812
|
+
- Add repository/service and handler tests covering humans, agents, soft-deleted rows, and authorization.
|
|
813
|
+
|
|
814
|
+
Validation:
|
|
815
|
+
|
|
816
|
+
- `pnpm check`
|
|
817
|
+
- Focused users/organization assignees tests.
|
|
818
|
+
|
|
819
|
+
Why this is independent:
|
|
820
|
+
|
|
821
|
+
- It exposes the already-existing `User.userType` distinction needed for assignment UI without changing task mutation behavior.
|
|
822
|
+
|
|
823
|
+
### Task 4 — Add product detail endpoint
|
|
824
|
+
|
|
825
|
+
Scope:
|
|
826
|
+
|
|
827
|
+
- Extend `ProductsApi`/service/handlers with `GET /products/:id`.
|
|
828
|
+
- Use product read policy.
|
|
829
|
+
- Add success, Not Found, and Unauthorized tests.
|
|
830
|
+
|
|
831
|
+
Validation:
|
|
832
|
+
|
|
833
|
+
- `pnpm check`
|
|
834
|
+
- Focused products API handler tests.
|
|
835
|
+
|
|
836
|
+
Why this is independent:
|
|
837
|
+
|
|
838
|
+
- Product detail is a simple read surface and does not require task/agent changes.
|
|
839
|
+
|
|
840
|
+
### Task 5 — Add task list/detail endpoints with status filtering
|
|
841
|
+
|
|
842
|
+
Scope:
|
|
843
|
+
|
|
844
|
+
- Extend `TasksApi` with:
|
|
845
|
+
- `GET /products/:productId/tasks?status=<TaskStatus>`
|
|
846
|
+
- `GET /tasks/:id`
|
|
847
|
+
- Extend `TasksRepo` list method with optional status filtering, or add a new filtered list method.
|
|
848
|
+
- Add `Tasks` service read/list methods.
|
|
849
|
+
- Use product/task read policies.
|
|
850
|
+
- Add tests for all-status list, single-status filter, invalid status decode, detail success, Not Found, soft-delete exclusion, and Unauthorized.
|
|
851
|
+
|
|
852
|
+
Validation:
|
|
853
|
+
|
|
854
|
+
- `pnpm check`
|
|
855
|
+
- Focused tasks repository and API handler tests.
|
|
856
|
+
|
|
857
|
+
Why this is independent:
|
|
858
|
+
|
|
859
|
+
- It exposes board data while leaving existing task mutations untouched.
|
|
860
|
+
|
|
861
|
+
### Task 6 — Add read-only task session endpoints
|
|
862
|
+
|
|
863
|
+
Scope:
|
|
864
|
+
|
|
865
|
+
- Create `packages/domain/src/Tasks/TaskSessionsApi.ts` and app handler module.
|
|
866
|
+
- Add to `RootApi` and `apps/products/src/Api.ts` in the same task.
|
|
867
|
+
- Add routes:
|
|
868
|
+
- `GET /tasks/:taskId/sessions`
|
|
869
|
+
- `GET /task-sessions/:id`
|
|
870
|
+
- Add `TaskSessionsRepo.forTaskId(taskId)` and harden active-row `byId` if needed.
|
|
871
|
+
- Add `TaskSessions` service if a service layer does not exist yet.
|
|
872
|
+
- Add list/detail policy use and tests.
|
|
873
|
+
|
|
874
|
+
Validation:
|
|
875
|
+
|
|
876
|
+
- `pnpm check`
|
|
877
|
+
- Focused task-session repository and API handler tests.
|
|
878
|
+
|
|
879
|
+
Why this is independent:
|
|
880
|
+
|
|
881
|
+
- It is read-only and does not require automatic session creation to exist yet, as tests can seed sessions directly.
|
|
882
|
+
|
|
883
|
+
### Task 7 — Add automatic task-session creation on qualifying agent assignment
|
|
884
|
+
|
|
885
|
+
Scope:
|
|
886
|
+
|
|
887
|
+
- Add agent-by-user lookup support in `AgentsRepo` if missing.
|
|
888
|
+
- Add active-session lookup and idempotent insert helper in `TaskSessionsRepo`.
|
|
889
|
+
- Add the partial unique active-session index migration if not already present.
|
|
890
|
+
- Update `Tasks.create` and `Tasks.update` to call a small `TaskSessionAutomation` service after persisting a task.
|
|
891
|
+
- Start a `Starting` session only for agent assignees and `Todo`/`InProgress` statuses.
|
|
892
|
+
- Keep no-op behavior for human assignees, no assignee, inactive agents, and ineligible statuses unless stricter assignment validation is implemented in the same task.
|
|
893
|
+
- Add idempotency and eligibility tests.
|
|
894
|
+
|
|
895
|
+
Validation:
|
|
896
|
+
|
|
897
|
+
- `pnpm check`
|
|
898
|
+
- Focused tasks/task-session automation tests.
|
|
899
|
+
- Migration/repository tests for the active-session uniqueness constraint.
|
|
900
|
+
|
|
901
|
+
Why this is independent:
|
|
902
|
+
|
|
903
|
+
- Read-only session APIs from Task 6 remain valid before and after this hook. This task adds the backend behavior required for sessions to appear automatically.
|
|
904
|
+
|
|
905
|
+
### Task 8 — Add git repository read/list endpoints
|
|
906
|
+
|
|
907
|
+
Scope:
|
|
908
|
+
|
|
909
|
+
- Extend `GitRepositoriesApi` with:
|
|
910
|
+
- `GET /products/:productId/git-repositories`
|
|
911
|
+
- `GET /git-repositories/:id`
|
|
912
|
+
- Add service methods and repository methods if missing.
|
|
913
|
+
- Use product/repository read policies.
|
|
914
|
+
- Add handler tests for success, soft-delete exclusion, Not Found, and Unauthorized.
|
|
915
|
+
|
|
916
|
+
Validation:
|
|
917
|
+
|
|
918
|
+
- `pnpm check`
|
|
919
|
+
- Focused git repository API tests.
|
|
920
|
+
|
|
921
|
+
Why this is independent:
|
|
922
|
+
|
|
923
|
+
- Repository selectors/settings can ship separately from task board reads.
|
|
924
|
+
|
|
925
|
+
### Task 9 — Add model provider instance read/list endpoints
|
|
926
|
+
|
|
927
|
+
Scope:
|
|
928
|
+
|
|
929
|
+
- Extend `ModelProviderInstancesApi` with:
|
|
930
|
+
- `GET /organizations/:organizationId/model-provider-instances`
|
|
931
|
+
- `GET /model-provider-instances/:id`
|
|
932
|
+
- Add service methods and repository methods if missing.
|
|
933
|
+
- Use organization/provider-instance read policies.
|
|
934
|
+
- Add tests proving token/API-key material is not returned.
|
|
935
|
+
- Decide during implementation whether optional update/delete provider-instance routes are needed for the first frontend milestone. If yes, include them in this task with focused tests; if no, document deferral in code/spec follow-up notes.
|
|
936
|
+
|
|
937
|
+
Validation:
|
|
938
|
+
|
|
939
|
+
- `pnpm check`
|
|
940
|
+
- Focused model provider instance API tests.
|
|
941
|
+
|
|
942
|
+
Why this is independent:
|
|
943
|
+
|
|
944
|
+
- Agent creation UI can use provider instance lists once this lands; it does not require agent management routes to be complete first.
|
|
945
|
+
|
|
946
|
+
### Task 10 — Add agent management API group
|
|
947
|
+
|
|
948
|
+
Scope:
|
|
949
|
+
|
|
950
|
+
- Create `packages/domain/src/Ai/AgentsApi.ts` and app handler module.
|
|
951
|
+
- Add to `RootApi` and `apps/products/src/Api.ts` in the same task.
|
|
952
|
+
- Add routes:
|
|
953
|
+
- `GET /organizations/:organizationId/agents`
|
|
954
|
+
- `GET /agents/:id`
|
|
955
|
+
- `POST /agents`
|
|
956
|
+
- `PUT /agents/:id`
|
|
957
|
+
- `DELETE /agents/:id`
|
|
958
|
+
- Reuse existing `Agents` service create/update/remove behavior where possible.
|
|
959
|
+
- Add list/detail service methods if missing.
|
|
960
|
+
- Ensure create validates referenced provider instance belongs to the same organization.
|
|
961
|
+
- Ensure update does not allow changing `organizationId`, `modelProviderInstanceId`, or `userId` unless a future spec adds move semantics.
|
|
962
|
+
- Add `AgentsPolicy` if not already present.
|
|
963
|
+
- Add handler tests for list/detail/create/update/delete, duplicate names, cross-organization provider instance references, soft-delete exclusion, and authorization.
|
|
964
|
+
|
|
965
|
+
Validation:
|
|
966
|
+
|
|
967
|
+
- `pnpm check`
|
|
968
|
+
- Focused agent repository/service/API handler tests.
|
|
969
|
+
|
|
970
|
+
Why this is independent:
|
|
971
|
+
|
|
972
|
+
- The domain/repository/service mostly exists already; this task exposes it publicly with authorization and tests.
|
|
973
|
+
|
|
974
|
+
### Task 11 — Final frontend-readiness integration pass
|
|
975
|
+
|
|
976
|
+
Scope:
|
|
977
|
+
|
|
978
|
+
- Verify `RootApi` Scalar docs include all new routes.
|
|
979
|
+
- Add or update a small API inventory document in `apps/products/src` or `.specs` if useful for frontend consumers.
|
|
980
|
+
- Run full validation for TypeScript packages.
|
|
981
|
+
- Fix route naming inconsistencies discovered across API groups.
|
|
982
|
+
- Confirm no generated docs expose secret-bearing schemas.
|
|
983
|
+
|
|
984
|
+
Validation:
|
|
985
|
+
|
|
986
|
+
- `pnpm check`
|
|
987
|
+
- Full `pnpm test` or all focused suites touched by Tasks 1–10.
|
|
988
|
+
|
|
989
|
+
Why this is independent:
|
|
990
|
+
|
|
991
|
+
- This task is documentation/validation hardening after the route clusters are already shippable.
|
|
992
|
+
|
|
993
|
+
## Open decisions for implementation
|
|
994
|
+
|
|
995
|
+
1. Whether provider-instance update/delete routes are necessary for the first frontend milestone or can be deferred.
|
|
996
|
+
2. Whether automatic task-session creation belongs in `apps/products` task service or an external worker. The public API contract assumes sessions eventually appear automatically, but v1 read APIs can ship before the hook.
|
|
997
|
+
3. Whether `GET /organizations/:organizationId/users` should live in `UsersApi` with an organization path or a dedicated future `OrganizationUsersApi`. Prefer the smallest consistent change during implementation.
|
|
998
|
+
4. Whether agent create/update should use existing `Agent.jsonCreate/jsonUpdate` directly or define narrow route-specific schemas if the generated variants expose server-managed fields.
|
|
999
|
+
`;
|
|
1000
|
+
await writeFile({path: specPath, content: spec});
|
|
1001
|
+
let readme = await readFile({path:'.specs/README.md'});
|
|
1002
|
+
const line = '- [Products Frontend API Readiness](./products-frontend-api-readiness.md) — Plan to add the missing read and management APIs needed to start a Linear-like frontend, including current-user/organization reads, status-filtered task lists, agent management, assignable users, provider/repository reads, read-only task sessions, and automatic agent-session creation semantics.\n';
|
|
1003
|
+
if (readme === null) {
|
|
1004
|
+
readme = '# Specifications\n\n' + line;
|
|
1005
|
+
} else if (!readme.includes('./products-frontend-api-readiness.md')) {
|
|
1006
|
+
const parts = readme.split('\n');
|
|
1007
|
+
const idx = parts.findIndex(l => l.trim().startsWith('- ['));
|
|
1008
|
+
if (idx === -1) {
|
|
1009
|
+
readme = readme.trimEnd() + '\n\n' + line;
|
|
1010
|
+
} else {
|
|
1011
|
+
parts.splice(idx, 0, line.trimEnd());
|
|
1012
|
+
readme = parts.join('\n');
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
await writeFile({path:'.specs/README.md', content:readme});
|
|
1016
|
+
console.log(`wrote ${specPath} and updated .specs/README.md`);
|