refactor(session): rename the seed boundary to end-seed

This commit is contained in:
Hypatia May
2026-07-30 15:32:06 +08:00
parent e8f0934bc2
commit 8527137230
44 changed files with 228 additions and 224 deletions

View File

@@ -52,11 +52,11 @@ function lastSeq(session: Session): number {
return event.seq
}
/** A seeded child's inherited prefix: its log minus the constructor's boundary. */
/** A seeded child's constructor seed: its log minus the end-seed marker. */
function inherited(session: Session): readonly SessionEvent[] {
const events = session.events
const last = events.at(-1)
if (last?.type !== 'session/inherited') throw new Error('seeded child is missing its inherited boundary')
if (last?.type !== 'session/end-seed') throw new Error('seeded child is missing its end-seed marker')
return events.slice(0, -1)
}
@@ -166,12 +166,12 @@ describe('SessionStore.fork', () => {
const child = sessions.fork(parent, undefined, SessionId('bracket-child'))
// Parent: nothing above the bracket, so its owner must treat it as live.
// Parent: no end-seed event follows the bracket, so its owner treats it as live.
expect(parent.events.at(-1)).toBe(open)
expect(parent.events.some(event => event.type === 'session/inherited')).toBe(false)
// Child: the same bracket sits below its boundary, so it is dead history.
expect(parent.events.some(event => event.type === 'session/end-seed')).toBe(false)
// Child: the same bracket is before end-seed, so it belongs to the seed.
const boundary = child.events.at(-1)
expect(boundary).toMatchObject({ type: 'session/inherited' })
expect(boundary).toMatchObject({ type: 'session/end-seed' })
expect(boundary!.seq).toBeGreaterThan(open.seq)
expect(child.firstLiveSeq).toBe(open.seq + 1)
expect(inherited(child).at(-1)).toMatchObject({ type: 'test/bracket-open', data: { id: 'op-1' } })

View File

@@ -382,7 +382,7 @@ describe('session-log invariants', () => {
.toThrow(/turn 1 is still open/)
})
it('accepts the inherited boundary whether or not a turn is open', async () => {
it('accepts end-seed whether or not a turn is open', async () => {
const { ctx } = await setup()
// Balanced seed: between turns.
expect(() => ctx.sessions.create(SessionId('inherited-between-turns'), { seed: [
@@ -393,7 +393,7 @@ describe('session-log invariants', () => {
const open = ctx.sessions.create(SessionId('inherited-inside-open-turn'), { seed: [
{ type: 'turn/start', seq: 0, time: 1, data: { turn: 1, trigger: { kind: 'message', source: { kind: 'user' } } } },
] })
expect(open.events.map(event => event.type)).toEqual(['turn/start', 'session/inherited'])
expect(open.events.map(event => event.type)).toEqual(['turn/start', 'session/end-seed'])
// Still open afterwards: the boundary moves no cursor.
expect(() => open.append('turn/start', { turn: 2, trigger: { kind: 'message', source: { kind: 'user' } } }))
.toThrow(/turn 1 is still open/)

View File

@@ -118,7 +118,7 @@ describe('Session properties', () => {
}))
})
it('replaying an already-inherited log adds no further boundary', () => {
it('replaying a log that already ends in end-seed adds no further marker', () => {
fc.assert(fc.property(logArb, (events) => {
const original = build(events)
const once = new Session(SessionId(`idem-a-${counter++}`), [...original.events])

View File

@@ -275,8 +275,8 @@ describe('interruptedTurnClosers', () => {
})
describe('lastActivityTime', () => {
const inheritedAt = (seq: number, time: number): SessionEvent =>
({ type: 'session/inherited', seq, time, data: {} })
const endSeedAt = (seq: number, time: number): SessionEvent =>
({ type: 'session/end-seed', seq, time, data: {} })
it('has no answer for an empty log', () => {
expect(lastActivityTime([])).toBeUndefined()
@@ -294,16 +294,16 @@ describe('lastActivityTime', () => {
const events: SessionEvent[] = [
userTurnStart(1, 0),
{ type: 'turn/end', seq: 1, time: 500, data: { turn: 1, reason: { kind: 'completed' } } },
inheritedAt(2, 9_000),
endSeedAt(2, 9_000),
]
// Resumed long after the work, but never worked in again.
expect(lastActivityTime(events)).toBe(500)
})
it('reports work done above a boundary', () => {
it('reports work appended after end-seed', () => {
const events: SessionEvent[] = [
userTurnStart(1, 0),
inheritedAt(1, 9_000),
endSeedAt(1, 9_000),
{ type: 'turn/end', seq: 2, time: 9_500, data: { turn: 1, reason: { kind: 'completed' } } },
]
expect(lastActivityTime(events)).toBe(9_500)
@@ -311,6 +311,6 @@ describe('lastActivityTime', () => {
it('has no answer for a log of nothing but boundaries', () => {
// Unreachable via the constructor, but the projection is a pure function.
expect(lastActivityTime([inheritedAt(0, 1), inheritedAt(1, 2)])).toBeUndefined()
expect(lastActivityTime([endSeedAt(0, 1), endSeedAt(1, 2)])).toBeUndefined()
})
})

View File

@@ -188,7 +188,7 @@ describe('Session', () => {
const replayed = new Session(SessionId('s3-replay'), [...original.events])
expect(replayed.deriveMessages()).toEqual(original.deriveMessages())
// The seed verbatim, plus the boundary the constructor appends over it.
// The seed verbatim, plus the end-seed event the constructor appends.
expect(replayed.events.slice(0, original.seq)).toEqual(original.events)
expect(replayed.seq).toBe(original.seq + 1)
expect(replayed.firstLiveSeq).toBe(original.seq)