feat(storage): move desktop SQLite ownership into a storage utility process
Main no longer opens desktop business SQLite. A single utility-process
storage owner holds the Assistant, Knowledge, attachment, document-result,
remote-binding and native model-ledger databases and runs whole domain
transactions; Main and Runtime callers use explicit async domain operations.
- Remove the Main readWithFallback path and the supervision worker's
separate business write connection; manifests are scanned read-only and
committed by the sole writer.
- Remote events are ACKed only after commit; process loss reports
unconfirmed writes instead of replaying them.
- Use native utilityProcess messaging with one in-flight bound (32) and
ordered waiting instead of capacity rejection and retries.
- Start the storage process before the window so startup overlaps.
- Keep short tool output in memory and store full output once in owned
storage; paging, branching, restart and deletion reuse that copy.
- Put required runtime launch files under goodbuddy-prefixed owned temp
directories and reclaim confirmed stale ones; stop writing duplicate
parsed.md.
- Add lint guards for SQLite ownership and architecture, concurrency
acceptance fixtures, and maintenance/architecture documentation.
Validation: typecheck and lint pass. Alternating baseline (edf019aa) vs
candidate runs show no regression on single-session, C01, C02, C04 and a
5-minute C06; Main p99 drops from ~21 ms to ~16.7 ms under concurrency.
Fresh portable copy (1.75M events) upgrade content unchanged; real-model
output paging passes with 4 external requests. Full npm test runs had
1-9 different timing-sensitive failures per run that pass when rerun.
Not covered: C03 review publication, C07 source body, app-level C05.
This commit is contained in:
@@ -1,8 +1,7 @@
|
||||
// @vitest-environment node
|
||||
import { spawn } from 'node:child_process'
|
||||
import { createRequire } from 'node:module'
|
||||
import { copyFile, mkdtemp, readFile, rm } from 'node:fs/promises'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { copyFile, mkdir, mkdtemp, readFile, rm } from 'node:fs/promises'
|
||||
import { join, resolve } from 'node:path'
|
||||
import { createServer } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
@@ -12,10 +11,14 @@ import { AssistantDatabase } from '../src/main/assistant/assistant-database'
|
||||
import { SupervisionStoryStore } from '../src/main/assistant/supervision-stories'
|
||||
import { SupervisionExperienceStore } from '../src/main/assistant/supervision-experiences'
|
||||
|
||||
it(process.env.GOODBUDDY_SUPERVISOR_SIDEBAR
|
||||
it(process.env.GOODBUDDY_STORY_GRAPH_FOCUS
|
||||
? 'focuses the production 3D story camera through canvas and sidebar selection'
|
||||
: process.env.GOODBUDDY_SUPERVISOR_SIDEBAR
|
||||
? 'renders the production supervision card at narrow widths in both themes'
|
||||
: 'renders the production supervisor at desktop, narrow, and mobile widths', async () => {
|
||||
const directory = await mkdtemp(join(tmpdir(), 'goodbuddy-supervisor-'))
|
||||
const temporaryRoot = resolve('temp/story-graph-focus')
|
||||
await mkdir(temporaryRoot, { recursive: true })
|
||||
const directory = await mkdtemp(join(temporaryRoot, 'supervisor-'))
|
||||
// Optional local acceptance run against an online backup. Never initialize/migrate or run models.
|
||||
let portable: string | undefined
|
||||
if (process.env.GOODBUDDY_SUPERVISOR_BACKUP) {
|
||||
@@ -48,6 +51,11 @@ it(process.env.GOODBUDDY_SUPERVISOR_SIDEBAR
|
||||
{
|
||||
name: 'supervisor-fixture',
|
||||
configureServer(server) {
|
||||
if (process.env.GOODBUDDY_STORY_GRAPH_FOCUS) server.middlewares.use('/focus-export.json', async (_request, response) => {
|
||||
const source = await readFile('docs/features/story-graph/3d-demo-data.js', 'utf8')
|
||||
response.setHeader('Content-Type', 'application/json')
|
||||
response.end(source.slice(source.indexOf('window.STORY_DATA = ') + 'window.STORY_DATA = '.length).trim().replace(/;$/, ''))
|
||||
})
|
||||
if (portable) server.middlewares.use('/portable-review.json', (_request, response) => {
|
||||
response.setHeader('Content-Type', 'application/json')
|
||||
response.end(portable)
|
||||
@@ -73,6 +81,7 @@ it(process.env.GOODBUDDY_SUPERVISOR_SIDEBAR
|
||||
await server.listen()
|
||||
const env: NodeJS.ProcessEnv = {
|
||||
...process.env,
|
||||
TEMP: directory, TMP: directory, TMPDIR: directory,
|
||||
GOODBUDDY_SUPERVISOR_URL:
|
||||
server.resolvedUrls!.local[0] + 'supervisor.html' + (process.env.GOODBUDDY_SUPERVISOR_SIDEBAR ? '?sidebar=1' : ''),
|
||||
GOODBUDDY_SUPERVISOR_DIRECTORY: directory,
|
||||
@@ -89,6 +98,7 @@ it(process.env.GOODBUDDY_SUPERVISOR_SIDEBAR
|
||||
driver
|
||||
)
|
||||
await copyFile(resolve('tests/support/supervisor-selection-driver.mjs'), join(directory, 'supervisor-selection-driver.mjs'))
|
||||
await copyFile(resolve('tests/support/story-graph-focus-driver.mjs'), join(directory, 'story-graph-focus-driver.mjs'))
|
||||
const child = spawn(createRequire(import.meta.url)('electron'), [driver], {
|
||||
env,
|
||||
stdio: ['ignore', 'pipe', 'pipe']
|
||||
@@ -107,7 +117,8 @@ it(process.env.GOODBUDDY_SUPERVISOR_SIDEBAR
|
||||
child.once('error', reject)
|
||||
})
|
||||
expect(code, output).toBe(0)
|
||||
console.log(output)
|
||||
if (process.env.GOODBUDDY_STORY_GRAPH_FOCUS) process.stdout.write(output)
|
||||
else console.log(output)
|
||||
} finally {
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user