Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,13 @@ model ManagedFileSessionSync {
@@index([projectId, deletedAt, groupSortAtMs, sessionId])
}

// Singleton completeness marker for catalog-wide Project Files reconciliation. Per-Session retry
// state remains represented by ManagedFileSessionSync.filesRevision < 0.
model ManagedFileIndexState {
id String @id
isReconciliationComplete Boolean @default(true)
}

// Narrow origin lifecycle retained after a live Session JSON is deleted. This is not a second
// editable Session model; it exists only to preserve managed-file ownership and deletion state.
model FileOriginSession {
Expand Down
5 changes: 5 additions & 0 deletions prisma/sqlite-check-constraints.json
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,11 @@
"name": "ManagedFile_source_check",
"expression": "\"source\" IN ('artifact', 'upload')"
},
{
"tableName": "ManagedFileIndexState",
"name": "ManagedFileIndexState_identity_check",
"expression": "\"id\" = 'project-files-index' AND \"isReconciliationComplete\" IN (false, true)"
},
{
"tableName": "UploadVersion",
"name": "UploadVersion_state_check",
Expand Down
1 change: 1 addition & 0 deletions scripts/ci/module-impact.json
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,7 @@
},
"project_files_repository": {
"ownerPaths": [
"src/main/project-files/index-state.ts",
"src/main/project-files/mutation-owner.ts",
"src/main/project-files/mutation-projection.ts",
"src/main/project-files/query-owner.ts",
Expand Down
4 changes: 4 additions & 0 deletions scripts/database-migration-ledger-smoke.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ const EXPECTED_MIGRATION_LEDGER = [
{
id: '0019_session_usage_attribution',
checksum: 'c505fe7e55e8428d29e8506ad305c04fd46d8def53385a1f7839fba21047ab9d'
},
{
id: '0020_project_files_index_state',
checksum: '3db4a30baa2b4614e29205ed56b2682c0579f1a91f5a2d293d21925253ed7fd1'
}
]
const LEGACY_PROJECT_ID = 'package-smoke-legacy-project'
Expand Down
6 changes: 3 additions & 3 deletions scripts/database-migration-ledger-smoke.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { PrismaClient } from '@prisma/client'
describe('packaged database migration ledger smoke', () => {
it('pins every packaged application migration identity and checksum', () => {
expect(MIGRATION_MANIFEST.at(-1)?.checksum).toBe(
'c505fe7e55e8428d29e8506ad305c04fd46d8def53385a1f7839fba21047ab9d'
'3db4a30baa2b4614e29205ed56b2682c0579f1a91f5a2d293d21925253ed7fd1'
)
expect(() => assertApplicationMigrationLedger(MIGRATION_MANIFEST)).not.toThrow()
expect(() => assertApplicationMigrationLedger(MIGRATION_MANIFEST.slice(0, -1))).toThrow(
Expand Down Expand Up @@ -93,7 +93,7 @@ describe('packaged database migration ledger smoke', () => {
'ALTER TABLE "SessionAuxiliaryTurnUsage" DROP COLUMN "providerId"'
)
await client.$executeRawUnsafe(
`DELETE FROM "_open_science_migrations" WHERE "id" = '0019_session_usage_attribution'`
`DELETE FROM "_open_science_migrations" WHERE "id" IN ('0019_session_usage_attribution', '0020_project_files_index_state')`
)

await migrateApplicationDatabase(client)
Expand Down Expand Up @@ -154,7 +154,7 @@ describe('packaged database migration ledger smoke', () => {
await client.$executeRawUnsafe('DROP INDEX "Review_sessionId_idx"')
await client.$executeRawUnsafe('DROP INDEX "Finding_reviewId_idx"')
await client.$executeRawUnsafe(
`DELETE FROM "_open_science_migrations" WHERE "id" IN ('0014_review_query_indexes', '0015_session_model_call_usage', '0016_compute_job_sensitive_data_encryption', '0017_agent_memory_project_scope', '0018_session_auxiliary_turn_usage', '0019_session_usage_attribution')`
`DELETE FROM "_open_science_migrations" WHERE "id" IN ('0014_review_query_indexes', '0015_session_model_call_usage', '0016_compute_job_sensitive_data_encryption', '0017_agent_memory_project_scope', '0018_session_auxiliary_turn_usage', '0019_session_usage_attribution', '0020_project_files_index_state')`
)
await client.$executeRawUnsafe(
'ALTER TABLE "ComputeJob" DROP COLUMN "sensitiveDataEncrypted"'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import { migrateApplicationDatabase, verifyCurrentApplicationSchema } from './mi

const CURRENT_AGENT_MEMORY_MIGRATION_ID = '0017_agent_memory_project_scope'
const SESSION_AUXILIARY_USAGE_MIGRATION_ID = '0018_session_auxiliary_turn_usage'
const CURRENT_MIGRATION_ID = '0019_session_usage_attribution'
const SESSION_USAGE_ATTRIBUTION_MIGRATION_ID = '0019_session_usage_attribution'
const CURRENT_MIGRATION_ID = '0020_project_files_index_state'
const MEMORY_AUXILIARY_SCHEMA_NAMES = [
'MemoryEntryFts',
'MemoryEntry_fts_insert',
Expand Down Expand Up @@ -299,16 +300,18 @@ describe('agent memory project scope migration', () => {
it('replays an unledgered partial memory migration and restores missing triggers', async () => {
await client.$executeRawUnsafe('DROP TRIGGER "MemoryEntry_fts_update"')
await client.$executeRawUnsafe(
`DELETE FROM "_open_science_migrations" WHERE "id" IN (?, ?, ?)`,
`DELETE FROM "_open_science_migrations" WHERE "id" IN (?, ?, ?, ?)`,
CURRENT_AGENT_MEMORY_MIGRATION_ID,
SESSION_AUXILIARY_USAGE_MIGRATION_ID,
SESSION_USAGE_ATTRIBUTION_MIGRATION_ID,
CURRENT_MIGRATION_ID
)

await expect(migrateApplicationDatabase(client)).resolves.toMatchObject({
applied: [
CURRENT_AGENT_MEMORY_MIGRATION_ID,
SESSION_AUXILIARY_USAGE_MIGRATION_ID,
SESSION_USAGE_ATTRIBUTION_MIGRATION_ID,
CURRENT_MIGRATION_ID
],
to: CURRENT_MIGRATION_ID
Expand Down
10 changes: 6 additions & 4 deletions src/main/database/application-database.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,8 @@ describe('application database (integration)', () => {
'0016_compute_job_sensitive_data_encryption',
'0017_agent_memory_project_scope',
'0018_session_auxiliary_turn_usage',
'0019_session_usage_attribution'
'0019_session_usage_attribution',
'0020_project_files_index_state'
]
})

Expand Down Expand Up @@ -744,7 +745,7 @@ describe('application database (integration)', () => {
it('backs up legacy data through the shared client on a portable storage path', async () => {
storageRoot = await mkdtemp(join(tmpdir(), 'open science 数据 legacy backup-'))
const databasePath = join(storageRoot, 'open-science.db')
const backupPath = `${databasePath}.before-0018_session_auxiliary_turn_usage.backup`
const backupPath = `${databasePath}.before-0019_session_usage_attribution.backup`
const seedClient = createProjectDbClient(storageRoot)
try {
await seedClient.$executeRawUnsafe(`CREATE TABLE "Project" (
Expand Down Expand Up @@ -784,7 +785,7 @@ describe('application database (integration)', () => {
backupClient.$queryRaw<Array<{ id: string }>>`
SELECT "id" FROM "_open_science_migrations" ORDER BY "id" DESC LIMIT 1
`
).resolves.toEqual([{ id: '0017_agent_memory_project_scope' }])
).resolves.toEqual([{ id: '0018_session_auxiliary_turn_usage' }])
} finally {
await backupClient.$disconnect()
}
Expand Down Expand Up @@ -1162,7 +1163,8 @@ describe('application database (integration)', () => {
'0016_compute_job_sensitive_data_encryption',
'0017_agent_memory_project_scope',
'0018_session_auxiliary_turn_usage',
'0019_session_usage_attribution'
'0019_session_usage_attribution',
'0020_project_files_index_state'
]
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,11 @@ describe('Compute Job sensitive data encryption migration', () => {
'0016_compute_job_sensitive_data_encryption',
'0017_agent_memory_project_scope',
'0018_session_auxiliary_turn_usage',
'0019_session_usage_attribution'
'0019_session_usage_attribution',
'0020_project_files_index_state'
],
from: '0015_session_model_call_usage',
to: '0019_session_usage_attribution'
to: '0020_project_files_index_state'
})
await expect(
access(`${databasePath}.before-0016_compute_job_sensitive_data_encryption.backup`)
Expand All @@ -79,10 +80,13 @@ describe('Compute Job sensitive data encryption migration', () => {
).rejects.toMatchObject({ code: 'ENOENT' })
await expect(
access(`${databasePath}.before-0018_session_auxiliary_turn_usage.backup`)
).resolves.toBeUndefined()
).rejects.toMatchObject({ code: 'ENOENT' })
await expect(
access(`${databasePath}.before-0019_session_usage_attribution.backup`)
).resolves.toBeUndefined()
await expect(
access(`${databasePath}.before-0020_project_files_index_state.backup`)
).resolves.toBeUndefined()
await expect(
client.$queryRaw<
Array<{ command: string; sensitiveDataEncrypted: boolean | null }>
Expand Down
10 changes: 7 additions & 3 deletions src/main/database/database-json-constraints-migration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,11 @@ describe('database JSON constraints migration', () => {
'0016_compute_job_sensitive_data_encryption',
'0017_agent_memory_project_scope',
'0018_session_auxiliary_turn_usage',
'0019_session_usage_attribution'
'0019_session_usage_attribution',
'0020_project_files_index_state'
],
from: '0007_notification_attention_metadata',
to: '0019_session_usage_attribution'
to: '0020_project_files_index_state'
})
await expect(access(`${databasePath}.before-${MIGRATION_ID}.backup`)).rejects.toMatchObject({
code: 'ENOENT'
Expand Down Expand Up @@ -191,10 +192,13 @@ describe('database JSON constraints migration', () => {
).rejects.toMatchObject({ code: 'ENOENT' })
await expect(
access(`${databasePath}.before-0018_session_auxiliary_turn_usage.backup`)
).resolves.toBeUndefined()
).rejects.toMatchObject({ code: 'ENOENT' })
await expect(
access(`${databasePath}.before-0019_session_usage_attribution.backup`)
).resolves.toBeUndefined()
await expect(
access(`${databasePath}.before-0020_project_files_index_state.backup`)
).resolves.toBeUndefined()

await expect(
client.$queryRaw<Array<{ scope: string; reviewerLog: string }>>`
Expand Down
3 changes: 2 additions & 1 deletion src/main/database/database-startup-logging.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ describe('database startup logging', () => {
'0016_compute_job_sensitive_data_encryption',
'0017_agent_memory_project_scope',
'0018_session_auxiliary_turn_usage',
'0019_session_usage_attribution'
'0019_session_usage_attribution',
'0020_project_files_index_state'
],
adoptedLegacy: true
})
Expand Down
6 changes: 6 additions & 0 deletions src/main/database/generated/runtime-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,11 @@ const RUNTIME_SCHEMA_TABLE_DDLS = [
"deleteOperationId" TEXT,

PRIMARY KEY ("projectId", "sessionId")
);`,
`CREATE TABLE IF NOT EXISTS "ManagedFileIndexState" (
"id" TEXT NOT NULL PRIMARY KEY,
"isReconciliationComplete" BOOLEAN NOT NULL DEFAULT true,
CONSTRAINT "ManagedFileIndexState_identity_check" CHECK ("id" = 'project-files-index' AND "isReconciliationComplete" IN (false, true))
);`,
`CREATE TABLE IF NOT EXISTS "FileOriginSession" (
"projectId" TEXT NOT NULL,
Expand Down Expand Up @@ -760,6 +765,7 @@ const RUNTIME_SCHEMA_TABLES = [
'ProjectDeletionIntent',
'ManagedFile',
'ManagedFileSessionSync',
'ManagedFileIndexState',
'FileOriginSession',
'ArtifactLineage',
'UploadFile',
Expand Down
Loading
Loading