diff --git a/guarantees/agent/golden/scenes/component-boundaries.scene.yaml b/guarantees/agent/golden/scenes/component-boundaries.scene.yaml index bb331f23..d1410ca4 100644 --- a/guarantees/agent/golden/scenes/component-boundaries.scene.yaml +++ b/guarantees/agent/golden/scenes/component-boundaries.scene.yaml @@ -153,6 +153,51 @@ workflow: - id: live-assignment-profile-denial-unit action: { verifier: agent.golden.live-assignment-profile-denial-unit } expect: { status: passed } + - id: live-graph-identity-unit + action: { verifier: agent.golden.live-graph-identity-unit } + expect: { status: passed } + - id: live-node-revision-unit + action: { verifier: agent.golden.live-node-revision-unit } + expect: { status: passed } + - id: live-graph-missing-unit + action: { verifier: agent.golden.live-graph-missing-unit } + expect: { status: passed } + - id: live-workspace-scope-unit + action: { verifier: agent.golden.live-workspace-scope-unit } + expect: { status: passed } + - id: live-workspace-branch-unit + action: { verifier: agent.golden.live-workspace-branch-unit } + expect: { status: passed } + - id: live-workspace-paths-unit + action: { verifier: agent.golden.live-workspace-paths-unit } + expect: { status: passed } + - id: live-frozen-source-denial-unit + action: { verifier: agent.golden.live-frozen-source-denial-unit } + expect: { status: passed } + - id: live-source-context-unit + action: { verifier: agent.golden.live-source-context-unit } + expect: { status: passed } + - id: live-workspace-base-unit + action: { verifier: agent.golden.live-workspace-base-unit } + expect: { status: passed } + - id: live-allocation-missing-unit + action: { verifier: agent.golden.live-allocation-missing-unit } + expect: { status: passed } + - id: live-predecessor-missing-unit + action: { verifier: agent.golden.live-predecessor-missing-unit } + expect: { status: passed } + - id: live-runtime-missing-unit + action: { verifier: agent.golden.live-runtime-missing-unit } + expect: { status: passed } + - id: live-runtime-reopen-unit + action: { verifier: agent.golden.live-runtime-reopen-unit } + expect: { status: passed } + - id: live-runtime-publication-unit + action: { verifier: agent.golden.live-runtime-publication-unit } + expect: { status: passed } + - id: live-graph-denial-unit + action: { verifier: agent.golden.live-graph-denial-unit } + expect: { status: passed } - id: live-assignment-records-native action: { verifier: agent.golden.live-assignment-records-native } expect: { status: passed } diff --git a/guarantees/agent/golden/sdk-complete.guarantee.yaml b/guarantees/agent/golden/sdk-complete.guarantee.yaml index 89ae8a10..fef44205 100644 --- a/guarantees/agent/golden/sdk-complete.guarantee.yaml +++ b/guarantees/agent/golden/sdk-complete.guarantee.yaml @@ -11,6 +11,8 @@ scene: required: true manifest: guarantees/agent/golden/scenes/sdk-complete.scene.yaml acceptanceCriteria: + - criterion: 4d3a65d9ab4becd82cc9336051f6e069a26bfcd662eb52fe3f7a32f67c8530bf + verifierRefs: [agent.golden.live.campaign, agent.golden.architecture-profile-custody-live-1, api.golden.execution-normal-sdk-clean-schema-live-1, agent.golden.architecture-record-custody-live-1] - criterion: 9778c4caf01add1b3815fa1cd3fe27ef11073cc98dbdc4a5deeb54e2a441f1dc verifierRefs: [sdk.golden.live.product] - criterion: fc80e789a38eb6ae75fada867226ac0157844505619b12c90c55cbe317c6cc9d diff --git a/guarantees/verifiers/golden.verifiers.yaml b/guarantees/verifiers/golden.verifiers.yaml index 83fc7dec..a22bd087 100644 --- a/guarantees/verifiers/golden.verifiers.yaml +++ b/guarantees/verifiers/golden.verifiers.yaml @@ -216,6 +216,81 @@ verifiers: ownerPackage: "@treeseed/agent" testFile: tests/modern/kernel/architecture/custody/profile-custody-readback.test.ts testName: "live profile inspection denies an independently substituted runtime or failed public read without repairing the original attempt" + agent.golden.live-graph-identity-unit: + kind: vitestCase + ownerPackage: "@treeseed/agent" + testFile: tests/modern/guarantees/monitoring/live-graph-inspection.test.ts + testName: "accepts exact independent graph authority without rewriting source observations and denies every substituted issued field" + agent.golden.live-node-revision-unit: + kind: vitestCase + ownerPackage: "@treeseed/agent" + testFile: tests/modern/guarantees/monitoring/live-graph-inspection.test.ts + testName: "retains a legitimate earlier completed attempt when independently verified request-changes history advances its graph node" + agent.golden.live-graph-missing-unit: + kind: vitestCase + ownerPackage: "@treeseed/agent" + testFile: tests/modern/guarantees/monitoring/live-graph-inspection.test.ts + testName: "denies missing independent graph before accepting a newly issued assignment" + agent.golden.live-workspace-scope-unit: + kind: vitestCase + ownerPackage: "@treeseed/agent" + testFile: tests/modern/guarantees/monitoring/live-graph-inspection.test.ts + testName: "retains exact simulation workspace authority and rejects malformed or substituted branch scope without rewriting observations" + agent.golden.live-workspace-branch-unit: + kind: vitestCase + ownerPackage: "@treeseed/agent" + testFile: tests/modern/guarantees/monitoring/live-graph-inspection.test.ts + testName: "denies a foreign simulation branch before accepting matching assignment views" + agent.golden.live-workspace-paths-unit: + kind: vitestCase + ownerPackage: "@treeseed/agent" + testFile: tests/modern/guarantees/monitoring/live-graph-inspection.test.ts + testName: "denies writable paths outside the original simulation workspace contract" + agent.golden.live-frozen-source-denial-unit: + kind: vitestCase + ownerPackage: "@treeseed/agent" + testFile: tests/modern/guarantees/monitoring/live-graph-inspection.test.ts + testName: "denies a self-consistent foreign Git base and context against the original frozen source" + agent.golden.live-source-context-unit: + kind: vitestCase + ownerPackage: "@treeseed/agent" + testFile: tests/modern/guarantees/monitoring/live-graph-inspection.test.ts + testName: "requires every frozen source and exact granted content while retaining original context and rejecting unissued commits" + agent.golden.live-workspace-base-unit: + kind: vitestCase + ownerPackage: "@treeseed/agent" + testFile: tests/modern/guarantees/monitoring/live-graph-inspection.test.ts + testName: "denies a Git workspace base absent from original exact context before accepting matching assignment views" + agent.golden.live-allocation-missing-unit: + kind: vitestCase + ownerPackage: "@treeseed/agent" + testFile: tests/modern/guarantees/monitoring/live-graph-inspection.test.ts + testName: "denies a missing owning allocation receipt before allowing a newly observed assignment to continue" + agent.golden.live-predecessor-missing-unit: + kind: vitestCase + ownerPackage: "@treeseed/agent" + testFile: tests/modern/guarantees/monitoring/live-graph-inspection.test.ts + testName: "denies a predecessor result absent from the complete owning assignment inventory before campaign progress" + agent.golden.live-runtime-missing-unit: + kind: vitestCase + ownerPackage: "@treeseed/agent" + testFile: tests/modern/guarantees/monitoring/live-graph-inspection.test.ts + testName: "denies absent independent provider publication rather than trusting the runtime build repeated in assignment views" + agent.golden.live-runtime-reopen-unit: + kind: vitestCase + ownerPackage: "@treeseed/agent" + testFile: tests/modern/guarantees/monitoring/live-graph-inspection.test.ts + testName: "retains original provider publication when a later session opens after the observed assignment was issued" + agent.golden.live-runtime-publication-unit: + kind: vitestCase + ownerPackage: "@treeseed/agent" + testFile: tests/modern/guarantees/monitoring/live-graph-inspection.test.ts + testName: "requires the exact independently published provider adapter build model and canonical limits while retaining unrelated histories" + agent.golden.live-graph-denial-unit: + kind: vitestCase + ownerPackage: "@treeseed/agent" + testFile: tests/modern/guarantees/monitoring/live-graph-inspection.test.ts + testName: "denies foreign source estimate node and graph revisions rather than accepting internally matching public assignment views" agent.golden.live-assignment-records-native: kind: vitestCase ownerPackage: "@treeseed/agent" diff --git a/tests/acceptance/campaign.test.ts b/tests/acceptance/campaign.test.ts index 9a6b6463..3e3fa52b 100644 --- a/tests/acceptance/campaign.test.ts +++ b/tests/acceptance/campaign.test.ts @@ -4,7 +4,7 @@ import test from 'node:test'; import { fileURLToPath } from 'node:url'; import { monitorCampaign, openSdkCampaignDiscussion, requirePlanningWindow, retainCampaignWorkdayStart } from './campaign.ts'; import { verifyFreezeIntegrity } from './freeze-integrity.ts'; -import { read } from './acceptance-cli.ts'; +import { read, row } from './acceptance-cli.ts'; import { verifyGolden } from './sdk-runtime-golden.test.ts'; import { prepareSdkCampaign, verifySdkExternalState } from './prepare-campaign.ts'; import { observeCampaign } from './workday/support/campaign-observation.ts'; @@ -58,7 +58,7 @@ test('Frozen SDK campaign drives planning acting review and terminal golden gate }, inspect: () => { assert.ok(currentRun, 'ACCEPTANCE_LIVE_ASSIGNMENT: Original observed workday required'); const assignments = readCompleteEvidence(['assignments', 'list', '--workday', workdayId], team, 50, 'ACCEPTANCE_LIVE_ASSIGNMENT'); - observeLiveAssignmentRecords(currentRun, assignments, observedAssignments, item => inspectLiveAssignmentProfile(item, team)); + observeLiveAssignmentRecords(currentRun, assignments, observedAssignments, item => inspectLiveAssignmentProfile(item, team, assignments, { 'treeseed-ai/sdk': row(freeze.sourceHeads).sdk })); }, now: Date.now, wait: () => new Promise(resolve => setTimeout(resolve, 30_000)), stop, collaboration: () => { verifyGolden('collaboration'); diff --git a/tests/acceptance/sdk-runtime-golden.test.ts b/tests/acceptance/sdk-runtime-golden.test.ts index a16a24c5..97955308 100644 --- a/tests/acceptance/sdk-runtime-golden.test.ts +++ b/tests/acceptance/sdk-runtime-golden.test.ts @@ -2,7 +2,7 @@ import assert from 'node:assert/strict'; import { isDeepStrictEqual } from 'node:util'; import { createHash } from 'node:crypto'; import test from 'node:test'; -import { assignmentReferenceSchema, assignmentTimingAwarenessReceiptSchema, authorizedContextItemSchema, estimateSchema, exactEntityReferenceSchema } from '@treeseed/sdk/agent-capacity'; +import { assignmentReferenceSchema, assignmentTimingAwarenessReceiptSchema, authorizedContextItemSchema, exactEntityReferenceSchema } from '@treeseed/sdk/agent-capacity'; import { canonicalStandardsJson } from '@treeseed/sdk/standards'; import { DEFAULT_CAPACITY_PAGE_LIMIT } from '@treeseed/sdk/capacity-pagination'; import { validatePortableContentData } from '@treeseed/sdk/content-validation'; @@ -11,6 +11,7 @@ import { readApprovedWorkReview, readDecisionContent, readGovernedContentFile, v import { verifyAssignmentAuthority, verifyAssignmentWorkspaceInventory, verifyTeardownAuthority, verifyTreeDxWorkspaceClosure, verifyMeasuredVerification, verifyNativeCounterAgreement, verifyTerminalUsageClock } from './workday/support/assignment-authority.ts'; import { verifyPlanningEvidence } from './workday/support/planning-evidence.ts'; import { readCompleteEvidence } from './workday/support/evidence-pages.ts'; +import { verifyAllocation } from './workday/support/monitoring/assignment-allocation.ts'; import { verifyReportRecordCustody } from './workday/support/record-custody.ts'; const rows = (value: unknown): Row[] => Array.isArray(value) ? value.map(row) : []; const text = (value: unknown): string => typeof value === 'string' ? value : ''; @@ -18,53 +19,7 @@ const text = (value: unknown): string => typeof value === 'string' ? value : ''; // These read-back gates do not stand in for campaign orchestration or external-state proof. const gates = ['lifecycle', 'collaboration', 'graph', 'revision', 'results', 'settlement', 'reporter', 'stopped'] as const; type Gate = typeof gates[number]; -function verifyAllocation(item: Row, run: Row): void { - const attempt = row(item.assignmentAttempt), envelope = row(item.capacityEnvelope); - const allocation = row(row(row(item.explanation).metadata).allocation), calibration = row(allocation.calibration); - const finite = (value: unknown): value is number => typeof value === 'number' && Number.isFinite(value); - const estimate = estimateSchema.safeParse(attempt.estimate); - assert.ok(estimate.success, 'ACCEPTANCE_ALLOCATION_ESTIMATE: Exact expected/maximum estimate required'); - assert.ok(allocation.admitted === true && Number.isInteger(allocation.allocatedSeconds) && Number(allocation.allocatedSeconds) > 0, - 'ACCEPTANCE_ALLOCATION_RECEIPT: Successful positive immutable allocation required'); - const allocated = Number(allocation.allocatedSeconds), desired = Number(allocation.desiredSeconds); - assert.ok(Number.isInteger(allocation.desiredSeconds) && desired > 0 && allocated <= desired, - 'ACCEPTANCE_ALLOCATION_DURATION: Allocation must be bounded by its calibrated task duration'); - assert.equal(row(attempt.limits).maximumSeconds, allocated, 'ACCEPTANCE_ALLOCATION_LIMIT: Immutable attempt limit drifted'); - assert.equal(envelope.requestedSeconds, allocated, 'ACCEPTANCE_ALLOCATION_RESERVATION: Requested authority drifted'); - assert.equal(envelope.reservedSeconds, allocated, 'ACCEPTANCE_ALLOCATION_RESERVATION: Reserved authority drifted'); - assert.ok(Array.isArray(allocation.constraints) && allocation.constraints.length > 0, - 'ACCEPTANCE_ALLOCATION_CONSTRAINTS: Original hard ceilings required'); - const constraints = rows(allocation.constraints); - assert.equal(new Set(constraints.map(value => value.id)).size, constraints.length, 'ACCEPTANCE_ALLOCATION_CONSTRAINTS: Duplicate ceiling authority'); - for (const constraint of constraints) assert.ok(text(constraint.id) && finite(constraint.remainingSeconds) - && Number(constraint.remainingSeconds) >= 0 && allocated <= Math.floor(Number(constraint.remainingSeconds)), - 'ACCEPTANCE_ALLOCATION_CONSTRAINTS: Invalid or exceeded ceiling'); - const minimum = [...constraints].sort((a, b) => Number(a.remainingSeconds) - Number(b.remainingSeconds) || text(a.id).localeCompare(text(b.id)))[0]!; - assert.equal(allocation.limitingConstraint, allocated < desired ? minimum.id : 'task-duration', 'ACCEPTANCE_ALLOCATION_CONSTRAINTS: Limiting authority mismatch'); - assert.ok(finite(calibration.multiplier) && calibration.multiplier > 0 && calibration.seconds === desired - && Math.ceil(estimate.data.expectedSeconds * calibration.multiplier) === desired, - 'ACCEPTANCE_ALLOCATION_CALIBRATION: Finite normalized calibration required'); - assert.ok(Array.isArray(calibration.measurementIds) && calibration.measurementIds.length <= 20 - && calibration.measurementIds.every(id => typeof id === 'string' && id.length > 0 && id === id.trim()) - && new Set(calibration.measurementIds).size === calibration.measurementIds.length, - 'ACCEPTANCE_ALLOCATION_CALIBRATION: At most twenty unique exact sample identities required'); - if (calibration.measurementIds.length === 0) assert.equal(desired, estimate.data.maximumSeconds, 'ACCEPTANCE_ALLOCATION_CALIBRATION: Cold start must preserve the maximum estimate'); - const opportunity = row(allocation.opportunity); - const weight = row(row(row(run.parameters).appliedPlan).policySnapshot).allocationWeight; - assert.ok(finite(weight) && weight > 0, 'ACCEPTANCE_ALLOCATION_POLICY: Original applied policy weight required'); - assert.equal(opportunity.weight, weight, 'ACCEPTANCE_ALLOCATION_POLICY: Receipt cannot invent another workday entitlement'); - assert.ok(['planning', 'acting'].includes(text(opportunity.phase)), 'ACCEPTANCE_ALLOCATION_PHASE: Exact original phase required'); - for (const field of ['shareSeconds', 'availableSeconds']) assert.ok(Number.isInteger(opportunity[field]), - 'ACCEPTANCE_ALLOCATION_SUPPLY: Original integer shared opportunity required'); - for (const field of ['shareSeconds', 'availableSeconds', 'remainingSupplySeconds', 'committedSeconds', 'planningCommittedSeconds']) { - assert.ok(finite(opportunity[field]) && Number(opportunity[field]) >= 0, 'ACCEPTANCE_ALLOCATION_SUPPLY: Finite weighted supply required'); - } - assert.ok(finite(opportunity.weight) && opportunity.weight > 0 && finite(opportunity.totalEligibleWeight) - && opportunity.totalEligibleWeight >= opportunity.weight && allocated <= Number(opportunity.availableSeconds) - && Number(opportunity.availableSeconds) <= Number(opportunity.shareSeconds) - && Number(opportunity.shareSeconds) <= Number(opportunity.remainingSupplySeconds) - && Number(opportunity.planningCommittedSeconds) <= Number(opportunity.committedSeconds), 'ACCEPTANCE_ALLOCATION_SUPPLY: Weighted supply authority exceeded'); -} + function phaseBoundaryCancelled(item: Row, run: Row): boolean { const parameters = row(run.parameters), time = row(row(row(item.capacityEnvelope).budget).time); const boundary = Date.parse(text(run.startedAt)) + Number(parameters.durationSeconds) * Number(parameters.planningPercent) * 10; diff --git a/tests/acceptance/workday/context-custody.test.ts b/tests/acceptance/workday/context-custody.test.ts index 8848269d..1a02e05b 100644 --- a/tests/acceptance/workday/context-custody.test.ts +++ b/tests/acceptance/workday/context-custody.test.ts @@ -7,7 +7,7 @@ import { parse } from 'yaml'; import { isDeepStrictEqual } from 'node:util'; import { acceptanceWorkdayId, read, row, type Row } from '../acceptance-cli.ts'; import { readWorkdayAssignments } from '../sdk-runtime-golden.test.ts'; -import { verifyAssignmentAuthority, verifyExactContextSource, verifyKnowledgeBookSource, verifyDraftProposalHandoff, verifyModelClockEvidence, modelExecutionInventory } from './support/assignment-authority.ts'; +import { verifyAssignmentAuthority, verifyGitInputContext, verifyExactContextSource, verifyKnowledgeBookSource, verifyDraftProposalHandoff, verifyModelClockEvidence, modelExecutionInventory } from './support/assignment-authority.ts'; import { readCompleteEvidence } from './support/evidence-pages.ts'; import { verifySdkArchitectureBook } from '../prepare-campaign.ts'; @@ -155,9 +155,7 @@ test('Actual accepted integration work retains its original single base exact in assert.ok(attempt.effectiveProfile.permissionCeiling.tools.includes('release')); assert.deepEqual(attempt.grant.sourceWrite, [repository]); assert.deepEqual(attempt.grant.contentWrite, []); assert.ok(Array.isArray(planned.dependsOn)); for (const dependency of planned.dependsOn) assert.ok(predecessors.some(value => value.previous.workItemId === dependency), 'ACCEPTANCE_INTEGRATION_PLAN: Accepted dependency missing from immutable predecessors'); - const base = [attempt.sourceRef, ...attempt.contextRefs].filter(ref => ref.store === 'git' && ref.repository === repository && ref.commit === workspace.baseCommit); - assert.ok(base.length > 0, 'ACCEPTANCE_INTEGRATION_BASE: Original admitted base must remain an explicit exact source/context ref'); - for (const ref of primary) assert.ok(attempt.contextRefs.some(value => value.store === 'git' && value.repository === ref.repository && value.commit === ref.commit), 'ACCEPTANCE_INTEGRATION_CONTEXT: Every separate native Git input must remain exact'); + verifyGitInputContext(item, primary); const result = assignmentResultSchema.parse(item.assignmentResult); assert.equal(result.assignmentId, attempt.id); assert.equal(result.status, 'completed'); const candidates = result.references.filter(ref => ref.kind === 'git' && ref.repository === repository); assert.equal(candidates.length, 1); assert.ok(Date.parse(result.completedAt) >= Date.parse(attempt.createdAt) && Date.parse(result.completedAt) <= Date.parse(attempt.deadline)); diff --git a/tests/acceptance/workday/support/assignment-authority.ts b/tests/acceptance/workday/support/assignment-authority.ts index dcd53fe5..2156df49 100644 --- a/tests/acceptance/workday/support/assignment-authority.ts +++ b/tests/acceptance/workday/support/assignment-authority.ts @@ -435,3 +435,14 @@ export function verifyAssignmentAuthority(item: Row): void { `${label}: Content context is not in the immutable exact grant`); } } + +/** Preserve the original native integration input assertions during live reads. */ +export function verifyGitInputContext(item: Row, primary: readonly ReturnType[]): void { + const attempt = assignmentAttemptSchema.parse(item.assignmentAttempt); + if (attempt.workspace.mode !== 'git') return; + const workspace = attempt.workspace, repository = workspace.repository; + const base = [attempt.sourceRef, ...attempt.contextRefs].filter(ref => ref.store === 'git' && ref.repository === repository && ref.commit === workspace.baseCommit); + assert.ok(base.length > 0, 'ACCEPTANCE_INTEGRATION_BASE: Original admitted base must remain an explicit exact source/context ref'); + for (const ref of primary) if (ref.kind === 'git' && ref.repository === repository) + assert.ok(attempt.contextRefs.some(value => value.store === 'git' && value.repository === ref.repository && value.commit === ref.commit), 'ACCEPTANCE_INTEGRATION_CONTEXT: Every separate native Git input must remain exact'); +} diff --git a/tests/acceptance/workday/support/decision-evidence.ts b/tests/acceptance/workday/support/decision-evidence.ts index f200f761..6b24dfed 100644 --- a/tests/acceptance/workday/support/decision-evidence.ts +++ b/tests/acceptance/workday/support/decision-evidence.ts @@ -119,14 +119,14 @@ export function verifyReviewFindingContent(reference: Row, decisionReference: Ro } /** Same classed approval assertion for final graph pairs and actually consumed predecessor reviews. */ -export function verifyApprovedWorkReview(review: Row, actor: Row, content: Row): void { +export function verifyWorkReview(review: Row, actor: Row, content: Row, disposition: 'approved' | 'request-changes' = 'approved'): void { const decision = verifyDecisionContent(content, String(review.projectId), 'ACCEPTANCE_REVIEW'); assert.ok(Array.isArray(decision.authorityRefs) && decision.authorityRefs.some(reference => isDeepStrictEqual(reference, row(review.assignmentAttempt).sourceRef)), 'ACCEPTANCE_REVIEW_AUTHORITY: Exact original proposal authority required'); const subject = row(decision.subjectRef), result = row(actor.assignmentResult), attempt = row(review.assignmentAttempt); assert.ok(review.status === 'completed' && row(review.assignmentResult).assignmentId === review.id - && row(row(review.lifecycleOutput).activityCompletion).reviewDisposition === 'approved' - && decision.decisionClass === 'work-review' && decision.disposition === 'approved', + && row(row(review.lifecycleOutput).activityCompletion).reviewDisposition === disposition + && decision.decisionClass === 'work-review' && decision.disposition === disposition, 'ACCEPTANCE_REVIEW_DISPOSITION: Lifecycle output and terminal status are not governed approval'); assert.ok(Array.isArray(result.references) && result.references.map(row).some(reference => subject.store === reference.kind && subject.repository === reference.repository && subject.commit === reference.commit && (!reference.path || subject.path === reference.path)), @@ -168,7 +168,7 @@ export function verifyReviewCandidateAuthority(review: Row, content: Row): void 'ACCEPTANCE_REVIEW_WRITE: Candidate content must remain immutable'); } -export function readApprovedWorkReview(review: Row, actor: Row, team: string, cache: Map): void { +export function readWorkReviewDecision(review: Row, team: string, cache: Map): Row { const references = row(review.assignmentResult).references; assert.ok(Array.isArray(references), 'ACCEPTANCE_REVIEW_AUTHORITY: Original result references required'); const decisions = references.map(row).filter(reference => reference.kind === 'treedx').map(reference => { @@ -178,5 +178,9 @@ export function readApprovedWorkReview(review: Row, actor: Row, team: string, ca return verifyDecisionContent(content, String(review.projectId), 'ACCEPTANCE_REVIEW'); }).filter((content): content is Row => content !== undefined && content.decisionClass === 'work-review'); assert.equal(decisions.length, 1, 'ACCEPTANCE_REVIEW_AUTHORITY: Exactly one returned classed work-review Decision required'); - verifyApprovedWorkReview(review, actor, decisions[0]!); + return decisions[0]!; +} + +export function readApprovedWorkReview(review: Row, actor: Row, team: string, cache: Map): void { + verifyWorkReview(review, actor, readWorkReviewDecision(review, team, cache)); } diff --git a/tests/acceptance/workday/support/monitoring/assignment-allocation.ts b/tests/acceptance/workday/support/monitoring/assignment-allocation.ts new file mode 100644 index 00000000..63076cf9 --- /dev/null +++ b/tests/acceptance/workday/support/monitoring/assignment-allocation.ts @@ -0,0 +1,54 @@ +import assert from 'node:assert/strict'; +import { estimateSchema } from '@treeseed/sdk/agent-capacity'; +import { row, type Row } from '../../../acceptance-cli.ts'; + +const rows = (value: unknown): Row[] => Array.isArray(value) ? value.map(row) : []; +const text = (value: unknown): string => typeof value === 'string' ? value : ''; + +export function verifyAllocation(item: Row, run: Row): void { + const attempt = row(item.assignmentAttempt), envelope = row(item.capacityEnvelope); + const allocation = row(row(row(item.explanation).metadata).allocation), calibration = row(allocation.calibration); + const finite = (value: unknown): value is number => typeof value === 'number' && Number.isFinite(value); + const estimate = estimateSchema.safeParse(attempt.estimate); + assert.ok(estimate.success, 'ACCEPTANCE_ALLOCATION_ESTIMATE: Exact expected/maximum estimate required'); + assert.ok(allocation.admitted === true && Number.isInteger(allocation.allocatedSeconds) && Number(allocation.allocatedSeconds) > 0, + 'ACCEPTANCE_ALLOCATION_RECEIPT: Successful positive immutable allocation required'); + const allocated = Number(allocation.allocatedSeconds), desired = Number(allocation.desiredSeconds); + assert.ok(Number.isInteger(allocation.desiredSeconds) && desired > 0 && allocated <= desired, + 'ACCEPTANCE_ALLOCATION_DURATION: Allocation must be bounded by its calibrated task duration'); + assert.equal(row(attempt.limits).maximumSeconds, allocated, 'ACCEPTANCE_ALLOCATION_LIMIT: Immutable attempt limit drifted'); + assert.equal(envelope.requestedSeconds, allocated, 'ACCEPTANCE_ALLOCATION_RESERVATION: Requested authority drifted'); + assert.equal(envelope.reservedSeconds, allocated, 'ACCEPTANCE_ALLOCATION_RESERVATION: Reserved authority drifted'); + assert.ok(Array.isArray(allocation.constraints) && allocation.constraints.length > 0, + 'ACCEPTANCE_ALLOCATION_CONSTRAINTS: Original hard ceilings required'); + const constraints = rows(allocation.constraints); + assert.equal(new Set(constraints.map(value => value.id)).size, constraints.length, 'ACCEPTANCE_ALLOCATION_CONSTRAINTS: Duplicate ceiling authority'); + for (const constraint of constraints) assert.ok(text(constraint.id) && finite(constraint.remainingSeconds) + && Number(constraint.remainingSeconds) >= 0 && allocated <= Math.floor(Number(constraint.remainingSeconds)), + 'ACCEPTANCE_ALLOCATION_CONSTRAINTS: Invalid or exceeded ceiling'); + const minimum = [...constraints].sort((a, b) => Number(a.remainingSeconds) - Number(b.remainingSeconds) || text(a.id).localeCompare(text(b.id)))[0]!; + assert.equal(allocation.limitingConstraint, allocated < desired ? minimum.id : 'task-duration', 'ACCEPTANCE_ALLOCATION_CONSTRAINTS: Limiting authority mismatch'); + assert.ok(finite(calibration.multiplier) && calibration.multiplier > 0 && calibration.seconds === desired + && Math.ceil(estimate.data.expectedSeconds * calibration.multiplier) === desired, + 'ACCEPTANCE_ALLOCATION_CALIBRATION: Finite normalized calibration required'); + assert.ok(Array.isArray(calibration.measurementIds) && calibration.measurementIds.length <= 20 + && calibration.measurementIds.every(id => typeof id === 'string' && id.length > 0 && id === id.trim()) + && new Set(calibration.measurementIds).size === calibration.measurementIds.length, + 'ACCEPTANCE_ALLOCATION_CALIBRATION: At most twenty unique exact sample identities required'); + if (calibration.measurementIds.length === 0) assert.equal(desired, estimate.data.maximumSeconds, 'ACCEPTANCE_ALLOCATION_CALIBRATION: Cold start must preserve the maximum estimate'); + const opportunity = row(allocation.opportunity); + const weight = row(row(row(run.parameters).appliedPlan).policySnapshot).allocationWeight; + assert.ok(finite(weight) && weight > 0, 'ACCEPTANCE_ALLOCATION_POLICY: Original applied policy weight required'); + assert.equal(opportunity.weight, weight, 'ACCEPTANCE_ALLOCATION_POLICY: Receipt cannot invent another workday entitlement'); + assert.ok(['planning', 'acting'].includes(text(opportunity.phase)), 'ACCEPTANCE_ALLOCATION_PHASE: Exact original phase required'); + for (const field of ['shareSeconds', 'availableSeconds']) assert.ok(Number.isInteger(opportunity[field]), + 'ACCEPTANCE_ALLOCATION_SUPPLY: Original integer shared opportunity required'); + for (const field of ['shareSeconds', 'availableSeconds', 'remainingSupplySeconds', 'committedSeconds', 'planningCommittedSeconds']) { + assert.ok(finite(opportunity[field]) && Number(opportunity[field]) >= 0, 'ACCEPTANCE_ALLOCATION_SUPPLY: Finite weighted supply required'); + } + assert.ok(finite(opportunity.weight) && opportunity.weight > 0 && finite(opportunity.totalEligibleWeight) + && opportunity.totalEligibleWeight >= opportunity.weight && allocated <= Number(opportunity.availableSeconds) + && Number(opportunity.availableSeconds) <= Number(opportunity.shareSeconds) + && Number(opportunity.shareSeconds) <= Number(opportunity.remainingSupplySeconds) + && Number(opportunity.planningCommittedSeconds) <= Number(opportunity.committedSeconds), 'ACCEPTANCE_ALLOCATION_SUPPLY: Weighted supply authority exceeded'); +} diff --git a/tests/acceptance/workday/support/monitoring/live-assignment-records.ts b/tests/acceptance/workday/support/monitoring/live-assignment-records.ts index 469d7c08..ae63116f 100644 --- a/tests/acceptance/workday/support/monitoring/live-assignment-records.ts +++ b/tests/acceptance/workday/support/monitoring/live-assignment-records.ts @@ -1,26 +1,159 @@ import assert from 'node:assert/strict'; -import { assignmentAttemptSchema, validateProviderAssignment } from '@treeseed/sdk/agent-capacity'; +import { isDeepStrictEqual } from 'node:util'; +import { assignmentAttemptSchema, assignmentResultSchema, capabilityAccountingLimitsSchema, executionNodeSchema, validateProviderAssignment } from '@treeseed/sdk/agent-capacity'; +import { simulationSourceBranch } from '@treeseed/sdk/capacity-provider/sandbox'; import { read, row, type Row } from '../../../acceptance-cli.ts'; -import { verifyAssignmentAuthority, verifyGovernedProfile, verifyHandlerInspection } from '../assignment-authority.ts'; -import { readGovernedContentFile } from '../decision-evidence.ts'; +import { verifyAssignmentAuthority, verifyGitInputContext, verifyGovernedProfile, verifyHandlerInspection } from '../assignment-authority.ts'; +import { readGovernedContentFile, readWorkReviewDecision, verifyWorkReview } from '../decision-evidence.ts'; import { verifyAppliedWorkdayCustody, verifyDerivedWorkdayModes } from '../campaign-observation.ts'; +import { verifyGraphProvenance, verifyPredecessorCustody } from '../evidence-pages.ts'; +import { verifyAllocation } from './assignment-allocation.ts'; +import { availabilityHistory } from './provider-history.ts'; const issuedAuthority = ({ status: _status, startedAt: _started, finishedAt: _finished, ...value }: Row) => value; -export function inspectLiveAssignmentProfile(item: Row, team: string): void { +/** Compare the issued build/model with original owning provider publication. */ +export function verifyLiveProviderRuntime(item: Row, publications: readonly Row[]): void { + const code = 'ACCEPTANCE_LIVE_RUNTIME', attempt = assignmentAttemptSchema.parse(item.assignmentAttempt); + const owners = publications.filter(value => value.providerId === attempt.provider.providerId && value.teamId === attempt.teamId + && Date.parse(String(value.openedAt)) <= Date.parse(attempt.createdAt)) + .sort((a, b) => Date.parse(String(b.openedAt)) - Date.parse(String(a.openedAt)) || String(b.id).localeCompare(String(a.id))); + const latest = owners[0]; + assert.ok(latest && ['open', 'closed'].includes(String(latest.status)) && Number.isSafeInteger(latest.sequence) && Number(latest.sequence) >= 1, + `${code}: Complete independently observed provider publication required`); + const opened = Date.parse(String(latest.openedAt)), refreshed = Date.parse(String(latest.refreshedAt)); + assert.ok(Number.isFinite(opened) && Number.isFinite(refreshed) && opened <= Date.parse(attempt.createdAt) && refreshed >= opened, + `${code}: Original publication clocks do not cover the issued provider`); + const snapshot = row(latest.snapshot); + assert.ok(Array.isArray(snapshot.adapters) && snapshot.adapters.length > 0, `${code}: Original executable adapter inventory required`); + const adapters = snapshot.adapters.map(row); + assert.ok(adapters.every(value => typeof value.id === 'string' && value.id) && new Set(adapters.map(value => value.id)).size === adapters.length, + `${code}: Unique executable adapter identities required`); + const selected = adapters.filter(value => value.id === attempt.provider.executionProviderId); + assert.equal(selected.length, 1, `${code}: Exact original execution provider required`); + assert.equal(selected[0]!.runtimeBuild, attempt.provider.runtimeBuild, `${code}: Issued build differs from independent provider publication`); + const limits = capabilityAccountingLimitsSchema.safeParse(selected[0]!.nativeLimits); + assert.ok(limits.success, `${code}: Original model/capability limits required`); + assert.deepEqual(limits.data, selected[0]!.nativeLimits, `${code}: Original limits must not be normalized`); + assert.equal(limits.data.modelConfigurationId, attempt.provider.modelConfigurationId, `${code}: Foreign model configuration`); +} + +/** A matching list/show pair cannot establish independent graph authority. */ +export function verifyLiveAssignmentGraph(item: Row, graph: Row, items: readonly Row[] = [item], decisions: ReadonlyMap = new Map()): void { + const code = 'ACCEPTANCE_LIVE_GRAPH', attempt = assignmentAttemptSchema.parse(item.assignmentAttempt); + assert.equal(graph.teamId, attempt.teamId, `${code}: Foreign graph`); + assert.ok(Number.isSafeInteger(graph.revision) && Number(graph.revision) >= attempt.graphRevision, + `${code}: Original issued graph revision is missing or in the future`); + assert.ok(Array.isArray(graph.nodes), `${code}: Complete owning graph required`); + const matches = graph.nodes.filter((node: Row) => node?.id === attempt.nodeId); + assert.equal(matches.length, 1, `${code}: Unique independent owning node required`); + const parsed = executionNodeSchema.safeParse(matches[0]); + assert.ok(parsed.success, `${code}: Canonical independent owning node required`); + const node = parsed.data; + assert.deepEqual(node, matches[0], `${code}: Original graph node must not be normalized`); + for (const field of ['teamId', 'projectId', 'agentClass', 'workItemId', 'sourceRef', 'authorityRefs', + 'estimate', 'requiredCapabilities', 'acceptanceCriteria'] as const) + assert.deepEqual(node[field], attempt[field], `${code}: Issued ${field} differs from independent graph`); + if (node.workdayId !== undefined) assert.equal(node.workdayId, attempt.workdayId, `${code}: Foreign node workday`); + assert.equal(node.kind, attempt.effectiveProfile.activity === 'chat' ? 'communication' : attempt.effectiveProfile.activity, + `${code}: Activity differs from graph authority`); + assert.equal(node.workspace, attempt.workspace.mode, `${code}: Workspace differs from graph authority`); + assert.ok(node.graphRevisionCreated <= attempt.graphRevision && node.graphRevisionUpdated <= Number(graph.revision), + `${code}: Node does not belong to the original issued graph history`); + verifyGraphProvenance(graph); + assert.ok(node.nodeRevision >= attempt.nodeRevision, `${code}: Graph cannot precede issued node revision`); + if (node.nodeRevision === attempt.nodeRevision) return; + // An older result can first appear after its real paired review advanced the + // node. Only original independently read classed Decisions explain that gap. + const pairs = (graph.edges as Row[]).filter(edge => edge.provenance === 'review-pair' && edge.graphRevisionRemoved === undefined + && (node.pairRole === 'actor' ? edge.fromNodeId === node.id : node.pairRole === 'reviewer' && edge.toNodeId === node.id)); + assert.equal(pairs.length, 1, `${code}: Original pair required for advanced node history`); + const pair = pairs[0]!, revisions = new Set(); + for (const review of items) { + const issued = assignmentAttemptSchema.parse(review.assignmentAttempt); + if (issued.nodeId !== pair.toNodeId || issued.workdayId !== attempt.workdayId || issued.projectId !== attempt.projectId + || issued.teamId !== attempt.teamId || issued.nodeRevision < attempt.nodeRevision || issued.nodeRevision >= node.nodeRevision + || Date.parse(String(review.completedAt)) < Date.parse(attempt.createdAt) + || row(row(review.lifecycleOutput).activityCompletion).reviewDisposition !== 'request-changes') continue; + assert.equal(review.status, 'completed', `${code}: Unfinished review cannot advance authority`); + assert.deepEqual(issued.sourceRef, attempt.sourceRef, `${code}: Foreign revision authority`); + const actors = items.filter(value => row(value.assignmentAttempt).nodeId === pair.fromNodeId + && issued.predecessorResultIds.includes(String(row(value.assignmentResult).id))); + assert.equal(actors.length, 1, `${code}: Original exact reviewed Actor required`); + const decision = decisions.get(String(review.id)); assert.ok(decision, `${code}: Original governed revision Decision required`); + verifyWorkReview(review, actors[0]!, decision, 'request-changes'); + assert.ok(!revisions.has(issued.nodeRevision), `${code}: Duplicate revision authority`); revisions.add(issued.nodeRevision); + } + assert.equal(attempt.nodeRevision + revisions.size, node.nodeRevision, `${code}: Every advanced revision needs independent original authority`); +} + +/** Existing simulation branch derivation and owning API whole-checkout scope. */ +export function verifyLiveSimulationWorkspace(item: Row, run: Row): void { + const attempt = assignmentAttemptSchema.parse(item.assignmentAttempt), code = 'ACCEPTANCE_LIVE_WORKSPACE'; + assert.equal(run.executionMode, 'simulation', `${code}: Original simulation authority required`); + if (attempt.workspace.mode !== 'git') return; + assert.ok(typeof run.id === 'string' && run.id === attempt.workdayId, `${code}: Exact owning workday required`); + assert.equal(attempt.workspace.branch, simulationSourceBranch(String(row(run.parameters).acceptanceCampaignId || 'local'), run.id, attempt.id), + `${code}: Exact campaign/workday/assignment branch required`); + assert.deepEqual(attempt.workspace.writablePaths, ['.'], `${code}: Exact original simulation checkout write scope required`); +} + +/** Compare source context with frozen bases and original consumed results. */ +export function verifyLiveSourceContext(item: Row, items: readonly Row[], frozenBases: Readonly>): void { + const attempt = assignmentAttemptSchema.parse(item.assignmentAttempt), code = 'ACCEPTANCE_LIVE_SOURCE_CONTEXT'; + assert.ok(frozenBases && typeof frozenBases === 'object' && !Array.isArray(frozenBases), `${code}: Original frozen source inventory required`); + const predecessors = items.filter(value => attempt.predecessorResultIds.includes(String(row(value.assignmentResult).id))) + .flatMap(value => assignmentResultSchema.parse(value.assignmentResult).references); + for (const repository of attempt.grant.sourceRead) { + const base = frozenBases[repository]; assert.ok(typeof base === 'string' && /^[a-f0-9]{40}$/u.test(base), `${code}: Exact frozen repository base required`); + const refs = attempt.contextRefs.filter(ref => ref.store === 'git' && ref.repository === repository); + assert.ok(refs.some(ref => ref.commit === base), `${code}: Original frozen source omitted`); + const consumed = predecessors.filter((ref): ref is Extract => ref.kind === 'git' && ref.repository === repository); + const allowed = new Set([base, ...consumed.map(ref => ref.commit)]); + assert.ok(refs.every(ref => ref.commit && allowed.has(ref.commit)), `${code}: Unissued foreign source context`); + for (const ref of consumed) assert.ok(refs.some(value => value.commit === ref.commit), `${code}: Original consumed source omitted`); + } + for (const reference of attempt.grant.contentRead) + assert.ok(attempt.contextRefs.some(ref => isDeepStrictEqual(ref, reference)), `${code}: Original exact granted content omitted`); +} + +export function inspectLiveAssignmentProfile(item: Row, team: string, items: readonly Row[], frozenBases: Readonly>): void { const attempt = assignmentAttemptSchema.parse(item.assignmentAttempt), profile = attempt.effectiveProfile; const shown = read(['assignments', 'show', attempt.id], team); assert.ok(validateProviderAssignment(shown).ok, 'ACCEPTANCE_LIVE_ASSIGNMENT: Independent public record is malformed'); assert.deepEqual(issuedAuthority(row(shown.assignmentAttempt)), issuedAuthority(attempt), 'ACCEPTANCE_LIVE_ASSIGNMENT: Independent public view changed issued authority'); - verifyGovernedProfile(item, readGovernedContentFile(profile.profileRef, attempt.projectId, team, new Map(), 'ACCEPTANCE_LIVE_PROFILE')); + const graph = read(['execution', 'graph', 'show'], team); + assert.ok(Array.isArray(items) && items.some(value => value.id === attempt.id), 'ACCEPTANCE_LIVE_ASSIGNMENT: Complete original assignment inventory required'); + const decisions = new Map(), content = new Map(); + if (Array.isArray(graph.nodes) && graph.nodes.some(value => row(value).id === attempt.nodeId && Number(row(value).nodeRevision) > attempt.nodeRevision)) { + for (const review of items) if (review.status === 'completed' && row(review.assignmentAttempt).workdayId === attempt.workdayId + && isDeepStrictEqual(row(review.assignmentAttempt).sourceRef, attempt.sourceRef) + && row(row(review.lifecycleOutput).activityCompletion).reviewDisposition === 'request-changes') + decisions.set(String(review.id), readWorkReviewDecision(review, team, content)); + } + verifyLiveAssignmentGraph(item, graph, items, decisions); + verifyPredecessorCustody([...items], graph); + verifyLiveSourceContext(item, items, frozenBases); + verifyGitInputContext(item, items.filter(value => attempt.predecessorResultIds.includes(String(row(value.assignmentResult).id))) + .flatMap(value => assignmentResultSchema.parse(value.assignmentResult).references)); + const run = row(read(['workdays', 'show', attempt.workdayId], team).run); + assert.equal(run.id, attempt.workdayId, 'ACCEPTANCE_LIVE_ASSIGNMENT: Foreign owning workday'); + assert.equal(run.teamId, attempt.teamId, 'ACCEPTANCE_LIVE_ASSIGNMENT: Foreign owning team'); + verifyAppliedWorkdayCustody(run); + verifyLiveSimulationWorkspace(item, run); + verifyAllocation(shown, run); + verifyLiveProviderRuntime(item, availabilityHistory(team)); + verifyGovernedProfile(item, readGovernedContentFile(profile.profileRef, attempt.projectId, team, new Map(), 'ACCEPTANCE_LIVE_PROFILE'), graph); const catalog = read(['agents', 'handlers', 'list', '--project', attempt.projectId, '--server', 'local'], team, true); const selected = read(['agents', 'handlers', 'show', profile.handler, '--project', attempt.projectId, '--server', 'local'], team, true); verifyHandlerInspection(item, catalog, selected); + verifyLiveAssignmentGraph(item, read(['execution', 'graph', 'show'], team), items, decisions); } /** Observe the existing canonical records before campaign progress. This is - * record/profile/grant custody, not proof of graph or provider completeness. */ + * record/profile/grant custody. Public graph inspection is mandatory for each + * new record; provider completeness remains a separate owning observation. */ export function observeLiveAssignmentRecords(run: Row, items: readonly Row[], retained: Map, inspect: (item: Row) => void): void { const label = 'ACCEPTANCE_LIVE_ASSIGNMENT'; assert.ok(typeof run.id === 'string' && run.id && typeof run.teamId === 'string' && run.teamId, `${label}: Exact workday and team required`); diff --git a/tests/acceptance/workday/support/monitoring/provider-history.ts b/tests/acceptance/workday/support/monitoring/provider-history.ts new file mode 100644 index 00000000..31b59526 --- /dev/null +++ b/tests/acceptance/workday/support/monitoring/provider-history.ts @@ -0,0 +1,27 @@ +import assert from 'node:assert/strict'; +import { decodeCapacityPageCursor } from '@treeseed/sdk/capacity-pagination'; +import { read, row, type Row } from '../../../acceptance-cli.ts'; + +export function availabilityHistory(team: string) { + const sessions = [], identities = new Set(), cursors = new Set(); let cursor: string | undefined; + let previous: { id: string; time: number } | undefined; + for (let pageNumber = 0; pageNumber < 40; pageNumber++) { + const observed = read(['capacity', 'status', '--limit', '100', ...(cursor ? ['--cursor', cursor] : [])], team), page = row(observed.page); + assert.ok(Array.isArray(observed.items) && observed.items.length <= 100 && page.limit === 100 && typeof page.hasMore === 'boolean', + 'ACCEPTANCE_AVAILABILITY_PAGE: Original complete page authority required'); + for (const value of observed.items) { + const session = row(value), id = typeof session.id === 'string' ? session.id : '', time = Date.parse(String(session.openedAt)); + assert.ok(id && Number.isFinite(time) && !identities.has(id), 'ACCEPTANCE_AVAILABILITY_PAGE: Unique session identity and owning opening clock required'); + assert.ok(!previous || time < previous.time || (time === previous.time && id < previous.id), 'ACCEPTANCE_AVAILABILITY_PAGE: Original descending opening order required'); + identities.add(id); previous = { id, time }; sessions.push(session); + } + if (!page.hasMore) { assert.equal(page.nextCursor, null, 'ACCEPTANCE_AVAILABILITY_PAGE: Explicit terminal cursor required'); return sessions; } + assert.ok(observed.items.length === 100 && typeof page.nextCursor === 'string' && page.nextCursor && !cursors.has(page.nextCursor), + 'ACCEPTANCE_AVAILABILITY_PAGE: Complete progressing inventory required'); + const next = decodeCapacityPageCursor(page.nextCursor), last = sessions.at(-1)!; + // Original repository creation/opening clocks are the same publication input. + assert.ok(next && next.id === last.id && next.createdAt === last.openedAt, 'ACCEPTANCE_AVAILABILITY_PAGE: Cursor must bind the actual last session'); + cursor = page.nextCursor; cursors.add(cursor); + } + assert.fail('ACCEPTANCE_AVAILABILITY_PAGE: Complete inventory exceeds the original forty-page acceptance bound'); +} diff --git a/tests/acceptance/workday/support/record-readback.ts b/tests/acceptance/workday/support/record-readback.ts index 76c22aa0..35c80384 100644 --- a/tests/acceptance/workday/support/record-readback.ts +++ b/tests/acceptance/workday/support/record-readback.ts @@ -5,11 +5,11 @@ import { resolve } from 'node:path'; import { resolveProviderConfig, loadProviderManifest, loadCapacityProviderIdentity } from '@treeseed/agent/provider-governance'; import { assignmentAttemptSchema, assignmentResultSchema, capabilityAccountingLimitsSchema, remainingCapabilitySeconds } from '@treeseed/sdk/agent-capacity'; import { capabilityOfferDigest, capabilityOfferSchema } from '@treeseed/sdk/capacity-provider'; -import { decodeCapacityPageCursor } from '@treeseed/sdk/capacity-pagination'; import { verifyPlatformRepository } from '@treeseed/sdk/platform'; import { acceptanceWorkdayId, read, row, type Row } from '../../acceptance-cli.ts'; import { readWorkdayAssignments, verifyGolden } from '../../sdk-runtime-golden.test.ts'; import { readCompleteEvidence } from './evidence-pages.ts'; +export { availabilityHistory } from './monitoring/provider-history.ts'; import { verifyAppliedWorkdayCustody, verifyDerivedWorkdayModes } from './campaign-observation.ts'; import { publicCanonicalRecords, verifyTerminalRecordCustody, verifyFailedExecutionCustody, verifyAvailabilityAccountingHistory, verifySandboxCloseoutCustody, verifyWorkdayContinuationCustody, verifySandboxHostAbsence, verifySandboxDirectoryAbsence, verifyProviderConformanceSignature, verifyProviderQualification, verifyProviderLocalSlotClosure, verifyProviderPollingSelection } from './record-custody.ts'; @@ -55,26 +55,4 @@ export function verify(f: ReturnType) { assert.equal(readFileSync(schemaPath, 'utf8'), bytes); assert.equal(execFileSync('git', ['rev-parse', 'HEAD'], { cwd: root, encoding: 'utf8' }).trim(), head); } -export function availabilityHistory(team: string) { - const sessions = [], identities = new Set(), cursors = new Set(); let cursor: string | undefined; - let previous: { id: string; time: number } | undefined; - for (let pageNumber = 0; pageNumber < 40; pageNumber++) { - const observed = read(['capacity', 'status', '--limit', '100', ...(cursor ? ['--cursor', cursor] : [])], team), page = row(observed.page); - assert.ok(Array.isArray(observed.items) && observed.items.length <= 100 && page.limit === 100 && typeof page.hasMore === 'boolean', - 'ACCEPTANCE_AVAILABILITY_PAGE: Original complete page authority required'); - for (const value of observed.items) { - const session = row(value), id = typeof session.id === 'string' ? session.id : '', time = Date.parse(String(session.openedAt)); - assert.ok(id && Number.isFinite(time) && !identities.has(id), 'ACCEPTANCE_AVAILABILITY_PAGE: Unique session identity and owning opening clock required'); - assert.ok(!previous || time < previous.time || (time === previous.time && id < previous.id), 'ACCEPTANCE_AVAILABILITY_PAGE: Original descending opening order required'); - identities.add(id); previous = { id, time }; sessions.push(session); - } - if (!page.hasMore) { assert.equal(page.nextCursor, null, 'ACCEPTANCE_AVAILABILITY_PAGE: Explicit terminal cursor required'); return sessions; } - assert.ok(observed.items.length === 100 && typeof page.nextCursor === 'string' && page.nextCursor && !cursors.has(page.nextCursor), - 'ACCEPTANCE_AVAILABILITY_PAGE: Complete progressing inventory required'); - const next = decodeCapacityPageCursor(page.nextCursor), last = sessions.at(-1)!; - // Original repository creation/opening clocks are the same publication input. - assert.ok(next && next.id === last.id && next.createdAt === last.openedAt, 'ACCEPTANCE_AVAILABILITY_PAGE: Cursor must bind the actual last session'); - cursor = page.nextCursor; cursors.add(cursor); - } - assert.fail('ACCEPTANCE_AVAILABILITY_PAGE: Complete inventory exceeds the original forty-page acceptance bound'); -} + diff --git a/tests/modern/guarantees/bindings.test.ts b/tests/modern/guarantees/bindings.test.ts index d1ce188e..acea8317 100644 --- a/tests/modern/guarantees/bindings.test.ts +++ b/tests/modern/guarantees/bindings.test.ts @@ -218,6 +218,7 @@ describe('capacity-provider guarantee execution bindings', () => { value.criterion === 'fc80e789a38eb6ae75fada867226ac0157844505619b12c90c55cbe317c6cc9d')) .toEqual([{ criterion: 'fc80e789a38eb6ae75fada867226ac0157844505619b12c90c55cbe317c6cc9d', verifierRefs: runtimeHandlers }]); expect(manifest.acceptanceCriteria).toEqual([ + { criterion: '4d3a65d9ab4becd82cc9336051f6e069a26bfcd662eb52fe3f7a32f67c8530bf', verifierRefs: ['agent.golden.live.campaign', 'agent.golden.architecture-profile-custody-live-1', 'api.golden.execution-normal-sdk-clean-schema-live-1', 'agent.golden.architecture-record-custody-live-1'] }, { criterion: '9778c4caf01add1b3815fa1cd3fe27ef11073cc98dbdc4a5deeb54e2a441f1dc', verifierRefs: ['sdk.golden.live.product'] }, { criterion: 'fc80e789a38eb6ae75fada867226ac0157844505619b12c90c55cbe317c6cc9d', verifierRefs: runtimeHandlers }, { criterion: '1b8f85527f4b286aff075cfe1892180d1407c503fcebab5193560cb915699ea3', verifierRefs: ['agent.golden.architecture-profile-custody-live-1', 'agent.golden.live.settlement', 'agent.golden.architecture-record-custody-live-1'] }, diff --git a/tests/modern/guarantees/monitoring/live-assignment-fixture.ts b/tests/modern/guarantees/monitoring/live-assignment-fixture.ts index 85e6e6d8..89c97df5 100644 --- a/tests/modern/guarantees/monitoring/live-assignment-fixture.ts +++ b/tests/modern/guarantees/monitoring/live-assignment-fixture.ts @@ -1,25 +1,66 @@ -import { assignmentAttemptSchema, compileWorkday, DEFAULT_WORKDAY_POLICY, type AssignmentAttempt } from '@treeseed/sdk/agent-capacity'; +import { allocateWorkdayCapacity, calculateAssignmentAllocation, appliedWorkdaySchema, assignmentAttemptSchema, compileWorkday, DEFAULT_WORKDAY_POLICY, type AssignmentAttempt } from '@treeseed/sdk/agent-capacity'; +import { simulationSourceBranch } from '@treeseed/sdk/capacity-provider/sandbox'; import { request } from '../../kernel/provider-kernel-fixture.ts'; import type { Row } from '../../../acceptance/acceptance-cli.ts'; // Controlled public assertion input only; never a real admission or usage receipt. -export function liveAssignmentRecord(attempt: AssignmentAttempt = assignmentAttemptSchema.parse({ +export function liveAssignmentRecord(input?: AssignmentAttempt): Row { + const attempt = input ?? assignmentAttemptSchema.parse({ ...assignmentAttemptSchema.parse(request().assignment.assignmentAttempt), deadline: '2026-09-13T13:00:00.000Z', -})): Row { +}); + if (!input && attempt.workspace.mode === 'git') Object.assign(attempt.workspace, { branch: simulationSourceBranch('local', attempt.workdayId, attempt.id), writablePaths: ['.'] }); const mode = ['acting', 'reviewing', 'reporting'].includes(attempt.effectiveProfile.activity) ? 'acting' : 'planning'; const envelope = { teamId: attempt.teamId, projectId: attempt.projectId, workDayId: attempt.workdayId, mode, projectAgentClassId: 'opaque-configured-row', capacityProviderId: attempt.provider.providerId, executionProviderId: attempt.provider.executionProviderId, reservationId: attempt.reservationId }; - return { id: attempt.id, membershipId: 'controlled-membership', ...envelope, stateVersion: 1, status: 'leased', leaseState: 'leased', + const item = { id: attempt.id, membershipId: 'controlled-membership', ...envelope, stateVersion: 1, status: 'leased', leaseState: 'leased', createdAt: attempt.createdAt, updatedAt: attempt.createdAt, attemptCount: attempt.attempt, executionNodeId: attempt.nodeId, executionNodeRevision: attempt.nodeRevision, graphRevision: attempt.graphRevision, assignmentAttempt: structuredClone(attempt), - capacityEnvelope: envelope, workspaceContext: {}, allowedOutputs: {}, explanation: {}, lifecycleOutput: {}, metadata: {} }; + capacityEnvelope: { ...envelope, requestedSeconds: attempt.limits.maximumSeconds, reservedSeconds: attempt.limits.maximumSeconds }, + workspaceContext: {}, allowedOutputs: {}, explanation: {} as Row, lifecycleOutput: {}, metadata: {} }; + const plan = appliedWorkdaySchema.parse(liveRun(item).parameters.appliedPlan), seconds = attempt.limits.maximumSeconds; + const opportunity = allocateWorkdayCapacity({ remainingSeconds: seconds, now: attempt.createdAt, + workdays: [{ plan, committedSeconds: 0, planningCommittedSeconds: 0, maximumAdditionalSeconds: seconds, actingReady: mode === 'acting' }] })[plan.id]; + item.explanation = { metadata: { allocation: { ...calculateAssignmentAllocation({ estimate: attempt.estimate, + measurements: [], constraints: [{ id: 'shared-model', remainingSeconds: seconds }] }), opportunity } } }; + return item; } -export function liveRun(item: Row): Row { +export function liveRun(item: Row) { const attempt = assignmentAttemptSchema.parse(item.assignmentAttempt); const plan = compileWorkday({ id: attempt.workdayId, teamId: attempt.teamId, executionMode: 'simulation', policyId: 'default', policyRevision: 1, startsAt: attempt.createdAt, agentIds: [], policy: { ...DEFAULT_WORKDAY_POLICY, durationSeconds: 3600 } }); + plan.state = 'active'; // Controlled record represents already admitted work, not a prospective plan. return { id: attempt.workdayId, teamId: attempt.teamId, executionMode: 'simulation', startedAt: attempt.createdAt, parameters: { ...plan.policySnapshot, deadlineAt: plan.endsAt, appliedPlan: plan, scheduledProjectIds: [attempt.projectId] } }; } + +// Controlled independent public graph input; this is not native SQL custody. +export function liveAssignmentGraph(item: Row): Row { + const attempt = assignmentAttemptSchema.parse(item.assignmentAttempt), conditionId = `${attempt.nodeId}.authority`; + const common = { schemaVersion: 'treeseed.execution-node/v1', teamId: attempt.teamId, projectId: attempt.projectId, + sourceRef: attempt.sourceRef, ruleRevision: 1, nodeRevision: attempt.nodeRevision, + graphRevisionCreated: attempt.graphRevision, graphRevisionUpdated: attempt.graphRevision }; + return { teamId: attempt.teamId, revision: attempt.graphRevision, digest: `sha256:${'e'.repeat(64)}`, nodes: [ + { ...common, id: attempt.nodeId, workdayId: attempt.workdayId, workItemId: attempt.workItemId, + kind: attempt.effectiveProfile.activity === 'chat' ? 'communication' : attempt.effectiveProfile.activity, + pairRole: attempt.effectiveProfile.activity === 'acting' ? 'actor' : null, maximumReviewCycles: 2, + status: 'assigned', authorityRefs: attempt.authorityRefs, agentClass: attempt.agentClass, + estimate: attempt.estimate, requiredCapabilities: attempt.requiredCapabilities, + requestedPermissions: attempt.effectiveProfile.permissionCeiling, workspace: attempt.workspace.mode, + ...(attempt.acceptanceCriteria ? { acceptanceCriteria: attempt.acceptanceCriteria } : {}) }, + { ...common, id: conditionId, kind: 'condition', pairRole: null, status: 'completed', + condition: { conditionType: 'authority', subjectRef: attempt.authorityRefs[0], expectedState: 'accepted' } }, + ], edges: [{ schemaVersion: 'treeseed.execution-edge/v1', id: `${attempt.nodeId}.governance`, teamId: attempt.teamId, + fromNodeId: conditionId, toNodeId: attempt.nodeId, provenance: 'governance', sourceRef: attempt.sourceRef, + graphRevisionCreated: attempt.graphRevision }] }; +} + +export function liveProviderSession(item: Row): Row { + const attempt = assignmentAttemptSchema.parse(item.assignmentAttempt); + return { id: 'controlled-runtime-session', membershipId: item.membershipId, teamId: attempt.teamId, + providerId: attempt.provider.providerId, openedAt: attempt.createdAt, refreshedAt: attempt.createdAt, status: 'open', sequence: 1, + snapshot: { adapters: [{ id: attempt.provider.executionProviderId, runtimeBuild: attempt.provider.runtimeBuild, + nativeLimits: { modelConfigurationId: attempt.provider.modelConfigurationId, dailyActiveSecondsLimit: 28800, + capabilityLimits: { [attempt.provider.executionCapabilityId]: { dailyActiveSecondsLimit: 7200 } } } }] } }; +} diff --git a/tests/modern/guarantees/monitoring/live-assignment-native.test.ts b/tests/modern/guarantees/monitoring/live-assignment-native.test.ts index 7818f8a0..07a188da 100644 --- a/tests/modern/guarantees/monitoring/live-assignment-native.test.ts +++ b/tests/modern/guarantees/monitoring/live-assignment-native.test.ts @@ -4,19 +4,27 @@ import { stringify } from 'yaml'; import { expect, it } from 'vitest'; import { ControlPlaneClient, defaultLocalControlPlaneServer } from '@treeseed/sdk/control-plane-client'; import { controlPlaneOperation } from '@treeseed/sdk/operator-contracts'; -import { assignmentResultSchema } from '@treeseed/sdk/agent-capacity'; +import { simulationSourceBranch } from '@treeseed/sdk/capacity-provider/sandbox'; +import { assignmentAttemptSchema, assignmentResultSchema, validateProviderAssignment } from '@treeseed/sdk/agent-capacity'; import { portableKernel } from '../../kernel/architecture/portable/portable-kernel-fixture.ts'; import { row, type Row } from '../../../acceptance/acceptance-cli.ts'; -import { verifyGovernedProfile, verifyHandlerInspection } from '../../../acceptance/workday/support/assignment-authority.ts'; -import { observeLiveAssignmentRecords } from '../../../acceptance/workday/support/monitoring/live-assignment-records.ts'; -import { liveAssignmentRecord, liveRun } from './live-assignment-fixture.ts'; +import { verifyGitInputContext, verifyGovernedProfile, verifyHandlerInspection } from '../../../acceptance/workday/support/assignment-authority.ts'; +import { observeLiveAssignmentRecords, verifyLiveAssignmentGraph, verifyLiveProviderRuntime, verifyLiveSimulationWorkspace, verifyLiveSourceContext } from '../../../acceptance/workday/support/monitoring/live-assignment-records.ts'; +import { verifyAllocation } from '../../../acceptance/workday/support/monitoring/assignment-allocation.ts'; +import { verifyPredecessorCustody } from '../../../acceptance/workday/support/evidence-pages.ts'; +import { liveAssignmentGraph, liveAssignmentRecord, liveProviderSession, liveRun } from './live-assignment-fixture.ts'; it('native configured Kernel Git result and public SDK assignment reads reach the live observer with denied original observations retained before exact retry', async () => { const f = await portableKernel(); let server: ReturnType | undefined; try { + const configured = f.profile.activityProfiles.acting!; configured.permissions.tools.push('verification'); + f.attempt.effectiveProfile.permissionCeiling = configured.permissions; f.attempt.grant.tools.push('verification'); const content = stringify(f.profile), path = 'agents/configured-native.yaml'; Object.assign(f.attempt.effectiveProfile.profileRef, { repository: 'controlled-library', commit: f.base, path, digest: `sha256:${createHash('sha256').update(content).digest('hex')}` }); + if (f.attempt.workspace.mode !== 'git') throw new Error('Original native Git fixture required'); + const campaign = 'native-renamed-campaign', branch = simulationSourceBranch(campaign, f.attempt.workdayId, f.attempt.id); + f.git('branch', '-m', branch); Object.assign(f.attempt.workspace, { branch, writablePaths: ['.'] }); const candidate = await f.candidate(), produced = await f.run(); expect(produced.status, `${produced.code}: ${produced.summary}`).toBe('completed'); const result = assignmentResultSchema.parse(produced.outputs?.assignmentResult); @@ -24,10 +32,43 @@ it('native configured Kernel Git result and public SDK assignment reads reach th expect(f.git('rev-parse', 'HEAD')).toBe(candidate); expect(f.git('merge-base', f.base, candidate)).toBe(f.base); const item: Row = { ...liveAssignmentRecord(f.attempt), status: 'completed', leaseState: 'released', completedAt: result.completedAt, assignmentResult: result }; Object.assign(row(item.assignmentAttempt), { status: 'completed', finishedAt: result.completedAt }); - const run = liveRun(item), retained = new Map(), before = structuredClone({ item, run }); + const run = liveRun(item); Object.assign(run.parameters, { acceptanceCampaignId: campaign }); + const retained = new Map(), before = structuredClone({ item, run }); let mode = 'exact'; const requests: string[] = [], failed: Array<{ mode: string; record: Row }> = []; server = createServer((request, response) => { requests.push(`${request.method} ${request.url}`); response.setHeader('content-type', 'application/json'); + if (request.url?.split('?')[0] === `/v1/teams/${f.attempt.teamId}/capacity/availability-sessions`) { + const publication = liveProviderSession(item), adapter = row((row(publication.snapshot).adapters as Row[])[0]); + if (mode === 'publication-denied') { response.writeHead(403).end(JSON.stringify({ status: 403, title: 'Runtime inventory denied', code: 'runtime_denied' })); return; } + if (mode === 'publication-interrupted') { request.socket.destroy(); return; } + if (mode === 'publication-provider') publication.providerId = 'foreign-provider'; + if (mode === 'publication-adapter') adapter.id = 'foreign-adapter'; + if (mode === 'publication-build') adapter.runtimeBuild = `sha256:${'f'.repeat(64)}`; + if (mode === 'publication-model') row(adapter.nativeLimits).modelConfigurationId = 'foreign-model'; + if (mode !== 'exact') failed.push({ mode, record: structuredClone(publication) }); + const later = structuredClone(publication); later.id = 'native-later-session'; + later.openedAt = new Date(Date.parse(String(publication.openedAt)) + 1000).toISOString(); later.refreshedAt = later.openedAt; + row((row(later.snapshot).adapters as Row[])[0]).runtimeBuild = `sha256:${'a'.repeat(64)}`; + response.end(JSON.stringify({ data: { items: [later, publication], page: { limit: 100, hasMore: false, nextCursor: null } } })); return; + } + if (request.url === `/v1/teams/${f.attempt.teamId}/execution-graph`) { + const graph = liveAssignmentGraph(item), nodes = graph.nodes as Row[], edges = graph.edges as Row[], node = nodes[0]!; + if (mode === 'graph-interrupted') { request.socket.destroy(); return; } + if (mode === 'graph-denied') { response.writeHead(403).end(JSON.stringify({ status: 403, title: 'Original graph denied', code: 'graph_denied' })); return; } + if (mode === 'graph-malformed') { response.end('{'); return; } + if (mode === 'graph-team') graph.teamId = 'foreign-team'; + if (mode === 'graph-revision') graph.revision = 0; + if (mode === 'graph-node') node.nodeRevision = Number(node.nodeRevision) + 1; + if (mode === 'graph-source') node.sourceRef = { ...row(node.sourceRef), id: 'foreign-source' }; + if (mode === 'graph-estimate') node.estimate = { ...row(node.estimate), maximumSeconds: Number(row(node.estimate).maximumSeconds) + 1 }; + if (mode === 'graph-workspace') node.workspace = 'read-only'; + if (mode === 'graph-predecessor') { + nodes.push({ ...node, id: 'missing-public-node', status: 'completed' }); + edges.push({ ...edges[0], id: 'missing-public-edge', fromNodeId: 'missing-public-node', provenance: 'work-item' }); + } + if (mode !== 'exact') failed.push({ mode, record: structuredClone(graph) }); + response.end(JSON.stringify({ data: graph })); return; + } if (request.method === 'GET' && request.url === `/v1/teams/${f.attempt.teamId}/workday-runs/${f.attempt.workdayId}`) { const observed = structuredClone(run); if (mode.startsWith('raw-workday-')) { @@ -44,6 +85,23 @@ it('native configured Kernel Git result and public SDK assignment reads reach th if (mode === 'denied') { response.writeHead(403).end(JSON.stringify({ status: 403, title: 'Controlled native denial', code: 'controlled_assignment_denied' })); return; } if (mode === 'interrupted') { request.socket.destroy(); return; } const returned = structuredClone(item); + if (mode === 'source-substitution') { + row(row(returned.assignmentAttempt).workspace).baseCommit = 'f'.repeat(40); + for (const reference of row(returned.assignmentAttempt).contextRefs as Row[]) if (reference.store === 'git') reference.commit = 'f'.repeat(40); + } + if (mode === 'immutable-grant') row(row(returned.assignmentAttempt).grant).tools = ['source.read', 'source.write']; + if (mode === 'dual-custody') (row(row(returned.assignmentAttempt).grant).contentWrite as Row[]).push({ store: 'treedx', model: 'decision', id: 'second-write', repository: 'native-library', commit: f.base, path: 'decisions/second.md' }); + if (mode === 'second-repository') (row(row(returned.assignmentAttempt).grant).sourceWrite as string[]).push('foreign/source'); + if (mode === 'readonly-write') row(returned.assignmentAttempt).workspace = { mode: 'read-only' }; + if (mode === 'workspace-branch') row(row(returned.assignmentAttempt).workspace).branch = 'simulation/foreign'; + if (mode === 'workspace-paths') row(row(returned.assignmentAttempt).workspace).writablePaths = ['docs']; + if (mode === 'workspace-base') row(row(returned.assignmentAttempt).workspace).baseCommit = 'f'.repeat(40); + if (mode === 'allocation-missing') delete row(returned.explanation).metadata; + if (mode === 'allocation-reservation') row(returned.capacityEnvelope).reservedSeconds = 0; + const allocation = row(row(row(returned.explanation).metadata).allocation); + if (mode === 'allocation-calibration') row(allocation.calibration).multiplier = 0; + if (mode === 'allocation-limiting') allocation.limitingConstraint = 'foreign-limit'; + if (mode === 'allocation-supply') row(allocation.opportunity).remainingSupplySeconds = 0; if (mode === 'derived-production') returned.executionMode = 'production'; if (mode === 'derived-malformed') returned.executionMode = null; if (mode === 'runtime') row(row(returned.assignmentAttempt).provider).runtimeBuild = `sha256:${'f'.repeat(64)}`; @@ -73,7 +131,7 @@ it('native configured Kernel Git result and public SDK assignment reads reach th path: { teamId: f.attempt.teamId, assignmentId: f.attempt.id }, query: {}, body: undefined, })).data); observeLiveAssignmentRecords(row(observed.run), [returned], observations, value => { - inspections++; verifyGovernedProfile(value, { path, content }); + inspections++; verifyLiveSourceContext(value, [value], { 'treeseed-ai/sdk': f.base }); verifyGitInputContext(value, []); verifyLiveSimulationWorkspace(value, run); verifyAllocation(value, run); verifyGovernedProfile(value, { path, content }); const handler = { id: f.attempt.effectiveProfile.handler, origin: f.attempt.effectiveProfile.handlerOrigin }; verifyHandlerInspection(value, { projectId: f.attempt.projectId, handlers: [handler] }, { projectId: f.attempt.projectId, handler }); }); @@ -109,6 +167,76 @@ it('native configured Kernel Git result and public SDK assignment reads reach th expect(requests.filter(value => value === `GET /v1/teams/${f.attempt.teamId}/workday-runs/${f.attempt.workdayId}`)).toHaveLength(27); expect(requests).toHaveLength(54); expect({ item, run }).toEqual(before); expect(f.requests).toHaveLength(1); + const inspectGraph = async () => { + const graph = row((await client.invoke(controlPlaneOperation('execution.graph.show'), { + path: { teamId: f.attempt.teamId }, query: {}, body: undefined, + })).data), observations = new Map(); + observeLiveAssignmentRecords(run, [item], observations, value => { + verifyLiveAssignmentGraph(value, graph); verifyPredecessorCustody([value], graph); + verifyAllocation(value, run); verifyGovernedProfile(value, { path, content }, graph); + const handler = { id: f.attempt.effectiveProfile.handler, origin: f.attempt.effectiveProfile.handlerOrigin }; + verifyHandlerInspection(value, { projectId: f.attempt.projectId, handlers: [handler] }, { projectId: f.attempt.projectId, handler }); + }); + return observations; + }; + mode = 'exact'; expect((await Promise.all([inspectGraph(), inspectGraph()])).every(value => value.size === 1)).toBe(true); + for (const denied of ['graph-team', 'graph-revision', 'graph-node', 'graph-source', 'graph-estimate', 'graph-workspace', 'graph-predecessor', + 'graph-denied', 'graph-malformed', 'graph-interrupted']) { + mode = denied; await expect(inspectGraph(), denied).rejects.toThrow(); expect({ item, run }).toEqual(before); + } + const graphFailures = structuredClone(failed); mode = 'exact'; expect((await inspectGraph()).size).toBe(1); + expect(failed).toEqual(graphFailures); expect(failed.slice(0, originalFailures.length)).toEqual(originalFailures); + expect(requests.filter(value => value.endsWith('/execution-graph'))).toHaveLength(13); + for (const denied of ['allocation-missing', 'allocation-reservation', 'allocation-calibration', 'allocation-limiting', 'allocation-supply']) { + mode = denied; const empty = new Map(); await expect(execute(empty), denied).rejects.toThrow('ACCEPTANCE_ALLOCATION'); + expect(empty.size).toBe(0); expect({ item, run }).toEqual(before); + } + const allFailures = structuredClone(failed); mode = 'exact'; const retried = new Map(); await execute(retried); + expect(retried.size).toBe(1); expect(failed).toEqual(allFailures); expect(failed.slice(0, graphFailures.length)).toEqual(graphFailures); + const inspectRuntime = async () => { + const returned = row((await client.invoke(controlPlaneOperation('capacity.status'), { + path: { teamId: f.attempt.teamId }, query: { limit: 100 }, body: undefined, + })).data); + if (!Array.isArray(returned.items)) throw new Error('Complete original native publication collection required'); + const items = returned.items.map(row); verifyLiveProviderRuntime(item, items); return items; + }; + expect((await Promise.all([inspectRuntime(), inspectRuntime()])).every(value => value.length === 2)).toBe(true); + for (const denied of ['publication-provider', 'publication-adapter', 'publication-build', 'publication-model', 'publication-denied', 'publication-interrupted']) { + mode = denied; await expect(inspectRuntime(), denied).rejects.toThrow(); expect({ item, run }).toEqual(before); + } + const runtimeFailures = structuredClone(failed); mode = 'exact'; await inspectRuntime(); + expect(failed).toEqual(runtimeFailures); expect(failed.slice(0, allFailures.length)).toEqual(allFailures); + mode = 'workspace-base'; const untouched = new Map(); + await expect(execute(untouched)).rejects.toThrow('ACCEPTANCE_INTEGRATION_BASE'); + expect(untouched.size).toBe(0); expect({ item, run }).toEqual(before); expect(retained).toEqual(held); + for (const denied of ['workspace-branch', 'workspace-paths']) { + mode = denied; const empty = new Map(); await expect(execute(empty)).rejects.toThrow('ACCEPTANCE_LIVE_WORKSPACE'); + expect(empty.size).toBe(0); expect({ item, run }).toEqual(before); + } + mode = 'source-substitution'; const deniedSource = new Map(); + await expect(execute(deniedSource)).rejects.toThrow('ACCEPTANCE_LIVE_SOURCE_CONTEXT'); expect(deniedSource.size).toBe(0); + const workspaceFailures = structuredClone(failed); mode = 'exact'; await execute(untouched); + expect(untouched.size).toBe(1); expect(failed).toEqual(workspaceFailures); + expect(failed.slice(0, runtimeFailures.length)).toEqual(runtimeFailures); + mode = 'immutable-grant'; await expect(execute()).rejects.toThrow('Issued authority changed'); expect(retained).toEqual(held); + for (const denied of ['dual-custody', 'second-repository', 'readonly-write']) { + mode = denied; const empty = new Map(); await expect(execute(empty), denied).rejects.toThrow('ACCEPTANCE_LIVE_ASSIGNMENT: Complete unchanged canonical public record required'); + expect(validateProviderAssignment(failed.at(-1)!.record).ok, denied).toBe(false); + expect(empty.size).toBe(0); expect(retained).toEqual(held); expect({ item, run }).toEqual(before); + } + const custodyFailures = structuredClone(failed); mode = 'exact'; await execute(); + expect(failed).toEqual(custodyFailures); expect(retained).toEqual(held); + const issuedChild = assignmentAttemptSchema.parse(item.assignmentAttempt); + issuedChild.id = 'native-consumer'; issuedChild.nodeId = 'native-consumer-node'; issuedChild.status = 'leased'; + issuedChild.createdAt = result.completedAt; delete issuedChild.startedAt; delete issuedChild.finishedAt; + issuedChild.predecessorResultIds = [result.id]; + issuedChild.contextRefs.push({ store: 'git', model: 'repository', id: 'consumed-native-candidate', repository: 'treeseed-ai/sdk', commit: candidate }); + if (issuedChild.workspace.mode !== 'git') throw new Error('Canonical native consumer requires Git'); + issuedChild.workspace.branch = simulationSourceBranch(campaign, issuedChild.workdayId, issuedChild.id); + issuedChild.workspace.baseCommit = candidate; + const child = liveAssignmentRecord(issuedChild); expect(validateProviderAssignment(child).ok).toBe(true); + verifyLiveSourceContext(child, [item, child], { 'treeseed-ai/sdk': f.base }); + expect(f.git('cat-file', '-t', candidate)).toBe('commit'); expect({ item, run }).toEqual(before); // Native owning Kernel/handler/Git and SDK transport; upstream record, // profile bytes and handler catalog are controlled. This does not prove // native API admission, TreeDX publication, model usage or physical closure. diff --git a/tests/modern/guarantees/monitoring/live-graph-inspection.test.ts b/tests/modern/guarantees/monitoring/live-graph-inspection.test.ts new file mode 100644 index 00000000..8712f87a --- /dev/null +++ b/tests/modern/guarantees/monitoring/live-graph-inspection.test.ts @@ -0,0 +1,204 @@ +import { beforeEach, expect, it, vi } from 'vitest'; +import { liveAssignmentGraph, liveAssignmentRecord, liveProviderSession, liveRun } from './live-assignment-fixture.ts'; +import { reviewedDecision, reviewedRevisionFixture } from '../../kernel/architecture/custody/graph/review-readiness-fixture.ts'; + +// Unit-only isolation of already separately covered profile/content boundaries. +// The real configured Kernel/public SDK integration exercises those boundaries. +const state = vi.hoisted(() => ({ item: {} as Record, graph: {} as Record, run: {} as Record, publications: [] as Record[], frozen: {} as Record })); +vi.mock('../../../acceptance/acceptance-cli.ts', async original => ({ + ...await original(), + read: (args: string[]) => args[0] === 'assignments' ? structuredClone(state.item) + : args[0] === 'execution' ? structuredClone(state.graph) : args[0] === 'workdays' ? { run: structuredClone(state.run) } + : args[0] === 'capacity' ? { items: structuredClone(state.publications), page: { limit: 100, hasMore: false, nextCursor: null } } : {}, +})); +vi.mock('../../../acceptance/workday/support/decision-evidence.ts', async original => ({ + ...await original(), readGovernedContentFile: () => ({}), +})); +vi.mock('../../../acceptance/workday/support/assignment-authority.ts', async original => ({ + ...await original(), + verifyGovernedProfile: () => undefined, verifyHandlerInspection: () => undefined, +})); +const { inspectLiveAssignmentProfile, verifyLiveAssignmentGraph, verifyLiveProviderRuntime, verifyLiveSimulationWorkspace, verifyLiveSourceContext } = await import('../../../acceptance/workday/support/monitoring/live-assignment-records.ts'); + +beforeEach(() => { + state.item = liveAssignmentRecord(); + state.item.assignmentAttempt.requiredCapabilities = ['code-change']; + state.frozen = Object.fromEntries(state.item.assignmentAttempt.contextRefs.filter((ref: Record) => ref.store === 'git').map((ref: Record) => [ref.repository, ref.commit])); + state.graph = liveAssignmentGraph(state.item); + state.run = liveRun(state.item); + state.publications = [liveProviderSession(state.item)]; +}); + +it('accepts exact independent graph authority without rewriting source observations and denies every substituted issued field', () => { + const before = structuredClone(state); + verifyLiveAssignmentGraph(state.item, state.graph); expect(state).toEqual(before); + for (const field of ['teamId', 'projectId', 'nodeRevision', 'agentClass', 'workItemId', 'sourceRef', 'authorityRefs', + 'estimate', 'requiredCapabilities', 'acceptanceCriteria', 'workspace', 'kind', 'workdayId', 'graphRevisionCreated', 'graphRevisionUpdated']) { + for (const value of [undefined, null, '', 'foreign-authority', 0, [], {}]) { + if (value === undefined && (field === 'workdayId' || before.graph.nodes[0][field] === undefined)) continue; + state.graph = structuredClone(before.graph); state.graph.nodes[0][field] = value; + const denied = structuredClone(state); + expect.soft(() => verifyLiveAssignmentGraph(state.item, state.graph), `${field}/${String(value)}`).toThrow(); + expect(state).toEqual(denied); + } + } + state.graph = structuredClone(before.graph); state.graph.nodes.push(structuredClone(state.graph.nodes[0])); + expect(() => verifyLiveAssignmentGraph(state.item, state.graph)).toThrow('ACCEPTANCE_LIVE_GRAPH'); + state.graph = structuredClone(before.graph); state.graph.nodes[0].sourceRef.id = ' proposal-with-whitespace '; + expect(() => verifyLiveAssignmentGraph(state.item, state.graph)).toThrow('ACCEPTANCE_LIVE_GRAPH'); + state.graph = structuredClone(before.graph); delete state.graph.nodes[0].workdayId; + expect(() => verifyLiveAssignmentGraph(state.item, state.graph)).not.toThrow(); // Shared governed nodes may omit a run binding. + state.graph = structuredClone(before.graph); expect(() => inspectLiveAssignmentProfile(state.item, state.item.teamId, [state.item], state.frozen)).not.toThrow(); +}); + +it('denies missing independent graph before accepting a newly issued assignment', () => { + state.graph = {}; + expect(() => inspectLiveAssignmentProfile(state.item, state.item.teamId, [state.item], state.frozen)).toThrow('ACCEPTANCE_LIVE_GRAPH'); +}); + +it('retains a legitimate earlier completed attempt when independently verified request-changes history advances its graph node', () => { + const f = reviewedRevisionFixture(), review = f.assignments[1]!, decision = reviewedDecision(f); + decision.disposition = 'request-changes'; + const before = structuredClone(f), decisions = new Map([[review.id, decision]]); + expect(() => verifyLiveAssignmentGraph(f.assignments[0]!, f.graph, f.assignments, decisions)).not.toThrow(); + expect(f).toEqual(before); + expect(() => verifyLiveAssignmentGraph(f.assignments[0]!, f.graph, f.assignments, new Map())).toThrow('ACCEPTANCE_LIVE_GRAPH'); + for (const change of [ + (value: Record) => { value.disposition = 'approved'; }, + (value: Record) => { value.subjectRef.commit = 'f'.repeat(40); }, + (value: Record) => { value.authorityRefs = []; }, + (value: Record) => { value.decidedByRefs = []; }, + (value: Record) => { value.decidedAt = 'invalid'; }, + ]) { + const changed = structuredClone(decision); change(changed); const retained = structuredClone(changed); + expect(() => verifyLiveAssignmentGraph(f.assignments[0]!, f.graph, f.assignments, new Map([[review.id, changed]]))).toThrow(); + expect(changed).toEqual(retained); expect(f).toEqual(before); + } + const skipped = structuredClone(f.graph); skipped.nodes[0]!.nodeRevision++; + expect(() => verifyLiveAssignmentGraph(f.assignments[0]!, skipped, f.assignments, decisions)).toThrow('ACCEPTANCE_LIVE_GRAPH'); + expect(() => verifyLiveAssignmentGraph(f.assignments[0]!, f.graph, f.assignments, decisions)).not.toThrow(); +}); + +it('denies a Git workspace base absent from original exact context before accepting matching assignment views', () => { + state.item.assignmentAttempt.workspace.baseCommit = 'f'.repeat(40); + expect(() => inspectLiveAssignmentProfile(state.item, state.item.teamId, [state.item], state.frozen)).toThrow('ACCEPTANCE_INTEGRATION_BASE'); +}); + +it('denies a foreign simulation branch before accepting matching assignment views', () => { + state.item.assignmentAttempt.workspace.branch = 'simulation/foreign-campaign/workday/assignment'; + expect(() => inspectLiveAssignmentProfile(state.item, state.item.teamId, [state.item], state.frozen)).toThrow('ACCEPTANCE_LIVE_WORKSPACE'); +}); + +it('denies writable paths outside the original simulation workspace contract', () => { + state.item.assignmentAttempt.workspace.writablePaths = ['docs']; + expect(() => inspectLiveAssignmentProfile(state.item, state.item.teamId, [state.item], state.frozen)).toThrow('ACCEPTANCE_LIVE_WORKSPACE'); +}); + +it('retains exact simulation workspace authority and rejects malformed or substituted branch scope without rewriting observations', () => { + const original = structuredClone(state); + verifyLiveSimulationWorkspace(state.item, state.run); expect(state).toEqual(original); + for (const field of ['branch', 'writablePaths']) for (const value of [undefined, null, '', [], ['docs'], ['.', '.'], 'simulation/foreign']) { + state.item = structuredClone(original.item); state.item.assignmentAttempt.workspace[field] = value; + const denied = structuredClone(state.item); + expect(() => verifyLiveSimulationWorkspace(state.item, state.run)).toThrow(); expect(state.item).toEqual(denied); + } + state.item = structuredClone(original.item); + for (const value of [undefined, '', 'foreign-workday']) { + const run = { ...state.run, id: value }; expect(() => verifyLiveSimulationWorkspace(state.item, run)).toThrow('ACCEPTANCE_LIVE_WORKSPACE'); + } + verifyLiveSimulationWorkspace(state.item, state.run); expect(state).toEqual(original); +}); + +it('retains original provider publication when a later session opens after the observed assignment was issued', () => { + const original = liveProviderSession(state.item), later = structuredClone(original); + later.id = 'later-runtime-session'; later.openedAt = new Date(Date.parse(String(original.openedAt)) + 1000).toISOString(); later.refreshedAt = later.openedAt; + const observations = [later, original], before = structuredClone(observations); + expect(() => verifyLiveProviderRuntime(state.item, observations)).not.toThrow(); + expect(observations).toEqual(before); +}); + +it('denies a self-consistent foreign Git base and context against the original frozen source', () => { + const original = structuredClone(state.item.assignmentAttempt.contextRefs.find((ref: Record) => ref.store === 'git')); + state.item.assignmentAttempt.workspace.baseCommit = 'f'.repeat(40); + for (const ref of state.item.assignmentAttempt.contextRefs) if (ref.store === 'git') ref.commit = 'f'.repeat(40); + expect(() => inspectLiveAssignmentProfile(state.item, state.item.teamId, [state.item], { [original.repository]: original.commit })) + .toThrow('ACCEPTANCE_LIVE_SOURCE_CONTEXT'); +}); + +it('requires every frozen source and exact granted content while retaining original context and rejecting unissued commits', () => { + const original = structuredClone(state), base = Object.values(state.frozen)[0]; + verifyLiveSourceContext(state.item, [state.item], state.frozen); expect(state).toEqual(original); + for (const frozen of [{}, { 'treeseed-ai/sdk': '' }, { 'treeseed-ai/sdk': 'staging' }, { 'treeseed-ai/sdk': 'f'.repeat(40) }]) + expect(() => verifyLiveSourceContext(state.item, [state.item], frozen)).toThrow('ACCEPTANCE_LIVE_SOURCE_CONTEXT'); + for (const commit of [undefined, '', 'staging', 'f'.repeat(40)]) { + const item = structuredClone(state.item); item.assignmentAttempt.contextRefs[0].commit = commit; + const before = structuredClone(item); expect(() => verifyLiveSourceContext(item, [item], state.frozen)).toThrow(); expect(item).toEqual(before); + } + const extra = structuredClone(state.item); + extra.assignmentAttempt.contextRefs.push({ ...extra.assignmentAttempt.contextRefs[0], id: 'foreign-source', commit: 'f'.repeat(40) }); + expect(() => verifyLiveSourceContext(extra, [extra], state.frozen)).toThrow('ACCEPTANCE_LIVE_SOURCE_CONTEXT'); + const missing = structuredClone(state.item), reference = { store: 'treedx', model: 'proposal', id: 'governed-input', repository: 'native-library', commit: base, path: 'proposals/input.md' }; + missing.assignmentAttempt.grant.contentRead.push(reference); + expect(() => verifyLiveSourceContext(missing, [missing], state.frozen)).toThrow('ACCEPTANCE_LIVE_SOURCE_CONTEXT'); + missing.assignmentAttempt.contextRefs.push(reference); + expect(() => verifyLiveSourceContext(missing, [missing], state.frozen)).not.toThrow(); expect(state).toEqual(original); +}); + +it('denies a missing owning allocation receipt before allowing a newly observed assignment to continue', () => { + delete state.item.explanation.metadata; + expect(() => inspectLiveAssignmentProfile(state.item, state.item.teamId, [state.item], state.frozen)).toThrow('ACCEPTANCE_ALLOCATION_RECEIPT'); +}); + +it('denies a predecessor result absent from the complete owning assignment inventory before campaign progress', () => { + state.item.assignmentAttempt.predecessorResultIds = ['missing-original-result']; + expect(() => inspectLiveAssignmentProfile(state.item, state.item.teamId, [state.item], state.frozen)).toThrow('ACCEPTANCE_PREDECESSOR_MISSING'); +}); + +it('denies absent independent provider publication rather than trusting the runtime build repeated in assignment views', () => { + state.publications = []; + expect(() => inspectLiveAssignmentProfile(state.item, state.item.teamId, [state.item], state.frozen)).toThrow(/ACCEPTANCE_(AVAILABILITY|LIVE_RUNTIME)/u); +}); + +it('requires the exact independently published provider adapter build model and canonical limits while retaining unrelated histories', () => { + const original = structuredClone(state.publications), item = structuredClone(state.item); + verifyLiveProviderRuntime(item, original); expect(state.publications).toEqual(original); + for (const change of [ + (value: Record) => { value.providerId = 'foreign-provider'; }, + (value: Record) => { value.teamId = 'foreign-team'; }, + (value: Record) => { value.status = 'expired'; }, + (value: Record) => { value.sequence = 0; }, + (value: Record) => { value.openedAt = 'invalid'; }, + (value: Record) => { value.openedAt = new Date(Date.parse(item.assignmentAttempt.createdAt) + 1).toISOString(); }, + (value: Record) => { value.refreshedAt = 'invalid'; }, + (value: Record) => { value.snapshot = {}; }, + (value: Record) => { value.snapshot.adapters = []; }, + (value: Record) => { value.snapshot.adapters.push(structuredClone(value.snapshot.adapters[0])); }, + (value: Record) => { value.snapshot.adapters[0].id = 'foreign-adapter'; }, + (value: Record) => { value.snapshot.adapters[0].runtimeBuild = `sha256:${'f'.repeat(64)}`; }, + (value: Record) => { value.snapshot.adapters[0].nativeLimits = {}; }, + (value: Record) => { value.snapshot.adapters[0].nativeLimits.modelConfigurationId = 'foreign-model'; }, + ]) { + const values = structuredClone(original); change(values[0]!); const before = structuredClone(values); + expect(() => verifyLiveProviderRuntime(item, values)).toThrow('ACCEPTANCE_LIVE_RUNTIME'); expect(values).toEqual(before); + } + const unrelated = { ...original[0], id: 'unrelated-session', providerId: 'unrelated-provider' }; + const values = [unrelated, ...structuredClone(original)], before = structuredClone(values); + verifyLiveProviderRuntime(item, values); expect(values).toEqual(before); expect(state.item).toEqual(item); +}); + +it('denies foreign source estimate node and graph revisions rather than accepting internally matching public assignment views', () => { + const original = structuredClone(state.graph); + for (const change of [ + () => { state.graph.teamId = 'foreign-team'; }, + () => { state.graph.revision = 0; }, + () => { state.graph.nodes = []; }, + () => { state.graph.nodes[0].nodeRevision++; }, + () => { state.graph.nodes[0].sourceRef.id = 'foreign-proposal'; }, + () => { state.graph.nodes[0].estimate.maximumSeconds++; }, + ]) { + state.graph = structuredClone(original); change(); + const before = structuredClone(state); + expect.soft(() => inspectLiveAssignmentProfile(state.item, state.item.teamId, [state.item], state.frozen)).toThrow('ACCEPTANCE_LIVE_GRAPH'); + expect(state).toEqual(before); + } +}); diff --git a/tests/modern/kernel/architecture/custody/graph/review-readiness-fixture.ts b/tests/modern/kernel/architecture/custody/graph/review-readiness-fixture.ts index 96705738..0f7874bf 100644 --- a/tests/modern/kernel/architecture/custody/graph/review-readiness-fixture.ts +++ b/tests/modern/kernel/architecture/custody/graph/review-readiness-fixture.ts @@ -35,8 +35,8 @@ export function reviewedGraphFixture(candidateStore: 'git' | 'treedx' = 'git') { id: item.executionNodeId, teamId: base.teamId, projectId: base.projectId, workItemId: item.assignmentAttempt.workItemId, kind: item.executionNodeId === 'review' ? 'reviewing' : 'acting', pairRole: item.executionNodeId === 'review' ? 'reviewer' : 'actor', sourceRef: base.sourceRef, authorityRefs: base.authorityRefs, ruleRevision: 1, nodeRevision: 1, agentClass: item.assignmentAttempt.agentClass, - status: 'completed', estimate: base.estimate, requiredCapabilities: [], requestedPermissions: base.effectiveProfile.permissionCeiling, - workspace: 'git', acceptanceCriteria: base.acceptanceCriteria, maximumReviewCycles: 2, graphRevisionCreated: 1, graphRevisionUpdated: 1 })); + status: 'completed', estimate: base.estimate, requiredCapabilities: [], requestedPermissions: item.assignmentAttempt.effectiveProfile.permissionCeiling, + workspace: item.assignmentAttempt.workspace.mode, acceptanceCriteria: base.acceptanceCriteria, maximumReviewCycles: 2, graphRevisionCreated: 1, graphRevisionUpdated: 1 })); const edges = [['paired', 'actor', 'review', 'review-pair'], ['dependent', 'review', 'next', 'work-item']].map(([id, fromNodeId, toNodeId, provenance]) => executionEdgeSchema.parse({ schemaVersion: 'treeseed.execution-edge/v1', id, teamId: base.teamId, fromNodeId, toNodeId, provenance, sourceRef: base.sourceRef, graphRevisionCreated: 1 })); diff --git a/tests/modern/kernel/architecture/custody/graph/review-readiness-native.test.ts b/tests/modern/kernel/architecture/custody/graph/review-readiness-native.test.ts index 97a68d72..e33abe61 100644 --- a/tests/modern/kernel/architecture/custody/graph/review-readiness-native.test.ts +++ b/tests/modern/kernel/architecture/custody/graph/review-readiness-native.test.ts @@ -10,7 +10,8 @@ import { ControlPlaneClient, defaultLocalControlPlaneServer } from '@treeseed/sd import { controlPlaneOperation } from '@treeseed/sdk/operator-contracts'; import { row } from '../../../../../acceptance/acceptance-cli.ts'; import { verifyPredecessorCustody } from '../../../../../acceptance/workday/support/evidence-pages.ts'; -import { verifyApprovedWorkReview, verifyReviewCandidateAuthority } from '../../../../../acceptance/workday/support/decision-evidence.ts'; +import { verifyLiveAssignmentGraph } from '../../../../../acceptance/workday/support/monitoring/live-assignment-records.ts'; +import { verifyWorkReview, verifyReviewCandidateAuthority } from '../../../../../acceptance/workday/support/decision-evidence.ts'; import { verifyAssignmentWorkspaceInventory } from '../../../../../acceptance/workday/support/assignment-authority.ts'; import { contentReviewFailures, mutateContentReview, mutateReadiness, mutateReview, mutateReviewGrant, readinessFailures, reviewedDecision, reviewedGraphFixture, reviewedRevisionFixture, reviewFailures, reviewGrantFailures } from './review-readiness-fixture.ts'; @@ -102,6 +103,7 @@ it('native committed review sources and public SDK graph assignments deny missin try { verifyAssignmentWorkspaceInventory(assignments); const pairs = verifyPredecessorCustody(assignments, graph); + const decisions = new Map>(); for (const review of assignments.filter(item => row(row(item.lifecycleOutput).activityCompletion).reviewDisposition === 'request-changes')) { const ids = row(review.assignmentAttempt).predecessorResultIds; assert.ok(Array.isArray(ids)); const actors = assignments.filter(item => row(row(item.assignmentAttempt).effectiveProfile).activity === 'acting' && ids.includes(row(item.assignmentResult).id)); @@ -115,8 +117,9 @@ it('native committed review sources and public SDK graph assignments deny missin const file = row(observed.files[0]), original = execFileSync('git', ['show', `${reference.commit}:${reference.path}`], { cwd: root, encoding: 'utf8' }); assert.equal(file.content, original); const content = row(parse(original.match(/^---\n([\s\S]*?)\n---/u)![1]!)); assert.deepEqual(file.frontmatter, content); - if (row(row(review.lifecycleOutput).activityCompletion).reviewDisposition === 'approved') verifyApprovedWorkReview(review, actor, content); - else { assert.equal(content.disposition, 'request-changes'); verifyReviewCandidateAuthority(review, content); } + if (row(row(review.lifecycleOutput).activityCompletion).reviewDisposition === 'approved') verifyWorkReview(review, actor, content); + else { assert.equal(content.disposition, 'request-changes'); verifyWorkReview(review, actor, content, 'request-changes'); } + decisions.set(String(review.id), content); const subject = row(content.subjectRef); if (subject.store === 'git') assert.equal(git('show', `${subject.commit}:candidate.txt`), 'Controlled original candidate bytes'); else { @@ -126,6 +129,7 @@ it('native committed review sources and public SDK graph assignments deny missin assert.deepEqual(candidateRead.files, [{ path: subject.path, content: 'Controlled original candidate bytes\n' }]); } } + for (const item of assignments) verifyLiveAssignmentGraph(item, graph, assignments, decisions); } finally { expect({ graph, assignments }).toEqual(before); } }; await Promise.all([observe(), observe()]); diff --git a/tests/modern/kernel/architecture/custody/graph/review-readiness.test.ts b/tests/modern/kernel/architecture/custody/graph/review-readiness.test.ts index 6d19e973..383e62ae 100644 --- a/tests/modern/kernel/architecture/custody/graph/review-readiness.test.ts +++ b/tests/modern/kernel/architecture/custody/graph/review-readiness.test.ts @@ -1,7 +1,7 @@ import { expect, it } from 'vitest'; import { gate, state } from '../../golden-readback-fixture.ts'; import { verifyPredecessorCustody } from '../../../../../acceptance/workday/support/evidence-pages.ts'; -import { verifyApprovedWorkReview, verifyReviewCandidateAuthority } from '../../../../../acceptance/workday/support/decision-evidence.ts'; +import { verifyWorkReview, verifyReviewCandidateAuthority } from '../../../../../acceptance/workday/support/decision-evidence.ts'; import { contentReviewFailures, mutateContentReview, mutateReadiness, mutateReview, mutateReviewGrant, readinessFailures, reviewedDecision, reviewedGraphFixture, reviewedRevisionFixture, reviewFailures, reviewGrantFailures } from './review-readiness-fixture.ts'; it('denies missing or contradictory original work-review bytes even when parsed approval views agree', () => { @@ -26,21 +26,21 @@ it('denies every omitted contradictory unfinished ambiguous or non-approving req it('requires the exact consumed Actor candidate original authority assigned Reviewer and measured approval interval', () => { const f = reviewedGraphFixture(), decision = reviewedDecision(f), held = structuredClone({ f, decision }); - expect(() => verifyApprovedWorkReview(f.assignments[1]!, f.assignments[0]!, decision)).not.toThrow(); + expect(() => verifyWorkReview(f.assignments[1]!, f.assignments[0]!, decision)).not.toThrow(); for (const mode of reviewFailures) { const changed = structuredClone(decision); mutateReview(changed, mode); const before = structuredClone(changed); - expect(() => verifyApprovedWorkReview(f.assignments[1]!, f.assignments[0]!, changed), mode).toThrow(); expect(changed).toEqual(before); + expect(() => verifyWorkReview(f.assignments[1]!, f.assignments[0]!, changed), mode).toThrow(); expect(changed).toEqual(before); } for (const mode of reviewGrantFailures) { const changed = reviewedGraphFixture(); mutateReviewGrant(changed, mode); const before = structuredClone(changed); - expect(() => verifyApprovedWorkReview(changed.assignments[1]!, changed.assignments[0]!, reviewedDecision(changed)), mode).toThrow(); + expect(() => verifyWorkReview(changed.assignments[1]!, changed.assignments[0]!, reviewedDecision(changed)), mode).toThrow(); expect(changed).toEqual(before); } const content = reviewedGraphFixture('treedx'); - expect(() => verifyApprovedWorkReview(content.assignments[1]!, content.assignments[0]!, reviewedDecision(content))).not.toThrow(); + expect(() => verifyWorkReview(content.assignments[1]!, content.assignments[0]!, reviewedDecision(content))).not.toThrow(); for (const mode of contentReviewFailures) { const changed = reviewedGraphFixture('treedx'); mutateContentReview(changed, mode); const before = structuredClone(changed); - expect(() => verifyApprovedWorkReview(changed.assignments[1]!, changed.assignments[0]!, reviewedDecision(changed)), mode).toThrow(); + expect(() => verifyWorkReview(changed.assignments[1]!, changed.assignments[0]!, reviewedDecision(changed)), mode).toThrow(); expect(changed).toEqual(before); } expect({ f, decision }).toEqual(held); diff --git a/tests/modern/kernel/architecture/custody/profile-custody-readback.test.ts b/tests/modern/kernel/architecture/custody/profile-custody-readback.test.ts index 06531dc3..c1ea86d9 100644 --- a/tests/modern/kernel/architecture/custody/profile-custody-readback.test.ts +++ b/tests/modern/kernel/architecture/custody/profile-custody-readback.test.ts @@ -1,13 +1,14 @@ import { createHash } from 'node:crypto'; import { stringify } from 'yaml'; import { describe, expect, it, vi } from 'vitest'; +import { simulationSourceBranch } from '@treeseed/sdk/capacity-provider/sandbox'; import { assignmentAttemptSchema } from '@treeseed/sdk/agent-capacity'; import { state } from '../golden-readback-fixture.ts'; import { request } from '../../provider-kernel-fixture.ts'; import { portableProfile } from '../portable/portable-kernel-fixture.ts'; import { verifyGovernedProfile, verifyHandlerInspection } from '../../../../acceptance/workday/support/assignment-authority.ts'; import { inspectLiveAssignmentProfile } from '../../../../acceptance/workday/support/monitoring/live-assignment-records.ts'; -import { liveAssignmentRecord } from '../../../guarantees/monitoring/live-assignment-fixture.ts'; +import { liveAssignmentGraph, liveAssignmentRecord, liveProviderSession, liveRun } from '../../../guarantees/monitoring/live-assignment-fixture.ts'; import { profileOrderFixture, profileOrderFailures, mutateProfileOrder, grantRequestCases, grantRequestFixture } from './graph/profile-order-fixture.ts'; await import('../../../../acceptance/workday/profile-custody.test.ts'); @@ -50,27 +51,33 @@ describe('independent governed profile assertion contract', () => { }); it('live profile inspection reads the independent canonical assignment exact governed bytes and public handler boundary before accepting a new observation', () => { const f = input(), item = liveAssignmentRecord(f.item.assignmentAttempt), attempt = f.item.assignmentAttempt, profile = attempt.effectiveProfile; + const liveAttempt = assignmentAttemptSchema.parse(item.assignmentAttempt); item.assignmentAttempt = liveAttempt; + Object.assign(liveAttempt.workspace, { branch: simulationSourceBranch('local', attempt.workdayId, attempt.id), writablePaths: ['.'] }); const handler = { id: profile.handler, origin: profile.handlerOrigin }; state.replies.set('assignments show', item); + state.replies.set('execution graph', liveAssignmentGraph(item)); + state.replies.set('workdays show', { run: liveRun(item) }); + state.replies.set('capacity status', { items: [liveProviderSession(item)], page: { limit: 100, hasMore: false, nextCursor: null } }); state.replies.set(`library read ${f.file.path}`, { result: { resolvedRef: profile.profileRef.commit, files: [f.file] } }); state.replies.set('agents handlers --project', { projectId: attempt.projectId, handlers: [handler] }); state.replies.set(`agents handlers ${profile.handler}`, { projectId: attempt.projectId, handler }); - const before = structuredClone([...state.replies]); inspectLiveAssignmentProfile(item, attempt.teamId); + const before = structuredClone([...state.replies]); inspectLiveAssignmentProfile(item, attempt.teamId, [item], Object.fromEntries(attempt.contextRefs.filter(ref => ref.store === 'git').map(ref => [ref.repository!, ref.commit]))); expect(state.calls.map(args => args.slice(0, 3))).toEqual([ - ['assignments', 'show', attempt.id], ['library', 'read', attempt.projectId], - ['agents', 'handlers', 'list'], ['agents', 'handlers', 'show'], + ['assignments', 'show', attempt.id], ['execution', 'graph', 'show'], ['workdays', 'show', attempt.workdayId], ['capacity', 'status', '--limit'], ['library', 'read', attempt.projectId], + ['agents', 'handlers', 'list'], ['agents', 'handlers', 'show'], ['execution', 'graph', 'show'], ]); expect(state.calls[0]).toContain(attempt.teamId); - for (const args of state.calls.slice(1)) expect(args).not.toContain('--team'); + for (const args of state.calls.filter(args => ['library', 'agents'].includes(args[0]!))) expect(args).not.toContain('--team'); + for (const args of state.calls.filter(args => args[0] === 'execution')) expect(args).toContain(attempt.teamId); expect([...state.replies]).toEqual(before); }); it('live profile inspection denies an independently substituted runtime or failed public read without repairing the original attempt', () => { const f = input(), item = liveAssignmentRecord(f.item.assignmentAttempt), shown = structuredClone(item), before = structuredClone(item); const attempt = shown.assignmentAttempt as Record, provider = attempt.provider as Record; provider.runtimeBuild = `sha256:${'f'.repeat(64)}`; state.replies.set('assignments show', shown); - expect(() => inspectLiveAssignmentProfile(item, f.item.assignmentAttempt.teamId)).toThrow('Independent public view changed issued authority'); + expect(() => inspectLiveAssignmentProfile(item, f.item.assignmentAttempt.teamId, [item], {})).toThrow('Independent public view changed issued authority'); state.failure = new Error('controlled_denial'); - expect(() => inspectLiveAssignmentProfile(item, f.item.assignmentAttempt.teamId)).toThrow('ACCEPTANCE_CLI_COMMAND'); + expect(() => inspectLiveAssignmentProfile(item, f.item.assignmentAttempt.teamId, [item], {})).toThrow('ACCEPTANCE_CLI_COMMAND'); expect(item).toEqual(before); }); it('native handler readback composition uses exact project and server without attaching an unsupported team option', () => {