From d0c4520363e5a8f1410a9fcc312b88c2dc999cf8 Mon Sep 17 00:00:00 2001 From: Adrian Webb Date: Sun, 11 Oct 2026 11:09:58 -0400 Subject: [PATCH 1/4] test: reproduce incomplete fixed SDK permission request proof --- .../acceptance/golden-product-fixture.ts | 18 +++++++++++++++++- .../operator-contracts/golden-product.test.ts | 12 +++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/tests/unit/operator-contracts/acceptance/golden-product-fixture.ts b/tests/unit/operator-contracts/acceptance/golden-product-fixture.ts index f5fad226..eaba1c65 100644 --- a/tests/unit/operator-contracts/acceptance/golden-product-fixture.ts +++ b/tests/unit/operator-contracts/acceptance/golden-product-fixture.ts @@ -2,6 +2,20 @@ import { createHash } from 'node:crypto'; import { parse, stringify } from 'yaml'; export const commit = 'a'.repeat(40); const libraryCommit = 'c'.repeat(40); +export const permissionFailures = ['missing-read', 'extra-read', 'duplicate-read', 'missing-write', 'extra-write', 'missing-tool', 'extra-tool', 'duplicate-tool', 'unknown-field'] as const; +export const permissionCases = Array.from({ length: 6 }, (_, index) => permissionFailures.map(mode => `${index}-${mode}`)).flat(); +export function mutatePermissionRequest(value: typeof proposal, failure: string): void { + const index = Number(failure[0]), mode = failure.slice(2), permissions = value.executionPlan.workItems[index]!.requestedPermissions; + if (mode === 'missing-read') permissions.content.read.pop(); + if (mode === 'extra-read') permissions.content.read.push('discussion'); + if (mode === 'duplicate-read') permissions.content.read.push(permissions.content.read[0]!); + if (mode === 'missing-write') Reflect.deleteProperty(permissions.content, 'write'); + if (mode === 'extra-write') permissions.content.write.push('proposal'); + if (mode === 'missing-tool') permissions.tools.pop(); + if (mode === 'extra-tool') permissions.tools.push('external.publish'); + if (mode === 'duplicate-tool') permissions.tools.push(permissions.tools[0]!); + if (mode === 'unknown-field') Reflect.set(permissions, 'alternateAuthority', true); +} export const graphOrderFailures = ['missing-edges', 'malformed-edge', 'duplicate-edge', 'missing-pair', 'extra-pair', 'missing-profile', 'transitive-profile', 'actor-profile', 'research-profile', 'work-item-profile', 'foreign-edge-team', 'edge-source-id', 'edge-source-revision', 'edge-source-digest', 'edge-source-commit', 'edge-source-path', 'edge-source-repository', 'missing-edge-source', @@ -65,7 +79,9 @@ export const proposal = { schemaVersion: 'treeseed.proposal/v1', status: 'ready' objective: `Controlled explicit ${id} objective`, estimate: { expectedSeconds: 120, maximumSeconds: 240 }, reviewEstimate: { expectedSeconds: 60, maximumSeconds: 120 }, contextRefs: [{ store: workspace, model: 'repository', id: 'sdk', repository: workspace === 'git' ? 'treeseed-ai/sdk' : 'repo_sdk_fixture', commit }], - requestedPermissions: { content: { read: ['proposal', 'decision'], write: [] }, tools: ['source.read'] }, + requestedPermissions: { content: { read: ['book', 'knowledge', 'objective', 'proposal', 'decision', 'note', 'question'], + write: agentClass === 'researcher' ? ['knowledge', 'note', 'question'] : agentClass === 'architect' ? ['book', 'knowledge', 'note', 'question'] : [] }, + tools: workspace === 'treedx' ? ['discussion', 'source.read'] : ['source.read', 'source.write', 'verification', ...(agentClass === 'releaser' ? ['release'] : [])] }, requiredCapabilities: ['source.read'], acceptanceCriteria: [`Verify controlled ${id} outcome.`] })) }, evidenceRefs: [{ store: 'url', model: 'issue', id: 'sdk-source-1', url: 'https://github.com/treeseed-ai/sdk/issues/299' }], summary: 'Prove the SDK can express high-level decision-governed execution while keeping all derived execution state API-owned.' }; diff --git a/tests/unit/operator-contracts/golden-product.test.ts b/tests/unit/operator-contracts/golden-product.test.ts index 3234c1ae..551d2c1f 100644 --- a/tests/unit/operator-contracts/golden-product.test.ts +++ b/tests/unit/operator-contracts/golden-product.test.ts @@ -3,10 +3,20 @@ import { readFileSync } from 'node:fs'; import { sdkGoldenSource, sdkGoldenLibraryFiles, verifySdkGoldenProposalReferences, verifySdkGoldenProduct as verifyProduct } from '../../acceptance/golden-product.ts'; import { stringify } from 'yaml'; import { createHash } from 'node:crypto'; -import { commit, release, review, proposal, proposalReadback, graphPairFailures, mutateGraphPair, graphOrderFailures, mutateGraphOrder } from './acceptance/golden-product-fixture.ts'; +import { commit, release, review, proposal, proposalReadback, graphPairFailures, mutateGraphPair, graphOrderFailures, mutateGraphOrder, permissionCases, mutatePermissionRequest } from './acceptance/golden-product-fixture.ts'; const verifySdkGoldenProduct = (items: Parameters[0], source: string) => Reflect.apply(verifyProduct, undefined, [items, source, proposalReadback().governance, proposalReadback().original]); describe('SDK golden product gate (fixtures are not acceptance)', () => { + it('rejects missing extra duplicate and alternate permission requests for every fixed authored SDK role without repairing original bytes', () => { + const exact = proposalReadback(), inspect = (value: typeof exact) => Reflect.apply(verifyProduct, undefined, [[release, review], commit, value.governance, value.original]); + expect(() => inspect(exact)).not.toThrow(); + for (const mode of permissionCases) { + const changed = structuredClone(proposal); mutatePermissionRequest(changed, mode); + const observed = proposalReadback(changed), held = structuredClone(observed); + expect.soft(() => inspect(observed), mode).toThrow('ACCEPTANCE_SDK_PERMISSIONS'); expect(observed).toEqual(held); + } + expect(() => inspect(exact)).not.toThrow(); + }); it('rejects counted graph pairs without independent research and exact immediate governed profile ordering', () => { const observed = proposalReadback(); const inspect = (value: typeof observed) => Reflect.apply(verifyProduct, undefined, [[release, review], commit, value.governance, value.original]); From 2a31756610bc67f3487d1befb22cfb7543d0c193 Mon Sep 17 00:00:00 2001 From: Adrian Webb Date: Sun, 11 Oct 2026 11:12:05 -0400 Subject: [PATCH 2/4] test: retain substituted Actor permission request regression --- tests/unit/operator-contracts/golden-product.test.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/unit/operator-contracts/golden-product.test.ts b/tests/unit/operator-contracts/golden-product.test.ts index 551d2c1f..cea9f770 100644 --- a/tests/unit/operator-contracts/golden-product.test.ts +++ b/tests/unit/operator-contracts/golden-product.test.ts @@ -10,6 +10,9 @@ describe('SDK golden product gate (fixtures are not acceptance)', () => { it('rejects missing extra duplicate and alternate permission requests for every fixed authored SDK role without repairing original bytes', () => { const exact = proposalReadback(), inspect = (value: typeof exact) => Reflect.apply(verifyProduct, undefined, [[release, review], commit, value.governance, value.original]); expect(() => inspect(exact)).not.toThrow(); + const substituted = structuredClone(exact); substituted.original.graph.nodes[0]!.requestedPermissions.tools = []; + const heldGraph = structuredClone(substituted); + expect.soft(() => inspect(substituted)).toThrow('ACCEPTANCE_SDK_GRAPH_PAIRS'); expect(substituted).toEqual(heldGraph); for (const mode of permissionCases) { const changed = structuredClone(proposal); mutatePermissionRequest(changed, mode); const observed = proposalReadback(changed), held = structuredClone(observed); From 3b4217ea3a494800692c0fa2c719c597b92139ec Mon Sep 17 00:00:00 2001 From: Adrian Webb Date: Sun, 11 Oct 2026 11:18:22 -0400 Subject: [PATCH 3/4] fix: verify exact authored SDK permission requests and graph custody --- .../scenes/component-boundaries.scene.yaml | 3 +++ guarantees/verifiers/golden.verifiers.yaml | 5 +++++ tests/acceptance/golden-product.ts | 16 ++++++++++++++-- .../acceptance/golden-product-fixture.ts | 3 ++- .../acceptance/golden-proposal-native.test.ts | 8 +++++++- 5 files changed, 31 insertions(+), 4 deletions(-) diff --git a/guarantees/agent/golden/scenes/component-boundaries.scene.yaml b/guarantees/agent/golden/scenes/component-boundaries.scene.yaml index ee05de17..a180019e 100644 --- a/guarantees/agent/golden/scenes/component-boundaries.scene.yaml +++ b/guarantees/agent/golden/scenes/component-boundaries.scene.yaml @@ -684,6 +684,9 @@ workflow: - id: standing-graph-order-unit action: { verifier: sdk.golden.standing-graph-order-unit } expect: { status: passed } + - id: fixed-permissions-unit + action: { verifier: sdk.golden.fixed-permissions-unit } + expect: { status: passed } - id: assignment-workday-native action: { verifier: sdk.golden.assignment-workday-native } expect: { status: passed } diff --git a/guarantees/verifiers/golden.verifiers.yaml b/guarantees/verifiers/golden.verifiers.yaml index ceaa0535..7eb09c59 100644 --- a/guarantees/verifiers/golden.verifiers.yaml +++ b/guarantees/verifiers/golden.verifiers.yaml @@ -1,6 +1,11 @@ schemaVersion: treeseed.guarantee-verifiers/v1 ownerPackage: "@treeseed/sdk" verifiers: + sdk.golden.fixed-permissions-unit: + kind: vitestCase + ownerPackage: "@treeseed/sdk" + testFile: tests/unit/operator-contracts/golden-product.test.ts + testName: "rejects missing extra duplicate and alternate permission requests for every fixed authored SDK role without repairing original bytes" sdk.golden.standing-graph-order-unit: kind: vitestCase ownerPackage: "@treeseed/sdk" diff --git a/tests/acceptance/golden-product.ts b/tests/acceptance/golden-product.ts index 6c36b9f5..0fdeeba5 100644 --- a/tests/acceptance/golden-product.ts +++ b/tests/acceptance/golden-product.ts @@ -1,7 +1,7 @@ import assert from 'node:assert/strict'; import { closeSync, constants, fstatSync, lstatSync, openSync, readFileSync, realpathSync } from 'node:fs'; import { basename, isAbsolute, resolve } from 'node:path'; -import { exactEntityReferenceSchema, executionEdgeSchema } from '@treeseed/sdk/agent-capacity'; +import { exactEntityReferenceSchema, executionEdgeSchema, activityProfileSchema } from '@treeseed/sdk/agent-capacity'; import { validatePortableContentData } from '@treeseed/sdk/content-validation'; import { createHash } from 'node:crypto'; import { parse, parseDocument, visit } from 'yaml'; @@ -92,13 +92,24 @@ function verifySdkGoldenAuthoredWork(proposal: Row, draft = false): void { && new Set(authored.map(item => item.id)).size === fixedRows.length, `${graphCode}: Six unique original authored work items required`); for (const [id, agentClass, workspace] of fixedRows) { - const item = authored.find(row => row.id === id); + const item: Row | undefined = authored.find(row => row.id === id); assert.ok(item && item.agentClass === agentClass && item.workspace === workspace && item.activity === 'acting' && item.review === 'required' && item.maximumReviewCycles === 2 && Array.isArray(item.dependsOn) && item.dependsOn.length === 0 && ['objective', 'contextRefs', 'requestedPermissions', 'requiredCapabilities', 'acceptanceCriteria', ...(draft ? [] : ['estimate', 'reviewEstimate'])].every(field => Object.hasOwn(item, field)), `${graphCode}: Original ${id} class workspace dependencies and review policy required`); + const permissionCode = `ACCEPTANCE_SDK_PERMISSIONS: ${graphCode}`; + const permissions = activityProfileSchema.shape.permissions.safeParse(item.requestedPermissions); + assert.ok(permissions.success, `${permissionCode}: Complete canonical original permission request required`); + const reads: string[] = ['book', 'knowledge', 'objective', 'proposal', 'decision', 'note', 'question']; + const writes: string[] = agentClass === 'researcher' ? ['knowledge', 'note', 'question'] + : agentClass === 'architect' ? ['book', 'knowledge', 'note', 'question'] : []; + const tools: string[] = workspace === 'treedx' ? ['discussion', 'source.read'] + : ['source.read', 'source.write', 'verification', ...(agentClass === 'releaser' ? ['release'] : [])]; + const lists: [readonly string[], readonly string[]][] = [[permissions.data.content.read, reads], [permissions.data.content.write, writes], [permissions.data.tools, tools]]; + for (const [actual, expected] of lists) + assert.deepEqual([...actual].sort(), [...expected].sort(), `${permissionCode}: Exact ${id} requested content and tools required`); if (draft) assert.ok(['estimate', 'reviewEstimate', 'ownerEstimate', 'reviewerEstimate'].every(field => !Object.hasOwn(item, field)) && typeof item.objective === 'string' && item.objective.trim() && Array.isArray(item.contextRefs) && item.contextRefs.length > 0 @@ -130,6 +141,7 @@ function verifySdkGoldenGraph(proposal: Row, attempt: Row, graph: Row | undefine && node.maximumReviewCycles === item.maximumReviewCycles && item.review === 'required', `${code}: Authored Actor or generated Reviewer authority drifted`); assert.deepEqual(node.sourceRef, attempt.sourceRef, `${code}: Pair must represent the exact original authored proposal`); + if (role === 'actor') assert.deepEqual(node.requestedPermissions, item.requestedPermissions, `${code}: Actor must retain its original authored permission request`); } const orderCode = 'ACCEPTANCE_SDK_GRAPH_ORDER'; assert.ok(Array.isArray(graph.edges), `${orderCode}: Independent complete public edges required`); diff --git a/tests/unit/operator-contracts/acceptance/golden-product-fixture.ts b/tests/unit/operator-contracts/acceptance/golden-product-fixture.ts index eaba1c65..c5f61630 100644 --- a/tests/unit/operator-contracts/acceptance/golden-product-fixture.ts +++ b/tests/unit/operator-contracts/acceptance/golden-product-fixture.ts @@ -42,7 +42,7 @@ export function mutateGraphOrder(graph: { edges: Array> } export const graphPairFailures = ['missing', 'foreign-team', 'duplicate-id', 'missing-node', 'extra-node', 'work-item', 'pair-role', 'kind', 'class', 'workspace', 'cycles', 'project', 'team', 'workday', 'source-id', 'source-revision', - 'source-digest', 'source-commit', 'source-path', 'source-repository'] as const; + 'source-digest', 'source-commit', 'source-path', 'source-repository', 'permission-request'] as const; export function mutateGraphPair(graph: { teamId: string; nodes: Array> }, mode: string): void { const node = graph.nodes[1]!; if (mode === 'missing') Reflect.deleteProperty(graph, 'nodes'); @@ -50,6 +50,7 @@ export function mutateGraphPair(graph: { teamId: string; nodes: Array)[mode] ?? mode, diff --git a/tests/unit/operator-contracts/acceptance/golden-proposal-native.test.ts b/tests/unit/operator-contracts/acceptance/golden-proposal-native.test.ts index 57aeecbf..4e78fb96 100644 --- a/tests/unit/operator-contracts/acceptance/golden-proposal-native.test.ts +++ b/tests/unit/operator-contracts/acceptance/golden-proposal-native.test.ts @@ -7,7 +7,7 @@ import { resolve } from 'node:path'; import { createHash } from 'node:crypto'; import { parse } from 'yaml'; import { expect, it } from 'vitest'; -import { commit, release, review, proposal, proposalReadback, graphPairFailures, mutateGraphPair, graphOrderFailures, mutateGraphOrder } from './golden-product-fixture.ts'; +import { commit, release, review, proposal, proposalReadback, graphPairFailures, mutateGraphPair, graphOrderFailures, mutateGraphOrder, permissionCases, mutatePermissionRequest } from './golden-product-fixture.ts'; import { workdayStartFixture } from './workday-start-fixture.ts'; it('native public SDK proposal reads in isolated product verification reject changed stored identity title and summary and retain denied observations before unchanged retry', async () => { @@ -69,6 +69,7 @@ it('native public SDK proposal reads in isolated product verification reject cha } if (mode === 'json') { response.end('{'); return; } const returned: Record = { ...proposal }; + if (mode.startsWith('permissions-')) { const changed = structuredClone(proposal); mutatePermissionRequest(changed, mode.slice(12)); returned.executionPlan = changed.executionPlan; } if (['reference-open', 'reference-repository', 'reference-path'].includes(mode)) returned.discussionRef = { ...proposal.discussionRef, digest: `sha256:${createHash('sha256').update(fixture.referenceFiles[2]!.files[0]!.content).digest('hex')}` }; if (mode === 'populated-status') returned.status = 'draft'; @@ -255,6 +256,11 @@ Reflect.apply(verifySdkGoldenProduct,undefined,[actors,${JSON.stringify(commit)} const preservedDrafts = structuredClone(deniedDrafts); writeFileSync(inputPath, inputBytes); expect((await observe()).stdout).toBe('verified'); expect(deniedDrafts).toEqual(preservedDrafts); expect(failed).toEqual(deniedReferences); expect(calls).toHaveLength(291); + const permissionStart = calls.length; + mode = 'exact'; expect((await Promise.all([observe(), observe()])).map(value => value.stdout)).toEqual(['verified', 'verified']); + for (const failure of permissionCases) { mode = `permissions-${failure}`; await expect.soft(observe(), mode).rejects.toThrow('ACCEPTANCE_SDK_PERMISSIONS'); } + const retainedPermissionFailures = structuredClone(failed); mode = 'exact'; expect((await observe()).stdout).toBe('verified'); + expect(failed).toEqual(retainedPermissionFailures); expect(calls.slice(permissionStart)).toHaveLength((permissionCases.length + 3) * 3); const graphStart = graphCalls.length; mode = 'exact'; expect((await Promise.all([observe(), observe()])).map(value => value.stdout)).toEqual(['verified', 'verified']); for (const denial of [...graphPairFailures, ...graphOrderFailures, 'denied', 'unavailable', 'interrupted', 'json']) { From 3683ef678bc78df292e0c81b06e9068bd71e9a29 Mon Sep 17 00:00:00 2001 From: Adrian Webb Date: Sun, 11 Oct 2026 11:20:34 -0400 Subject: [PATCH 4/4] test: retain permission permutations and reuse immutable native input --- .../acceptance/golden-proposal-native.test.ts | 4 +++- tests/unit/operator-contracts/golden-product.test.ts | 11 +++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/tests/unit/operator-contracts/acceptance/golden-proposal-native.test.ts b/tests/unit/operator-contracts/acceptance/golden-proposal-native.test.ts index 4e78fb96..fccaae83 100644 --- a/tests/unit/operator-contracts/acceptance/golden-proposal-native.test.ts +++ b/tests/unit/operator-contracts/acceptance/golden-proposal-native.test.ts @@ -17,6 +17,7 @@ it('native public SDK proposal reads in isolated product verification reject cha : kind === 'reference-repository' ? bytes.replace('repository: repo_sdk_fixture', 'repository: repo_foreign') : kind === 'reference-path' ? bytes.replace('path: proposals/governance/sdk-proposal.mdx', 'path: proposals/foreign.mdx') : bytes; const original = structuredClone({ release, review, proposal }); + const nativeBase = proposalReadback(proposal).original; const retainedInput = workdayStartFixture(), inputPath = resolve(retainedInput.root, 'proposal-input.json'); const initial = { ...structuredClone(proposal), status: 'draft' }; for (const item of initial.executionPlan.workItems) { Reflect.deleteProperty(item, 'estimate'); Reflect.deleteProperty(item, 'reviewEstimate'); } @@ -28,7 +29,8 @@ it('native public SDK proposal reads in isolated product verification reject cha const fail = (error: Error) => { for (const item of pending.values()) { clearTimeout(item.timer); item.reject(error); } pending.clear(); }; const server = createServer((request, response) => { let bytes = ''; request.setEncoding('utf8'); request.on('data', value => { bytes += String(value); }); request.on('end', () => { - const fixture = proposalReadback(proposal, mode === 'budget-boundary' ? 1_048_576 : undefined).original, path = request.method === 'POST' ? JSON.parse(bytes).paths?.[0] : undefined; + const fixture = mode === 'budget-boundary' ? proposalReadback(proposal, 1_048_576).original : structuredClone(nativeBase); + const path = request.method === 'POST' ? JSON.parse(bytes).paths?.[0] : undefined; if (request.url === '/v1/teams/sdk-team/execution-graph') { graphCalls.push(`${request.method} ${request.url}`); response.setHeader('content-type', 'application/json'); if (mode.startsWith('graph-')) graphFailures.push(mode); diff --git a/tests/unit/operator-contracts/golden-product.test.ts b/tests/unit/operator-contracts/golden-product.test.ts index cea9f770..87ade3d7 100644 --- a/tests/unit/operator-contracts/golden-product.test.ts +++ b/tests/unit/operator-contracts/golden-product.test.ts @@ -13,6 +13,17 @@ describe('SDK golden product gate (fixtures are not acceptance)', () => { const substituted = structuredClone(exact); substituted.original.graph.nodes[0]!.requestedPermissions.tools = []; const heldGraph = structuredClone(substituted); expect.soft(() => inspect(substituted)).toThrow('ACCEPTANCE_SDK_GRAPH_PAIRS'); expect(substituted).toEqual(heldGraph); + const permuted = structuredClone(proposal); + for (const item of permuted.executionPlan.workItems) { item.requestedPermissions.content.read.reverse(); item.requestedPermissions.content.write.reverse(); item.requestedPermissions.tools.reverse(); } + const reordered = proposalReadback(permuted), actor = structuredClone(release), reviewer = structuredClone(review); + for (const item of permuted.executionPlan.workItems) { + reordered.original.initialProposal.executionPlan.workItems.find(value => value.id === item.id)!.requestedPermissions = structuredClone(item.requestedPermissions); + reordered.original.graph.nodes.find(value => value.workItemId === item.id && value.pairRole === 'actor')!.requestedPermissions = structuredClone(item.requestedPermissions); + } + for (const item of [actor, reviewer]) item.assignmentAttempt.sourceRef.digest = `sha256:${reordered.governance.activeContentHash}`; + const heldPermutation = structuredClone({ reordered, actor, reviewer }); + expect(() => Reflect.apply(verifyProduct, undefined, [[actor, reviewer], commit, reordered.governance, reordered.original])).not.toThrow(); + expect({ reordered, actor, reviewer }).toEqual(heldPermutation); for (const mode of permissionCases) { const changed = structuredClone(proposal); mutatePermissionRequest(changed, mode); const observed = proposalReadback(changed), held = structuredClone(observed);