policy: scope seed-boundary slicing to delegation children

Review fix (ds-review-bot on #623): the unconditional slice regressed the
public SessionStore.fork path — a generic fork child gets seedLength but no
policy baseline, so slicing discarded its seed-carried sandbox/approval
switches and silently widened it to the deployment defaults (a forked
read-only/never parent produced a workspace-write/ask child).

overrideOf now branches on baseline presence: with a header baseline (a
delegation child) the fold covers only own post-seed switches — the
baseline captured from the parent's FULL log subsumes seed history; without
one, the whole log — seeded switches ARE the replayed inherited truth. The
permission preset fold scopes the same way. Red-first: generic-fork
seed-carried override tests in both policy suites.
This commit is contained in:
kingwl
2026-07-26 23:06:15 +08:00
parent b02d3e4c03
commit 99f5fab7bc
17 changed files with 78 additions and 41 deletions
@@ -205,4 +205,17 @@ describe('delegation inheritance (overrideOf over the header baseline)', () => {
expect(() => ctx.sandboxPolicy.overrideOf(child)).toThrow(/sandboxMode/)
})
it('a generic SessionStore.fork child (seedLength, NO baseline) keeps its seed-carried override', async () => {
const ctx = await mounted({ mode: 'workspace-write' })
// The public fork path sets seedLength but captures no delegation
// baseline; the seed boundary must not discard the replayed policy state
// it exists to subsume — with nothing to subsume it, seeded switches ARE
// the child's inherited truth.
const child = inheritedSession('sess-generic-fork', { seedLength: 1 })
setSandboxMode(child, 'read-only')
expect(ctx.sandboxPolicy.overrideOf(child)).toBe('read-only')
expect(ctx.sandboxPolicy.resolve({ session: child }).mode).toBe('read-only')
})
})