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
_getAdapter from import in plan-slice.ts:10
- Delete
_getAdapter from import in plan-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.
Summary
Six tool handlers import
_getAdapter()(an internal function marked with underscore convention) fromgsd-db.tsand 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 atgsd-db.ts:879with 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.tsfunctions can replace directlytools/complete-task.tsUPDATE tasks SET status = 'pending' WHERE milestone_id = :mid AND slice_id = :sid AND id = :tidupdateTaskStatus(mid, sid, tid, 'pending')— exists atgsd-db.ts:1203tools/complete-slice.tsUPDATE slices SET status = 'pending' WHERE milestone_id = :mid AND id = :sidupdateSliceStatus(mid, sid, 'pending')— exists atgsd-db.ts:1298tools/validate-milestone.tsINSERT OR REPLACE INTO assessments (path, milestone_id, slice_id, task_id, status, scope, full_content, created_at) VALUES (...)insertAssessment()— exists atgsd-db.ts:1672, exact matchCategory 2: New functions needed in
gsd-db.tstools/complete-milestone.tsUPDATE milestones SET status = 'complete', completed_at = :completed_at WHERE id = :midupdateMilestoneStatus(id: string, status: string, completedAt?: string | null): voidtools/complete-milestone.tsUPDATE milestones SET status = 'active', completed_at = NULL WHERE id = :mid('active', null)tools/complete-task.tsUPDATE tasks SET full_summary_md = :md WHERE milestone_id = :mid AND slice_id = :sid AND id = :tidsetTaskSummaryMd(mid: string, sid: string, tid: string, md: string): voidtools/complete-slice.tsUPDATE slices SET full_summary_md = :summary_md, full_uat_md = :uat_md WHERE milestone_id = :mid AND id = :sidsetSliceSummaryMd(mid: string, sid: string, summaryMd: string, uatMd: string): voidCategory 3: Dead imports (no callsite, just delete)
tools/plan-slice.tstools/plan-milestone.tsOut-of-scope:
_getAdapter()usage outsidetools/28+ callsites in non-tool files. Same pattern but different scope — tracked here for awareness, not for this issue:
memory-store.tsgsd-db.tscontext-store.tsdb-writer.tsworkflow-manifest.tsrequireDb()helper wrapping_getAdapter()workflow-projections.tsworkflow-migration.tsmd-importer.tsauto-post-unit.tstriage-resolution.tscommands-maintenance.tscommands-inspect.tsdoctor-engine-checks.tsFix plan
Step 1: Add 3 new functions to
gsd-db.tsStep 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
_getAdapterfrom import inplan-slice.ts:10_getAdapterfrom import inplan-milestone.ts:9Step 4: Remove
_getAdapterimports from all modified tool filesAfter replacing raw SQL, none of the 6 tool files need
_getAdapteranymore.