From b02d3e4c03f936c83ae877af5749ee716ee3da4a Mon Sep 17 00:00:00 2001 From: kingwl Date: Sun, 26 Jul 2026 22:32:24 +0800 Subject: [PATCH] test(subagent): drop the vacuous fork-dedup case; align wording with the header baseline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dedup guard pinned 'no duplicate stamp' — under the header baseline no stamping exists, so its event-count assertion became a tautology and its fence assertions a subset of the stale-seed case. The spec docblock claimed the session invariants pin the stamping design (superseded); it now names the three review-found timing threats the suite pins as distinct shapes. Service overrideOf JSDoc shrinks to a pointer at the owning pure export. --- docs/cordis-catalog/services.md | 26 +++--------- .../cordis/tool-cordis/src/api-catalog.ts | 4 +- packages/sandbox/sandbox-policy/src/index.ts | 13 ++---- .../tests/inheritance.spec.ts | 41 ++++--------------- packages/ui/user-approval/src/index.ts | 13 ++---- 5 files changed, 21 insertions(+), 76 deletions(-) diff --git a/docs/cordis-catalog/services.md b/docs/cordis-catalog/services.md index 6804e87819..024994840e 100644 --- a/docs/cordis-catalog/services.md +++ b/docs/cordis-catalog/services.md @@ -244,19 +244,12 @@ Approval service that applies session policy before answerers and logs every ask async request(req: ApprovalRequest): Promise /** - * A session's approval-policy OVERRIDE — the override chain alone, never - * the configured default: the fold of the session's OWN switches (events - * past the seed boundary — a fork seed's stale parent switch is subsumed by - * the baseline captured after it), else the header's inherited delegation - * baseline. The subagent driver stamps `overrideOf(parent.session)` into - * each child's creation meta, so a `'never'` (headless/CI) parent cannot - * mint children that fall back to a prompting default, at any depth - * ([rationale](../../../.agents/notes/implemented/feature/2026-07-25-subagent-policy-inheritance.md)). + * {@link approvalOverrideOf} surfaced on the service, for consumers that + * reach the seam through `ctx.get('approval')` (the subagent driver's + * delegation capture) rather than a value import. * @param session - the session whose override chain to resolve. * @returns the effective override, or `undefined` for a session following * the configured default. - * @throws when the durable header baseline is outside the closed policy - * vocabulary (a corrupt or foreign log; durable-boundary validation). */ overrideOf(session: Session): ApprovalPolicy | undefined ``` @@ -973,19 +966,12 @@ The sandbox-policy service (`ctx.sandboxPolicy`). Owns the deployment default mo resolve(request: SandboxPolicyRequest = {}): SandboxExecutionPolicy /** - * A session's sandbox-mode OVERRIDE — the override chain alone, never the - * deployment default: the fold of the session's OWN switches (events past - * the seed boundary — a fork seed's stale parent switch is subsumed by the - * baseline captured after it), else the header's inherited delegation - * baseline. The subagent driver stamps `overrideOf(parent.session)` into - * each child's creation meta, so the chain collapses one level per - * delegation and a tightened parent binds children at any depth - * ([rationale](../../../.agents/notes/implemented/feature/2026-07-25-subagent-policy-inheritance.md)). + * {@link sandboxOverrideOf} surfaced on the service, for consumers that + * reach policy through `ctx.get('sandboxPolicy')` (the subagent driver's + * delegation capture, pty-local) rather than a value import. * @param session - the session whose override chain to resolve. * @returns the effective override, or `undefined` for a session following * the deployment default. - * @throws when the durable header baseline is outside the closed mode - * vocabulary (a corrupt or foreign log; durable-boundary validation). */ overrideOf(session: Session): SandboxMode | undefined ``` diff --git a/packages/cordis/tool-cordis/src/api-catalog.ts b/packages/cordis/tool-cordis/src/api-catalog.ts index b801d47443..0028a153fd 100644 --- a/packages/cordis/tool-cordis/src/api-catalog.ts +++ b/packages/cordis/tool-cordis/src/api-catalog.ts @@ -152,7 +152,7 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [ }, { signature: 'overrideOf(session: Session): ApprovalPolicy | undefined', - jsDoc: '/**\n * A session\'s approval-policy OVERRIDE — the override chain alone, never\n * the configured default: the fold of the session\'s OWN switches (events\n * past the seed boundary — a fork seed\'s stale parent switch is subsumed by\n * the baseline captured after it), else the header\'s inherited delegation\n * baseline. The subagent driver stamps `overrideOf(parent.session)` into\n * each child\'s creation meta, so a `\'never\'` (headless/CI) parent cannot\n * mint children that fall back to a prompting default, at any depth\n * ([rationale](../../../.agents/notes/implemented/feature/2026-07-25-subagent-policy-inheritance.md)).\n * @param session - the session whose override chain to resolve.\n * @returns the effective override, or `undefined` for a session following\n * the configured default.\n * @throws when the durable header baseline is outside the closed policy\n * vocabulary (a corrupt or foreign log; durable-boundary validation).\n */', + jsDoc: '/**\n * {@link approvalOverrideOf} surfaced on the service, for consumers that\n * reach the seam through `ctx.get(\'approval\')` (the subagent driver\'s\n * delegation capture) rather than a value import.\n * @param session - the session whose override chain to resolve.\n * @returns the effective override, or `undefined` for a session following\n * the configured default.\n */', }, ], }, @@ -492,7 +492,7 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [ }, { signature: 'overrideOf(session: Session): SandboxMode | undefined', - jsDoc: '/**\n * A session\'s sandbox-mode OVERRIDE — the override chain alone, never the\n * deployment default: the fold of the session\'s OWN switches (events past\n * the seed boundary — a fork seed\'s stale parent switch is subsumed by the\n * baseline captured after it), else the header\'s inherited delegation\n * baseline. The subagent driver stamps `overrideOf(parent.session)` into\n * each child\'s creation meta, so the chain collapses one level per\n * delegation and a tightened parent binds children at any depth\n * ([rationale](../../../.agents/notes/implemented/feature/2026-07-25-subagent-policy-inheritance.md)).\n * @param session - the session whose override chain to resolve.\n * @returns the effective override, or `undefined` for a session following\n * the deployment default.\n * @throws when the durable header baseline is outside the closed mode\n * vocabulary (a corrupt or foreign log; durable-boundary validation).\n */', + jsDoc: '/**\n * {@link sandboxOverrideOf} surfaced on the service, for consumers that\n * reach policy through `ctx.get(\'sandboxPolicy\')` (the subagent driver\'s\n * delegation capture, pty-local) rather than a value import.\n * @param session - the session whose override chain to resolve.\n * @returns the effective override, or `undefined` for a session following\n * the deployment default.\n */', }, ], }, diff --git a/packages/sandbox/sandbox-policy/src/index.ts b/packages/sandbox/sandbox-policy/src/index.ts index 317d0f971e..2dba47a643 100644 --- a/packages/sandbox/sandbox-policy/src/index.ts +++ b/packages/sandbox/sandbox-policy/src/index.ts @@ -106,19 +106,12 @@ export class SandboxPolicyService extends Service { } /** - * A session's sandbox-mode OVERRIDE — the override chain alone, never the - * deployment default: the fold of the session's OWN switches (events past - * the seed boundary — a fork seed's stale parent switch is subsumed by the - * baseline captured after it), else the header's inherited delegation - * baseline. The subagent driver stamps `overrideOf(parent.session)` into - * each child's creation meta, so the chain collapses one level per - * delegation and a tightened parent binds children at any depth - * ([rationale](../../../.agents/notes/implemented/feature/2026-07-25-subagent-policy-inheritance.md)). + * {@link sandboxOverrideOf} surfaced on the service, for consumers that + * reach policy through `ctx.get('sandboxPolicy')` (the subagent driver's + * delegation capture, pty-local) rather than a value import. * @param session - the session whose override chain to resolve. * @returns the effective override, or `undefined` for a session following * the deployment default. - * @throws when the durable header baseline is outside the closed mode - * vocabulary (a corrupt or foreign log; durable-boundary validation). */ overrideOf(session: Session): SandboxMode | undefined { return sandboxOverrideOf(session) diff --git a/packages/subagent/subagent-inprocess/tests/inheritance.spec.ts b/packages/subagent/subagent-inprocess/tests/inheritance.spec.ts index 49dfa03423..f4f0f85318 100644 --- a/packages/subagent/subagent-inprocess/tests/inheritance.spec.ts +++ b/packages/subagent/subagent-inprocess/tests/inheritance.spec.ts @@ -8,9 +8,11 @@ * * Red/green anchor for the delegation-bypass gap: a parent switched to * `read-only` must not mint children that run under the (wider) deployment - * default. The stamping design is itself pinned by the mounted session - * invariants: an implementation that appends the inherited override OUTSIDE - * the child's first turn fails these suites through the turn-enclosure check. + * default. The captured overrides ride the child's creation-time header, so + * three review-found timing threats are pinned as distinct shapes: a parent + * switch racing the asynchronous creation, a veto-capable prompt-submit + * listener closing a promptless first turn, and an injection-persisted turn + * before any prompt turn opens (header asserted before the child runs). */ import { describe, expect, it, beforeEach, afterEach } from 'vitest' @@ -276,35 +278,6 @@ describe('sandbox-mode inheritance against the real fs fence', () => { expect(ctx.sandboxPolicy.resolve({ session: child.session }).mode).toBe('read-only') }) - it('a FORK child whose seed already folds to the parent mode gets NO duplicate stamp (guard)', async () => { - const script: Script = [] - const captured: Agent[] = [] - const { ctx, parent } = await setupWalled(script) - registerDelegate(ctx, captured) - const blocked = join(workspace, 'fork-dedup-blocked.txt') - script.push( - () => { - setSandboxMode(parent.session, 'read-only') - return textResponse('turn one') - }, - toolCallResponse('d-fork', 'delegate', { fork: true }), - toolCallResponse('c-write', 'write', { file_path: blocked, content: 'escaped' }), - textResponse('fork child done'), - textResponse('turn two done'), - ) - parent.followup([{ type: 'text', text: 'turn one' }]) - await parent.whenIdle() - parent.followup([{ type: 'text', text: 'turn two: delegate' }]) - await parent.whenIdle() - - const child = captured[0] as Agent - // The seed-carried override keeps enforcing… - await expect(readFile(blocked, 'utf8')).rejects.toMatchObject({ code: 'ENOENT' }) - expect(ctx.sandboxPolicy.resolve({ session: child.session }).mode).toBe('read-only') - // …and inheritance did not append a redundant copy on top of it. - expect(overrideEvents(child).sandbox).toBe(1) - }) - it('inherits the mode AT delegation, not a parent switch racing child creation', async () => { const script: Script = [] const captured: Agent[] = [] @@ -371,7 +344,7 @@ describe('sandbox-mode inheritance against the real fs fence', () => { }) describe('inheritance survives prompt vetoes', () => { - it('stamps the child even when an earlier-registered prompt-submit listener vetoes without next()', async () => { + it('keeps the baseline when an earlier-registered prompt-submit listener vetoes without next()', async () => { const script: Script = [] const { ctx, parent } = await setupWalled(script) // A veto-capable listener registered BEFORE the child exists — the @@ -409,7 +382,7 @@ describe('inheritance survives prompt vetoes', () => { }) describe('inheritance guards (must hold before AND after the fix)', () => { - it('a child of an unswitched parent runs under the live deployment default, with ZERO stamped events', async () => { + it('a child of an unswitched parent runs under the live deployment default, with NO baseline or events', async () => { const script: Script = [] const { parent } = await setupWalled(script) const allowed = join(workspace, 'default-allowed.txt') diff --git a/packages/ui/user-approval/src/index.ts b/packages/ui/user-approval/src/index.ts index 4a6d40ca1c..2c44d674b5 100644 --- a/packages/ui/user-approval/src/index.ts +++ b/packages/ui/user-approval/src/index.ts @@ -350,19 +350,12 @@ export class ApprovalService extends Service { } /** - * A session's approval-policy OVERRIDE — the override chain alone, never - * the configured default: the fold of the session's OWN switches (events - * past the seed boundary — a fork seed's stale parent switch is subsumed by - * the baseline captured after it), else the header's inherited delegation - * baseline. The subagent driver stamps `overrideOf(parent.session)` into - * each child's creation meta, so a `'never'` (headless/CI) parent cannot - * mint children that fall back to a prompting default, at any depth - * ([rationale](../../../.agents/notes/implemented/feature/2026-07-25-subagent-policy-inheritance.md)). + * {@link approvalOverrideOf} surfaced on the service, for consumers that + * reach the seam through `ctx.get('approval')` (the subagent driver's + * delegation capture) rather than a value import. * @param session - the session whose override chain to resolve. * @returns the effective override, or `undefined` for a session following * the configured default. - * @throws when the durable header baseline is outside the closed policy - * vocabulary (a corrupt or foreign log; durable-boundary validation). */ overrideOf(session: Session): ApprovalPolicy | undefined { return approvalOverrideOf(session)