Merge pull request #2759 from igouss/fix/tool-handlers-bypass-db-port-2726
refactor(gsd): wire tool handlers through DB port layer, remove _getAdapter from all tools
This commit is contained in:
commit
473583d349
7 changed files with 57 additions and 89 deletions
|
|
@ -1308,6 +1308,20 @@ export function updateSliceStatus(milestoneId: string, sliceId: string, status:
|
|||
});
|
||||
}
|
||||
|
||||
export function setTaskSummaryMd(milestoneId: string, sliceId: string, taskId: string, md: string): void {
|
||||
if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
||||
currentDb.prepare(
|
||||
`UPDATE tasks SET full_summary_md = :md WHERE milestone_id = :mid AND slice_id = :sid AND id = :tid`,
|
||||
).run({ ":mid": milestoneId, ":sid": sliceId, ":tid": taskId, ":md": md });
|
||||
}
|
||||
|
||||
export function setSliceSummaryMd(milestoneId: string, sliceId: string, summaryMd: string, uatMd: string): void {
|
||||
if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
||||
currentDb.prepare(
|
||||
`UPDATE slices SET full_summary_md = :summary_md, full_uat_md = :uat_md WHERE milestone_id = :mid AND id = :sid`,
|
||||
).run({ ":mid": milestoneId, ":sid": sliceId, ":summary_md": summaryMd, ":uat_md": uatMd });
|
||||
}
|
||||
|
||||
export interface TaskRow {
|
||||
milestone_id: string;
|
||||
slice_id: string;
|
||||
|
|
@ -1490,11 +1504,11 @@ export function getMilestone(id: string): MilestoneRow | null {
|
|||
* Used by park/unpark to keep the DB in sync with the filesystem marker.
|
||||
* See: https://github.com/gsd-build/gsd-2/issues/2694
|
||||
*/
|
||||
export function updateMilestoneStatus(milestoneId: string, status: string): void {
|
||||
export function updateMilestoneStatus(milestoneId: string, status: string, completedAt?: string | null): void {
|
||||
if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
||||
currentDb.prepare(
|
||||
`UPDATE milestones SET status = :status WHERE id = :id`,
|
||||
).run({ ":status": status, ":id": milestoneId });
|
||||
`UPDATE milestones SET status = :status, completed_at = :completed_at WHERE id = :id`,
|
||||
).run({ ":status": status, ":completed_at": completedAt ?? null, ":id": milestoneId });
|
||||
}
|
||||
|
||||
export function getActiveMilestoneFromDb(): MilestoneRow | null {
|
||||
|
|
@ -1706,6 +1720,20 @@ export function insertAssessment(entry: {
|
|||
});
|
||||
}
|
||||
|
||||
export function deleteAssessmentByScope(milestoneId: string, scope: string): void {
|
||||
if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
||||
currentDb.prepare(
|
||||
`DELETE FROM assessments WHERE milestone_id = :mid AND scope = :scope`,
|
||||
).run({ ":mid": milestoneId, ":scope": scope });
|
||||
}
|
||||
|
||||
export function deleteVerificationEvidence(milestoneId: string, sliceId: string, taskId: string): void {
|
||||
if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
||||
currentDb.prepare(
|
||||
`DELETE FROM verification_evidence WHERE milestone_id = :mid AND slice_id = :sid AND task_id = :tid`,
|
||||
).run({ ":mid": milestoneId, ":sid": sliceId, ":tid": taskId });
|
||||
}
|
||||
|
||||
export function deleteTask(milestoneId: string, sliceId: string, taskId: string): void {
|
||||
if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
||||
// Must delete verification_evidence first (FK constraint)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import {
|
|||
getMilestone,
|
||||
getMilestoneSlices,
|
||||
getSliceTasks,
|
||||
_getAdapter,
|
||||
updateMilestoneStatus,
|
||||
} from "../gsd-db.js";
|
||||
import { resolveMilestonePath, clearPathCache } from "../paths.js";
|
||||
import { saveFile, clearParseCache } from "../files.js";
|
||||
|
|
@ -165,13 +165,7 @@ export async function handleCompleteMilestone(
|
|||
}
|
||||
|
||||
// All guards passed — perform write
|
||||
const adapter = _getAdapter()!;
|
||||
adapter.prepare(
|
||||
`UPDATE milestones SET status = 'complete', completed_at = :completed_at WHERE id = :mid`,
|
||||
).run({
|
||||
":completed_at": completedAt,
|
||||
":mid": params.milestoneId,
|
||||
});
|
||||
updateMilestoneStatus(params.milestoneId, 'complete', completedAt);
|
||||
});
|
||||
|
||||
if (guardError) {
|
||||
|
|
@ -199,12 +193,7 @@ export async function handleCompleteMilestone(
|
|||
process.stderr.write(
|
||||
`gsd-db: complete_milestone — disk render failed, rolling back DB status: ${(renderErr as Error).message}\n`,
|
||||
);
|
||||
const rollbackAdapter = _getAdapter();
|
||||
if (rollbackAdapter) {
|
||||
rollbackAdapter.prepare(
|
||||
`UPDATE milestones SET status = 'active', completed_at = NULL WHERE id = :mid`,
|
||||
).run({ ":mid": params.milestoneId });
|
||||
}
|
||||
updateMilestoneStatus(params.milestoneId, 'active', null);
|
||||
invalidateStateCache();
|
||||
return { error: `disk render failed: ${(renderErr as Error).message}` };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import {
|
|||
getSliceTasks,
|
||||
getMilestone,
|
||||
updateSliceStatus,
|
||||
_getAdapter,
|
||||
setSliceSummaryMd,
|
||||
} from "../gsd-db.js";
|
||||
import { resolveSliceFile, resolveSlicePath, clearPathCache } from "../paths.js";
|
||||
import { checkOwnership, sliceUnitKey } from "../unit-ownership.js";
|
||||
|
|
@ -299,31 +299,13 @@ export async function handleCompleteSlice(
|
|||
process.stderr.write(
|
||||
`gsd-db: complete_slice — disk render failed, rolling back DB status: ${(renderErr as Error).message}\n`,
|
||||
);
|
||||
const rollbackAdapter = _getAdapter();
|
||||
if (rollbackAdapter) {
|
||||
rollbackAdapter.prepare(
|
||||
`UPDATE slices SET status = 'pending' WHERE milestone_id = :mid AND id = :sid`,
|
||||
).run({
|
||||
":mid": params.milestoneId,
|
||||
":sid": params.sliceId,
|
||||
});
|
||||
}
|
||||
updateSliceStatus(params.milestoneId, params.sliceId, 'pending');
|
||||
invalidateStateCache();
|
||||
return { error: `disk render failed: ${(renderErr as Error).message}` };
|
||||
}
|
||||
|
||||
// Store rendered markdown in DB for D004 recovery
|
||||
const adapter = _getAdapter();
|
||||
if (adapter) {
|
||||
adapter.prepare(
|
||||
`UPDATE slices SET full_summary_md = :summary_md, full_uat_md = :uat_md WHERE milestone_id = :mid AND id = :sid`,
|
||||
).run({
|
||||
":summary_md": summaryMd,
|
||||
":uat_md": uatMd,
|
||||
":mid": params.milestoneId,
|
||||
":sid": params.sliceId,
|
||||
});
|
||||
}
|
||||
setSliceSummaryMd(params.milestoneId, params.sliceId, summaryMd, uatMd);
|
||||
|
||||
// Invalidate all caches
|
||||
invalidateStateCache();
|
||||
|
|
|
|||
|
|
@ -20,7 +20,9 @@ import {
|
|||
getMilestone,
|
||||
getSlice,
|
||||
getTask,
|
||||
_getAdapter,
|
||||
updateTaskStatus,
|
||||
setTaskSummaryMd,
|
||||
deleteVerificationEvidence,
|
||||
} from "../gsd-db.js";
|
||||
import { resolveSliceFile, resolveTasksDir, clearPathCache } from "../paths.js";
|
||||
import { checkOwnership, taskUnitKey } from "../unit-ownership.js";
|
||||
|
|
@ -248,42 +250,17 @@ export async function handleCompleteTask(
|
|||
process.stderr.write(
|
||||
`gsd-db: complete_task — disk render failed, rolling back DB status: ${(renderErr as Error).message}\n`,
|
||||
);
|
||||
const rollbackAdapter = _getAdapter();
|
||||
if (rollbackAdapter) {
|
||||
// Delete orphaned verification_evidence rows first (FK constraint
|
||||
// references tasks, so evidence must go before status change).
|
||||
// Without this, retries accumulate duplicate evidence rows (#2724).
|
||||
rollbackAdapter.prepare(
|
||||
`DELETE FROM verification_evidence WHERE milestone_id = :mid AND slice_id = :sid AND task_id = :tid`,
|
||||
).run({
|
||||
":mid": params.milestoneId,
|
||||
":sid": params.sliceId,
|
||||
":tid": params.taskId,
|
||||
});
|
||||
rollbackAdapter.prepare(
|
||||
`UPDATE tasks SET status = 'pending' WHERE milestone_id = :mid AND slice_id = :sid AND id = :tid`,
|
||||
).run({
|
||||
":mid": params.milestoneId,
|
||||
":sid": params.sliceId,
|
||||
":tid": params.taskId,
|
||||
});
|
||||
}
|
||||
// Delete orphaned verification_evidence rows first (FK constraint
|
||||
// references tasks, so evidence must go before status change).
|
||||
// Without this, retries accumulate duplicate evidence rows (#2724).
|
||||
deleteVerificationEvidence(params.milestoneId, params.sliceId, params.taskId);
|
||||
updateTaskStatus(params.milestoneId, params.sliceId, params.taskId, 'pending');
|
||||
invalidateStateCache();
|
||||
return { error: `disk render failed: ${(renderErr as Error).message}` };
|
||||
}
|
||||
|
||||
// Store rendered markdown in DB for D004 recovery
|
||||
const adapter = _getAdapter();
|
||||
if (adapter) {
|
||||
adapter.prepare(
|
||||
`UPDATE tasks SET full_summary_md = :md WHERE milestone_id = :mid AND slice_id = :sid AND id = :tid`,
|
||||
).run({
|
||||
":md": summaryMd,
|
||||
":mid": params.milestoneId,
|
||||
":sid": params.sliceId,
|
||||
":tid": params.taskId,
|
||||
});
|
||||
}
|
||||
setTaskSummaryMd(params.milestoneId, params.sliceId, params.taskId, summaryMd);
|
||||
|
||||
// Invalidate all caches
|
||||
invalidateStateCache();
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import {
|
|||
insertSlice,
|
||||
upsertMilestonePlanning,
|
||||
upsertSlicePlanning,
|
||||
_getAdapter,
|
||||
} from "../gsd-db.js";
|
||||
import { invalidateStateCache } from "../state.js";
|
||||
import { renderRoadmapFromDb } from "../markdown-renderer.js";
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import {
|
|||
upsertSlicePlanning,
|
||||
upsertTaskPlanning,
|
||||
insertGateRow,
|
||||
_getAdapter,
|
||||
} from "../gsd-db.js";
|
||||
import type { GateId } from "../types.js";
|
||||
import { invalidateStateCache } from "../state.js";
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ import { join } from "node:path";
|
|||
|
||||
import {
|
||||
transaction,
|
||||
_getAdapter,
|
||||
insertAssessment,
|
||||
deleteAssessmentByScope,
|
||||
} from "../gsd-db.js";
|
||||
import { resolveMilestonePath, clearPathCache } from "../paths.js";
|
||||
import { saveFile, clearParseCache } from "../files.js";
|
||||
|
|
@ -97,16 +98,14 @@ export async function handleValidateMilestone(
|
|||
const validatedAt = new Date().toISOString();
|
||||
|
||||
transaction(() => {
|
||||
const adapter = _getAdapter()!;
|
||||
adapter.prepare(
|
||||
`INSERT OR REPLACE INTO assessments (path, milestone_id, slice_id, task_id, status, scope, full_content, created_at)
|
||||
VALUES (:path, :mid, NULL, NULL, :verdict, 'milestone-validation', :content, :created_at)`,
|
||||
).run({
|
||||
":path": validationPath,
|
||||
":mid": params.milestoneId,
|
||||
":verdict": params.verdict,
|
||||
":content": validationMd,
|
||||
":created_at": validatedAt,
|
||||
insertAssessment({
|
||||
path: validationPath,
|
||||
milestoneId: params.milestoneId,
|
||||
sliceId: null,
|
||||
taskId: null,
|
||||
status: params.verdict,
|
||||
scope: 'milestone-validation',
|
||||
fullContent: validationMd,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -118,12 +117,7 @@ export async function handleValidateMilestone(
|
|||
process.stderr.write(
|
||||
`gsd-db: validate_milestone — disk render failed, rolling back DB row: ${(renderErr as Error).message}\n`,
|
||||
);
|
||||
const rollbackAdapter = _getAdapter();
|
||||
if (rollbackAdapter) {
|
||||
rollbackAdapter.prepare(
|
||||
`DELETE FROM assessments WHERE milestone_id = :mid AND scope = 'milestone-validation'`,
|
||||
).run({ ":mid": params.milestoneId });
|
||||
}
|
||||
deleteAssessmentByScope(params.milestoneId, 'milestone-validation');
|
||||
return { error: `disk render failed: ${(renderErr as Error).message}` };
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue