gsd-pi 2.74.0-dev.2b524c3 → 2.74.0-dev.b741afb
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/cli.js +85 -0
- package/dist/headless-query.js +4 -1
- package/dist/help-text.js +23 -0
- package/dist/resources/extensions/gsd/auto/detect-stuck.js +11 -4
- package/dist/resources/extensions/gsd/auto/phases.js +45 -1
- package/dist/resources/extensions/gsd/auto-post-unit.js +52 -56
- package/dist/resources/extensions/gsd/auto-prompts.js +12 -0
- package/dist/resources/extensions/gsd/auto.js +8 -2
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +21 -8
- package/dist/resources/extensions/gsd/commands/catalog.js +26 -1
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +20 -0
- package/dist/resources/extensions/gsd/commands/handlers/workflow.js +68 -9
- package/dist/resources/extensions/gsd/commands-add-tests.js +111 -0
- package/dist/resources/extensions/gsd/commands-backlog.js +140 -0
- package/dist/resources/extensions/gsd/commands-do.js +79 -0
- package/dist/resources/extensions/gsd/commands-maintenance.js +6 -6
- package/dist/resources/extensions/gsd/commands-pr-branch.js +180 -0
- package/dist/resources/extensions/gsd/commands-session-report.js +82 -0
- package/dist/resources/extensions/gsd/commands-ship.js +187 -0
- package/dist/resources/extensions/gsd/db-writer.js +3 -5
- package/dist/resources/extensions/gsd/graph-context.js +66 -0
- package/dist/resources/extensions/gsd/gsd-db.js +321 -0
- package/dist/resources/extensions/gsd/index.js +15 -2
- package/dist/resources/extensions/gsd/md-importer.js +3 -4
- package/dist/resources/extensions/gsd/memory-store.js +19 -51
- package/dist/resources/extensions/gsd/milestone-validation-gates.js +13 -12
- package/dist/resources/extensions/gsd/native-git-bridge.js +7 -4
- package/dist/resources/extensions/gsd/prompts/add-tests.md +35 -0
- package/dist/resources/extensions/gsd/state.js +5 -1
- package/dist/resources/extensions/gsd/tools/complete-slice.js +15 -0
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +3 -14
- package/dist/resources/extensions/gsd/triage-resolution.js +2 -5
- package/dist/resources/extensions/gsd/workflow-manifest.js +8 -69
- package/dist/resources/extensions/gsd/workflow-migration.js +21 -22
- package/dist/resources/extensions/gsd/workflow-projections.js +4 -1
- package/dist/resources/extensions/gsd/workflow-reconcile.js +14 -11
- package/dist/tsconfig.extensions.tsbuildinfo +1 -0
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +7 -7
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +7 -7
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/package.json +3 -2
- package/packages/daemon/package.json +2 -2
- package/packages/mcp-server/dist/index.d.ts +3 -0
- package/packages/mcp-server/dist/index.d.ts.map +1 -1
- package/packages/mcp-server/dist/index.js +3 -0
- package/packages/mcp-server/dist/index.js.map +1 -1
- package/packages/mcp-server/dist/readers/graph.d.ts +87 -0
- package/packages/mcp-server/dist/readers/graph.d.ts.map +1 -0
- package/packages/mcp-server/dist/readers/graph.js +548 -0
- package/packages/mcp-server/dist/readers/graph.js.map +1 -0
- package/packages/mcp-server/dist/readers/index.d.ts +2 -0
- package/packages/mcp-server/dist/readers/index.d.ts.map +1 -1
- package/packages/mcp-server/dist/readers/index.js +1 -0
- package/packages/mcp-server/dist/readers/index.js.map +1 -1
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +65 -0
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/package.json +2 -2
- package/packages/mcp-server/src/index.ts +15 -0
- package/packages/mcp-server/src/readers/graph.test.ts +426 -0
- package/packages/mcp-server/src/readers/graph.ts +708 -0
- package/packages/mcp-server/src/readers/index.ts +12 -0
- package/packages/mcp-server/src/server.ts +83 -0
- package/packages/mcp-server/tsconfig.json +1 -0
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -0
- package/packages/native/package.json +2 -2
- package/packages/native/tsconfig.tsbuildinfo +1 -0
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-agent-core/tsconfig.json +1 -0
- package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -0
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-ai/tsconfig.json +1 -0
- package/packages/pi-ai/tsconfig.tsbuildinfo +1 -0
- package/packages/pi-coding-agent/tsconfig.json +1 -0
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -0
- package/packages/pi-tui/package.json +1 -1
- package/packages/pi-tui/tsconfig.json +1 -0
- package/packages/pi-tui/tsconfig.tsbuildinfo +1 -0
- package/packages/rpc-client/package.json +1 -1
- package/packages/rpc-client/tsconfig.json +1 -0
- package/packages/rpc-client/tsconfig.tsbuildinfo +1 -0
- package/src/resources/extensions/gsd/auto/detect-stuck.ts +12 -4
- package/src/resources/extensions/gsd/auto/loop-deps.ts +6 -0
- package/src/resources/extensions/gsd/auto/phases.ts +68 -1
- package/src/resources/extensions/gsd/auto-post-unit.ts +60 -57
- package/src/resources/extensions/gsd/auto-prompts.ts +13 -0
- package/src/resources/extensions/gsd/auto.ts +7 -0
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +24 -8
- package/src/resources/extensions/gsd/commands/catalog.ts +26 -1
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +20 -0
- package/src/resources/extensions/gsd/commands/handlers/workflow.ts +74 -9
- package/src/resources/extensions/gsd/commands-add-tests.ts +137 -0
- package/src/resources/extensions/gsd/commands-backlog.ts +182 -0
- package/src/resources/extensions/gsd/commands-do.ts +109 -0
- package/src/resources/extensions/gsd/commands-maintenance.ts +6 -6
- package/src/resources/extensions/gsd/commands-pr-branch.ts +234 -0
- package/src/resources/extensions/gsd/commands-session-report.ts +101 -0
- package/src/resources/extensions/gsd/commands-ship.ts +219 -0
- package/src/resources/extensions/gsd/db-writer.ts +3 -5
- package/src/resources/extensions/gsd/graph-context.ts +85 -0
- package/src/resources/extensions/gsd/gsd-db.ts +467 -0
- package/src/resources/extensions/gsd/index.ts +18 -2
- package/src/resources/extensions/gsd/md-importer.ts +3 -5
- package/src/resources/extensions/gsd/memory-store.ts +31 -62
- package/src/resources/extensions/gsd/milestone-validation-gates.ts +13 -14
- package/src/resources/extensions/gsd/native-git-bridge.ts +11 -12
- package/src/resources/extensions/gsd/prompts/add-tests.md +35 -0
- package/src/resources/extensions/gsd/state.ts +9 -2
- package/src/resources/extensions/gsd/tests/commands-backlog.test.ts +158 -0
- package/src/resources/extensions/gsd/tests/commands-do.test.ts +127 -0
- package/src/resources/extensions/gsd/tests/commands-pr-branch.test.ts +68 -0
- package/src/resources/extensions/gsd/tests/commands-session-report.test.ts +82 -0
- package/src/resources/extensions/gsd/tests/commands-ship.test.ts +71 -0
- package/src/resources/extensions/gsd/tests/commands-workflow-custom.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +154 -0
- package/src/resources/extensions/gsd/tests/graph-context.test.ts +337 -0
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +68 -1
- package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +140 -0
- package/src/resources/extensions/gsd/tests/single-writer-invariant.test.ts +180 -0
- package/src/resources/extensions/gsd/tests/workflow-logger-wiring.test.ts +223 -0
- package/src/resources/extensions/gsd/tools/complete-slice.ts +19 -0
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +3 -11
- package/src/resources/extensions/gsd/triage-resolution.ts +2 -7
- package/src/resources/extensions/gsd/workflow-manifest.ts +9 -104
- package/src/resources/extensions/gsd/workflow-migration.ts +21 -29
- package/src/resources/extensions/gsd/workflow-projections.ts +8 -1
- package/src/resources/extensions/gsd/workflow-reconcile.ts +15 -15
- /package/dist/web/standalone/.next/static/{YzIEI9sxJy4t5xgClF08g → XnHY5eXUsTCFmNodWHetD}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{YzIEI9sxJy4t5xgClF08g → XnHY5eXUsTCFmNodWHetD}/_ssgManifest.js +0 -0
|
@@ -4,6 +4,21 @@
|
|
|
4
4
|
//
|
|
5
5
|
// Exposes a unified sync API for decisions and requirements storage.
|
|
6
6
|
// Schema is initialized on first open with WAL mode for file-backed DBs.
|
|
7
|
+
//
|
|
8
|
+
// ─── Single-writer invariant ─────────────────────────────────────────────
|
|
9
|
+
// This file is the ONLY place in the codebase that issues write SQL
|
|
10
|
+
// (INSERT / UPDATE / DELETE / REPLACE / BEGIN-COMMIT transactions) against
|
|
11
|
+
// the engine database at `.gsd/gsd.db`. All other modules must call the
|
|
12
|
+
// typed wrappers exported here. The structural test
|
|
13
|
+
// `tests/single-writer-invariant.test.ts` fails CI if a new bypass appears.
|
|
14
|
+
//
|
|
15
|
+
// `_getAdapter()` is retained for read-only SELECTs in query modules
|
|
16
|
+
// (context-store, memory-store queries, doctor checks, projections).
|
|
17
|
+
// Do NOT use it for writes — add a wrapper here instead.
|
|
18
|
+
//
|
|
19
|
+
// The separate `.gsd/unit-claims.db` managed by `unit-ownership.ts` is an
|
|
20
|
+
// intentionally independent store for cross-worktree claim races and is
|
|
21
|
+
// excluded from this invariant.
|
|
7
22
|
import { createRequire } from "node:module";
|
|
8
23
|
import { existsSync, copyFileSync, mkdirSync, realpathSync } from "node:fs";
|
|
9
24
|
import { dirname } from "node:path";
|
|
@@ -863,6 +878,50 @@ export function transaction(fn) {
|
|
|
863
878
|
_txDepth--;
|
|
864
879
|
}
|
|
865
880
|
}
|
|
881
|
+
/**
|
|
882
|
+
* Wrap a block of reads in a DEFERRED transaction so that all SELECTs observe
|
|
883
|
+
* a consistent snapshot of the DB even if a concurrent writer commits between
|
|
884
|
+
* them. Use this for multi-query read flows (e.g. tool executors that query
|
|
885
|
+
* milestone + slices + counts and want one snapshot). Re-entrant — if already
|
|
886
|
+
* inside a transaction, runs fn() without starting a nested one.
|
|
887
|
+
*/
|
|
888
|
+
export function readTransaction(fn) {
|
|
889
|
+
if (!currentDb)
|
|
890
|
+
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
891
|
+
if (_txDepth > 0) {
|
|
892
|
+
_txDepth++;
|
|
893
|
+
try {
|
|
894
|
+
return fn();
|
|
895
|
+
}
|
|
896
|
+
finally {
|
|
897
|
+
_txDepth--;
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
_txDepth++;
|
|
901
|
+
currentDb.exec("BEGIN DEFERRED");
|
|
902
|
+
try {
|
|
903
|
+
const result = fn();
|
|
904
|
+
currentDb.exec("COMMIT");
|
|
905
|
+
return result;
|
|
906
|
+
}
|
|
907
|
+
catch (err) {
|
|
908
|
+
try {
|
|
909
|
+
currentDb.exec("ROLLBACK");
|
|
910
|
+
}
|
|
911
|
+
catch (rollbackErr) {
|
|
912
|
+
// A failed ROLLBACK after a failed read is a split-brain signal —
|
|
913
|
+
// the transaction is in an indeterminate state. Surface it via the
|
|
914
|
+
// logger instead of swallowing it.
|
|
915
|
+
logError("db", "snapshotState ROLLBACK failed", {
|
|
916
|
+
error: rollbackErr.message,
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
throw err;
|
|
920
|
+
}
|
|
921
|
+
finally {
|
|
922
|
+
_txDepth--;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
866
925
|
export function insertDecision(d) {
|
|
867
926
|
if (!currentDb)
|
|
868
927
|
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
@@ -2045,3 +2104,265 @@ export function getPendingGatesForTurn(milestoneId, sliceId, turn, taskId) {
|
|
|
2045
2104
|
export function getPendingGateCountForTurn(milestoneId, sliceId, turn) {
|
|
2046
2105
|
return getPendingGatesForTurn(milestoneId, sliceId, turn).length;
|
|
2047
2106
|
}
|
|
2107
|
+
// ─── Single-writer bypass wrappers ───────────────────────────────────────
|
|
2108
|
+
// These wrappers exist so modules outside this file never need to call
|
|
2109
|
+
// `_getAdapter()` for writes. Each one is a byte-equivalent replacement for
|
|
2110
|
+
// a raw prepare/run previously issued from another module. Keep them
|
|
2111
|
+
// minimal and direct — they exist to hold SQL text in one place, not to
|
|
2112
|
+
// add new behavior.
|
|
2113
|
+
/** Delete a decision row by id. Used by db-writer.ts rollback on disk-write failure. */
|
|
2114
|
+
export function deleteDecisionById(id) {
|
|
2115
|
+
if (!currentDb)
|
|
2116
|
+
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
2117
|
+
currentDb.prepare("DELETE FROM decisions WHERE id = :id").run({ ":id": id });
|
|
2118
|
+
}
|
|
2119
|
+
/** Delete a requirement row by id. Used by db-writer.ts rollback on disk-write failure. */
|
|
2120
|
+
export function deleteRequirementById(id) {
|
|
2121
|
+
if (!currentDb)
|
|
2122
|
+
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
2123
|
+
currentDb.prepare("DELETE FROM requirements WHERE id = :id").run({ ":id": id });
|
|
2124
|
+
}
|
|
2125
|
+
/** Delete an artifact row by path. Used by db-writer.ts rollback on disk-write failure. */
|
|
2126
|
+
export function deleteArtifactByPath(path) {
|
|
2127
|
+
if (!currentDb)
|
|
2128
|
+
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
2129
|
+
currentDb.prepare("DELETE FROM artifacts WHERE path = :path").run({ ":path": path });
|
|
2130
|
+
}
|
|
2131
|
+
/**
|
|
2132
|
+
* Drop all rows from tasks/slices/milestones in dependency order inside a
|
|
2133
|
+
* transaction. Used by `gsd recover` to rebuild engine state from markdown.
|
|
2134
|
+
*/
|
|
2135
|
+
export function clearEngineHierarchy() {
|
|
2136
|
+
if (!currentDb)
|
|
2137
|
+
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
2138
|
+
transaction(() => {
|
|
2139
|
+
currentDb.exec("DELETE FROM tasks");
|
|
2140
|
+
currentDb.exec("DELETE FROM slices");
|
|
2141
|
+
currentDb.exec("DELETE FROM milestones");
|
|
2142
|
+
});
|
|
2143
|
+
}
|
|
2144
|
+
/**
|
|
2145
|
+
* INSERT OR IGNORE a slice during event replay (workflow-reconcile.ts).
|
|
2146
|
+
* Strict insert-or-ignore semantics are required here to avoid the
|
|
2147
|
+
* `insertSlice` ON CONFLICT path that could downgrade an already-completed
|
|
2148
|
+
* slice back to 'pending'.
|
|
2149
|
+
*/
|
|
2150
|
+
export function insertOrIgnoreSlice(args) {
|
|
2151
|
+
if (!currentDb)
|
|
2152
|
+
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
2153
|
+
currentDb.prepare(`INSERT OR IGNORE INTO slices (milestone_id, id, title, status, created_at)
|
|
2154
|
+
VALUES (:mid, :sid, :title, 'pending', :ts)`).run({
|
|
2155
|
+
":mid": args.milestoneId,
|
|
2156
|
+
":sid": args.sliceId,
|
|
2157
|
+
":title": args.title,
|
|
2158
|
+
":ts": args.createdAt,
|
|
2159
|
+
});
|
|
2160
|
+
}
|
|
2161
|
+
/**
|
|
2162
|
+
* INSERT OR IGNORE a task during event replay (workflow-reconcile.ts).
|
|
2163
|
+
* Same rationale as `insertOrIgnoreSlice`.
|
|
2164
|
+
*/
|
|
2165
|
+
export function insertOrIgnoreTask(args) {
|
|
2166
|
+
if (!currentDb)
|
|
2167
|
+
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
2168
|
+
currentDb.prepare(`INSERT OR IGNORE INTO tasks (milestone_id, slice_id, id, title, status, created_at)
|
|
2169
|
+
VALUES (:mid, :sid, :tid, :title, 'pending', :ts)`).run({
|
|
2170
|
+
":mid": args.milestoneId,
|
|
2171
|
+
":sid": args.sliceId,
|
|
2172
|
+
":tid": args.taskId,
|
|
2173
|
+
":title": args.title,
|
|
2174
|
+
":ts": args.createdAt,
|
|
2175
|
+
});
|
|
2176
|
+
}
|
|
2177
|
+
/**
|
|
2178
|
+
* Stamp the `replan_triggered_at` column on a slice. Used by triage-resolution
|
|
2179
|
+
* when a user capture requests a replan so the dispatcher can detect the
|
|
2180
|
+
* trigger via DB in addition to the on-disk REPLAN-TRIGGER.md marker.
|
|
2181
|
+
*/
|
|
2182
|
+
export function setSliceReplanTriggeredAt(milestoneId, sliceId, ts) {
|
|
2183
|
+
if (!currentDb)
|
|
2184
|
+
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
2185
|
+
currentDb.prepare("UPDATE slices SET replan_triggered_at = :ts WHERE milestone_id = :mid AND id = :sid").run({ ":ts": ts, ":mid": milestoneId, ":sid": sliceId });
|
|
2186
|
+
}
|
|
2187
|
+
/**
|
|
2188
|
+
* INSERT OR REPLACE a quality_gates row. Used by milestone-validation-gates.ts
|
|
2189
|
+
* to persist milestone-level (MV*) gate outcomes after validate-milestone runs.
|
|
2190
|
+
*/
|
|
2191
|
+
export function upsertQualityGate(g) {
|
|
2192
|
+
if (!currentDb)
|
|
2193
|
+
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
2194
|
+
currentDb.prepare(`INSERT OR REPLACE INTO quality_gates
|
|
2195
|
+
(milestone_id, slice_id, gate_id, scope, task_id, status, verdict, rationale, findings, evaluated_at)
|
|
2196
|
+
VALUES (:mid, :sid, :gid, :scope, :tid, :status, :verdict, :rationale, :findings, :evaluated_at)`).run({
|
|
2197
|
+
":mid": g.milestoneId,
|
|
2198
|
+
":sid": g.sliceId,
|
|
2199
|
+
":gid": g.gateId,
|
|
2200
|
+
":scope": g.scope,
|
|
2201
|
+
":tid": g.taskId,
|
|
2202
|
+
":status": g.status,
|
|
2203
|
+
":verdict": g.verdict,
|
|
2204
|
+
":rationale": g.rationale,
|
|
2205
|
+
":findings": g.findings,
|
|
2206
|
+
":evaluated_at": g.evaluatedAt,
|
|
2207
|
+
});
|
|
2208
|
+
}
|
|
2209
|
+
/**
|
|
2210
|
+
* Atomically replace all workflow state from a manifest. Lifted verbatim from
|
|
2211
|
+
* workflow-manifest.ts so the single-writer invariant holds. Only touches
|
|
2212
|
+
* engine tables + decisions. Does NOT modify artifacts or memories.
|
|
2213
|
+
*/
|
|
2214
|
+
export function restoreManifest(manifest) {
|
|
2215
|
+
if (!currentDb)
|
|
2216
|
+
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
2217
|
+
const db = currentDb;
|
|
2218
|
+
transaction(() => {
|
|
2219
|
+
// Clear engine tables (order matters for foreign-key-like consistency)
|
|
2220
|
+
db.exec("DELETE FROM verification_evidence");
|
|
2221
|
+
db.exec("DELETE FROM tasks");
|
|
2222
|
+
db.exec("DELETE FROM slices");
|
|
2223
|
+
db.exec("DELETE FROM milestones");
|
|
2224
|
+
db.exec("DELETE FROM decisions WHERE 1=1");
|
|
2225
|
+
// Restore milestones
|
|
2226
|
+
const msStmt = db.prepare(`INSERT INTO milestones (id, title, status, depends_on, created_at, completed_at,
|
|
2227
|
+
vision, success_criteria, key_risks, proof_strategy,
|
|
2228
|
+
verification_contract, verification_integration, verification_operational, verification_uat,
|
|
2229
|
+
definition_of_done, requirement_coverage, boundary_map_markdown)
|
|
2230
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
2231
|
+
for (const m of manifest.milestones) {
|
|
2232
|
+
msStmt.run(m.id, m.title, m.status, JSON.stringify(m.depends_on), m.created_at, m.completed_at, m.vision, JSON.stringify(m.success_criteria), JSON.stringify(m.key_risks), JSON.stringify(m.proof_strategy), m.verification_contract, m.verification_integration, m.verification_operational, m.verification_uat, JSON.stringify(m.definition_of_done), m.requirement_coverage, m.boundary_map_markdown);
|
|
2233
|
+
}
|
|
2234
|
+
// Restore slices
|
|
2235
|
+
const slStmt = db.prepare(`INSERT INTO slices (milestone_id, id, title, status, risk, depends, demo,
|
|
2236
|
+
created_at, completed_at, full_summary_md, full_uat_md,
|
|
2237
|
+
goal, success_criteria, proof_level, integration_closure, observability_impact,
|
|
2238
|
+
sequence, replan_triggered_at)
|
|
2239
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
2240
|
+
for (const s of manifest.slices) {
|
|
2241
|
+
slStmt.run(s.milestone_id, s.id, s.title, s.status, s.risk, JSON.stringify(s.depends), s.demo, s.created_at, s.completed_at, s.full_summary_md, s.full_uat_md, s.goal, s.success_criteria, s.proof_level, s.integration_closure, s.observability_impact, s.sequence, s.replan_triggered_at);
|
|
2242
|
+
}
|
|
2243
|
+
// Restore tasks
|
|
2244
|
+
const tkStmt = db.prepare(`INSERT INTO tasks (milestone_id, slice_id, id, title, status,
|
|
2245
|
+
one_liner, narrative, verification_result, duration, completed_at,
|
|
2246
|
+
blocker_discovered, deviations, known_issues, key_files, key_decisions,
|
|
2247
|
+
full_summary_md, description, estimate, files, verify,
|
|
2248
|
+
inputs, expected_output, observability_impact, sequence)
|
|
2249
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
2250
|
+
for (const t of manifest.tasks) {
|
|
2251
|
+
tkStmt.run(t.milestone_id, t.slice_id, t.id, t.title, t.status, t.one_liner, t.narrative, t.verification_result, t.duration, t.completed_at, t.blocker_discovered ? 1 : 0, t.deviations, t.known_issues, JSON.stringify(t.key_files), JSON.stringify(t.key_decisions), t.full_summary_md, t.description, t.estimate, JSON.stringify(t.files), t.verify, JSON.stringify(t.inputs), JSON.stringify(t.expected_output), t.observability_impact, t.sequence);
|
|
2252
|
+
}
|
|
2253
|
+
// Restore decisions
|
|
2254
|
+
const dcStmt = db.prepare(`INSERT INTO decisions (seq, id, when_context, scope, decision, choice, rationale, revisable, made_by, superseded_by)
|
|
2255
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
2256
|
+
for (const d of manifest.decisions) {
|
|
2257
|
+
dcStmt.run(d.seq, d.id, d.when_context, d.scope, d.decision, d.choice, d.rationale, d.revisable, d.made_by, d.superseded_by);
|
|
2258
|
+
}
|
|
2259
|
+
// Restore verification evidence
|
|
2260
|
+
const evStmt = db.prepare(`INSERT INTO verification_evidence (task_id, slice_id, milestone_id, command, exit_code, verdict, duration_ms, created_at)
|
|
2261
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
2262
|
+
for (const e of manifest.verification_evidence) {
|
|
2263
|
+
evStmt.run(e.task_id, e.slice_id, e.milestone_id, e.command, e.exit_code, e.verdict, e.duration_ms, e.created_at);
|
|
2264
|
+
}
|
|
2265
|
+
});
|
|
2266
|
+
}
|
|
2267
|
+
/**
|
|
2268
|
+
* Bulk delete + insert a legacy milestone hierarchy for markdown → DB migration.
|
|
2269
|
+
* Used by workflow-migration.ts to populate engine tables from parsed ROADMAP/PLAN
|
|
2270
|
+
* files. All operations run inside a single transaction.
|
|
2271
|
+
*/
|
|
2272
|
+
export function bulkInsertLegacyHierarchy(payload) {
|
|
2273
|
+
if (!currentDb)
|
|
2274
|
+
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
2275
|
+
const db = currentDb;
|
|
2276
|
+
const { milestones, slices, tasks, clearMilestoneIds, createdAt } = payload;
|
|
2277
|
+
if (clearMilestoneIds.length === 0)
|
|
2278
|
+
return;
|
|
2279
|
+
const placeholders = clearMilestoneIds.map(() => "?").join(",");
|
|
2280
|
+
transaction(() => {
|
|
2281
|
+
db.prepare(`DELETE FROM tasks WHERE milestone_id IN (${placeholders})`).run(...clearMilestoneIds);
|
|
2282
|
+
db.prepare(`DELETE FROM slices WHERE milestone_id IN (${placeholders})`).run(...clearMilestoneIds);
|
|
2283
|
+
db.prepare(`DELETE FROM milestones WHERE id IN (${placeholders})`).run(...clearMilestoneIds);
|
|
2284
|
+
const insertMilestone = db.prepare("INSERT INTO milestones (id, title, status, created_at) VALUES (?, ?, ?, ?)");
|
|
2285
|
+
for (const m of milestones) {
|
|
2286
|
+
insertMilestone.run(m.id, m.title, m.status, createdAt);
|
|
2287
|
+
}
|
|
2288
|
+
const insertSliceStmt = db.prepare("INSERT INTO slices (id, milestone_id, title, status, risk, depends, sequence, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
|
|
2289
|
+
for (const s of slices) {
|
|
2290
|
+
insertSliceStmt.run(s.id, s.milestoneId, s.title, s.status, s.risk, "[]", s.sequence, createdAt);
|
|
2291
|
+
}
|
|
2292
|
+
const insertTaskStmt = db.prepare("INSERT INTO tasks (id, slice_id, milestone_id, title, description, status, estimate, files, sequence) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
|
2293
|
+
for (const t of tasks) {
|
|
2294
|
+
insertTaskStmt.run(t.id, t.sliceId, t.milestoneId, t.title, "", t.status, "", "[]", t.sequence);
|
|
2295
|
+
}
|
|
2296
|
+
});
|
|
2297
|
+
}
|
|
2298
|
+
// ─── Memory store writers ────────────────────────────────────────────────
|
|
2299
|
+
// All memory writes go through gsd-db.ts so the single-writer invariant
|
|
2300
|
+
// holds. These are direct pass-throughs to the SQL previously in
|
|
2301
|
+
// memory-store.ts — same bindings, same behavior.
|
|
2302
|
+
export function insertMemoryRow(args) {
|
|
2303
|
+
if (!currentDb)
|
|
2304
|
+
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
2305
|
+
currentDb.prepare(`INSERT INTO memories (id, category, content, confidence, source_unit_type, source_unit_id, created_at, updated_at)
|
|
2306
|
+
VALUES (:id, :category, :content, :confidence, :source_unit_type, :source_unit_id, :created_at, :updated_at)`).run({
|
|
2307
|
+
":id": args.id,
|
|
2308
|
+
":category": args.category,
|
|
2309
|
+
":content": args.content,
|
|
2310
|
+
":confidence": args.confidence,
|
|
2311
|
+
":source_unit_type": args.sourceUnitType,
|
|
2312
|
+
":source_unit_id": args.sourceUnitId,
|
|
2313
|
+
":created_at": args.createdAt,
|
|
2314
|
+
":updated_at": args.updatedAt,
|
|
2315
|
+
});
|
|
2316
|
+
}
|
|
2317
|
+
export function rewriteMemoryId(placeholderId, realId) {
|
|
2318
|
+
if (!currentDb)
|
|
2319
|
+
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
2320
|
+
currentDb.prepare("UPDATE memories SET id = :real_id WHERE id = :placeholder").run({
|
|
2321
|
+
":real_id": realId,
|
|
2322
|
+
":placeholder": placeholderId,
|
|
2323
|
+
});
|
|
2324
|
+
}
|
|
2325
|
+
export function updateMemoryContentRow(id, content, confidence, updatedAt) {
|
|
2326
|
+
if (!currentDb)
|
|
2327
|
+
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
2328
|
+
if (confidence != null) {
|
|
2329
|
+
currentDb.prepare("UPDATE memories SET content = :content, confidence = :confidence, updated_at = :updated_at WHERE id = :id").run({ ":content": content, ":confidence": confidence, ":updated_at": updatedAt, ":id": id });
|
|
2330
|
+
}
|
|
2331
|
+
else {
|
|
2332
|
+
currentDb.prepare("UPDATE memories SET content = :content, updated_at = :updated_at WHERE id = :id").run({ ":content": content, ":updated_at": updatedAt, ":id": id });
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
export function incrementMemoryHitCount(id, updatedAt) {
|
|
2336
|
+
if (!currentDb)
|
|
2337
|
+
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
2338
|
+
currentDb.prepare("UPDATE memories SET hit_count = hit_count + 1, updated_at = :updated_at WHERE id = :id").run({ ":updated_at": updatedAt, ":id": id });
|
|
2339
|
+
}
|
|
2340
|
+
export function supersedeMemoryRow(oldId, newId, updatedAt) {
|
|
2341
|
+
if (!currentDb)
|
|
2342
|
+
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
2343
|
+
currentDb.prepare("UPDATE memories SET superseded_by = :new_id, updated_at = :updated_at WHERE id = :old_id").run({ ":new_id": newId, ":updated_at": updatedAt, ":old_id": oldId });
|
|
2344
|
+
}
|
|
2345
|
+
export function markMemoryUnitProcessed(unitKey, activityFile, processedAt) {
|
|
2346
|
+
if (!currentDb)
|
|
2347
|
+
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
2348
|
+
currentDb.prepare(`INSERT OR IGNORE INTO memory_processed_units (unit_key, activity_file, processed_at)
|
|
2349
|
+
VALUES (:key, :file, :at)`).run({ ":key": unitKey, ":file": activityFile, ":at": processedAt });
|
|
2350
|
+
}
|
|
2351
|
+
export function decayMemoriesBefore(cutoffTs, now) {
|
|
2352
|
+
if (!currentDb)
|
|
2353
|
+
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
2354
|
+
currentDb.prepare(`UPDATE memories
|
|
2355
|
+
SET confidence = MAX(0.1, confidence - 0.1), updated_at = :now
|
|
2356
|
+
WHERE superseded_by IS NULL AND updated_at < :cutoff AND confidence > 0.1`).run({ ":now": now, ":cutoff": cutoffTs });
|
|
2357
|
+
}
|
|
2358
|
+
export function supersedeLowestRankedMemories(limit, now) {
|
|
2359
|
+
if (!currentDb)
|
|
2360
|
+
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
2361
|
+
currentDb.prepare(`UPDATE memories SET superseded_by = 'CAP_EXCEEDED', updated_at = :now
|
|
2362
|
+
WHERE id IN (
|
|
2363
|
+
SELECT id FROM memories
|
|
2364
|
+
WHERE superseded_by IS NULL
|
|
2365
|
+
ORDER BY (confidence * (1.0 + hit_count * 0.1)) ASC
|
|
2366
|
+
LIMIT :limit
|
|
2367
|
+
)`).run({ ":now": now, ":limit": limit });
|
|
2368
|
+
}
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
export { isDepthConfirmationAnswer, isDepthVerified, isGateQuestionId, isQueuePhaseActive, setQueuePhaseActive, shouldBlockContextWrite, shouldBlockPendingGate, shouldBlockPendingGateBash, shouldBlockQueueExecution, setPendingGate, clearPendingGate, getPendingGate, } from "./bootstrap/write-gate.js";
|
|
2
2
|
export default async function registerExtension(pi) {
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
// Always register the core /gsd command first, in isolation.
|
|
4
|
+
// This ensures /gsd is available even if the full bootstrap (shortcuts,
|
|
5
|
+
// tools, hooks) fails — e.g. due to a Windows-specific import error.
|
|
6
|
+
const { registerGSDCommand } = await import("./commands/index.js");
|
|
7
|
+
registerGSDCommand(pi);
|
|
8
|
+
// Full setup (shortcuts, tools, hooks) in a separate try/catch so that
|
|
9
|
+
// any platform-specific load failure doesn't take out the core command.
|
|
10
|
+
try {
|
|
11
|
+
const { registerGsdExtension } = await import("./bootstrap/register-extension.js");
|
|
12
|
+
registerGsdExtension(pi);
|
|
13
|
+
}
|
|
14
|
+
catch (err) {
|
|
15
|
+
const { logWarning } = await import("./workflow-logger.js");
|
|
16
|
+
logWarning("bootstrap", `Extension setup partially failed — /gsd commands are available but shortcuts/tools may be missing: ${err instanceof Error ? err.message : String(err)}`);
|
|
17
|
+
}
|
|
5
18
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// Exports: parseDecisionsTable, parseRequirementsSections, migrateFromMarkdown
|
|
6
6
|
import { readFileSync, readdirSync, existsSync } from 'node:fs';
|
|
7
7
|
import { join } from 'node:path';
|
|
8
|
-
import { upsertDecision, upsertRequirement, insertArtifact, insertMilestone, insertSlice, insertTask, openDatabase, transaction, _getAdapter, } from './gsd-db.js';
|
|
8
|
+
import { upsertDecision, upsertRequirement, insertArtifact, insertMilestone, insertSlice, insertTask, openDatabase, transaction, updateSliceStatus, _getAdapter, } from './gsd-db.js';
|
|
9
9
|
import { resolveGsdRootFile, resolveMilestoneFile, resolveSliceFile, resolveTasksDir, milestonesDir, gsdRoot, resolveTaskFiles, } from './paths.js';
|
|
10
10
|
import { findMilestoneIds } from './guided-flow.js';
|
|
11
11
|
import { parseRoadmap, parsePlan } from './parsers-legacy.js';
|
|
@@ -571,9 +571,8 @@ export function migrateHierarchyToDb(basePath) {
|
|
|
571
571
|
return t.done && existsSync(summaryFile);
|
|
572
572
|
});
|
|
573
573
|
if (allTasksDone && hasSliceSummary) {
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
adapter.prepare(`UPDATE slices SET status = 'complete' WHERE id = :sid AND milestone_id = :mid`).run({ ':sid': sliceEntry.id, ':mid': milestoneId });
|
|
574
|
+
if (_getAdapter()) {
|
|
575
|
+
updateSliceStatus(milestoneId, sliceEntry.id, 'complete');
|
|
577
576
|
process.stderr.write(`gsd-migrate: ${milestoneId}/${sliceEntry.id} all tasks + slice summary complete — upgrading slice to complete\n`);
|
|
578
577
|
}
|
|
579
578
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
//
|
|
3
3
|
// Storage layer for auto-learned project memories. Follows context-store.ts patterns.
|
|
4
4
|
// All functions degrade gracefully: return empty results when DB unavailable, never throw.
|
|
5
|
-
import { isDbAvailable, _getAdapter, transaction } from './gsd-db.js';
|
|
5
|
+
import { isDbAvailable, _getAdapter, transaction, insertMemoryRow, rewriteMemoryId, updateMemoryContentRow, incrementMemoryHitCount, supersedeMemoryRow, markMemoryUnitProcessed, decayMemoriesBefore, supersedeLowestRankedMemories, } from './gsd-db.js';
|
|
6
6
|
// ─── Category Display Order ─────────────────────────────────────────────────
|
|
7
7
|
const CATEGORY_PRIORITY = {
|
|
8
8
|
gotcha: 0,
|
|
@@ -112,27 +112,23 @@ export function createMemory(fields) {
|
|
|
112
112
|
const now = new Date().toISOString();
|
|
113
113
|
// Insert with a temporary placeholder ID — seq is auto-assigned
|
|
114
114
|
const placeholder = `_TMP_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
':updated_at': now,
|
|
115
|
+
insertMemoryRow({
|
|
116
|
+
id: placeholder,
|
|
117
|
+
category: fields.category,
|
|
118
|
+
content: fields.content,
|
|
119
|
+
confidence: fields.confidence ?? 0.8,
|
|
120
|
+
sourceUnitType: fields.source_unit_type ?? null,
|
|
121
|
+
sourceUnitId: fields.source_unit_id ?? null,
|
|
122
|
+
createdAt: now,
|
|
123
|
+
updatedAt: now,
|
|
125
124
|
});
|
|
126
|
-
// Derive the real ID from the assigned seq
|
|
125
|
+
// Derive the real ID from the assigned seq (SELECT is still fine via adapter)
|
|
127
126
|
const row = adapter.prepare('SELECT seq FROM memories WHERE id = :id').get({ ':id': placeholder });
|
|
128
127
|
if (!row)
|
|
129
128
|
return placeholder; // fallback — should not happen
|
|
130
129
|
const seq = row['seq'];
|
|
131
130
|
const realId = `MEM${String(seq).padStart(3, '0')}`;
|
|
132
|
-
|
|
133
|
-
':real_id': realId,
|
|
134
|
-
':placeholder': placeholder,
|
|
135
|
-
});
|
|
131
|
+
rewriteMemoryId(placeholder, realId);
|
|
136
132
|
return realId;
|
|
137
133
|
}
|
|
138
134
|
catch {
|
|
@@ -145,17 +141,8 @@ export function createMemory(fields) {
|
|
|
145
141
|
export function updateMemoryContent(id, content, confidence) {
|
|
146
142
|
if (!isDbAvailable())
|
|
147
143
|
return false;
|
|
148
|
-
const adapter = _getAdapter();
|
|
149
|
-
if (!adapter)
|
|
150
|
-
return false;
|
|
151
144
|
try {
|
|
152
|
-
|
|
153
|
-
if (confidence != null) {
|
|
154
|
-
adapter.prepare('UPDATE memories SET content = :content, confidence = :confidence, updated_at = :updated_at WHERE id = :id').run({ ':content': content, ':confidence': confidence, ':updated_at': now, ':id': id });
|
|
155
|
-
}
|
|
156
|
-
else {
|
|
157
|
-
adapter.prepare('UPDATE memories SET content = :content, updated_at = :updated_at WHERE id = :id').run({ ':content': content, ':updated_at': now, ':id': id });
|
|
158
|
-
}
|
|
145
|
+
updateMemoryContentRow(id, content, confidence, new Date().toISOString());
|
|
159
146
|
return true;
|
|
160
147
|
}
|
|
161
148
|
catch {
|
|
@@ -168,11 +155,8 @@ export function updateMemoryContent(id, content, confidence) {
|
|
|
168
155
|
export function reinforceMemory(id) {
|
|
169
156
|
if (!isDbAvailable())
|
|
170
157
|
return false;
|
|
171
|
-
const adapter = _getAdapter();
|
|
172
|
-
if (!adapter)
|
|
173
|
-
return false;
|
|
174
158
|
try {
|
|
175
|
-
|
|
159
|
+
incrementMemoryHitCount(id, new Date().toISOString());
|
|
176
160
|
return true;
|
|
177
161
|
}
|
|
178
162
|
catch {
|
|
@@ -185,11 +169,8 @@ export function reinforceMemory(id) {
|
|
|
185
169
|
export function supersedeMemory(oldId, newId) {
|
|
186
170
|
if (!isDbAvailable())
|
|
187
171
|
return false;
|
|
188
|
-
const adapter = _getAdapter();
|
|
189
|
-
if (!adapter)
|
|
190
|
-
return false;
|
|
191
172
|
try {
|
|
192
|
-
|
|
173
|
+
supersedeMemoryRow(oldId, newId, new Date().toISOString());
|
|
193
174
|
return true;
|
|
194
175
|
}
|
|
195
176
|
catch {
|
|
@@ -220,12 +201,8 @@ export function isUnitProcessed(unitKey) {
|
|
|
220
201
|
export function markUnitProcessed(unitKey, activityFile) {
|
|
221
202
|
if (!isDbAvailable())
|
|
222
203
|
return false;
|
|
223
|
-
const adapter = _getAdapter();
|
|
224
|
-
if (!adapter)
|
|
225
|
-
return false;
|
|
226
204
|
try {
|
|
227
|
-
|
|
228
|
-
VALUES (:key, :file, :at)`).run({ ':key': unitKey, ':file': activityFile, ':at': new Date().toISOString() });
|
|
205
|
+
markMemoryUnitProcessed(unitKey, activityFile, new Date().toISOString());
|
|
229
206
|
return true;
|
|
230
207
|
}
|
|
231
208
|
catch {
|
|
@@ -244,16 +221,14 @@ export function decayStaleMemories(thresholdUnits = 20) {
|
|
|
244
221
|
if (!adapter)
|
|
245
222
|
return;
|
|
246
223
|
try {
|
|
247
|
-
// Find the timestamp of the Nth most recent processed unit
|
|
224
|
+
// Find the timestamp of the Nth most recent processed unit (read-only SELECT)
|
|
248
225
|
const row = adapter.prepare(`SELECT processed_at FROM memory_processed_units
|
|
249
226
|
ORDER BY processed_at DESC
|
|
250
227
|
LIMIT 1 OFFSET :offset`).get({ ':offset': thresholdUnits - 1 });
|
|
251
228
|
if (!row)
|
|
252
229
|
return; // not enough processed units yet
|
|
253
230
|
const cutoff = row['processed_at'];
|
|
254
|
-
|
|
255
|
-
SET confidence = MAX(0.1, confidence - 0.1), updated_at = :now
|
|
256
|
-
WHERE superseded_by IS NULL AND updated_at < :cutoff AND confidence > 0.1`).run({ ':now': new Date().toISOString(), ':cutoff': cutoff });
|
|
231
|
+
decayMemoriesBefore(cutoff, new Date().toISOString());
|
|
257
232
|
}
|
|
258
233
|
catch {
|
|
259
234
|
// non-fatal
|
|
@@ -274,14 +249,7 @@ export function enforceMemoryCap(max = 50) {
|
|
|
274
249
|
if (count <= max)
|
|
275
250
|
return;
|
|
276
251
|
const excess = count - max;
|
|
277
|
-
|
|
278
|
-
adapter.prepare(`UPDATE memories SET superseded_by = 'CAP_EXCEEDED', updated_at = :now
|
|
279
|
-
WHERE id IN (
|
|
280
|
-
SELECT id FROM memories
|
|
281
|
-
WHERE superseded_by IS NULL
|
|
282
|
-
ORDER BY (confidence * (1.0 + hit_count * 0.1)) ASC
|
|
283
|
-
LIMIT :limit
|
|
284
|
-
)`).run({ ':now': new Date().toISOString(), ':limit': excess });
|
|
252
|
+
supersedeLowestRankedMemories(excess, new Date().toISOString());
|
|
285
253
|
}
|
|
286
254
|
catch {
|
|
287
255
|
// non-fatal
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* gate registry so the definitions stay in lockstep with prompt builders,
|
|
11
11
|
* dispatch rules, and state derivation. See gate-registry.ts.
|
|
12
12
|
*/
|
|
13
|
-
import {
|
|
13
|
+
import { isDbAvailable, upsertQualityGate } from "./gsd-db.js";
|
|
14
14
|
import { getGatesForTurn } from "./gate-registry.js";
|
|
15
15
|
/**
|
|
16
16
|
* Insert milestone-level quality_gates records for a validation run.
|
|
@@ -24,21 +24,22 @@ import { getGatesForTurn } from "./gate-registry.js";
|
|
|
24
24
|
* in gate-registry.ts automatically flows through here.
|
|
25
25
|
*/
|
|
26
26
|
export function insertMilestoneValidationGates(milestoneId, sliceId, verdict, evaluatedAt) {
|
|
27
|
-
|
|
28
|
-
if (!db)
|
|
27
|
+
if (!isDbAvailable())
|
|
29
28
|
return;
|
|
30
29
|
const gateVerdict = verdict === "pass" ? "pass" : "flag";
|
|
31
30
|
const milestoneGates = getGatesForTurn("validate-milestone");
|
|
32
31
|
for (const def of milestoneGates) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
32
|
+
upsertQualityGate({
|
|
33
|
+
milestoneId,
|
|
34
|
+
sliceId,
|
|
35
|
+
gateId: def.id,
|
|
36
|
+
scope: "milestone",
|
|
37
|
+
taskId: "",
|
|
38
|
+
status: "complete",
|
|
39
|
+
verdict: gateVerdict,
|
|
40
|
+
rationale: `${def.promptSection} — milestone validation verdict: ${verdict}`,
|
|
41
|
+
findings: "",
|
|
42
|
+
evaluatedAt,
|
|
42
43
|
});
|
|
43
44
|
}
|
|
44
45
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
//
|
|
5
5
|
// Both READ and WRITE operations are native — push operations remain as
|
|
6
6
|
// execSync calls because git2 credential handling is too complex.
|
|
7
|
-
import {
|
|
7
|
+
import { execFileSync } from "node:child_process";
|
|
8
8
|
import { GSDError, GSD_GIT_ERROR } from "./errors.js";
|
|
9
9
|
import { GIT_NO_PROMPT_ENV } from "./git-constants.js";
|
|
10
10
|
import { getErrorMessage } from "./error-utils.js";
|
|
@@ -206,7 +206,7 @@ export function nativeIsRepo(basePath) {
|
|
|
206
206
|
return native.gitIsRepo(basePath);
|
|
207
207
|
}
|
|
208
208
|
try {
|
|
209
|
-
|
|
209
|
+
execFileSync("git", ["rev-parse", "--git-dir"], { cwd: basePath, stdio: "pipe" });
|
|
210
210
|
return true;
|
|
211
211
|
}
|
|
212
212
|
catch {
|
|
@@ -629,7 +629,10 @@ export function nativeCommit(basePath, message, options) {
|
|
|
629
629
|
}
|
|
630
630
|
// Fallback: use git commit with stdin pipe for safe multi-line messages
|
|
631
631
|
try {
|
|
632
|
-
const
|
|
632
|
+
const args = ["commit", "--no-verify", "-F", "-"];
|
|
633
|
+
if (options?.allowEmpty)
|
|
634
|
+
args.push("--allow-empty");
|
|
635
|
+
const result = execFileSync("git", args, {
|
|
633
636
|
cwd: basePath,
|
|
634
637
|
stdio: ["pipe", "pipe", "pipe"],
|
|
635
638
|
encoding: "utf-8",
|
|
@@ -767,7 +770,7 @@ export function nativeResetHard(basePath) {
|
|
|
767
770
|
native.gitResetHard(basePath);
|
|
768
771
|
return;
|
|
769
772
|
}
|
|
770
|
-
|
|
773
|
+
execFileSync("git", ["reset", "--hard", "HEAD"], { cwd: basePath, stdio: "pipe" });
|
|
771
774
|
}
|
|
772
775
|
/**
|
|
773
776
|
* Soft reset to a target ref (git reset --soft <ref>).
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
You are generating tests for recently completed GSD work.
|
|
2
|
+
|
|
3
|
+
## Slice: {{sliceId}} — {{sliceTitle}}
|
|
4
|
+
|
|
5
|
+
### Summary
|
|
6
|
+
|
|
7
|
+
{{sliceSummary}}
|
|
8
|
+
|
|
9
|
+
### Existing Test Patterns
|
|
10
|
+
|
|
11
|
+
{{existingTestPatterns}}
|
|
12
|
+
|
|
13
|
+
## Working Directory
|
|
14
|
+
|
|
15
|
+
`{{workingDirectory}}`
|
|
16
|
+
|
|
17
|
+
## Instructions
|
|
18
|
+
|
|
19
|
+
1. Read the slice summary above to understand what was built
|
|
20
|
+
2. Identify the source files that were created or modified for this slice
|
|
21
|
+
3. Read the implementation code to understand behavior, edge cases, and error paths
|
|
22
|
+
4. Write comprehensive tests following the project's existing test patterns and framework
|
|
23
|
+
5. Run the tests to verify they pass
|
|
24
|
+
6. Fix any failures
|
|
25
|
+
|
|
26
|
+
### Rules
|
|
27
|
+
|
|
28
|
+
- Follow the project's existing test patterns (framework, assertions, file structure)
|
|
29
|
+
- Test behavior, not implementation details
|
|
30
|
+
- Cover: happy path, edge cases, error conditions, boundary values
|
|
31
|
+
- Do NOT modify implementation files — only create or update test files
|
|
32
|
+
- Name test files consistently with the project's conventions
|
|
33
|
+
- Keep tests focused and readable
|
|
34
|
+
|
|
35
|
+
{{skillActivation}}
|
|
@@ -271,7 +271,11 @@ function reconcileDiskToDb(basePath) {
|
|
|
271
271
|
try {
|
|
272
272
|
roadmapContent = readFileSync(roadmapPath, "utf-8");
|
|
273
273
|
}
|
|
274
|
-
catch {
|
|
274
|
+
catch (err) {
|
|
275
|
+
logWarning("state", "reconcileDiskToDb: roadmap read failed, skipping milestone", {
|
|
276
|
+
mid,
|
|
277
|
+
error: err.message,
|
|
278
|
+
});
|
|
275
279
|
continue;
|
|
276
280
|
}
|
|
277
281
|
const parsed = parseRoadmap(roadmapContent);
|