Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/web-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -710,17 +710,17 @@ jobs:
if (
report.testResults?.length !== 1
|| result?.status !== 'passed'
|| result?.assertionResults?.length !== 1
|| report.numTotalTests !== 1
|| report.numPassedTests !== 1
|| result?.assertionResults?.length !== 2
|| report.numTotalTests !== 2
|| report.numPassedTests !== 2
|| report.numFailedTests !== 0
|| report.numPendingTests !== 0
|| report.numTodoTests !== 0
|| report.numFailedTestSuites !== 0
|| report.numPendingTestSuites !== 0
|| markerCount !== 1
) {
throw new Error('The mandatory S5 PostgreSQL proof must run exactly one file and one passing test with one terminal marker and no skips.')
throw new Error('The mandatory S5 PostgreSQL proof must run exactly one file, two passing tests, one terminal marker, and no skips.')
}
NODE
- name: Remove the disposable S5 HTTP authorization proof database
Expand Down
64 changes: 55 additions & 9 deletions web/__tests__/epic-172-s5-postgres-routes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -420,16 +420,62 @@ run('S5 real PostgreSQL HTTP authorization boundary', () => {
request(ownerCredential, `/api/mcps/terminal-state/${ids.ownerTask}`),
{ params: Promise.resolve({ taskId: ids.ownerTask }) },
)
expect(await terminalState.json()).toMatchObject({
terminalPackages: [{
runtimeAuditId: ids.ownerAudit,
workPackageId: ids.ownerPackage,
state: 'terminal',
deliveryOutcome: 'submitted',
terminalOutcome: 'succeeded',
}],
})
expect(await terminalState.json()).toMatchObject({ terminalPackages: [] })

console.log('S5_POSTGRES_HTTP_AUTHORIZATION_OK')
})

it('keeps one real PostgreSQL observation across a committed ordinary and protected transition', async () => {
const { readS5AuthoritativeTaskState } = await import('@/lib/mcps/s5-server-reader')
let transitionCommitted = false
const transitionedTerminalAt = '2099-01-01T00:00:00.000Z'
const before = await readS5AuthoritativeTaskState(ids.ownerTask, ids.owner, async () => {
await admin.begin(async (tx) => {
await tx`
update tasks set status = 'failed', updated_at = clock_timestamp()
where id = ${ids.ownerTask}::uuid
`
await tx`
update work_packages set status = 'failed', updated_at = clock_timestamp()
where id = ${ids.ownerPackage}::uuid
`
await tx`
update work_package_local_run_evidence
set terminal = '{"status":"failed"}'::jsonb, terminal_at = ${transitionedTerminalAt}::timestamptz
where id = ${ids.ownerEvidence}::uuid
`
await tx`
update filesystem_mcp_runtime_audits
set status = 'failed',
assembly = '{"state":"not_assembled","failureStage":"preflight"}'::jsonb,
delivery = '{"state":"not_exposed"}'::jsonb,
terminal = '{"status":"failed","failureCode":"preflight_failed"}'::jsonb,
terminal_at = ${transitionedTerminalAt}::timestamptz
where id = ${ids.ownerAudit}::uuid
`
})
transitionCommitted = true
})
expect(transitionCommitted).toBe(true)
// The exporter and the least-privilege reader both retain the old state;
// current terminal authority remains the blocked package, not its old audit.
expect(before.taskStatus).toBe('approved')
expect(before.localEvidenceAvailable).toBe(true)
expect(before.packages).toMatchObject([{ workPackageId: ids.ownerPackage, status: 'blocked' }])
expect(before.terminalPackages).toEqual([])
const beforeEvidence = before.evidenceRecords.find((evidence) => evidence.id === ids.ownerEvidence)
expect(beforeEvidence?.terminalAt).not.toBe(transitionedTerminalAt)

const after = await readS5AuthoritativeTaskState(ids.ownerTask, ids.owner)
expect(after.taskStatus).toBe('failed')
expect(after.packages).toMatchObject([{ workPackageId: ids.ownerPackage, status: 'failed' }])
expect(after.terminalPackages).toMatchObject([{
runtimeAuditId: ids.ownerAudit, workPackageId: ids.ownerPackage,
state: 'terminal', terminalOutcome: 'failed',
}])
expect(after.localEvidenceAvailable).toBe(true)
expect(after.evidenceRecords.find((evidence) => evidence.id === ids.ownerEvidence)?.terminalAt)
.toBe(transitionedTerminalAt)
expect(before.freshnessFingerprint).not.toBe(after.freshnessFingerprint)
})
})
36 changes: 35 additions & 1 deletion web/__tests__/epic-172-s5-protected-reader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ const ENV_NAME = 'FORGE_LOCAL_RUN_EVIDENCE_READER_DATABASE_URL'
const original = process.env[ENV_NAME]

function mockClient(behaviour: () => Promise<unknown>) {
const client = Object.assign(vi.fn(behaviour), { end: vi.fn().mockResolvedValue(undefined) })
const client = Object.assign(vi.fn(behaviour), {
begin: vi.fn(async (_options: unknown, run: (tx: unknown) => Promise<unknown>) => run(client)),
unsafe: vi.fn().mockResolvedValue(undefined),
end: vi.fn().mockResolvedValue(undefined),
})
postgresFactory.mockReturnValue(client)
return client
}
Expand Down Expand Up @@ -138,4 +142,34 @@ describe('S5 protected local run evidence reader', () => {
await expect(readS5ProtectedLocalRunEvidence('task-1')).resolves.toBeNull()
expect(postgresFactory).not.toHaveBeenCalled()
})

it('imports only a validated exported snapshot from the same database', async () => {
process.env[ENV_NAME] = 'postgres://forge_local_evidence_reader@localhost/forge'
const client = mockClient(() => Promise.resolve([{ evidenceRows: [], auditRows: [] }]))
const { readS5ProtectedTerminalSnapshot } = await import('@/lib/mcps/s5-protected-reader')
await expect(readS5ProtectedTerminalSnapshot('task-1', {
snapshotId: '00000003-0000001B-1', databaseUrl: 'postgres://forge_app@localhost/forge',
})).resolves.toEqual({ evidenceRows: [], auditRows: [] })
expect(client.begin).toHaveBeenCalledWith('isolation level repeatable read read only', expect.any(Function))
expect(client.unsafe).toHaveBeenCalledWith("set transaction snapshot '00000003-0000001B-1'")
for (const snapshotId of [
'00000003-0000001B-1', 'FFFFFFFF-00000000-ABCDEF12',
]) {
await expect(readS5ProtectedTerminalSnapshot('task-1', {
snapshotId, databaseUrl: 'postgres://forge_app@localhost/forge',
})).resolves.toEqual({ evidenceRows: [], auditRows: [] })
}
for (const snapshotId of [
'000003A1-1', "00000003-0000001B-1'; select 1; --",
'00000003-0000001B-1 --', ' 00000003-0000001B-1',
'00000003-0000001B-1 ', '00000003_0000001B_1',
]) {
await expect(readS5ProtectedTerminalSnapshot('task-1', {
snapshotId, databaseUrl: 'postgres://forge_app@localhost/forge',
})).resolves.toBeNull()
}
await expect(readS5ProtectedTerminalSnapshot('task-1', {
snapshotId: '00000003-0000001B-1', databaseUrl: 'postgres://forge_app@other-host/forge',
})).resolves.toBeNull()
})
})
88 changes: 88 additions & 0 deletions web/__tests__/epic-172-s5-server-reader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,46 @@ vi.mock('server-only', () => ({ default: {} }))

import {
computeFreshnessFingerprint,
canonicalTaskPresentationProjection,
isS5FreshnessFingerprint,
normalizeS5RecoveryMarkers,
normalizeS5TerminalAudit,
s5EffectiveAdmissionDecision,
} from '@/lib/mcps/s5-server-reader'

describe('S5 authoritative reader identities', () => {
it('joins recovery actions and terminal evidence into one fail-closed task DTO', () => {
const taskId = '00000000-0000-4000-8000-000000000001'
const packageId = '00000000-0000-4000-8000-000000000002'
const auditId = '00000000-0000-4000-8000-000000000003'
const fingerprint = `sha256:${'a'.repeat(64)}`
const state = {
computedAt: '2026-07-30T00:00:00.000Z', observedAtMs: 0, localEvidenceAvailable: true,
taskId, projectId: '00000000-0000-4000-8000-000000000004', taskStatus: 'approved', freshnessFingerprint: fingerprint,
packages: [{
workPackageId: packageId, title: 'Packet package', assignedRole: 'backend', status: 'blocked',
requestedCapabilities: ['filesystem.project.write'], boundedRuntimeRequestedCapabilities: [], blockingCapabilities: [],
currentDecision: null, decisionHistory: [], blockMetadata: null, pointerFingerprint: '', pointerVersion: '0',
}],
projectGrant: null,
recoveryMarkers: [{
workPackageId: packageId, kind: 'packet_issuance' as const, state: 'current' as const, action: 'retry_execution',
allowedActions: ['retry_execution', 'decline_packet_recovery'], evidenceId: auditId, evidenceFingerprint: fingerprint,
}],
terminalPackages: [{
runtimeAuditId: auditId, workPackageId: packageId, state: 'terminal' as const, assemblyState: 'assembled' as const,
deliveryOutcome: 'submitted' as const, terminalOutcome: 'succeeded' as const, terminalAt: '2026-07-30T00:00:00.000Z',
}], evidenceRecords: [],
}

const projection = canonicalTaskPresentationProjection(state)
expect(projection.freshnessFingerprint).toBe(fingerprint)
expect(projection.admission).toEqual([{ workPackageId: packageId, title: 'Packet package', requiresMcp: false, decision: 'unavailable' }])
expect(projection.recoveries[0].actions).toEqual([])
expect(projection.terminals[0]).toMatchObject({ workPackageId: packageId, state: 'terminal', outcome: 'succeeded' })
expect(JSON.stringify(projection)).not.toContain('claimToken')
})

it('canonicalizes nested mutable state independent of object insertion order', () => {
expect(computeFreshnessFingerprint({
task: { status: 'approved', id: 'task-1' },
Expand Down Expand Up @@ -115,4 +149,58 @@ describe('S5 authoritative reader identities', () => {
state: 'unavailable',
})
})

it('keeps recovery actionable when a historical audit is not a current terminal package', () => {
const state = {
computedAt: '2026-07-30T00:00:00.000Z', observedAtMs: 0, localEvidenceAvailable: true,
taskId: 'task', projectId: 'project', taskStatus: 'approved', freshnessFingerprint: `sha256:${'b'.repeat(64)}`,
packages: [{ workPackageId: 'package', title: 'Recovered package', assignedRole: 'backend', status: 'blocked', requestedCapabilities: [], boundedRuntimeRequestedCapabilities: [], blockingCapabilities: [], currentDecision: null, decisionHistory: [], blockMetadata: null, pointerFingerprint: '', pointerVersion: '0' }],
projectGrant: null,
recoveryMarkers: [{ workPackageId: 'package', kind: 'packet_issuance' as const, state: 'current' as const, action: 'retry_execution', allowedActions: ['retry_execution'], evidenceId: 'audit', evidenceFingerprint: `sha256:${'c'.repeat(64)}` }],
terminalPackages: [], evidenceRecords: [],
}
expect(canonicalTaskPresentationProjection(state).recoveries[0]?.actions).toHaveLength(1)
})

it('uses current terminal status and reconciled effective admission rather than package decision history', () => {
const state = {
computedAt: '2026-07-30T00:00:00.000Z', observedAtMs: 0, localEvidenceAvailable: true,
taskId: 'task', projectId: 'project', taskStatus: 'completed', freshnessFingerprint: `sha256:${'d'.repeat(64)}`,
packages: [{ workPackageId: 'package', title: 'Terminal package', assignedRole: 'backend', status: 'completed', requestedCapabilities: ['filesystem.project.read'], boundedRuntimeRequestedCapabilities: ['filesystem.project.read'], blockingCapabilities: [], currentDecision: null, decisionHistory: [], blockMetadata: null, pointerFingerprint: '', pointerVersion: '0', effectiveAdmission: { phase: 'approved' as const, source: 'project-level' as const, status: 'approved' as const, grantMode: 'always_allow' as const, consumed: false, coveredCapabilities: ['filesystem.project.read'], revocationReason: null } }],
projectGrant: null,
recoveryMarkers: [{ workPackageId: 'package', kind: 'packet_issuance' as const, state: 'current' as const, action: 'retry_execution', allowedActions: ['retry_execution'], evidenceId: 'audit', evidenceFingerprint: `sha256:${'e'.repeat(64)}` }],
terminalPackages: [], evidenceRecords: [],
}
const projection = canonicalTaskPresentationProjection(state)
expect(projection.recoveries[0]?.actions).toEqual([])
expect(projection.admission[0]?.decision).toBe('approved')
})

it('uses exact admission coverage for local, project, consumed, and revoked authority', () => {
const packageBase = {
workPackageId: 'package', title: 'Package', assignedRole: 'backend', status: 'blocked',
requestedCapabilities: ['filesystem.project.read', 'filesystem.project.search'],
boundedRuntimeRequestedCapabilities: ['filesystem.project.read', 'filesystem.project.search'],
blockingCapabilities: [], currentDecision: null, decisionHistory: [], blockMetadata: null,
pointerFingerprint: '', pointerVersion: '0',
}
const grant = (overrides: Record<string, unknown>) => ({
phase: 'approved' as const, source: 'package-local' as const, status: 'approved' as const,
grantMode: 'allow_once' as const, consumed: false, coveredCapabilities: ['filesystem.project.read'],
revocationReason: null, ...overrides,
})
expect(s5EffectiveAdmissionDecision({ ...packageBase, effectiveAdmission: grant({}) })).toBe('unavailable')
expect(s5EffectiveAdmissionDecision({ ...packageBase, effectiveAdmission: grant({
coveredCapabilities: ['filesystem.project.read', 'filesystem.project.search'],
}) })).toBe('approved')
expect(s5EffectiveAdmissionDecision({ ...packageBase, effectiveAdmission: grant({
source: 'project-level', grantMode: 'always_allow', coveredCapabilities: ['filesystem.project.read', 'filesystem.project.search'],
}) })).toBe('approved')
expect(s5EffectiveAdmissionDecision({ ...packageBase, effectiveAdmission: grant({
coveredCapabilities: ['filesystem.project.read', 'filesystem.project.search'], consumed: true,
}) })).toBe('unavailable')
expect(s5EffectiveAdmissionDecision({ ...packageBase, effectiveAdmission: grant({
phase: 'revoked', status: 'not_issued', grantMode: null, coveredCapabilities: [], revocationReason: 'project_grant_removed',
}) })).toBe('denied')
})
})
Loading