Merge remote-tracking branch 'origin/master' into xtr/agent-loop-message-machine

# Conflicts:
#	docs/cordis-catalog/services.md
This commit is contained in:
_Kerman
2026-07-24 21:41:02 +08:00
41 changed files with 922 additions and 144 deletions

View File

@@ -180,6 +180,9 @@ export class PersistenceCoordinator<TornMarker = unknown> {
if (snapshot === undefined) {
return Promise.reject(new TypeError('session metadata must be losslessly JSON-serializable'))
}
if (!Number.isSafeInteger(snapshot.createdAt) || snapshot.createdAt < 0) {
return Promise.reject(new TypeError('session metadata createdAt must be a non-negative safe integer'))
}
return this.serialize(snapshot.id, () => this.createCore(snapshot))
}

View File

@@ -84,6 +84,22 @@ export function runPersistenceContract(name: string, make: () => Promise<Contrac
}
})
it('rejects a fractional creation timestamp without reserving its session id', async () => {
const { persistence, dispose } = await make()
try {
const m = { ...meta('fractional-created-at'), createdAt: 1.5 }
await expect(persistence.create(m))
.rejects.toThrow('session metadata createdAt must be a non-negative safe integer')
const valid = meta('fractional-created-at')
await persistence.create(valid)
await persistence.append(valid.id, oneTurnLog())
expect((await persistence.load(valid.id)).meta.createdAt).toBe(valid.createdAt)
} finally {
await dispose()
}
})
it('crash recovery: load preserves an interrupted (unclosed) turn and closes it with turn/end {interrupted}', async () => {
const { persistence, dispose } = await make()
try {