issue-flow 0.17.0 → 0.19.1
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/README.md +34 -100
- package/dist/agent-EWKA7357.js +32 -0
- package/dist/{analyze-6M3KEOX7.js → analyze-Z7ODC27F.js} +42 -41
- package/dist/analyze-Z7ODC27F.js.map +1 -0
- package/dist/{apply-ADB3ZK7W.js → apply-VNSWQS6O.js} +22 -24
- package/dist/apply-VNSWQS6O.js.map +1 -0
- package/dist/artifacts-AMJQJFFV.js +82 -0
- package/dist/artifacts-AMJQJFFV.js.map +1 -0
- package/dist/{bench-FM7I7EIV.js → bench-ZJEIACUK.js} +42 -42
- package/dist/{chunk-RYOUZ6EK.js → chunk-23VHVVKW.js} +3 -3
- package/dist/chunk-2EYLOS5I.js +99 -0
- package/dist/chunk-2EYLOS5I.js.map +1 -0
- package/dist/{chunk-ANI2NTR4.js → chunk-2EZW4X57.js} +28 -25
- package/dist/chunk-2EZW4X57.js.map +1 -0
- package/dist/{chunk-E6KXCV3T.js → chunk-2Q6PG3MT.js} +15 -15
- package/dist/chunk-2Q6PG3MT.js.map +1 -0
- package/dist/{chunk-C5TL7T4V.js → chunk-2YM2VH6R.js} +2 -2
- package/dist/{chunk-LPWPFIIB.js → chunk-2YYP7AE6.js} +14 -16
- package/dist/{chunk-LPWPFIIB.js.map → chunk-2YYP7AE6.js.map} +1 -1
- package/dist/chunk-3JD25HHJ.js +278 -0
- package/dist/chunk-3JD25HHJ.js.map +1 -0
- package/dist/{chunk-ISZ54SVE.js → chunk-46S7APLQ.js} +41 -17
- package/dist/chunk-46S7APLQ.js.map +1 -0
- package/dist/chunk-6AOZCWN2.js +21 -0
- package/dist/chunk-6AOZCWN2.js.map +1 -0
- package/dist/{chunk-QPGEFLMY.js → chunk-7CMVL4X6.js} +7 -7
- package/dist/chunk-7MJGYPHF.js +1571 -0
- package/dist/chunk-7MJGYPHF.js.map +1 -0
- package/dist/chunk-7ZZUSMBJ.js +35 -0
- package/dist/chunk-7ZZUSMBJ.js.map +1 -0
- package/dist/{chunk-DUS5ZNKL.js → chunk-DIMDAYZS.js} +7 -6
- package/dist/{chunk-DGRTJD5R.js → chunk-DXH4TQRB.js} +7 -7
- package/dist/{chunk-CRGIQFUQ.js → chunk-E2H3JV2E.js} +3 -1
- package/dist/chunk-E2H3JV2E.js.map +1 -0
- package/dist/{chunk-YUNBOUQH.js → chunk-ERAG5IQB.js} +8 -8
- package/dist/{chunk-D6ZBGJ3L.js → chunk-F2NFJ27Z.js} +130 -36
- package/dist/chunk-F2NFJ27Z.js.map +1 -0
- package/dist/{chunk-SOM4QA6C.js → chunk-GD7JZMSJ.js} +93 -18
- package/dist/chunk-GD7JZMSJ.js.map +1 -0
- package/dist/{chunk-YII4U7ZD.js → chunk-HEHHQU4G.js} +70 -81
- package/dist/chunk-HEHHQU4G.js.map +1 -0
- package/dist/{chunk-KZ5EY5XX.js → chunk-HHXWAWU6.js} +159 -114
- package/dist/chunk-HHXWAWU6.js.map +1 -0
- package/dist/{chunk-SGTTR4XW.js → chunk-HWWB22HC.js} +152 -100
- package/dist/chunk-HWWB22HC.js.map +1 -0
- package/dist/chunk-IMB6FA2F.js +673 -0
- package/dist/chunk-IMB6FA2F.js.map +1 -0
- package/dist/{chunk-6YQT3SSM.js → chunk-IQYXXFRC.js} +22 -6
- package/dist/chunk-IQYXXFRC.js.map +1 -0
- package/dist/chunk-J6VJ5M2U.js +1201 -0
- package/dist/chunk-J6VJ5M2U.js.map +1 -0
- package/dist/{chunk-B3TSS7BT.js → chunk-KDQRNVBZ.js} +3 -3
- package/dist/{chunk-A4H7P7T7.js → chunk-LRWIP7QS.js} +41 -26
- package/dist/chunk-LRWIP7QS.js.map +1 -0
- package/dist/{chunk-DV7HUC4Z.js → chunk-LYC57OXF.js} +20 -117
- package/dist/chunk-LYC57OXF.js.map +1 -0
- package/dist/{chunk-HAKDLR6N.js → chunk-MAJ6QDRL.js} +65 -78
- package/dist/chunk-MAJ6QDRL.js.map +1 -0
- package/dist/{chunk-HJBF4WD2.js → chunk-MHA7Q3KU.js} +288 -108
- package/dist/chunk-MHA7Q3KU.js.map +1 -0
- package/dist/{chunk-UK4E2FUA.js → chunk-MQTVMB7W.js} +103 -76
- package/dist/chunk-MQTVMB7W.js.map +1 -0
- package/dist/chunk-NP47HRX2.js +136 -0
- package/dist/chunk-NP47HRX2.js.map +1 -0
- package/dist/{chunk-MI5TREN3.js → chunk-NYDAHE3Y.js} +52 -46
- package/dist/chunk-NYDAHE3Y.js.map +1 -0
- package/dist/{chunk-UWDCIFTP.js → chunk-P7ZYOC23.js} +41 -57
- package/dist/chunk-P7ZYOC23.js.map +1 -0
- package/dist/{chunk-3R27MJ5R.js → chunk-PFWHHYEO.js} +3 -3
- package/dist/{chunk-2SUFAE6X.js → chunk-PHPAK2L6.js} +44 -33
- package/dist/{chunk-2SUFAE6X.js.map → chunk-PHPAK2L6.js.map} +1 -1
- package/dist/{chunk-PPZ3K4YM.js → chunk-PRGPSNGA.js} +2 -2
- package/dist/{chunk-32JSYXEK.js → chunk-RGFMQFX7.js} +9 -23
- package/dist/{chunk-32JSYXEK.js.map → chunk-RGFMQFX7.js.map} +1 -1
- package/dist/{chunk-YVPZUN6X.js → chunk-TJVX42KY.js} +335 -38
- package/dist/chunk-TJVX42KY.js.map +1 -0
- package/dist/{chunk-RCZJQUTC.js → chunk-UDRAD7MF.js} +60 -27
- package/dist/chunk-UDRAD7MF.js.map +1 -0
- package/dist/{chunk-HY5HLFDU.js → chunk-UHDXXLYH.js} +148 -135
- package/dist/chunk-UHDXXLYH.js.map +1 -0
- package/dist/{chunk-RNFUVKC3.js → chunk-UPAAZU4W.js} +30 -13
- package/dist/chunk-UPAAZU4W.js.map +1 -0
- package/dist/{chunk-L6HSKTQE.js → chunk-W7LZLJAF.js} +4 -4
- package/dist/{chunk-HLAPOUEM.js → chunk-WB2FVIUS.js} +44 -12
- package/dist/chunk-WB2FVIUS.js.map +1 -0
- package/dist/{chunk-VE3IYNDN.js → chunk-WPHC6JBR.js} +9 -9
- package/dist/chunk-WPHC6JBR.js.map +1 -0
- package/dist/{chunk-KZXQWC7U.js → chunk-XHCP6ZNO.js} +201 -64
- package/dist/chunk-XHCP6ZNO.js.map +1 -0
- package/dist/{chunk-7F7LGMVO.js → chunk-XKR3VD2Z.js} +12 -12
- package/dist/{chunk-7F7LGMVO.js.map → chunk-XKR3VD2Z.js.map} +1 -1
- package/dist/{chunk-ICM463EO.js → chunk-XNK5UN2D.js} +5 -649
- package/dist/chunk-XNK5UN2D.js.map +1 -0
- package/dist/{chunk-Z5O2XBWA.js → chunk-XQLNZW7I.js} +40 -30
- package/dist/chunk-XQLNZW7I.js.map +1 -0
- package/dist/{chunk-QMIQR7EE.js → chunk-ZAYATXJD.js} +32 -316
- package/dist/chunk-ZAYATXJD.js.map +1 -0
- package/dist/cli.js +128 -60
- package/dist/cli.js.map +1 -1
- package/dist/compat-YPDZB34U.js +24 -0
- package/dist/{config-KGZUPAUP.js → config-56B4OHZE.js} +20 -17
- package/dist/{contract-JH2WBIO6.js → contract-HWLFFL3F.js} +2 -2
- package/dist/{conventions-RYN7OXJN.js → conventions-DW2CM5I3.js} +20 -16
- package/dist/{conventions-RYN7OXJN.js.map → conventions-DW2CM5I3.js.map} +1 -1
- package/dist/db-5NU77PJA.js +264 -0
- package/dist/db-5NU77PJA.js.map +1 -0
- package/dist/diagnostics-UCTS2VUF.js +25 -0
- package/dist/execute-R7AVOLNG.js +38 -0
- package/dist/{generate-CX6KNHZS.js → generate-O7GUXLN4.js} +46 -43
- package/dist/generate-O7GUXLN4.js.map +1 -0
- package/dist/{git-A2IP32PQ.js → git-LCG45YXD.js} +2 -3
- package/dist/history-6GSURU3H.js +101 -0
- package/dist/history-6GSURU3H.js.map +1 -0
- package/dist/init-DD6DBRC5.js +28 -0
- package/dist/{operations-CAKIT2BO.js → operations-HU5ZFEBA.js} +78 -52
- package/dist/operations-HU5ZFEBA.js.map +1 -0
- package/dist/{permissions-G4XCAESF.js → permissions-NNNSRC4P.js} +5 -6
- package/dist/plan-XR6B7EM3.js +38 -0
- package/dist/{policy-WX6O2JID.js → policy-RFJOJQL6.js} +17 -15
- package/dist/{policy-WX6O2JID.js.map → policy-RFJOJQL6.js.map} +1 -1
- package/dist/pr-OSCNJCOO.js +42 -0
- package/dist/pr-review-IKIYHFBO.js +33 -0
- package/dist/prd-HFDZJJQR.js +37 -0
- package/dist/{ps-76L6ZAZL.js → ps-AKRHGPSR.js} +26 -15
- package/dist/ps-AKRHGPSR.js.map +1 -0
- package/dist/{recorder-N7U66HCO.js → recorder-T7426CKV.js} +11 -7
- package/dist/registry-DWRKD65A.js +17 -0
- package/dist/repository-TABBRW2C.js +80 -0
- package/dist/resume-GDXKK5XP.js +278 -0
- package/dist/resume-GDXKK5XP.js.map +1 -0
- package/dist/review-JJHMQA2O.js +38 -0
- package/dist/routing-FPNUVE7T.js +35 -0
- package/dist/run-RLCURLLL.js +61 -0
- package/dist/{session-publisher-KAJQRHNZ.js → session-publisher-Q5TNEZPX.js} +3 -3
- package/dist/session-publisher-Q5TNEZPX.js.map +1 -0
- package/dist/{state-manager-UPHO2EA2.js → state-manager-IGNTD4AB.js} +9 -4
- package/dist/state-manager-IGNTD4AB.js.map +1 -0
- package/dist/{usage-U7QNGCPK.js → usage-XW27AL5K.js} +69 -63
- package/dist/usage-XW27AL5K.js.map +1 -0
- package/dist/web-NCDBVZ5Y.js +205 -0
- package/dist/web-NCDBVZ5Y.js.map +1 -0
- package/package.json +20 -7
- package/prompts/analyze.md +16 -19
- package/prompts/execute.md +73 -144
- package/prompts/generate.md +23 -10
- package/prompts/plan.md +32 -60
- package/prompts/pr-review.md +58 -40
- package/prompts/pr.md +50 -16
- package/prompts/prd.md +26 -19
- package/prompts/review.md +41 -18
- package/web/AGENTS.md +115 -7
- package/web/public/app.css +393 -245
- package/web/public/app.js +212 -143
- package/web/public/index.html +106 -78
- package/dist/agent-TB6W7PT7.js +0 -30
- package/dist/analyze-6M3KEOX7.js.map +0 -1
- package/dist/apply-ADB3ZK7W.js.map +0 -1
- package/dist/chunk-6YQT3SSM.js.map +0 -1
- package/dist/chunk-A4H7P7T7.js.map +0 -1
- package/dist/chunk-ANI2NTR4.js.map +0 -1
- package/dist/chunk-CRGIQFUQ.js.map +0 -1
- package/dist/chunk-D6ZBGJ3L.js.map +0 -1
- package/dist/chunk-DV7HUC4Z.js.map +0 -1
- package/dist/chunk-E6KXCV3T.js.map +0 -1
- package/dist/chunk-HAKDLR6N.js.map +0 -1
- package/dist/chunk-HJBF4WD2.js.map +0 -1
- package/dist/chunk-HLAPOUEM.js.map +0 -1
- package/dist/chunk-HY5HLFDU.js.map +0 -1
- package/dist/chunk-ICM463EO.js.map +0 -1
- package/dist/chunk-ISZ54SVE.js.map +0 -1
- package/dist/chunk-KZ5EY5XX.js.map +0 -1
- package/dist/chunk-KZXQWC7U.js.map +0 -1
- package/dist/chunk-MI5TREN3.js.map +0 -1
- package/dist/chunk-QMIQR7EE.js.map +0 -1
- package/dist/chunk-RCZJQUTC.js.map +0 -1
- package/dist/chunk-RNFUVKC3.js.map +0 -1
- package/dist/chunk-SGTTR4XW.js.map +0 -1
- package/dist/chunk-SOM4QA6C.js.map +0 -1
- package/dist/chunk-UK4E2FUA.js.map +0 -1
- package/dist/chunk-UWDCIFTP.js.map +0 -1
- package/dist/chunk-VE3IYNDN.js.map +0 -1
- package/dist/chunk-WCCZJL7W.js +0 -141
- package/dist/chunk-WCCZJL7W.js.map +0 -1
- package/dist/chunk-XEYH6ZZI.js +0 -289
- package/dist/chunk-XEYH6ZZI.js.map +0 -1
- package/dist/chunk-XZUEFTUP.js +0 -243
- package/dist/chunk-XZUEFTUP.js.map +0 -1
- package/dist/chunk-YII4U7ZD.js.map +0 -1
- package/dist/chunk-YVPZUN6X.js.map +0 -1
- package/dist/chunk-Z5O2XBWA.js.map +0 -1
- package/dist/diagnostics-HTCWF7F4.js +0 -23
- package/dist/execute-QL3ZQIAP.js +0 -37
- package/dist/generate-CX6KNHZS.js.map +0 -1
- package/dist/init-PDVQDRS6.js +0 -24
- package/dist/operations-CAKIT2BO.js.map +0 -1
- package/dist/plan-PRORGS3L.js +0 -34
- package/dist/pr-27RMQI5F.js +0 -39
- package/dist/pr-review-VNESMAMT.js +0 -32
- package/dist/prd-ZNC4T5BS.js +0 -33
- package/dist/ps-76L6ZAZL.js.map +0 -1
- package/dist/registry-XLHHYLTR.js +0 -15
- package/dist/resume-VLWIE264.js +0 -244
- package/dist/resume-VLWIE264.js.map +0 -1
- package/dist/review-7CREJDKB.js +0 -35
- package/dist/routing-VCDYHSXA.js +0 -33
- package/dist/run-3MPORLF5.js +0 -56
- package/dist/usage-U7QNGCPK.js.map +0 -1
- package/dist/web-HUNDHCJ4.js +0 -172
- package/dist/web-HUNDHCJ4.js.map +0 -1
- /package/dist/{agent-TB6W7PT7.js.map → agent-EWKA7357.js.map} +0 -0
- /package/dist/{bench-FM7I7EIV.js.map → bench-ZJEIACUK.js.map} +0 -0
- /package/dist/{chunk-RYOUZ6EK.js.map → chunk-23VHVVKW.js.map} +0 -0
- /package/dist/{chunk-C5TL7T4V.js.map → chunk-2YM2VH6R.js.map} +0 -0
- /package/dist/{chunk-QPGEFLMY.js.map → chunk-7CMVL4X6.js.map} +0 -0
- /package/dist/{chunk-DUS5ZNKL.js.map → chunk-DIMDAYZS.js.map} +0 -0
- /package/dist/{chunk-DGRTJD5R.js.map → chunk-DXH4TQRB.js.map} +0 -0
- /package/dist/{chunk-YUNBOUQH.js.map → chunk-ERAG5IQB.js.map} +0 -0
- /package/dist/{chunk-B3TSS7BT.js.map → chunk-KDQRNVBZ.js.map} +0 -0
- /package/dist/{chunk-3R27MJ5R.js.map → chunk-PFWHHYEO.js.map} +0 -0
- /package/dist/{chunk-PPZ3K4YM.js.map → chunk-PRGPSNGA.js.map} +0 -0
- /package/dist/{chunk-L6HSKTQE.js.map → chunk-W7LZLJAF.js.map} +0 -0
- /package/dist/{config-KGZUPAUP.js.map → compat-YPDZB34U.js.map} +0 -0
- /package/dist/{contract-JH2WBIO6.js.map → config-56B4OHZE.js.map} +0 -0
- /package/dist/{diagnostics-HTCWF7F4.js.map → contract-HWLFFL3F.js.map} +0 -0
- /package/dist/{execute-QL3ZQIAP.js.map → diagnostics-UCTS2VUF.js.map} +0 -0
- /package/dist/{git-A2IP32PQ.js.map → execute-R7AVOLNG.js.map} +0 -0
- /package/dist/{init-PDVQDRS6.js.map → git-LCG45YXD.js.map} +0 -0
- /package/dist/{permissions-G4XCAESF.js.map → init-DD6DBRC5.js.map} +0 -0
- /package/dist/{plan-PRORGS3L.js.map → permissions-NNNSRC4P.js.map} +0 -0
- /package/dist/{pr-27RMQI5F.js.map → plan-XR6B7EM3.js.map} +0 -0
- /package/dist/{pr-review-VNESMAMT.js.map → pr-OSCNJCOO.js.map} +0 -0
- /package/dist/{prd-ZNC4T5BS.js.map → pr-review-IKIYHFBO.js.map} +0 -0
- /package/dist/{recorder-N7U66HCO.js.map → prd-HFDZJJQR.js.map} +0 -0
- /package/dist/{registry-XLHHYLTR.js.map → recorder-T7426CKV.js.map} +0 -0
- /package/dist/{review-7CREJDKB.js.map → registry-DWRKD65A.js.map} +0 -0
- /package/dist/{routing-VCDYHSXA.js.map → repository-TABBRW2C.js.map} +0 -0
- /package/dist/{run-3MPORLF5.js.map → review-JJHMQA2O.js.map} +0 -0
- /package/dist/{session-publisher-KAJQRHNZ.js.map → routing-FPNUVE7T.js.map} +0 -0
- /package/dist/{state-manager-UPHO2EA2.js.map → run-RLCURLLL.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/storage/db/repository.ts","../src/storage/db/index.ts","../src/storage/db/driver.ts","../src/storage/db/migrations.ts"],"sourcesContent":["import { createHash, randomUUID } from 'node:crypto';\nimport { mkdir, readFile } from 'node:fs/promises';\nimport { basename, dirname } from 'node:path';\nimport type { SessionEvent, SessionSnapshot } from '../../core/session-state.js';\nimport type { ExecutionPlan } from '../../execution/types.js';\nimport { taskPlanSchema } from '../../schemas.js';\nimport type { ExecutionRecord } from '../../telemetry/types.js';\nimport type { TaskPlan } from '../../types.js';\nimport { writeFileAtomic } from '../../utils/fs.js';\nimport type {\n ProviderHealthRecord,\n ProvidersHealth,\n UserStoryNumberingDecision,\n} from '../schemas.js';\nimport {\n databaseOptionsForProject,\n type OpenIssueFlowDatabaseOptions,\n openIssueFlowDatabase,\n} from './index.js';\n\n/** Identity needed to address one plan in the shared SQLite database. */\nexport interface PlanRepositoryContext {\n tasksPath: string;\n projectId: string;\n issueId: string;\n projectRoot: string;\n /** Test/embedding seam for a non-default Issue Flow home. */\n databaseOptions?: OpenIssueFlowDatabaseOptions;\n /** Explicit row retention. Omitted values retain history indefinitely. */\n retention?: StoredRetentionPolicy;\n}\n\nexport interface StoredRetentionPolicy {\n executions?: number;\n events?: number;\n snapshots?: number;\n}\n\n/** Identity for one multi-issue queue projection in the shared database. */\nexport interface QueueRepositoryContext {\n planFile: string;\n projectId: string;\n projectRoot: string;\n databaseOptions?: OpenIssueFlowDatabaseOptions;\n retention?: StoredRetentionPolicy;\n}\n\nconst contexts = new Map<string, PlanRepositoryContext>();\nconst providerHealthContexts = new Map<string, PlanRepositoryContext>();\nconst verificationContexts = new Map<string, PlanRepositoryContext>();\nconst lastBranchContexts = new Map<string, PlanRepositoryContext>();\nconst queueContexts = new Map<string, QueueRepositoryContext>();\nconst agentProjectionWindows = new Set<string>();\n\n/** Keep the tolerant US-NNN interpretation next to the indexed representation. */\nfunction storyNumber(id: string): number | null {\n const matches = id.match(/\\d+/g);\n if (matches === null || matches.length === 0) return null;\n const value = Number.parseInt(matches.at(-1) ?? '', 10);\n return Number.isNaN(value) ? null : value;\n}\n\n/** Register the SQLite-backed projection for an issue path resolved by storage. */\nexport function registerPlanRepository(context: PlanRepositoryContext): void {\n contexts.set(context.tasksPath, context);\n}\n\nexport function getPlanRepository(path: string): PlanRepositoryContext | undefined {\n return contexts.get(path);\n}\n\n/** Register the JSON compatibility projection for a multi-issue queue. */\nexport function registerQueueRepository(context: QueueRepositoryContext): void {\n queueContexts.set(context.planFile, context);\n}\n\nexport function getQueueRepository(path: string): QueueRepositoryContext | undefined {\n return queueContexts.get(path);\n}\n\n/** Register project- and issue-level compatibility projections with their canonical store. */\nexport function registerStorageProjections(input: {\n context: PlanRepositoryContext;\n providersHealthFile?: string;\n verifyFile?: string;\n lastBranchFile?: string;\n}): void {\n if (input.providersHealthFile !== undefined) {\n providerHealthContexts.set(input.providersHealthFile, input.context);\n }\n if (input.verifyFile !== undefined) verificationContexts.set(input.verifyFile, input.context);\n if (input.lastBranchFile !== undefined)\n lastBranchContexts.set(input.lastBranchFile, input.context);\n}\n\nexport function getProviderHealthRepository(path: string): PlanRepositoryContext | undefined {\n return providerHealthContexts.get(path);\n}\n\nexport function getVerificationRepository(path: string): PlanRepositoryContext | undefined {\n return verificationContexts.get(path);\n}\n\nexport function getLastBranchRepository(path: string): PlanRepositoryContext | undefined {\n return lastBranchContexts.get(path);\n}\n\n/**\n * Compatibility bootstrap for direct engine consumers that bind telemetry\n * without first resolving an Issue Flow issue path. Production issue commands\n * always register their real project identity in `resolve.ts`; this preserves\n * the standalone API without making telemetry parse or rewrite its projection.\n */\nexport async function ensurePlanRepository(path: string): Promise<PlanRepositoryContext | null> {\n const known = getPlanRepository(path);\n if (known !== undefined) return known;\n const digest = createHash('sha256').update(path).digest('hex').slice(0, 24);\n const context: PlanRepositoryContext = {\n tasksPath: path,\n projectId: `projection-${digest}`,\n issueId: `projection-${digest}`,\n projectRoot: dirname(path),\n };\n try {\n const plan = parsePlan(await readFile(path, 'utf-8'), path);\n await saveStoredPlan(context, plan);\n registerPlanRepository(context);\n return context;\n } catch {\n return null;\n }\n}\n\nexport function resetPlanRepositories(): void {\n contexts.clear();\n providerHealthContexts.clear();\n verificationContexts.clear();\n lastBranchContexts.clear();\n queueContexts.clear();\n agentProjectionWindows.clear();\n}\n\n/** Apply only an explicitly configured, positive row limit for one project. */\nfunction applyStoredRetention(\n database: Awaited<ReturnType<typeof openIssueFlowDatabase>>,\n projectId: string,\n retention: StoredRetentionPolicy | undefined,\n): void {\n const trim = (table: 'executions' | 'events' | 'snapshots', limit: number | undefined) => {\n if (limit === undefined || limit === 0) return;\n const timestamp =\n table === 'executions' ? 'started_at' : table === 'events' ? 'occurred_at' : 'updated_at';\n database\n .prepare(\n `DELETE FROM ${table} WHERE id IN (\n SELECT id FROM ${table} WHERE project_id = ?\n ORDER BY ${timestamp} DESC, rowid DESC LIMIT -1 OFFSET ?\n )`,\n )\n .run(projectId, limit);\n };\n trim('executions', retention?.executions);\n trim('events', retention?.events);\n trim('snapshots', retention?.snapshots);\n}\n\nfunction ensureStoredProject(\n database: Awaited<ReturnType<typeof openIssueFlowDatabase>>,\n context: PlanRepositoryContext,\n timestamp: string,\n): void {\n database\n .prepare(\n `INSERT INTO projects (id, root, remote_url, created_at, updated_at) VALUES (?, ?, NULL, ?, ?)\n ON CONFLICT(id) DO UPDATE SET root = excluded.root, updated_at = excluded.updated_at`,\n )\n .run(context.projectId, context.projectRoot, timestamp, timestamp);\n}\n\n/**\n * Evidence and other issue-scoped facts can arrive before the plan phase has\n * materialized a canonical pipeline. Keep those writes relationally valid\n * without making the evidence writer parse or create a task-plan projection.\n * A later plan write replaces this placeholder with the plan's own status and\n * descriptive fields.\n */\nfunction ensureStoredIssue(\n database: Awaited<ReturnType<typeof openIssueFlowDatabase>>,\n context: PlanRepositoryContext,\n timestamp: string,\n): void {\n database\n .prepare(\n `INSERT INTO issues (project_id, id, title, status, branch_name, created_at, updated_at)\n VALUES (?, ?, NULL, 'pending', NULL, ?, ?)\n ON CONFLICT(project_id, id) DO NOTHING`,\n )\n .run(context.projectId, context.issueId, timestamp, timestamp);\n}\n\n/** Prevent telemetry projection refreshes while an agent owns tasks.json. */\nexport function setAgentProjectionWindow(path: string, active: boolean): void {\n if (active) agentProjectionWindows.add(path);\n else agentProjectionWindows.delete(path);\n}\n\nfunction parsePlan(value: string, path: string): TaskPlan {\n try {\n return taskPlanSchema.parse(JSON.parse(value)) as TaskPlan;\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n throw new Error(`Invalid SQLite task plan for ${path}: ${detail}`, { cause: error });\n }\n}\n\nasync function withDatabase<T>(\n work: (database: Awaited<ReturnType<typeof openIssueFlowDatabase>>) => T,\n options: OpenIssueFlowDatabaseOptions = {},\n): Promise<T> {\n const database = await openIssueFlowDatabase(options);\n try {\n return work(database);\n } finally {\n database.close();\n }\n}\n\n/** Read the canonical plan plus execution rows, never the projection file. */\nexport async function loadStoredPlan(context: PlanRepositoryContext): Promise<TaskPlan> {\n return withDatabase((database) => {\n const row = database\n .prepare('SELECT * FROM pipelines WHERE project_id = ? AND issue_id = ?')\n .get<Record<string, unknown>>(context.projectId, context.issueId);\n if (row === undefined) {\n throw new Error(`No SQLite task plan exists for issue ${context.issueId}. Run plan first.`);\n }\n // Version 4 is fully relational. The fallback keeps a database imported by\n // an older binary readable until its next normal plan materialization.\n if (row.project === null || row.project === undefined) {\n return parsePlan(String(row.state_json), context.tasksPath);\n }\n const dependencies = new Map<string, string[]>();\n for (const dependency of database\n .prepare(\n 'SELECT story_id, depends_on_story_id FROM story_dependencies WHERE project_id = ? AND issue_id = ?',\n )\n .all<{ story_id: string; depends_on_story_id: string }>(context.projectId, context.issueId)) {\n dependencies.set(dependency.story_id, [\n ...(dependencies.get(dependency.story_id) ?? []),\n dependency.depends_on_story_id,\n ]);\n }\n const stories = database\n .prepare(\n 'SELECT * FROM stories WHERE project_id = ? AND issue_id = ? ORDER BY priority, rowid',\n )\n .all<Record<string, unknown>>(context.projectId, context.issueId)\n .map((story) => ({\n id: String(story.id),\n title: String(story.title),\n description: String(story.description ?? ''),\n acceptanceCriteria: JSON.parse(String(story.acceptance_criteria_json ?? '[]')) as string[],\n priority: Number(story.priority),\n passes: Number(story.passes) === 1,\n notes: String(story.notes ?? ''),\n ...(story.duration_seconds === null\n ? {}\n : { durationSeconds: Number(story.duration_seconds) }),\n ...(story.status === null\n ? {}\n : { status: String(story.status) as TaskPlan['userStories'][number]['status'] }),\n ...(story.stage === null\n ? {}\n : { stage: String(story.stage) as TaskPlan['userStories'][number]['stage'] }),\n ...(story.stage_since === null ? {} : { stageSince: String(story.stage_since) }),\n ...(story.stage_detail === null ? {} : { stageDetail: String(story.stage_detail) }),\n ...(story.input_tokens === null ? {} : { inputTokens: Number(story.input_tokens) }),\n ...(story.output_tokens === null ? {} : { outputTokens: Number(story.output_tokens) }),\n ...(story.cache_read_tokens === null\n ? {}\n : { cacheReadTokens: Number(story.cache_read_tokens) }),\n ...(story.cache_creation_tokens === null\n ? {}\n : { cacheCreationTokens: Number(story.cache_creation_tokens) }),\n ...(dependencies.has(String(story.id))\n ? { dependencies: dependencies.get(String(story.id)) }\n : {}),\n }));\n const plan: TaskPlan = {\n project: String(row.project),\n issueNumber: String(row.issue_number),\n issueUrl: String(row.issue_url ?? ''),\n branchName: String(row.branch_name ?? ''),\n ...(Number(row.no_branch) === 1 ? { noBranch: true } : {}),\n ...(row.close_issue == null ? {} : { closeIssue: Number(row.close_issue) === 1 }),\n ...(row.issue_closed_at == null ? {} : { issueClosedAt: String(row.issue_closed_at) }),\n description: String(row.description ?? ''),\n issueStatus: String(row.issue_status) as TaskPlan['issueStatus'],\n completedAt: (row.completed_at as string | null) ?? null,\n lastAttemptAt: (row.last_attempt_at as string | null) ?? null,\n lastError:\n row.last_error_category === null\n ? null\n : {\n category: String(row.last_error_category),\n message: String(row.last_error_message),\n at: String(row.last_error_at),\n },\n correctionCycle: Number(row.correction_cycle),\n maxCorrectionCycles: Number(row.max_correction_cycles),\n lastReviewFindings: (row.last_review_findings as string | null) ?? null,\n pipeline: {\n ...(Number(row.analyze_completed) === 1 ? { analyzeCompleted: true } : {}),\n prdCompleted: Number(row.prd_completed) === 1,\n jsonCompleted: Number(row.json_completed) === 1,\n executionCompleted: Number(row.execution_completed) === 1,\n reviewCompleted: Number(row.review_completed) === 1,\n prCreated: Number(row.pr_created) === 1,\n ...(row.pr_review_completed === null\n ? {}\n : { prReviewCompleted: Number(row.pr_review_completed) === 1 }),\n },\n ...(row.run_status === null\n ? {}\n : {\n runState: {\n status: row.run_status as TaskPlan['runState'] extends infer R\n ? R extends { status: infer S }\n ? S\n : never\n : never,\n currentPhase: (row.run_phase as string | null) ?? null,\n attempt: Number(row.run_attempt ?? 0),\n lastHeartbeatAt: (row.run_heartbeat_at as string | null) ?? null,\n blockedReason: (row.run_blocked_reason as string | null) ?? null,\n owner:\n row.run_owner_pid === null\n ? null\n : {\n pid: Number(row.run_owner_pid),\n host: String(row.run_owner_host),\n startedAt: String(row.run_owner_started_at),\n },\n },\n }),\n ...(row.pr_number === null\n ? {}\n : {\n pullRequest: {\n number: Number(row.pr_number),\n url: String(row.pr_url),\n headBranch: String(row.pr_head_branch),\n createdAt: String(row.pr_created_at),\n },\n }),\n ...(row.pr_review_enabled === null\n ? {}\n : {\n prReview: {\n enabled: Number(row.pr_review_enabled) === 1,\n ...(row.pr_review_pull_request_number === null\n ? {}\n : { pullRequestNumber: Number(row.pr_review_pull_request_number) }),\n rounds: Number(row.pr_review_rounds ?? 0),\n ...(row.pr_review_recommendation === null\n ? {}\n : {\n lastRecommendation: String(row.pr_review_recommendation) as NonNullable<\n TaskPlan['prReview']\n >['lastRecommendation'],\n }),\n ...(row.pr_reviewed_at === null\n ? {}\n : { lastReviewedAt: String(row.pr_reviewed_at) }),\n },\n }),\n userStories: stories,\n };\n const executions = database\n .prepare(\n 'SELECT payload_json FROM executions WHERE project_id = ? AND issue_id = ? ORDER BY started_at, rowid',\n )\n .all<{ payload_json: string }>(context.projectId, context.issueId)\n .map((execution) => JSON.parse(execution.payload_json));\n return executions.length > 0 ? { ...plan, executions } : plan;\n }, context.databaseOptions);\n}\n\nexport function writePlanRows(\n database: Awaited<ReturnType<typeof openIssueFlowDatabase>>,\n context: PlanRepositoryContext,\n plan: TaskPlan,\n): void {\n const timestamp = plan.lastAttemptAt ?? new Date().toISOString();\n database\n .prepare(\n `INSERT INTO projects (id, root, remote_url, created_at, updated_at) VALUES (?, ?, NULL, ?, ?)\n ON CONFLICT(id) DO UPDATE SET root = excluded.root, updated_at = excluded.updated_at`,\n )\n .run(context.projectId, context.projectRoot, timestamp, timestamp);\n database\n .prepare(\n `INSERT INTO issues (project_id, id, title, status, branch_name, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(project_id, id) DO UPDATE SET title = excluded.title, status = excluded.status,\n branch_name = excluded.branch_name, updated_at = excluded.updated_at`,\n )\n .run(\n context.projectId,\n context.issueId,\n plan.description || null,\n plan.issueStatus,\n plan.branchName || null,\n timestamp,\n timestamp,\n );\n // `state_json` is kept as an empty compatibility column for pre-v4 database\n // files. Relational columns below are the source of truth.\n database\n .prepare(\n `INSERT INTO pipelines (project_id, issue_id, state_json, updated_at) VALUES (?, ?, ?, ?)\n ON CONFLICT(project_id, issue_id) DO UPDATE SET state_json = excluded.state_json,\n updated_at = excluded.updated_at`,\n )\n .run(context.projectId, context.issueId, '{}', timestamp);\n database\n .prepare(\n `UPDATE pipelines SET project = ?, issue_number = ?, issue_url = ?, branch_name = ?, no_branch = ?,\n description = ?, issue_status = ?, completed_at = ?, last_attempt_at = ?, last_error_category = ?,\n last_error_message = ?, last_error_at = ?, correction_cycle = ?, max_correction_cycles = ?,\n last_review_findings = ?, analyze_completed = ?, prd_completed = ?, json_completed = ?,\n execution_completed = ?, review_completed = ?, pr_created = ?, pr_review_completed = ?, run_status = ?,\n run_phase = ?, run_attempt = ?, run_heartbeat_at = ?, run_blocked_reason = ?, run_owner_pid = ?,\n run_owner_host = ?, run_owner_started_at = ?, pr_number = ?, pr_url = ?, pr_head_branch = ?,\n pr_created_at = ?, pr_review_enabled = ?, pr_review_pull_request_number = ?, pr_review_rounds = ?,\n pr_review_recommendation = ?, pr_reviewed_at = ?, close_issue = ?, issue_closed_at = ? WHERE project_id = ? AND issue_id = ?`,\n )\n .run(\n plan.project,\n String(plan.issueNumber),\n plan.issueUrl,\n plan.branchName,\n plan.noBranch === true ? 1 : 0,\n plan.description,\n plan.issueStatus,\n plan.completedAt,\n plan.lastAttemptAt,\n plan.lastError?.category ?? null,\n plan.lastError?.message ?? null,\n plan.lastError?.at ?? null,\n plan.correctionCycle,\n plan.maxCorrectionCycles,\n plan.lastReviewFindings,\n plan.pipeline.analyzeCompleted === true ? 1 : 0,\n plan.pipeline.prdCompleted ? 1 : 0,\n plan.pipeline.jsonCompleted ? 1 : 0,\n plan.pipeline.executionCompleted ? 1 : 0,\n plan.pipeline.reviewCompleted ? 1 : 0,\n plan.pipeline.prCreated ? 1 : 0,\n plan.pipeline.prReviewCompleted === undefined\n ? null\n : plan.pipeline.prReviewCompleted\n ? 1\n : 0,\n plan.runState?.status ?? null,\n plan.runState?.currentPhase ?? null,\n plan.runState?.attempt ?? null,\n plan.runState?.lastHeartbeatAt ?? null,\n plan.runState?.blockedReason ?? null,\n plan.runState?.owner?.pid ?? null,\n plan.runState?.owner?.host ?? null,\n plan.runState?.owner?.startedAt ?? null,\n plan.pullRequest?.number ?? null,\n plan.pullRequest?.url ?? null,\n plan.pullRequest?.headBranch ?? null,\n plan.pullRequest?.createdAt ?? null,\n plan.prReview === undefined ? null : plan.prReview.enabled ? 1 : 0,\n plan.prReview?.pullRequestNumber ?? null,\n plan.prReview?.rounds ?? null,\n plan.prReview?.lastRecommendation ?? null,\n plan.prReview?.lastReviewedAt ?? null,\n plan.closeIssue === undefined ? null : plan.closeIssue ? 1 : 0,\n plan.issueClosedAt ?? null,\n context.projectId,\n context.issueId,\n );\n\n database\n .prepare('DELETE FROM story_dependencies WHERE project_id = ? AND issue_id = ?')\n .run(context.projectId, context.issueId);\n database\n .prepare('DELETE FROM stories WHERE project_id = ? AND issue_id = ?')\n .run(context.projectId, context.issueId);\n for (const story of plan.userStories) {\n database\n .prepare(\n `INSERT INTO stories (project_id, issue_id, id, title, priority, passes, notes, story_number)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,\n )\n .run(\n context.projectId,\n context.issueId,\n story.id,\n story.title,\n story.priority,\n story.passes ? 1 : 0,\n story.notes,\n storyNumber(story.id),\n );\n database\n .prepare(\n `UPDATE stories SET description = ?, acceptance_criteria_json = ?, duration_seconds = ?, status = ?,\n stage = ?, stage_since = ?, stage_detail = ?, input_tokens = ?, output_tokens = ?,\n cache_read_tokens = ?, cache_creation_tokens = ? WHERE project_id = ? AND issue_id = ? AND id = ?`,\n )\n .run(\n story.description,\n JSON.stringify(story.acceptanceCriteria),\n story.durationSeconds ?? null,\n story.status ?? null,\n story.stage ?? null,\n story.stageSince ?? null,\n story.stageDetail ?? null,\n story.inputTokens ?? null,\n story.outputTokens ?? null,\n story.cacheReadTokens ?? null,\n story.cacheCreationTokens ?? null,\n context.projectId,\n context.issueId,\n story.id,\n );\n }\n for (const story of plan.userStories) {\n for (const dependency of story.dependencies ?? []) {\n database\n .prepare(\n `INSERT INTO story_dependencies (project_id, issue_id, story_id, depends_on_story_id)\n VALUES (?, ?, ?, ?)`,\n )\n .run(context.projectId, context.issueId, story.id, dependency);\n }\n }\n\n if (plan.pullRequest !== undefined) {\n const pullRequestId = `pr:${context.projectId}:${context.issueId}:${plan.pullRequest.number}`;\n database\n .prepare(\n `INSERT INTO pull_requests (id, project_id, issue_id, number, url, status, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(id) DO UPDATE SET url = excluded.url, status = excluded.status`,\n )\n .run(\n pullRequestId,\n context.projectId,\n context.issueId,\n plan.pullRequest.number,\n plan.pullRequest.url,\n plan.pipeline.prCreated ? 'created' : 'pending',\n plan.pullRequest.createdAt,\n );\n if (plan.prReview?.lastReviewedAt !== undefined) {\n database\n .prepare(\n `INSERT INTO reviews (id, pull_request_id, status, created_at, payload_json)\n VALUES (?, ?, ?, ?, ?)\n ON CONFLICT(id) DO UPDATE SET status = excluded.status, created_at = excluded.created_at,\n payload_json = excluded.payload_json`,\n )\n .run(\n `review:${pullRequestId}:${plan.prReview.rounds}`,\n pullRequestId,\n plan.prReview.lastRecommendation ?? 'unknown',\n plan.prReview.lastReviewedAt,\n JSON.stringify(plan.prReview),\n );\n }\n }\n}\n\n/** Persist the canonical plan and refresh its file projection atomically. */\nexport async function saveStoredPlan(\n context: PlanRepositoryContext,\n plan: TaskPlan,\n): Promise<void> {\n await withDatabase(\n (database) => database.transaction(() => writePlanRows(database, context, plan)),\n context.databaseOptions,\n );\n await materializePlan(context);\n}\n\n/** Write the compatibility file used by agents and legacy prompt contracts. */\nexport async function materializePlan(\n context: PlanRepositoryContext,\n plan?: TaskPlan,\n): Promise<void> {\n const projection = plan ?? (await loadStoredPlan(context));\n const content = `${JSON.stringify(projection, null, 2)}\\n`;\n await writeFileAtomic(context.tasksPath, content);\n const sha256 = createHash('sha256').update(content).digest('hex');\n await withDatabase(\n (database) =>\n database\n .prepare(\n `INSERT INTO migrated_artifacts (source_path, sha256, migrated_at, table_counts_json)\n VALUES (?, ?, ?, ?)\n ON CONFLICT(source_path) DO UPDATE SET sha256 = excluded.sha256,\n migrated_at = excluded.migrated_at, table_counts_json = excluded.table_counts_json`,\n )\n .run(context.tasksPath, sha256, new Date().toISOString(), '{}'),\n context.databaseOptions,\n );\n}\n\n/** Persist a queue's coordination state before refreshing its readable projection. */\nexport async function saveStoredQueue(\n context: QueueRepositoryContext,\n plan: ExecutionPlan,\n): Promise<void> {\n await withDatabase(\n (database) =>\n database.transaction(() => {\n ensureStoredProject(\n database,\n {\n tasksPath: '',\n projectId: context.projectId,\n issueId: '',\n projectRoot: context.projectRoot,\n },\n plan.updatedAt,\n );\n database\n .prepare(\n `INSERT INTO queues (id, project_id, status, payload_json, created_at, updated_at,\n requested_json, branch_name, no_branch, pr_review, truncated)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(id) DO UPDATE SET status = excluded.status, payload_json = excluded.payload_json,\n updated_at = excluded.updated_at, requested_json = excluded.requested_json,\n branch_name = excluded.branch_name, no_branch = excluded.no_branch,\n pr_review = excluded.pr_review, truncated = excluded.truncated`,\n )\n .run(\n plan.id,\n context.projectId,\n plan.status,\n JSON.stringify(plan),\n plan.createdAt,\n plan.updatedAt,\n JSON.stringify(plan.requested),\n plan.branchName,\n plan.noBranch ? 1 : 0,\n plan.prReview ? 1 : 0,\n plan.truncated ? 1 : 0,\n );\n database.prepare('DELETE FROM queue_dependencies WHERE queue_id = ?').run(plan.id);\n database.prepare('DELETE FROM queue_issues WHERE queue_id = ?').run(plan.id);\n for (const entry of plan.issues) {\n database\n .prepare(\n `INSERT INTO issues (project_id, id, title, status, branch_name, created_at, updated_at)\n VALUES (?, ?, ?, ?, NULL, ?, ?)\n ON CONFLICT(project_id, id) DO UPDATE SET title = excluded.title, status = excluded.status,\n updated_at = excluded.updated_at`,\n )\n .run(\n context.projectId,\n entry.id,\n entry.title,\n entry.status,\n plan.createdAt,\n plan.updatedAt,\n );\n database\n .prepare(\n `INSERT INTO queue_issues (queue_id, project_id, issue_id, position, status, number, title, url,\n source, origin, role, priority, heuristic, failed_phase, last_error_category,\n last_error_message, last_error_at, attempts, blocked_reason, started_at, completed_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n )\n .run(\n plan.id,\n context.projectId,\n entry.id,\n entry.position,\n entry.status,\n entry.number,\n entry.title,\n entry.url,\n entry.source,\n entry.origin,\n entry.role,\n entry.priority,\n entry.heuristic ? 1 : 0,\n entry.failedPhase,\n entry.lastError?.category ?? null,\n entry.lastError?.message ?? null,\n entry.lastError?.at ?? null,\n entry.attempts,\n entry.blockedReason,\n entry.startedAt,\n entry.completedAt,\n );\n for (const dependency of entry.dependsOn) {\n database\n .prepare(\n 'INSERT INTO queue_dependencies (queue_id, issue_id, depends_on_issue_id) VALUES (?, ?, ?)',\n )\n .run(plan.id, entry.id, dependency);\n }\n }\n }),\n context.databaseOptions,\n );\n await mkdir(dirname(context.planFile), { recursive: true });\n await writeFileAtomic(context.planFile, `${JSON.stringify(plan, null, 2)}\\n`);\n}\n\n/** Load the canonical queue. The projection remains only for older JSON mode callers. */\nexport async function loadStoredQueue(\n context: QueueRepositoryContext,\n): Promise<ExecutionPlan | null> {\n return withDatabase((database) => {\n const row = database\n .prepare('SELECT payload_json FROM queues WHERE id = ? AND project_id = ?')\n .get<{ payload_json: string }>(\n context.planFile === '' ? '' : basename(dirname(context.planFile)),\n context.projectId,\n );\n return row === undefined ? null : (JSON.parse(row.payload_json) as ExecutionPlan);\n }, context.databaseOptions);\n}\n\n/**\n * Reingest only the fields an execution agent is allowed to change. This is a\n * deliberate merge, not a file import: telemetry and pipeline updates made\n * while the agent ran remain authoritative in the database.\n */\nexport async function ingestAgentPlan(\n context: PlanRepositoryContext,\n baseline?: Pick<TaskPlan, 'lastReviewFindings' | 'lastError'>,\n): Promise<TaskPlan> {\n const submitted = parsePlan(await readFile(context.tasksPath, 'utf-8'), context.tasksPath);\n await withDatabase(\n (database) =>\n database.transaction(() => {\n for (const story of submitted.userStories) {\n database\n .prepare(\n 'UPDATE stories SET passes = ?, notes = ? WHERE project_id = ? AND issue_id = ? AND id = ?',\n )\n .run(story.passes ? 1 : 0, story.notes, context.projectId, context.issueId, story.id);\n }\n if (baseline !== undefined) {\n // A correction can acknowledge its own findings, never erase a newer review.\n if (submitted.lastReviewFindings === null) {\n database\n .prepare(\n 'UPDATE pipelines SET last_review_findings = NULL WHERE project_id = ? AND issue_id = ? AND last_review_findings IS ?',\n )\n .run(context.projectId, context.issueId, baseline.lastReviewFindings);\n }\n database\n .prepare(\n `UPDATE pipelines SET last_error_category = ?, last_error_message = ?, last_error_at = ?\n WHERE project_id = ? AND issue_id = ? AND last_error_category IS ? AND last_error_message IS ? AND last_error_at IS ?`,\n )\n .run(\n submitted.lastError?.category ?? null,\n submitted.lastError?.message ?? null,\n submitted.lastError?.at ?? null,\n context.projectId,\n context.issueId,\n baseline.lastError?.category ?? null,\n baseline.lastError?.message ?? null,\n baseline.lastError?.at ?? null,\n );\n }\n }),\n context.databaseOptions,\n );\n const merged = await loadStoredPlan(context);\n await materializePlan(context, merged);\n return merged;\n}\n\n/** Promote a newly generated plan after the plan phase has validated it. */\nexport async function ingestGeneratedPlan(context: PlanRepositoryContext): Promise<TaskPlan> {\n const plan = parsePlan(await readFile(context.tasksPath, 'utf-8'), context.tasksPath);\n // Generated output cannot grant/revoke CLI authorization or fake confirmation.\n const current = await loadStoredPlan(context).catch((error: unknown) => {\n if (error instanceof Error && error.message.startsWith('No SQLite task plan exists'))\n return null;\n throw error;\n });\n delete plan.closeIssue;\n delete plan.issueClosedAt;\n if (current?.closeIssue !== undefined) plan.closeIssue = current.closeIssue;\n if (current?.issueClosedAt !== undefined) plan.issueClosedAt = current.issueClosedAt;\n await saveStoredPlan(context, plan);\n return plan;\n}\n\nfunction executionColumns(record: Record<string, unknown>): {\n status: string;\n startedAt: string;\n finishedAt: string | null;\n durationMs: number | null;\n costStatus: string;\n costAmount: number | null;\n} {\n const cost = record.cost as { status?: string; amount?: number } | undefined;\n return {\n status: String(record.status),\n startedAt: String(record.startedAt),\n finishedAt: (record.finishedAt as string | null | undefined) ?? null,\n durationMs: (record.durationMs as number | null | undefined) ?? null,\n costStatus: cost?.status ?? 'unknown',\n costAmount: cost?.status === 'unknown' ? null : (cost?.amount ?? null),\n };\n}\n\n/** Insert or update one invocation independently from the plan projection. */\nexport async function saveExecution(\n context: PlanRepositoryContext,\n execution: { id: string } & Record<string, unknown>,\n): Promise<void> {\n await withDatabase(\n (database) =>\n database.transaction(() => {\n const columns = executionColumns(execution);\n const agent = execution.agent as Record<string, unknown> | undefined;\n const model = agent?.model as Record<string, unknown> | undefined;\n const usage = execution.usage as Record<string, unknown> | null | undefined;\n const runId = (execution.sessionId as string | null | undefined) ?? execution.id;\n const phaseId = `phase:${runId}:${String(execution.purpose ?? 'unknown')}`;\n // Documentation phases can invoke telemetry before a task plan exists.\n // Keep every relation valid in the same transaction as the execution.\n ensureStoredProject(database, context, columns.startedAt);\n ensureStoredIssue(database, context, columns.startedAt);\n database\n .prepare(\n `INSERT INTO runs (id, project_id, issue_id, status, started_at, finished_at, session_id)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(id) DO UPDATE SET status = excluded.status, finished_at = excluded.finished_at`,\n )\n .run(\n runId,\n context.projectId,\n context.issueId,\n columns.status,\n columns.startedAt,\n columns.finishedAt,\n (execution.sessionId as string | null | undefined) ?? null,\n );\n database\n .prepare(\n `INSERT INTO phases\n (id, run_id, name, status, started_at, finished_at, duration_ms, input_tokens, output_tokens,\n cache_read_tokens, cache_creation_tokens, cost_status, cost_amount)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(id) DO UPDATE SET status = excluded.status, finished_at = excluded.finished_at,\n duration_ms = excluded.duration_ms, input_tokens = excluded.input_tokens,\n output_tokens = excluded.output_tokens, cache_read_tokens = excluded.cache_read_tokens,\n cache_creation_tokens = excluded.cache_creation_tokens, cost_status = excluded.cost_status,\n cost_amount = excluded.cost_amount`,\n )\n .run(\n phaseId,\n runId,\n String(execution.purpose ?? 'unknown'),\n columns.status,\n columns.startedAt,\n columns.finishedAt,\n columns.durationMs,\n (usage?.inputTokens as number | undefined) ?? null,\n (usage?.outputTokens as number | undefined) ?? null,\n (usage?.cacheReadTokens as number | undefined) ?? null,\n (usage?.cacheCreationTokens as number | undefined) ?? null,\n columns.costStatus,\n columns.costAmount,\n );\n database\n .prepare(\n `INSERT INTO executions\n (id, project_id, issue_id, run_id, phase_id, status, started_at, finished_at, duration_ms, cost_status, cost_amount, payload_json)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(id) DO UPDATE SET status = excluded.status, finished_at = excluded.finished_at,\n duration_ms = excluded.duration_ms, cost_status = excluded.cost_status, cost_amount = excluded.cost_amount,\n payload_json = excluded.payload_json`,\n )\n .run(\n execution.id,\n context.projectId,\n context.issueId,\n runId,\n phaseId,\n columns.status,\n columns.startedAt,\n columns.finishedAt,\n columns.durationMs,\n columns.costStatus,\n columns.costAmount,\n JSON.stringify(execution),\n );\n database\n .prepare(\n `UPDATE executions SET session_id = ?, purpose = ?, attempt = ?, trigger = ?, trigger_reason = ?,\n input_tokens = ?, output_tokens = ?, cache_read_tokens = ?, cache_creation_tokens = ?, reasoning_tokens = ?,\n harness = ?, provider = ?, model_requested = ?, model_resolved = ? WHERE id = ?`,\n )\n .run(\n (execution.sessionId as string | null | undefined) ?? null,\n (execution.purpose as string | undefined) ?? null,\n (execution.attempt as number | undefined) ?? null,\n (execution.trigger as string | undefined) ?? null,\n (execution.triggerReason as string | null | undefined) ?? null,\n (usage?.inputTokens as number | undefined) ?? null,\n (usage?.outputTokens as number | undefined) ?? null,\n (usage?.cacheReadTokens as number | undefined) ?? null,\n (usage?.cacheCreationTokens as number | undefined) ?? null,\n (usage?.reasoningTokens as number | undefined) ?? null,\n (agent?.harness as string | undefined) ?? null,\n (agent?.provider as string | null | undefined) ?? null,\n (model?.requested as string | null | undefined) ?? null,\n (model?.resolved as string | null | undefined) ?? null,\n execution.id,\n );\n applyStoredRetention(database, context.projectId, context.retention);\n }),\n context.databaseOptions,\n );\n // Direct library consumers historically read the projection themselves.\n // Keep that contract only for their synthetic context; real issue paths\n // intentionally leave projection refresh to the phase boundary so an\n // execution ending cannot overwrite an agent's pending file mutation.\n if (\n context.projectId.startsWith('projection-') &&\n !agentProjectionWindows.has(context.tasksPath)\n ) {\n await materializePlan(context);\n }\n}\n\n/** Provider breaker state is a project-scoped canonical record, not a JSON file. */\nexport async function readStoredProvidersHealth(\n context: PlanRepositoryContext,\n): Promise<ProvidersHealth> {\n return withDatabase((database) => {\n const providers = Object.fromEntries(\n database\n .prepare('SELECT provider, payload_json FROM provider_health WHERE project_id = ?')\n .all<{ provider: string; payload_json: string }>(context.projectId)\n .map((row) => [row.provider, JSON.parse(row.payload_json) as ProviderHealthRecord]),\n );\n return { schemaVersion: 1, providers };\n }, context.databaseOptions);\n}\n\n/** Apply one health transition under SQLite's write transaction. */\nexport async function updateStoredProviderHealth(\n context: PlanRepositoryContext,\n provider: string,\n update: (record: ProviderHealthRecord) => ProviderHealthRecord,\n): Promise<ProviderHealthRecord> {\n return withDatabase(\n (database) =>\n database.transaction(() => {\n const updatedAt = new Date().toISOString();\n ensureStoredProject(database, context, updatedAt);\n const previous = database\n .prepare('SELECT payload_json FROM provider_health WHERE project_id = ? AND provider = ?')\n .get<{ payload_json: string }>(context.projectId, provider);\n const current: ProviderHealthRecord =\n previous === undefined\n ? {\n status: 'healthy',\n failures: [],\n consecutiveFailures: 0,\n cooldownLevel: 0,\n cooldownUntil: null,\n lastFailureKind: null,\n lastFailureAt: null,\n lastSuccessAt: null,\n probeInFlight: false,\n probeStartedAt: null,\n }\n : (JSON.parse(previous.payload_json) as ProviderHealthRecord);\n const next = update(current);\n database\n .prepare(\n `INSERT INTO provider_health\n (project_id, provider, payload_json, updated_at, status, consecutive_failures, cooldown_level,\n cooldown_until, last_failure_kind, last_failure_at, last_success_at, probe_in_flight, probe_started_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(project_id, provider) DO UPDATE SET payload_json = excluded.payload_json,\n updated_at = excluded.updated_at, status = excluded.status,\n consecutive_failures = excluded.consecutive_failures, cooldown_level = excluded.cooldown_level,\n cooldown_until = excluded.cooldown_until, last_failure_kind = excluded.last_failure_kind,\n last_failure_at = excluded.last_failure_at, last_success_at = excluded.last_success_at,\n probe_in_flight = excluded.probe_in_flight, probe_started_at = excluded.probe_started_at`,\n )\n .run(\n context.projectId,\n provider,\n JSON.stringify(next),\n updatedAt,\n next.status,\n next.consecutiveFailures,\n next.cooldownLevel,\n next.cooldownUntil,\n next.lastFailureKind,\n next.lastFailureAt,\n next.lastSuccessAt,\n next.probeInFlight ? 1 : 0,\n next.probeStartedAt,\n );\n database\n .prepare('DELETE FROM provider_health_failures WHERE project_id = ? AND provider = ?')\n .run(context.projectId, provider);\n for (const failure of next.failures) {\n database\n .prepare(\n `INSERT INTO provider_health_failures (project_id, provider, occurred_at, kind)\n VALUES (?, ?, ?, ?)`,\n )\n .run(context.projectId, provider, failure.at, failure.kind);\n }\n return next;\n }),\n context.databaseOptions,\n );\n}\n\n/** Append a verification result while its JSON file remains a readable projection. */\nexport async function saveStoredVerification(\n context: PlanRepositoryContext,\n evidence: Record<string, unknown>,\n): Promise<void> {\n await withDatabase((database) => {\n database.transaction(() => {\n const createdAt = String(evidence.at ?? new Date().toISOString());\n ensureStoredProject(database, context, createdAt);\n ensureStoredIssue(database, context, createdAt);\n database\n .prepare(\n `INSERT INTO verifications (id, project_id, issue_id, status, created_at, payload_json)\n VALUES (?, ?, ?, ?, ?, ?)`,\n )\n .run(\n randomUUID(),\n context.projectId,\n context.issueId,\n String(evidence.verdict ?? 'unknown'),\n createdAt,\n JSON.stringify(evidence),\n );\n });\n }, context.databaseOptions);\n}\n\n/** Branch changes are audit events; the compatibility dotfile is never authoritative. */\nexport async function loadStoredLastBranch(context: PlanRepositoryContext): Promise<string | null> {\n return withDatabase((database) => {\n const row = database\n .prepare(\n `SELECT payload_json FROM audit_log WHERE project_id = ? AND action = ?\n ORDER BY occurred_at DESC, rowid DESC LIMIT 1`,\n )\n .get<{ payload_json: string }>(context.projectId, `last_branch:${context.issueId}`);\n if (row === undefined) return null;\n const payload = JSON.parse(row.payload_json) as { branch?: unknown };\n return typeof payload.branch === 'string' && payload.branch !== '' ? payload.branch : null;\n }, context.databaseOptions);\n}\n\nexport async function saveStoredLastBranch(\n context: PlanRepositoryContext,\n branch: string,\n): Promise<void> {\n await withDatabase((database) => {\n database.transaction(() => {\n const occurredAt = new Date().toISOString();\n ensureStoredProject(database, context, occurredAt);\n database\n .prepare(\n `INSERT INTO audit_log (id, project_id, occurred_at, action, payload_json)\n VALUES (?, ?, ?, ?, ?)`,\n )\n .run(\n randomUUID(),\n context.projectId,\n occurredAt,\n `last_branch:${context.issueId}`,\n JSON.stringify({ branch }),\n );\n });\n }, context.databaseOptions);\n}\n\n/** Store the project-wide numbering audit beside the indexed story history. */\nexport async function saveStoredUserStoryNumbering(\n context: Pick<PlanRepositoryContext, 'projectId' | 'projectRoot' | 'databaseOptions'>,\n decision: UserStoryNumberingDecision,\n): Promise<void> {\n await withDatabase((database) => {\n database.transaction(() => {\n ensureStoredProject(database, { ...context, tasksPath: '', issueId: '' }, decision.decidedAt);\n database\n .prepare(\n `INSERT INTO user_story_numbering (project_id, next_number, source, issue_id, decided_at, detail)\n VALUES (?, ?, ?, ?, ?, ?)\n ON CONFLICT(project_id) DO UPDATE SET next_number = excluded.next_number,\n source = excluded.source, issue_id = excluded.issue_id, decided_at = excluded.decided_at,\n detail = excluded.detail`,\n )\n .run(\n context.projectId,\n decision.nextNumber,\n decision.source,\n decision.issueNumber,\n decision.decidedAt,\n decision.detail ?? null,\n );\n });\n }, context.databaseOptions);\n}\n\n/** A current session snapshot and its indexed identity for monitor readers. */\nexport interface StoredSession {\n projectId: string;\n issueId: string;\n sessionId: string;\n snapshot: SessionSnapshot;\n updatedAt: string;\n}\n\n/**\n * Persist one reduced session event and its resulting projection together.\n *\n * The session publisher deliberately queues this work after its synchronous\n * reducer has accepted the event, so monitoring storage can never interrupt a\n * pipeline. The event sequence is the publisher version and is unique per run.\n */\nexport async function saveSessionEvent(\n context: PlanRepositoryContext,\n input: { sessionId: string; sequence: number; event: SessionEvent; snapshot: SessionSnapshot },\n): Promise<void> {\n await withDatabase(\n (database) =>\n database.transaction(() => {\n const updatedAt = input.snapshot.updatedAt ?? input.event.at;\n const startedAt = input.snapshot.startedAt ?? input.event.at;\n database\n .prepare(\n `INSERT INTO projects (id, root, remote_url, created_at, updated_at) VALUES (?, ?, NULL, ?, ?)\n ON CONFLICT(id) DO UPDATE SET root = excluded.root, updated_at = excluded.updated_at`,\n )\n .run(context.projectId, context.projectRoot, startedAt, updatedAt);\n database\n .prepare(\n `INSERT INTO issues (project_id, id, title, status, branch_name, created_at, updated_at)\n VALUES (?, ?, NULL, ?, NULL, ?, ?)\n ON CONFLICT(project_id, id) DO UPDATE SET updated_at = excluded.updated_at`,\n )\n .run(context.projectId, context.issueId, 'in_progress', startedAt, updatedAt);\n database\n .prepare(\n `INSERT INTO runs (id, project_id, issue_id, status, started_at, finished_at, session_id, heartbeat_at, pid, host)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(id) DO UPDATE SET status = excluded.status, finished_at = excluded.finished_at,\n heartbeat_at = excluded.heartbeat_at, pid = excluded.pid, host = excluded.host`,\n )\n .run(\n input.sessionId,\n context.projectId,\n context.issueId,\n input.snapshot.status,\n startedAt,\n input.snapshot.endedAt,\n input.sessionId,\n updatedAt,\n process.pid,\n process.env.HOSTNAME ?? null,\n );\n database\n .prepare(\n `INSERT INTO events (id, project_id, run_id, occurred_at, kind, payload_json, session_id, sequence)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,\n )\n .run(\n randomUUID(),\n context.projectId,\n input.sessionId,\n input.event.at,\n input.event.type,\n JSON.stringify(input.event),\n input.sessionId,\n input.sequence,\n );\n database\n .prepare(\n `INSERT INTO snapshots (id, project_id, run_id, created_at, payload_json, issue_id, session_id, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,\n )\n .run(\n randomUUID(),\n context.projectId,\n input.sessionId,\n updatedAt,\n JSON.stringify(input.snapshot),\n context.issueId,\n input.sessionId,\n updatedAt,\n );\n applyStoredRetention(database, context.projectId, context.retention);\n }),\n context.databaseOptions,\n );\n}\n\n/** Read the most recent snapshot for every session active within a time window. */\nexport async function listStoredSessions(input: {\n activeSince: string;\n databaseOptions?: OpenIssueFlowDatabaseOptions;\n}): Promise<StoredSession[]> {\n return withDatabase((database) => {\n return database\n .prepare(\n `SELECT project_id, issue_id, session_id, payload_json, updated_at FROM snapshots AS current\n WHERE current.session_id IS NOT NULL AND current.updated_at >= ?\n AND current.id = (\n SELECT newer.id FROM snapshots AS newer\n WHERE newer.project_id = current.project_id AND newer.session_id = current.session_id\n ORDER BY newer.updated_at DESC, newer.rowid DESC LIMIT 1\n )\n ORDER BY current.updated_at DESC, current.rowid DESC`,\n )\n .all<{\n project_id: string;\n issue_id: string;\n session_id: string;\n payload_json: string;\n updated_at: string;\n }>(input.activeSince)\n .map((row) => ({\n projectId: row.project_id,\n issueId: row.issue_id,\n sessionId: row.session_id,\n snapshot: JSON.parse(row.payload_json) as SessionSnapshot,\n updatedAt: row.updated_at,\n }));\n }, input.databaseOptions);\n}\n\n/** Latest snapshot for each durable run, used to enrich the live lock registry. */\nexport async function listStoredRunSnapshots(\n input: { databaseOptions?: OpenIssueFlowDatabaseOptions } = {},\n): Promise<StoredSession[]> {\n return withDatabase((database) => {\n return database\n .prepare(\n `SELECT run.project_id, run.issue_id, run.session_id, snapshot.payload_json, snapshot.updated_at\n FROM runs AS run\n JOIN snapshots AS snapshot ON snapshot.id = (\n SELECT latest.id FROM snapshots AS latest\n WHERE latest.run_id = run.id ORDER BY latest.updated_at DESC, latest.rowid DESC LIMIT 1\n )\n WHERE run.session_id IS NOT NULL`,\n )\n .all<{\n project_id: string;\n issue_id: string;\n session_id: string;\n payload_json: string;\n updated_at: string;\n }>()\n .map((row) => ({\n projectId: row.project_id,\n issueId: row.issue_id,\n sessionId: row.session_id,\n snapshot: JSON.parse(row.payload_json) as SessionSnapshot,\n updatedAt: row.updated_at,\n }));\n }, input.databaseOptions);\n}\n\n/** Event history for one monitor session, in its original publisher order. */\nexport async function listStoredSessionEvents(input: {\n projectId: string;\n sessionId: string;\n databaseOptions?: OpenIssueFlowDatabaseOptions;\n}): Promise<Array<{ seq: number; event: SessionEvent }>> {\n return withDatabase(\n (database) =>\n database\n .prepare(\n `SELECT sequence, payload_json FROM events\n WHERE project_id = ? AND session_id = ? ORDER BY sequence, rowid`,\n )\n .all<{ sequence: number; payload_json: string }>(input.projectId, input.sessionId)\n .map((row) => ({ seq: row.sequence, event: JSON.parse(row.payload_json) as SessionEvent })),\n input.databaseOptions,\n );\n}\n\n/** Keep a quiet, live session discoverable without creating a synthetic event. */\nexport async function touchStoredSession(\n context: PlanRepositoryContext,\n sessionId: string,\n updatedAt = new Date().toISOString(),\n): Promise<void> {\n await withDatabase((database) => {\n database.transaction(() => {\n database\n .prepare('UPDATE runs SET heartbeat_at = ? WHERE id = ? AND project_id = ?')\n .run(updatedAt, sessionId, context.projectId);\n const latest = database\n .prepare(\n `SELECT id FROM snapshots WHERE project_id = ? AND session_id = ?\n ORDER BY updated_at DESC, rowid DESC LIMIT 1`,\n )\n .get<{ id: string }>(context.projectId, sessionId);\n if (latest !== undefined) {\n database\n .prepare('UPDATE snapshots SET updated_at = ? WHERE id = ?')\n .run(updatedAt, latest.id);\n }\n });\n }, context.databaseOptions);\n}\n\nexport async function loadExecution(\n context: PlanRepositoryContext,\n id: string,\n): Promise<Record<string, unknown> | null> {\n return withDatabase((database) => {\n const row = database\n .prepare(\n 'SELECT payload_json FROM executions WHERE id = ? AND project_id = ? AND issue_id = ?',\n )\n .get<{ payload_json: string }>(id, context.projectId, context.issueId);\n return row === undefined ? null : (JSON.parse(row.payload_json) as Record<string, unknown>);\n });\n}\n\n/** Execution history is queried directly from SQLite; tasks.json is only a projection. */\nexport async function listStoredExecutions(input: {\n projectId: string;\n issueId?: string;\n since?: string;\n databaseOptions?: OpenIssueFlowDatabaseOptions;\n}): Promise<ExecutionRecord[]> {\n return withDatabase((database) => {\n const clauses = ['project_id = ?'];\n const values: string[] = [input.projectId];\n if (input.issueId !== undefined) {\n clauses.push('issue_id = ?');\n values.push(input.issueId);\n }\n if (input.since !== undefined) {\n clauses.push('started_at >= ?');\n values.push(input.since);\n }\n return database\n .prepare(\n `SELECT payload_json FROM executions WHERE ${clauses.join(' AND ')} ORDER BY started_at, rowid`,\n )\n .all<{ payload_json: string }>(...values)\n .map((row) => JSON.parse(row.payload_json) as ExecutionRecord);\n }, input.databaseOptions ?? databaseOptionsForProject(input.projectId));\n}\n\nexport interface StoredUserStoryNumber {\n number: number;\n issueId: string;\n storyId: string;\n}\n\n/** Use the relational story index for project-wide US-NNN continuity. */\nexport async function findHighestStoredUserStoryNumber(input: {\n projectId: string;\n excludeIssueId?: string;\n databaseOptions?: OpenIssueFlowDatabaseOptions;\n}): Promise<StoredUserStoryNumber | null> {\n return withDatabase((database) => {\n const statement =\n input.excludeIssueId === undefined\n ? 'SELECT issue_id, id, story_number FROM stories WHERE project_id = ? AND story_number IS NOT NULL ORDER BY story_number DESC LIMIT 1'\n : 'SELECT issue_id, id, story_number FROM stories WHERE project_id = ? AND issue_id <> ? AND story_number IS NOT NULL ORDER BY story_number DESC LIMIT 1';\n const row = database\n .prepare(statement)\n .get<{ issue_id: string; id: string; story_number: number }>(\n input.projectId,\n ...(input.excludeIssueId === undefined ? [] : [input.excludeIssueId]),\n );\n return row === undefined\n ? null\n : { number: row.story_number, issueId: row.issue_id, storyId: row.id };\n }, input.databaseOptions ?? databaseOptionsForProject(input.projectId));\n}\n\n/** A stable, JSON-friendly diagnostic export that never exposes SQL to callers. */\nexport async function exportStoredState(\n options: OpenIssueFlowDatabaseOptions = {},\n): Promise<Record<string, unknown>> {\n return withDatabase((database) => {\n const tables = [\n 'projects',\n 'issues',\n 'pipelines',\n 'stories',\n 'story_dependencies',\n 'runs',\n 'phases',\n 'executions',\n 'events',\n 'snapshots',\n 'pull_requests',\n 'reviews',\n 'verifications',\n 'provider_health',\n 'queues',\n 'queue_issues',\n 'queue_dependencies',\n 'user_story_numbering',\n 'provider_health_failures',\n 'migrated_artifacts',\n 'audit_log',\n ];\n return Object.fromEntries(\n tables.map((table) => [table, database.prepare(`SELECT * FROM ${table}`).all()]),\n );\n }, options);\n}\n","import { join } from 'node:path';\nimport { type GetGlobalRootOptions, getGlobalRoot } from '../paths.js';\nimport { type DatabaseDriver, type OpenDatabaseOptions, openDatabase } from './driver.js';\nimport { migrateDatabase } from './migrations.js';\n\nexport const DATABASE_FILENAME = 'issue-flow.db';\n\nexport function getDatabasePath(options: GetGlobalRootOptions = {}): string {\n return join(getGlobalRoot(options), DATABASE_FILENAME);\n}\n\nexport interface OpenIssueFlowDatabaseOptions extends OpenDatabaseOptions, GetGlobalRootOptions {}\n\nconst projectDatabaseOptions = new Map<string, OpenIssueFlowDatabaseOptions>();\n\nexport function registerProjectDatabaseOptions(\n projectId: string,\n options: OpenIssueFlowDatabaseOptions,\n): void {\n projectDatabaseOptions.set(projectId, options);\n}\n\nexport function databaseOptionsForProject(projectId: string): OpenIssueFlowDatabaseOptions {\n return projectDatabaseOptions.get(projectId) ?? {};\n}\n\nexport function resetProjectDatabaseOptions(): void {\n projectDatabaseOptions.clear();\n}\n\nexport async function openIssueFlowDatabase(\n options: OpenIssueFlowDatabaseOptions = {},\n): Promise<DatabaseDriver> {\n const database = await openDatabase(getDatabasePath(options), options);\n try {\n migrateDatabase(database);\n return database;\n } catch (error) {\n database.close();\n throw error;\n }\n}\n","import { mkdirSync, statfsSync } from 'node:fs';\nimport { dirname } from 'node:path';\n\n/** Values accepted by the small SQLite abstraction. */\nexport type SqlValue = string | number | bigint | Uint8Array | null;\n\nexport interface SqlStatement {\n run(...values: SqlValue[]): { changes: number | bigint; lastInsertRowid: number | bigint };\n get<T>(): T | undefined;\n get<T>(...values: SqlValue[]): T | undefined;\n all<T>(): T[];\n all<T>(...values: SqlValue[]): T[];\n}\n\n/**\n * The only database interface consumers may depend on. Keeping it deliberately\n * small makes replacing node:sqlite possible without leaking its API.\n */\nexport interface DatabaseDriver {\n exec(sql: string): void;\n prepare(sql: string): SqlStatement;\n transaction<T>(work: () => T): T;\n integrityCheck(): string;\n backup(destination: string): void;\n vacuum(): void;\n close(): void;\n}\n\nexport interface OpenDatabaseOptions {\n onWarning?: (message: string) => void;\n isNetworkFilesystem?: (path: string) => boolean;\n}\n\nconst NETWORK_FILESYSTEM_TYPES = new Set([0x6969, 0x517b, 0xff534d42]);\n\n/** Best-effort detection; a failed probe must never prevent local use. */\nexport function isNetworkFilesystem(path: string): boolean {\n try {\n return NETWORK_FILESYSTEM_TYPES.has(Number(statfsSync(dirname(path)).type));\n } catch {\n return false;\n }\n}\n\nfunction isSqliteExperimentalWarning(warning: unknown): boolean {\n if (!(warning instanceof Error) || warning.name !== 'ExperimentalWarning') return false;\n return warning.message.includes('SQLite is an experimental feature');\n}\n\n/**\n * node:sqlite emitted an ExperimentalWarning on Node 22. Filter only that\n * exact warning while loading the built-in; every other warning keeps Node's\n * normal behaviour. The import stays here so this is the sole node:sqlite seam.\n */\nasync function loadDatabaseSync(): Promise<new (path: string) => NodeDatabase> {\n const originalEmitWarning = process.emitWarning;\n process.emitWarning = ((warning: string | Error, ...args: unknown[]) => {\n const normalized =\n typeof warning === 'string'\n ? Object.assign(new Error(warning), {\n name: typeof args[0] === 'string' ? args[0] : 'Warning',\n })\n : warning;\n if (isSqliteExperimentalWarning(normalized)) return;\n return (originalEmitWarning as (...input: unknown[]) => unknown)(warning, ...args);\n }) as typeof process.emitWarning;\n\n try {\n const sqlite = await import('node:sqlite');\n return sqlite.DatabaseSync as unknown as new (\n path: string,\n ) => NodeDatabase;\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n throw new Error(\n `SQLite storage requires Node.js >= 22.13.0 with the built-in node:sqlite module. ${detail}`,\n { cause: error },\n );\n } finally {\n process.emitWarning = originalEmitWarning;\n }\n}\n\ninterface NodeDatabase {\n exec(sql: string): void;\n prepare(sql: string): SqlStatement;\n close(): void;\n}\n\nclass NodeSqliteDriver implements DatabaseDriver {\n constructor(private readonly database: NodeDatabase) {}\n\n exec(sql: string): void {\n this.database.exec(sql);\n }\n\n prepare(sql: string): SqlStatement {\n return this.database.prepare(sql);\n }\n\n transaction<T>(work: () => T): T {\n this.exec('BEGIN IMMEDIATE');\n try {\n const value = work();\n this.exec('COMMIT');\n return value;\n } catch (error) {\n try {\n this.exec('ROLLBACK');\n } catch {\n // Preserve the original error, which explains why the transaction failed.\n }\n throw error;\n }\n }\n\n integrityCheck(): string {\n return (\n this.prepare('PRAGMA integrity_check').get<{ integrity_check: string }>()?.integrity_check ??\n 'unknown'\n );\n }\n\n backup(destination: string): void {\n mkdirSync(dirname(destination), { recursive: true });\n // VACUUM INTO creates a consistent SQLite snapshot without copying a live\n // WAL file by hand. Binding keeps a user supplied destination out of SQL.\n this.prepare('VACUUM INTO ?').run(destination);\n }\n\n vacuum(): void {\n this.exec('VACUUM');\n }\n\n close(): void {\n this.database.close();\n }\n}\n\n/** Open and configure the single Issue Flow SQLite database. */\nexport async function openDatabase(\n path: string,\n options: OpenDatabaseOptions = {},\n): Promise<DatabaseDriver> {\n mkdirSync(dirname(path), { recursive: true });\n const DatabaseSync = await loadDatabaseSync();\n const driver = new NodeSqliteDriver(new DatabaseSync(path));\n\n try {\n driver.exec('PRAGMA foreign_keys = ON');\n driver.exec('PRAGMA busy_timeout = 5000');\n driver.exec('PRAGMA synchronous = NORMAL');\n const networkFilesystem = (options.isNetworkFilesystem ?? isNetworkFilesystem)(path);\n driver.exec(`PRAGMA journal_mode = ${networkFilesystem ? 'DELETE' : 'WAL'}`);\n if (networkFilesystem) {\n options.onWarning?.(\n `SQLite database is on a network filesystem; using journal_mode=DELETE instead of WAL: ${path}`,\n );\n }\n return driver;\n } catch (error) {\n driver.close();\n throw error;\n }\n}\n","import type { DatabaseDriver } from './driver.js';\n\nexport interface Migration {\n version: number;\n name: string;\n up(database: DatabaseDriver): void;\n}\n\nconst INITIAL_SCHEMA = `\nCREATE TABLE projects (\n id TEXT PRIMARY KEY,\n root TEXT NOT NULL,\n remote_url TEXT,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL\n);\nCREATE TABLE issues (\n project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,\n id TEXT NOT NULL,\n title TEXT,\n status TEXT NOT NULL,\n branch_name TEXT,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n PRIMARY KEY (project_id, id)\n);\nCREATE TABLE pipelines (\n project_id TEXT NOT NULL,\n issue_id TEXT NOT NULL,\n state_json TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n PRIMARY KEY (project_id, issue_id),\n FOREIGN KEY (project_id, issue_id) REFERENCES issues(project_id, id) ON DELETE CASCADE\n);\nCREATE TABLE stories (\n project_id TEXT NOT NULL,\n issue_id TEXT NOT NULL,\n id TEXT NOT NULL,\n title TEXT NOT NULL,\n priority INTEGER NOT NULL,\n passes INTEGER NOT NULL CHECK (passes IN (0, 1)),\n notes TEXT NOT NULL DEFAULT '',\n PRIMARY KEY (project_id, issue_id, id),\n FOREIGN KEY (project_id, issue_id) REFERENCES issues(project_id, id) ON DELETE CASCADE\n);\nCREATE TABLE story_dependencies (\n project_id TEXT NOT NULL,\n issue_id TEXT NOT NULL,\n story_id TEXT NOT NULL,\n depends_on_story_id TEXT NOT NULL,\n PRIMARY KEY (project_id, issue_id, story_id, depends_on_story_id),\n FOREIGN KEY (project_id, issue_id, story_id) REFERENCES stories(project_id, issue_id, id) ON DELETE CASCADE,\n FOREIGN KEY (project_id, issue_id, depends_on_story_id) REFERENCES stories(project_id, issue_id, id) ON DELETE CASCADE,\n CHECK (story_id <> depends_on_story_id)\n);\nCREATE TABLE runs (\n id TEXT PRIMARY KEY,\n project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,\n issue_id TEXT,\n status TEXT NOT NULL,\n started_at TEXT NOT NULL,\n finished_at TEXT,\n FOREIGN KEY (project_id, issue_id) REFERENCES issues(project_id, id) ON DELETE CASCADE\n);\nCREATE TABLE phases (\n id TEXT PRIMARY KEY,\n run_id TEXT NOT NULL REFERENCES runs(id) ON DELETE CASCADE,\n name TEXT NOT NULL,\n status TEXT NOT NULL,\n started_at TEXT NOT NULL,\n finished_at TEXT\n);\nCREATE TABLE executions (\n id TEXT PRIMARY KEY,\n project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,\n issue_id TEXT,\n run_id TEXT REFERENCES runs(id) ON DELETE SET NULL,\n phase_id TEXT REFERENCES phases(id) ON DELETE SET NULL,\n status TEXT NOT NULL,\n started_at TEXT NOT NULL,\n finished_at TEXT,\n duration_ms INTEGER,\n cost_status TEXT NOT NULL CHECK (cost_status IN ('reported', 'estimated', 'unknown')),\n cost_amount REAL,\n CHECK ((cost_status = 'unknown' AND cost_amount IS NULL) OR (cost_status IN ('reported', 'estimated') AND cost_amount IS NOT NULL AND cost_amount >= 0)),\n FOREIGN KEY (project_id, issue_id) REFERENCES issues(project_id, id) ON DELETE CASCADE\n);\nCREATE TABLE events (\n id TEXT PRIMARY KEY,\n project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,\n run_id TEXT REFERENCES runs(id) ON DELETE CASCADE,\n occurred_at TEXT NOT NULL,\n kind TEXT NOT NULL,\n payload_json TEXT NOT NULL\n);\nCREATE TABLE snapshots (\n id TEXT PRIMARY KEY,\n project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,\n run_id TEXT REFERENCES runs(id) ON DELETE CASCADE,\n created_at TEXT NOT NULL,\n payload_json TEXT NOT NULL\n);\nCREATE TABLE pull_requests (\n id TEXT PRIMARY KEY,\n project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,\n issue_id TEXT,\n number INTEGER,\n url TEXT,\n status TEXT NOT NULL,\n created_at TEXT NOT NULL,\n FOREIGN KEY (project_id, issue_id) REFERENCES issues(project_id, id) ON DELETE CASCADE\n);\nCREATE TABLE reviews (\n id TEXT PRIMARY KEY,\n pull_request_id TEXT NOT NULL REFERENCES pull_requests(id) ON DELETE CASCADE,\n status TEXT NOT NULL,\n created_at TEXT NOT NULL,\n payload_json TEXT NOT NULL\n);\nCREATE TABLE verifications (\n id TEXT PRIMARY KEY,\n project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,\n issue_id TEXT,\n status TEXT NOT NULL,\n created_at TEXT NOT NULL,\n payload_json TEXT NOT NULL,\n FOREIGN KEY (project_id, issue_id) REFERENCES issues(project_id, id) ON DELETE CASCADE\n);\nCREATE TABLE provider_health (\n project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,\n provider TEXT NOT NULL,\n payload_json TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n PRIMARY KEY (project_id, provider)\n);\nCREATE TABLE queues (\n id TEXT PRIMARY KEY,\n project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,\n status TEXT NOT NULL,\n payload_json TEXT NOT NULL,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL\n);\nCREATE TABLE queue_issues (\n queue_id TEXT NOT NULL REFERENCES queues(id) ON DELETE CASCADE,\n project_id TEXT NOT NULL,\n issue_id TEXT NOT NULL,\n position INTEGER NOT NULL,\n status TEXT NOT NULL,\n PRIMARY KEY (queue_id, project_id, issue_id),\n UNIQUE (queue_id, position),\n FOREIGN KEY (project_id, issue_id) REFERENCES issues(project_id, id) ON DELETE CASCADE\n);\nCREATE TABLE migrated_artifacts (\n source_path TEXT PRIMARY KEY,\n sha256 TEXT NOT NULL,\n migrated_at TEXT NOT NULL,\n table_counts_json TEXT NOT NULL\n);\nCREATE TABLE audit_log (\n id TEXT PRIMARY KEY,\n project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,\n occurred_at TEXT NOT NULL,\n action TEXT NOT NULL,\n payload_json TEXT NOT NULL\n);\nCREATE INDEX executions_project_issue_started_idx ON executions(project_id, issue_id, started_at);\nCREATE INDEX runs_project_status_started_idx ON runs(project_id, status, started_at);\nCREATE INDEX stories_project_issue_priority_idx ON stories(project_id, issue_id, priority);\nCREATE INDEX phases_run_name_idx ON phases(run_id, name);\nCREATE INDEX events_project_run_occurred_idx ON events(project_id, run_id, occurred_at);\n`;\n\nexport const migrations: readonly Migration[] = [\n {\n version: 1,\n name: 'initial relational storage',\n up: (database) => database.exec(INITIAL_SCHEMA),\n },\n {\n version: 2,\n name: 'preserve imported execution details',\n up: (database) =>\n database.exec(\"ALTER TABLE executions ADD COLUMN payload_json TEXT NOT NULL DEFAULT '{}'\"),\n },\n {\n version: 3,\n name: 'index numeric user story identifiers',\n up: (database) =>\n database.exec(`\n ALTER TABLE stories ADD COLUMN story_number INTEGER;\n CREATE INDEX stories_project_number_idx ON stories(project_id, story_number DESC);\n `),\n },\n {\n version: 4,\n name: 'complete relational state and history indexes',\n up: (database) =>\n database.exec(`\n ALTER TABLE pipelines ADD COLUMN project TEXT;\n ALTER TABLE pipelines ADD COLUMN issue_number TEXT;\n ALTER TABLE pipelines ADD COLUMN issue_url TEXT;\n ALTER TABLE pipelines ADD COLUMN branch_name TEXT;\n ALTER TABLE pipelines ADD COLUMN no_branch INTEGER NOT NULL DEFAULT 0 CHECK (no_branch IN (0, 1));\n ALTER TABLE pipelines ADD COLUMN description TEXT;\n ALTER TABLE pipelines ADD COLUMN issue_status TEXT;\n ALTER TABLE pipelines ADD COLUMN completed_at TEXT;\n ALTER TABLE pipelines ADD COLUMN last_attempt_at TEXT;\n ALTER TABLE pipelines ADD COLUMN last_error_category TEXT;\n ALTER TABLE pipelines ADD COLUMN last_error_message TEXT;\n ALTER TABLE pipelines ADD COLUMN last_error_at TEXT;\n ALTER TABLE pipelines ADD COLUMN correction_cycle INTEGER NOT NULL DEFAULT 0;\n ALTER TABLE pipelines ADD COLUMN max_correction_cycles INTEGER NOT NULL DEFAULT 3;\n ALTER TABLE pipelines ADD COLUMN last_review_findings TEXT;\n ALTER TABLE pipelines ADD COLUMN analyze_completed INTEGER CHECK (analyze_completed IN (0, 1));\n ALTER TABLE pipelines ADD COLUMN prd_completed INTEGER CHECK (prd_completed IN (0, 1));\n ALTER TABLE pipelines ADD COLUMN json_completed INTEGER CHECK (json_completed IN (0, 1));\n ALTER TABLE pipelines ADD COLUMN execution_completed INTEGER CHECK (execution_completed IN (0, 1));\n ALTER TABLE pipelines ADD COLUMN review_completed INTEGER CHECK (review_completed IN (0, 1));\n ALTER TABLE pipelines ADD COLUMN pr_created INTEGER CHECK (pr_created IN (0, 1));\n ALTER TABLE pipelines ADD COLUMN pr_review_completed INTEGER CHECK (pr_review_completed IN (0, 1));\n ALTER TABLE pipelines ADD COLUMN run_status TEXT;\n ALTER TABLE pipelines ADD COLUMN run_phase TEXT;\n ALTER TABLE pipelines ADD COLUMN run_attempt INTEGER;\n ALTER TABLE pipelines ADD COLUMN run_heartbeat_at TEXT;\n ALTER TABLE pipelines ADD COLUMN run_blocked_reason TEXT;\n ALTER TABLE pipelines ADD COLUMN run_owner_pid INTEGER;\n ALTER TABLE pipelines ADD COLUMN run_owner_host TEXT;\n ALTER TABLE pipelines ADD COLUMN run_owner_started_at TEXT;\n ALTER TABLE pipelines ADD COLUMN pr_number INTEGER;\n ALTER TABLE pipelines ADD COLUMN pr_url TEXT;\n ALTER TABLE pipelines ADD COLUMN pr_head_branch TEXT;\n ALTER TABLE pipelines ADD COLUMN pr_created_at TEXT;\n ALTER TABLE pipelines ADD COLUMN pr_review_enabled INTEGER CHECK (pr_review_enabled IN (0, 1));\n ALTER TABLE pipelines ADD COLUMN pr_review_rounds INTEGER;\n ALTER TABLE pipelines ADD COLUMN pr_review_recommendation TEXT;\n ALTER TABLE pipelines ADD COLUMN pr_reviewed_at TEXT;\n\n ALTER TABLE stories ADD COLUMN description TEXT;\n ALTER TABLE stories ADD COLUMN acceptance_criteria_json TEXT NOT NULL DEFAULT '[]';\n ALTER TABLE stories ADD COLUMN duration_seconds REAL;\n ALTER TABLE stories ADD COLUMN status TEXT;\n ALTER TABLE stories ADD COLUMN stage TEXT;\n ALTER TABLE stories ADD COLUMN stage_since TEXT;\n ALTER TABLE stories ADD COLUMN stage_detail TEXT;\n ALTER TABLE stories ADD COLUMN input_tokens INTEGER;\n ALTER TABLE stories ADD COLUMN output_tokens INTEGER;\n ALTER TABLE stories ADD COLUMN cache_read_tokens INTEGER;\n ALTER TABLE stories ADD COLUMN cache_creation_tokens INTEGER;\n\n ALTER TABLE runs ADD COLUMN session_id TEXT;\n ALTER TABLE runs ADD COLUMN heartbeat_at TEXT;\n ALTER TABLE runs ADD COLUMN pid INTEGER;\n ALTER TABLE runs ADD COLUMN host TEXT;\n ALTER TABLE events ADD COLUMN session_id TEXT;\n ALTER TABLE events ADD COLUMN sequence INTEGER;\n ALTER TABLE snapshots ADD COLUMN issue_id TEXT;\n ALTER TABLE snapshots ADD COLUMN session_id TEXT;\n ALTER TABLE snapshots ADD COLUMN updated_at TEXT;\n ALTER TABLE executions ADD COLUMN session_id TEXT;\n ALTER TABLE executions ADD COLUMN purpose TEXT;\n ALTER TABLE executions ADD COLUMN attempt INTEGER;\n ALTER TABLE executions ADD COLUMN trigger TEXT;\n ALTER TABLE executions ADD COLUMN trigger_reason TEXT;\n ALTER TABLE executions ADD COLUMN input_tokens INTEGER;\n ALTER TABLE executions ADD COLUMN output_tokens INTEGER;\n ALTER TABLE executions ADD COLUMN cache_read_tokens INTEGER;\n ALTER TABLE executions ADD COLUMN cache_creation_tokens INTEGER;\n ALTER TABLE executions ADD COLUMN reasoning_tokens INTEGER;\n ALTER TABLE executions ADD COLUMN harness TEXT;\n ALTER TABLE executions ADD COLUMN provider TEXT;\n ALTER TABLE executions ADD COLUMN model_requested TEXT;\n ALTER TABLE executions ADD COLUMN model_resolved TEXT;\n\n ALTER TABLE provider_health ADD COLUMN status TEXT;\n ALTER TABLE provider_health ADD COLUMN consecutive_failures INTEGER NOT NULL DEFAULT 0;\n ALTER TABLE provider_health ADD COLUMN cooldown_level INTEGER NOT NULL DEFAULT 0;\n ALTER TABLE provider_health ADD COLUMN cooldown_until TEXT;\n ALTER TABLE provider_health ADD COLUMN last_failure_kind TEXT;\n ALTER TABLE provider_health ADD COLUMN last_failure_at TEXT;\n ALTER TABLE provider_health ADD COLUMN last_success_at TEXT;\n ALTER TABLE provider_health ADD COLUMN probe_in_flight INTEGER NOT NULL DEFAULT 0 CHECK (probe_in_flight IN (0, 1));\n ALTER TABLE provider_health ADD COLUMN probe_started_at TEXT;\n CREATE TABLE provider_health_failures (\n project_id TEXT NOT NULL,\n provider TEXT NOT NULL,\n occurred_at TEXT NOT NULL,\n kind TEXT NOT NULL,\n PRIMARY KEY (project_id, provider, occurred_at, kind),\n FOREIGN KEY (project_id, provider) REFERENCES provider_health(project_id, provider) ON DELETE CASCADE\n );\n\n ALTER TABLE queues ADD COLUMN requested_json TEXT NOT NULL DEFAULT '[]';\n ALTER TABLE queues ADD COLUMN branch_name TEXT;\n ALTER TABLE queues ADD COLUMN no_branch INTEGER NOT NULL DEFAULT 0 CHECK (no_branch IN (0, 1));\n ALTER TABLE queues ADD COLUMN pr_review INTEGER NOT NULL DEFAULT 0 CHECK (pr_review IN (0, 1));\n ALTER TABLE queues ADD COLUMN truncated INTEGER NOT NULL DEFAULT 0 CHECK (truncated IN (0, 1));\n ALTER TABLE queue_issues ADD COLUMN number INTEGER;\n ALTER TABLE queue_issues ADD COLUMN title TEXT;\n ALTER TABLE queue_issues ADD COLUMN url TEXT;\n ALTER TABLE queue_issues ADD COLUMN source TEXT;\n ALTER TABLE queue_issues ADD COLUMN origin TEXT;\n ALTER TABLE queue_issues ADD COLUMN role TEXT;\n ALTER TABLE queue_issues ADD COLUMN priority TEXT;\n ALTER TABLE queue_issues ADD COLUMN heuristic INTEGER NOT NULL DEFAULT 0 CHECK (heuristic IN (0, 1));\n ALTER TABLE queue_issues ADD COLUMN failed_phase TEXT;\n ALTER TABLE queue_issues ADD COLUMN last_error_category TEXT;\n ALTER TABLE queue_issues ADD COLUMN last_error_message TEXT;\n ALTER TABLE queue_issues ADD COLUMN last_error_at TEXT;\n ALTER TABLE queue_issues ADD COLUMN attempts INTEGER NOT NULL DEFAULT 0;\n ALTER TABLE queue_issues ADD COLUMN blocked_reason TEXT;\n ALTER TABLE queue_issues ADD COLUMN started_at TEXT;\n ALTER TABLE queue_issues ADD COLUMN completed_at TEXT;\n CREATE TABLE queue_dependencies (\n queue_id TEXT NOT NULL REFERENCES queues(id) ON DELETE CASCADE,\n issue_id TEXT NOT NULL,\n depends_on_issue_id TEXT NOT NULL,\n PRIMARY KEY (queue_id, issue_id, depends_on_issue_id),\n CHECK (issue_id <> depends_on_issue_id)\n );\n CREATE TABLE user_story_numbering (\n project_id TEXT PRIMARY KEY REFERENCES projects(id) ON DELETE CASCADE,\n next_number INTEGER NOT NULL CHECK (next_number > 0),\n source TEXT NOT NULL,\n issue_id TEXT NOT NULL,\n decided_at TEXT NOT NULL,\n detail TEXT\n );\n CREATE UNIQUE INDEX events_run_sequence_idx ON events(run_id, sequence) WHERE run_id IS NOT NULL AND sequence IS NOT NULL;\n CREATE INDEX snapshots_project_session_updated_idx ON snapshots(project_id, session_id, updated_at DESC);\n CREATE INDEX events_project_session_sequence_idx ON events(project_id, session_id, sequence);\n CREATE INDEX executions_project_purpose_started_idx ON executions(project_id, purpose, started_at);\n CREATE INDEX queue_issues_project_status_idx ON queue_issues(project_id, status, position);\n CREATE INDEX provider_health_failures_lookup_idx ON provider_health_failures(project_id, provider, occurred_at DESC);\n `),\n },\n {\n version: 5,\n name: 'preserve pr review target state',\n up: (database) =>\n database.exec('ALTER TABLE pipelines ADD COLUMN pr_review_pull_request_number INTEGER;'),\n },\n {\n version: 6,\n name: 'record completed project adoption and runtime phase history',\n up: (database) =>\n database.exec(`\n CREATE TABLE project_imports (\n project_id TEXT PRIMARY KEY REFERENCES projects(id) ON DELETE CASCADE,\n completed_at TEXT NOT NULL\n );\n ALTER TABLE phases ADD COLUMN duration_ms INTEGER;\n ALTER TABLE phases ADD COLUMN input_tokens INTEGER;\n ALTER TABLE phases ADD COLUMN output_tokens INTEGER;\n ALTER TABLE phases ADD COLUMN cache_read_tokens INTEGER;\n ALTER TABLE phases ADD COLUMN cache_creation_tokens INTEGER;\n ALTER TABLE phases ADD COLUMN cost_status TEXT;\n ALTER TABLE phases ADD COLUMN cost_amount REAL;\n CREATE INDEX phases_run_status_started_idx ON phases(run_id, status, started_at);\n CREATE INDEX reviews_pull_request_created_idx ON reviews(pull_request_id, created_at DESC);\n `),\n },\n {\n version: 7,\n name: 'index execution history by harness and run',\n up: (database) =>\n database.exec(`\n CREATE INDEX executions_harness_started_idx ON executions(harness, started_at);\n CREATE INDEX executions_run_id_idx ON executions(run_id);\n `),\n },\n {\n version: 8,\n name: 'explicit issue closure authorization',\n up: (database) =>\n database.exec(`\n ALTER TABLE pipelines ADD COLUMN close_issue INTEGER CHECK (close_issue IN (0, 1));\n ALTER TABLE pipelines ADD COLUMN issue_closed_at TEXT;\n `),\n },\n];\n\nexport const CURRENT_SCHEMA_VERSION = migrations.at(-1)?.version ?? 0;\n\nfunction userVersion(database: DatabaseDriver): number {\n return Number(\n database.prepare('PRAGMA user_version').get<{ user_version: number }>()?.user_version ?? 0,\n );\n}\n\nexport function migrateDatabase(database: DatabaseDriver): number {\n const current = userVersion(database);\n if (current > CURRENT_SCHEMA_VERSION) {\n throw new Error(\n `Database schema version ${current} is newer than this Issue Flow supports (${CURRENT_SCHEMA_VERSION}). Upgrade Issue Flow before opening this database.`,\n );\n }\n\n database.exec(\n 'CREATE TABLE IF NOT EXISTS schema_migrations (version INTEGER PRIMARY KEY, name TEXT NOT NULL, applied_at TEXT NOT NULL)',\n );\n for (const migration of migrations) {\n if (migration.version <= current) continue;\n database.transaction(() => {\n migration.up(database);\n database\n .prepare('INSERT INTO schema_migrations (version, name, applied_at) VALUES (?, ?, ?)')\n .run(migration.version, migration.name, new Date().toISOString());\n database.exec(`PRAGMA user_version = ${migration.version}`);\n });\n }\n return CURRENT_SCHEMA_VERSION;\n}\n"],"mappings":"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","names":["dirname","dirname"]}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/issues/hash.ts
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
5
|
+
function normalizeText(value) {
|
|
6
|
+
return value.replace(/\r\n?/g, "\n").trim();
|
|
7
|
+
}
|
|
8
|
+
function hashIssueContent(title, body) {
|
|
9
|
+
const payload = JSON.stringify({
|
|
10
|
+
title: normalizeText(title),
|
|
11
|
+
body: normalizeText(body)
|
|
12
|
+
});
|
|
13
|
+
const digest = createHash("sha256").update(payload, "utf8").digest("hex");
|
|
14
|
+
return `sha256:${digest}`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// src/issues/markdown.ts
|
|
18
|
+
function parseIssueMarkdown(content) {
|
|
19
|
+
const lines = content.replace(/\r\n?/g, "\n").split("\n");
|
|
20
|
+
const headingIndex = lines.findIndex((line) => line.trim().length > 0);
|
|
21
|
+
const heading = headingIndex === -1 ? void 0 : lines[headingIndex].match(/^#[ \t]+(.*)$/);
|
|
22
|
+
if (!heading) {
|
|
23
|
+
return { title: "", body: lines.join("\n").trim() };
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
title: heading[1].trim(),
|
|
27
|
+
body: lines.slice(headingIndex + 1).join("\n").trim()
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export {
|
|
32
|
+
hashIssueContent,
|
|
33
|
+
parseIssueMarkdown
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=chunk-7ZZUSMBJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/issues/hash.ts","../src/issues/markdown.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\n\n/**\n * Content hashing shared by every Issue provider.\n *\n * The hash is what lets the resolver tell \"the local and the remote Issue are\n * the same demand\" from \"they diverged\", so it has to be stable across\n * platforms: Windows checkouts store CRLF, editors add or drop trailing\n * newlines, and neither should count as a content change.\n */\n\n/** Normalizes line endings (CRLF/CR to LF) and trims the edges. */\nfunction normalizeText(value: string): string {\n return value.replace(/\\r\\n?/g, '\\n').trim();\n}\n\n/**\n * Returns `sha256:<hex>` for the given title and body.\n *\n * The payload is a canonical JSON object rather than a concatenation, so text\n * moved from the title into the body always changes the hash.\n */\nexport function hashIssueContent(title: string, body: string): string {\n const payload = JSON.stringify({\n title: normalizeText(title),\n body: normalizeText(body),\n });\n const digest = createHash('sha256').update(payload, 'utf8').digest('hex');\n return `sha256:${digest}`;\n}\n","/**\n * Title lives in the leading H1, body is everything after it.\n *\n * Only the first non-empty line is considered: an H1 further down belongs to\n * the body (issue descriptions routinely use headings), so promoting it would\n * silently retitle the Issue.\n */\nexport function parseIssueMarkdown(content: string): { title: string; body: string } {\n const lines = content.replace(/\\r\\n?/g, '\\n').split('\\n');\n const headingIndex = lines.findIndex((line) => line.trim().length > 0);\n const heading = headingIndex === -1 ? undefined : lines[headingIndex].match(/^#[ \\t]+(.*)$/);\n\n if (!heading) {\n return { title: '', body: lines.join('\\n').trim() };\n }\n\n return {\n title: heading[1].trim(),\n body: lines\n .slice(headingIndex + 1)\n .join('\\n')\n .trim(),\n };\n}\n"],"mappings":";;;AAAA,SAAS,kBAAkB;AAY3B,SAAS,cAAc,OAAuB;AAC5C,SAAO,MAAM,QAAQ,UAAU,IAAI,EAAE,KAAK;AAC5C;AAQO,SAAS,iBAAiB,OAAe,MAAsB;AACpE,QAAM,UAAU,KAAK,UAAU;AAAA,IAC7B,OAAO,cAAc,KAAK;AAAA,IAC1B,MAAM,cAAc,IAAI;AAAA,EAC1B,CAAC;AACD,QAAM,SAAS,WAAW,QAAQ,EAAE,OAAO,SAAS,MAAM,EAAE,OAAO,KAAK;AACxE,SAAO,UAAU,MAAM;AACzB;;;ACtBO,SAAS,mBAAmB,SAAkD;AACnF,QAAM,QAAQ,QAAQ,QAAQ,UAAU,IAAI,EAAE,MAAM,IAAI;AACxD,QAAM,eAAe,MAAM,UAAU,CAAC,SAAS,KAAK,KAAK,EAAE,SAAS,CAAC;AACrE,QAAM,UAAU,iBAAiB,KAAK,SAAY,MAAM,YAAY,EAAE,MAAM,eAAe;AAE3F,MAAI,CAAC,SAAS;AACZ,WAAO,EAAE,OAAO,IAAI,MAAM,MAAM,KAAK,IAAI,EAAE,KAAK,EAAE;AAAA,EACpD;AAEA,SAAO;AAAA,IACL,OAAO,QAAQ,CAAC,EAAE,KAAK;AAAA,IACvB,MAAM,MACH,MAAM,eAAe,CAAC,EACtB,KAAK,IAAI,EACT,KAAK;AAAA,EACV;AACF;","names":[]}
|
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
} from "./chunk-P3LRUNC6.js";
|
|
5
5
|
import {
|
|
6
6
|
writeFileAtomic
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-PFWHHYEO.js";
|
|
8
8
|
|
|
9
9
|
// src/core/session/publishers.ts
|
|
10
|
-
import { utimes } from "fs/promises";
|
|
10
|
+
import { utimes } from "node:fs/promises";
|
|
11
11
|
|
|
12
12
|
// src/core/session/events.ts
|
|
13
13
|
var DEFAULT_LOG_LIMIT = 200;
|
|
@@ -102,7 +102,7 @@ function applyGitEvent(snapshot, event) {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
// src/core/session/reducer-log.ts
|
|
105
|
-
import { stripVTControlCharacters } from "util";
|
|
105
|
+
import { stripVTControlCharacters } from "node:util";
|
|
106
106
|
function applyLogEvent(snapshot, event, options) {
|
|
107
107
|
switch (event.type) {
|
|
108
108
|
case "log": {
|
|
@@ -189,7 +189,7 @@ function applyMetricsEvent(snapshot, event) {
|
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
// src/core/session/reducer-phase.ts
|
|
192
|
-
import { stripVTControlCharacters as stripVTControlCharacters2 } from "util";
|
|
192
|
+
import { stripVTControlCharacters as stripVTControlCharacters2 } from "node:util";
|
|
193
193
|
|
|
194
194
|
// src/core/session/reducer-stage.ts
|
|
195
195
|
function isTerminalStage(stage) {
|
|
@@ -487,7 +487,7 @@ function applyResilienceEvent(snapshot, event) {
|
|
|
487
487
|
}
|
|
488
488
|
|
|
489
489
|
// src/core/session/reducer-session.ts
|
|
490
|
-
import { stripVTControlCharacters as stripVTControlCharacters3 } from "util";
|
|
490
|
+
import { stripVTControlCharacters as stripVTControlCharacters3 } from "node:util";
|
|
491
491
|
function applySessionEvent(snapshot, event) {
|
|
492
492
|
switch (event.type) {
|
|
493
493
|
case "session:start": {
|
|
@@ -878,6 +878,7 @@ function getSessionPublisher() {
|
|
|
878
878
|
}
|
|
879
879
|
|
|
880
880
|
export {
|
|
881
|
+
DEFAULT_SESSION_HEARTBEAT_MS,
|
|
881
882
|
createInitialSnapshot,
|
|
882
883
|
reduceSessionEvent,
|
|
883
884
|
NullPublisher,
|
|
@@ -886,4 +887,4 @@ export {
|
|
|
886
887
|
setSessionPublisher,
|
|
887
888
|
getSessionPublisher
|
|
888
889
|
};
|
|
889
|
-
//# sourceMappingURL=chunk-
|
|
890
|
+
//# sourceMappingURL=chunk-DIMDAYZS.js.map
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
getGlobalRoot
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-MAJ6QDRL.js";
|
|
5
5
|
import {
|
|
6
6
|
getProjectRoot
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-TJVX42KY.js";
|
|
8
8
|
import {
|
|
9
9
|
writeFileAtomic
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-PFWHHYEO.js";
|
|
11
11
|
|
|
12
12
|
// src/agents/permissions.ts
|
|
13
|
-
import { mkdir, readFile } from "fs/promises";
|
|
14
|
-
import { homedir } from "os";
|
|
15
|
-
import { dirname, join } from "path";
|
|
13
|
+
import { mkdir, readFile } from "node:fs/promises";
|
|
14
|
+
import { homedir } from "node:os";
|
|
15
|
+
import { dirname, join } from "node:path";
|
|
16
16
|
var MANAGED_MARKER = "issue-flow:managed";
|
|
17
17
|
function managedEntries(root) {
|
|
18
18
|
const glob = `${root.replace(/\\/g, "/")}/**`;
|
|
@@ -74,4 +74,4 @@ export {
|
|
|
74
74
|
ensureCursorStorageGrant,
|
|
75
75
|
cursorStorageGranted
|
|
76
76
|
};
|
|
77
|
-
//# sourceMappingURL=chunk-
|
|
77
|
+
//# sourceMappingURL=chunk-DXH4TQRB.js.map
|
|
@@ -53,6 +53,8 @@ function keyOf(record, key) {
|
|
|
53
53
|
return record.agent.model.resolved ?? record.agent.model.requested ?? "unknown";
|
|
54
54
|
case "purpose":
|
|
55
55
|
return record.purpose;
|
|
56
|
+
case "trigger":
|
|
57
|
+
return record.trigger;
|
|
56
58
|
case "status":
|
|
57
59
|
return record.status;
|
|
58
60
|
default: {
|
|
@@ -80,4 +82,4 @@ export {
|
|
|
80
82
|
summarize,
|
|
81
83
|
groupBy
|
|
82
84
|
};
|
|
83
|
-
//# sourceMappingURL=chunk-
|
|
85
|
+
//# sourceMappingURL=chunk-E2H3JV2E.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/telemetry/aggregate.ts"],"sourcesContent":["import type { TaskPlan } from '../types.js';\nimport type { ExecutionRecord, ExecutionStatus, ExecutionSummary } from './types.js';\n\nfunction emptySummary(discarded = 0): ExecutionSummary {\n return {\n count: 0,\n discarded,\n byStatus: {},\n usage: {\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n cacheCreationTokens: 0,\n reasoningTokens: 0,\n },\n totalCost: { reported: 0, estimated: 0, unknownExecutions: 0 },\n };\n}\n\nfunction add(into: ExecutionSummary, record: ExecutionRecord): void {\n into.count += 1;\n into.byStatus[record.status] = (into.byStatus[record.status] ?? 0) + 1;\n if (record.usage !== null) {\n into.usage.inputTokens += record.usage.inputTokens ?? 0;\n into.usage.outputTokens += record.usage.outputTokens ?? 0;\n into.usage.cacheReadTokens += record.usage.cacheReadTokens ?? 0;\n into.usage.cacheCreationTokens += record.usage.cacheCreationTokens ?? 0;\n into.usage.reasoningTokens += record.usage.reasoningTokens ?? 0;\n }\n if (record.cost.status === 'reported') {\n into.totalCost.reported += record.cost.amount;\n } else if (record.cost.status === 'estimated') {\n into.totalCost.estimated += record.cost.amount;\n } else {\n into.totalCost.unknownExecutions += 1;\n }\n}\n\nfunction isPlan(value: TaskPlan | readonly ExecutionRecord[]): value is TaskPlan {\n return !Array.isArray(value) && 'userStories' in value;\n}\n\nexport function summarize(\n input: TaskPlan | readonly ExecutionRecord[],\n discarded = 0,\n): ExecutionSummary {\n const records = isPlan(input) ? (input.executions ?? []) : input;\n const summary = emptySummary(discarded);\n for (const record of records) add(summary, record);\n return summary;\n}\n\nexport type GroupKey = 'harness' | 'provider' | 'model' | 'purpose' | 'trigger' | 'status';\n\nfunction keyOf(record: ExecutionRecord, key: GroupKey): string {\n switch (key) {\n case 'harness':\n return record.agent.harness;\n case 'provider':\n return record.agent.provider ?? 'unknown';\n case 'model':\n return record.agent.model.resolved ?? record.agent.model.requested ?? 'unknown';\n case 'purpose':\n return record.purpose;\n case 'trigger':\n return record.trigger;\n case 'status':\n return record.status;\n default: {\n const _exhaustive: never = key;\n return _exhaustive;\n }\n }\n}\n\nexport function groupBy(\n records: readonly ExecutionRecord[],\n key: GroupKey,\n): Map<string, ExecutionSummary> {\n const groups = new Map<string, ExecutionRecord[]>();\n for (const record of records) {\n const name = keyOf(record, key);\n const bucket = groups.get(name);\n if (bucket === undefined) groups.set(name, [record]);\n else bucket.push(record);\n }\n const result = new Map<string, ExecutionSummary>();\n for (const [name, bucket] of groups) {\n result.set(name, summarize(bucket));\n }\n return result;\n}\n\nexport type { ExecutionStatus };\n"],"mappings":";;;AAGA,SAAS,aAAa,YAAY,GAAqB;AACrD,SAAO;AAAA,IACL,OAAO;AAAA,IACP;AAAA,IACA,UAAU,CAAC;AAAA,IACX,OAAO;AAAA,MACL,aAAa;AAAA,MACb,cAAc;AAAA,MACd,iBAAiB;AAAA,MACjB,qBAAqB;AAAA,MACrB,iBAAiB;AAAA,IACnB;AAAA,IACA,WAAW,EAAE,UAAU,GAAG,WAAW,GAAG,mBAAmB,EAAE;AAAA,EAC/D;AACF;AAEA,SAAS,IAAI,MAAwB,QAA+B;AAClE,OAAK,SAAS;AACd,OAAK,SAAS,OAAO,MAAM,KAAK,KAAK,SAAS,OAAO,MAAM,KAAK,KAAK;AACrE,MAAI,OAAO,UAAU,MAAM;AACzB,SAAK,MAAM,eAAe,OAAO,MAAM,eAAe;AACtD,SAAK,MAAM,gBAAgB,OAAO,MAAM,gBAAgB;AACxD,SAAK,MAAM,mBAAmB,OAAO,MAAM,mBAAmB;AAC9D,SAAK,MAAM,uBAAuB,OAAO,MAAM,uBAAuB;AACtE,SAAK,MAAM,mBAAmB,OAAO,MAAM,mBAAmB;AAAA,EAChE;AACA,MAAI,OAAO,KAAK,WAAW,YAAY;AACrC,SAAK,UAAU,YAAY,OAAO,KAAK;AAAA,EACzC,WAAW,OAAO,KAAK,WAAW,aAAa;AAC7C,SAAK,UAAU,aAAa,OAAO,KAAK;AAAA,EAC1C,OAAO;AACL,SAAK,UAAU,qBAAqB;AAAA,EACtC;AACF;AAEA,SAAS,OAAO,OAAiE;AAC/E,SAAO,CAAC,MAAM,QAAQ,KAAK,KAAK,iBAAiB;AACnD;AAEO,SAAS,UACd,OACA,YAAY,GACM;AAClB,QAAM,UAAU,OAAO,KAAK,IAAK,MAAM,cAAc,CAAC,IAAK;AAC3D,QAAM,UAAU,aAAa,SAAS;AACtC,aAAW,UAAU,QAAS,KAAI,SAAS,MAAM;AACjD,SAAO;AACT;AAIA,SAAS,MAAM,QAAyB,KAAuB;AAC7D,UAAQ,KAAK;AAAA,IACX,KAAK;AACH,aAAO,OAAO,MAAM;AAAA,IACtB,KAAK;AACH,aAAO,OAAO,MAAM,YAAY;AAAA,IAClC,KAAK;AACH,aAAO,OAAO,MAAM,MAAM,YAAY,OAAO,MAAM,MAAM,aAAa;AAAA,IACxE,KAAK;AACH,aAAO,OAAO;AAAA,IAChB,KAAK;AACH,aAAO,OAAO;AAAA,IAChB,KAAK;AACH,aAAO,OAAO;AAAA,IAChB,SAAS;AACP,YAAM,cAAqB;AAC3B,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEO,SAAS,QACd,SACA,KAC+B;AAC/B,QAAM,SAAS,oBAAI,IAA+B;AAClD,aAAW,UAAU,SAAS;AAC5B,UAAM,OAAO,MAAM,QAAQ,GAAG;AAC9B,UAAM,SAAS,OAAO,IAAI,IAAI;AAC9B,QAAI,WAAW,OAAW,QAAO,IAAI,MAAM,CAAC,MAAM,CAAC;AAAA,QAC9C,QAAO,KAAK,MAAM;AAAA,EACzB;AACA,QAAM,SAAS,oBAAI,IAA8B;AACjD,aAAW,CAAC,MAAM,MAAM,KAAK,QAAQ;AACnC,WAAO,IAAI,MAAM,UAAU,MAAM,CAAC;AAAA,EACpC;AACA,SAAO;AACT;","names":[]}
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
NullPublisher
|
|
4
|
+
} from "./chunk-DIMDAYZS.js";
|
|
5
|
+
import {
|
|
6
|
+
isoNow
|
|
7
|
+
} from "./chunk-GD7JZMSJ.js";
|
|
2
8
|
import {
|
|
3
9
|
getBaseBranch,
|
|
4
10
|
getCommitsSince,
|
|
@@ -9,13 +15,7 @@ import {
|
|
|
9
15
|
normalizeRemoteUrl,
|
|
10
16
|
run,
|
|
11
17
|
stripRemoteUrlCredentials
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import {
|
|
14
|
-
NullPublisher
|
|
15
|
-
} from "./chunk-DUS5ZNKL.js";
|
|
16
|
-
import {
|
|
17
|
-
isoNow
|
|
18
|
-
} from "./chunk-SOM4QA6C.js";
|
|
18
|
+
} from "./chunk-TJVX42KY.js";
|
|
19
19
|
|
|
20
20
|
// src/core/session-git.ts
|
|
21
21
|
async function listPullRequests(branch, options = {}) {
|
|
@@ -112,4 +112,4 @@ export {
|
|
|
112
112
|
listPullRequests,
|
|
113
113
|
publishGitState
|
|
114
114
|
};
|
|
115
|
-
//# sourceMappingURL=chunk-
|
|
115
|
+
//# sourceMappingURL=chunk-ERAG5IQB.js.map
|