persistence: advance schemas after removing policy columns

The simplification deletes sandbox_mode and approval_policy from the parent branch's SQLite layouts. Restoring master's older version numbers would violate the monotonic schema contract and could make a database created by the parent look current under a different layout.

Advance durable session persistence from schema 11 to 12 and the disposable session-query index from 6 to 7. The former rejects the incompatible parent layout; the latter resets its derived tables through the existing version-mismatch path.

JSONL shares SESSION_FORMAT_VERSION 0 during pre-release, so explicitly reject the retired sandboxMode and approvalPolicy header fields instead of silently dropping the only inherited policy facts from a parent-produced child log. New logs carry those facts as ordinary seeded events.

Focused JSONL, SQLite persistence, and SQLite query suites cover all affected source lines and branches.
This commit is contained in:
Tianyi Cui
2026-07-28 21:40:51 +08:00
parent 39ff4866ac
commit db0b43bf86
5 changed files with 12 additions and 3 deletions
@@ -64,6 +64,9 @@ export function toHeaderLine(header: SessionHeader): HeaderLine {
* @returns the header, absent optional fields omitted.
*/
export function fromHeaderLine(line: HeaderLine): SessionHeader {
if (Object.hasOwn(line, 'sandboxMode') || Object.hasOwn(line, 'approvalPolicy')) {
throw new Error('session header uses retired policy baseline fields')
}
return {
version: line.version,
id: line.id,
@@ -1023,6 +1023,12 @@ describe('SessionPersistenceJsonl: edge cases', () => {
expect(ids).toContain('big')
})
it.each(['sandboxMode', 'approvalPolicy'] as const)('rejects the retired %s header field', (field) => {
const line = { ...toHeaderLine(meta('retired-policy-header')), [field]: 'read-only' }
expect(() => scanLog(Buffer.from(`${JSON.stringify(line)}\n`)))
.toThrow(/retired policy baseline fields/)
})
it('list rejects a header whose cwd does not identify its physical log', async () => {
const m = meta('misplaced', '/stored')
await ctx.sessionPersistence.create(m)
@@ -17,7 +17,7 @@ import type { SessionEvent, SessionId, SessionHeader, SurfaceOp } from '@deepsee
* layout; orthogonal to a session's own `version` (which versions the EVENT
* vocabulary, stored per session in the `sessions` row).
*/
export const SCHEMA_VERSION = 10
export const SCHEMA_VERSION = 12
/** SQLite application id protecting unrelated databases from persistence writes. */
export const SESSION_PERSISTENCE_SQLITE_APPLICATION_ID = 0x44534850
@@ -609,7 +609,7 @@ describe('SessionPersistenceSqlite: durability and crash semantics', () => {
})
it('exposes the schema version constant', () => {
expect(SCHEMA_VERSION).toBe(10)
expect(SCHEMA_VERSION).toBe(12)
})
it('keeps the revision stable for an empty repair hook', async () => {
@@ -5,7 +5,7 @@ import { mkdir, open } from 'node:fs/promises'
import { dirname, resolve } from 'node:path'
/** Current derived-index schema version. Incompatible versions reset in place. */
export const SESSION_QUERY_SQLITE_SCHEMA_VERSION = 5
export const SESSION_QUERY_SQLITE_SCHEMA_VERSION = 7
/** SQLite application id protecting unrelated databases from derived resets. */
export const SESSION_QUERY_SQLITE_APPLICATION_ID = 0x44534851