-
Notifications
You must be signed in to change notification settings - Fork 0
Tool handlers bypass DB port layer via _getAdapter() raw SQL #14
Description
Summary
Six tool handlers import _getAdapter() (an internal function marked with underscore convention) from gsd-db.ts and either issue raw SQL directly or carry a dead import. This bypasses the DB port layer that is the foundation of the single-writer architecture.
Impact
Tool handlers are Boundary adapters (hexagonal architecture). They should call named port functions on gsd-db.ts, not reach through to the SQLite adapter. Any schema change, column rename, or DB migration now has to audit raw SQL scattered across tool handlers in addition to the DB module.
_getAdapter() is exported as a named export at gsd-db.ts:879 with an underscore prefix signaling internal use — but TypeScript has no real access control on named exports, so any file can grab it.
All callsites (exhaustive)
Category 1: Existing gsd-db.ts functions can replace directly
| File | Line | Raw SQL | Replacement |
|---|---|---|---|
tools/complete-task.ts |
251-259 | UPDATE tasks SET status = 'pending' WHERE milestone_id = :mid AND slice_id = :sid AND id = :tid |
updateTaskStatus(mid, sid, tid, 'pending') — exists at gsd-db.ts:1203 |
tools/complete-slice.ts |
302-310 | UPDATE slices SET status = 'pending' WHERE milestone_id = :mid AND id = :sid |
updateSliceStatus(mid, sid, 'pending') — exists at gsd-db.ts:1298 |
tools/validate-milestone.ts |
105-115 | INSERT OR REPLACE INTO assessments (path, milestone_id, slice_id, task_id, status, scope, full_content, created_at) VALUES (...) |
insertAssessment() — exists at gsd-db.ts:1672, exact match |
Category 2: New functions needed in gsd-db.ts
| File | Line | Raw SQL | New function |
|---|---|---|---|
tools/complete-milestone.ts |
168-174 | UPDATE milestones SET status = 'complete', completed_at = :completed_at WHERE id = :mid |
updateMilestoneStatus(id: string, status: string, completedAt?: string | null): void |
tools/complete-milestone.ts |
202-206 | UPDATE milestones SET status = 'active', completed_at = NULL WHERE id = :mid |
Same function as above with ('active', null) |
tools/complete-task.ts |
266-276 | UPDATE tasks SET full_summary_md = :md WHERE milestone_id = :mid AND slice_id = :sid AND id = :tid |
setTaskSummaryMd(mid: string, sid: string, tid: string, md: string): void |
tools/complete-slice.ts |
316-325 | UPDATE slices SET full_summary_md = :summary_md, full_uat_md = :uat_md WHERE milestone_id = :mid AND id = :sid |
setSliceSummaryMd(mid: string, sid: string, summaryMd: string, uatMd: string): void |
Category 3: Dead imports (no callsite, just delete)
| File | Line |
|---|---|
tools/plan-slice.ts |
10 |
tools/plan-milestone.ts |
9 |
Out-of-scope: _getAdapter() usage outside tools/
28+ callsites in non-tool files. Same pattern but different scope — tracked here for awareness, not for this issue:
| File | Callsites | Nature |
|---|---|---|
memory-store.ts |
11 | Entire module is a DB abstraction — should be promoted into gsd-db.ts |
context-store.ts |
3 | Same pattern as memory-store |
db-writer.ts |
3 | Lower-level module |
workflow-manifest.ts |
1 | requireDb() helper wrapping _getAdapter() |
workflow-projections.ts |
1 | Live-DB probe |
workflow-migration.ts |
3 | Migration utility |
md-importer.ts |
2 | Import + nullcheck |
auto-post-unit.ts |
1 | Rogue detection query |
triage-resolution.ts |
1 | |
commands-maintenance.ts |
1 | Bulk DELETE during recover |
commands-inspect.ts |
1 | Admin inspection |
doctor-engine-checks.ts |
1 | Integrity checks |
Fix plan
Step 1: Add 3 new functions to gsd-db.ts
export function updateMilestoneStatus(id: string, status: string, completedAt?: string | null): void {
const adapter = _getAdapter();
if (!adapter) throw new GsdStaleStateError('updateMilestoneStatus');
adapter.prepare('UPDATE milestones SET status = :status, completed_at = :completedAt WHERE id = :id')
.run({ id, status, completedAt: completedAt ?? null });
}
export function setTaskSummaryMd(milestoneId: string, sliceId: string, taskId: string, md: string): void {
const adapter = _getAdapter();
if (!adapter) throw new GsdStaleStateError('setTaskSummaryMd');
adapter.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 });
}
export function setSliceSummaryMd(milestoneId: string, sliceId: string, summaryMd: string, uatMd: string): void {
const adapter = _getAdapter();
if (!adapter) throw new GsdStaleStateError('setSliceSummaryMd');
adapter.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 });
}Step 2: Replace raw SQL in tool handlers
complete-task.ts:251→updateTaskStatus(mid, sid, tid, 'pending')complete-task.ts:266→setTaskSummaryMd(mid, sid, tid, summaryMd)complete-slice.ts:302→updateSliceStatus(mid, sid, 'pending')complete-slice.ts:316→setSliceSummaryMd(mid, sid, summaryMd, uatMd)complete-milestone.ts:168→updateMilestoneStatus(mid, 'complete', completedAt)complete-milestone.ts:202→updateMilestoneStatus(mid, 'active', null)validate-milestone.ts:105→insertAssessment({ ... })
Step 3: Remove dead imports
- Delete
_getAdapterfrom import inplan-slice.ts:10 - Delete
_getAdapterfrom import inplan-milestone.ts:9
Step 4: Remove _getAdapter imports from all modified tool files
After replacing raw SQL, none of the 6 tool files need _getAdapter anymore.