mirror of
https://github.com/BradGroux/veritas-kanban.git
synced 2026-10-10 05:37:53 +00:00
fix: preserve template task fields on creation (#1431)
This commit is contained in:
parent
d3d8896b37
commit
a345649e33
3 changed files with 73 additions and 9 deletions
11
docs/audits/TASK-TEMPLATE-FIELDS-1430.md
Normal file
11
docs/audits/TASK-TEMPLATE-FIELDS-1430.md
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# Template task fields at creation
|
||||
|
||||
Issue #1430 was found during packaged macOS Create Task acceptance. A two-task blueprint previewed its subtasks and dependency correctly, but POST `/api/tasks` silently discarded both fields. The shared creation interface and task service already supported them; the route schema omitted them and stripped them during validation.
|
||||
|
||||
Creation now accepts optional `subtasks` and `blockedBy` using the same validation contracts as updates. The unchanged subtask schema is shared by POST and PATCH. There is no storage migration, priority change, or UI change in this slice.
|
||||
|
||||
Four new route regressions failed before the fix: valid fields never reached the service, and malformed subtasks, acceptance criteria, and dependency IDs were accepted instead of rejected. After the fix, all 60 tests in the combined candidate's exact task-route coverage file passed. Shared build, source server typecheck, changed-file formatting, and lint passed; lint retained five existing warnings. Separate specification and standards reviews found no actionable issues.
|
||||
|
||||
The unsigned packaged macOS candidate was rebuilt with this fix and the separate Critical-priority change in #1429. At the native minimum 1180×760, the light and dark checks selected a synthetic two-task blueprint, entered a custom variable, created real tasks through the API, and verified interpolated subtask titles, acceptance criteria, and the second task's dependency on the first task's actual ID. Reloading the app and inspecting its authenticated task-list response confirmed those same values persisted. Only template discovery was supplied by a test fixture; task creation and persistence were real in an isolated temporary application profile. An initial extra readback request lacked app authentication; the harness was corrected to observe the application's own reload response.
|
||||
|
||||
This native check is combined-candidate evidence, not standalone release or installed-app acceptance. The installed application and user data were unchanged. Full CI for the new combined revision, remaining UI consumers, maintained documentation media, and signed release acceptance remain separate work.
|
||||
|
|
@ -470,6 +470,57 @@ describe('Tasks Routes (actual module)', () => {
|
|||
});
|
||||
|
||||
describe('POST /api/tasks', () => {
|
||||
it('preserves template subtasks and blueprint dependencies on creation', async () => {
|
||||
const subtasks = [
|
||||
{
|
||||
id: 'subtask-1',
|
||||
title: 'Verify release',
|
||||
completed: false,
|
||||
created: '2026-09-01T12:00:00Z',
|
||||
acceptanceCriteria: ['Evidence recorded'],
|
||||
criteriaChecked: [false],
|
||||
},
|
||||
];
|
||||
const blockedBy = ['prerequisite-task'];
|
||||
mockTaskService.createTask.mockImplementation(async (input) => ({
|
||||
id: 'blueprint-task',
|
||||
...input,
|
||||
}));
|
||||
const res = await request(app).post('/api/tasks').send({
|
||||
title: 'Blueprint follow-up',
|
||||
subtasks,
|
||||
blockedBy,
|
||||
});
|
||||
expect(res.status).toBe(201);
|
||||
expect(mockTaskService.createTask).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ subtasks, blockedBy })
|
||||
);
|
||||
expect(res.body.subtasks).toEqual(subtasks);
|
||||
expect(res.body.blockedBy).toEqual(blockedBy);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ subtasks: [{ id: 'bad', title: 'Missing required fields' }] },
|
||||
{
|
||||
subtasks: [
|
||||
{
|
||||
id: 'bad',
|
||||
title: 'Bad criteria',
|
||||
completed: false,
|
||||
created: '2026-09-01',
|
||||
acceptanceCriteria: [42],
|
||||
},
|
||||
],
|
||||
},
|
||||
{ blockedBy: [42] },
|
||||
])('rejects malformed template task fields: %j', async (fields) => {
|
||||
const res = await request(app)
|
||||
.post('/api/tasks')
|
||||
.send({ title: 'Invalid blueprint', ...fields });
|
||||
expect(res.status).toBe(400);
|
||||
expect(mockTaskService.createTask).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('accepts critical priority on creation', async () => {
|
||||
mockTaskService.createTask.mockImplementation(async (input) => ({
|
||||
id: 'critical-task',
|
||||
|
|
|
|||
|
|
@ -190,6 +190,15 @@ const reviewCommentSchema = z.object({
|
|||
|
||||
export const reviewScoresSchema = z.array(z.number().int().min(0).max(10)).length(4);
|
||||
|
||||
const subtaskSchema = z.object({
|
||||
id: z.string(),
|
||||
title: z.string(),
|
||||
completed: z.boolean(),
|
||||
created: z.string(),
|
||||
acceptanceCriteria: z.array(z.string()).optional(),
|
||||
criteriaChecked: z.array(z.boolean()).optional(),
|
||||
});
|
||||
|
||||
const createTaskSchema = z.object({
|
||||
title: z.string().min(1).max(200),
|
||||
description: z.string().optional().default(''),
|
||||
|
|
@ -202,6 +211,8 @@ const createTaskSchema = z.object({
|
|||
executionPolicy: TaskExecutionPolicySchema.optional(),
|
||||
reviewScores: reviewScoresSchema.optional(),
|
||||
reviewComments: z.array(reviewCommentSchema).optional(),
|
||||
subtasks: z.array(subtaskSchema).optional(),
|
||||
blockedBy: z.array(z.string()).optional(),
|
||||
});
|
||||
|
||||
const gitSchema = z
|
||||
|
|
@ -332,15 +343,6 @@ const reviewStateSchema = z.object({
|
|||
summary: z.string().optional(),
|
||||
});
|
||||
|
||||
const subtaskSchema = z.object({
|
||||
id: z.string(),
|
||||
title: z.string(),
|
||||
completed: z.boolean(),
|
||||
created: z.string(),
|
||||
acceptanceCriteria: z.array(z.string()).optional(),
|
||||
criteriaChecked: z.array(z.boolean()).optional(),
|
||||
});
|
||||
|
||||
const githubSchema = z
|
||||
.object({
|
||||
issueNumber: z.number().int().positive(),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue