From 1ca854ffb9a8e059bd0768376b4d2faf6111e055 Mon Sep 17 00:00:00 2001 From: Kurt Overmier Date: Fri, 17 Apr 2026 07:51:59 -0500 Subject: [PATCH 1/2] fix(gateway): unwrap receipt envelope in flow_* proxy (closes #37) `proxyRestToolCall` read receipt fields (`governance`, `createdAt`, facts) directly off the tarotscript response root. The /run worker returns an envelope: `{ verified, receipt: { hash, createdAt, governance, facts, ... } }`. So every field read resolved to undefined and the flow_quality / flow_governance / flow_status branches silently returned `{ hash }`-only echoes. Fix: unwrap once and read from `envelope.receipt`. flow_quality and flow_governance now return real governance posture + quality signals (confidence, shadow_density, position_count). flow_status surfaces verified + createdAt. flow_summary returns the full envelope so callers can see verification state alongside the receipt. Regression tests cover the three read paths against a fixture that mirrors the production envelope shape (engine#23 / tarotscript#199). Co-Authored-By: Claude Opus 4.7 (1M context) --- src/gateway.ts | 26 ++++++++++++++++----- test/gateway.test.ts | 55 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 74 insertions(+), 7 deletions(-) diff --git a/src/gateway.ts b/src/gateway.ts index 81f2a2f..55e06a0 100644 --- a/src/gateway.ts +++ b/src/gateway.ts @@ -236,24 +236,38 @@ async function proxyRestToolCall( return { content: [{ type: 'text', text: `Receipt lookup failed: HTTP ${response.status}` }], isError: true }; } - const receipt = await response.json() as Record; + // Worker envelope: { verified, receipt: { hash, createdAt, governance, facts, ... } } + // Earlier code read these fields off the envelope root (receipt.governance), + // silently getting undefined and returning `{ hash }`-only echoes. + // tarotscript#199 sibling: engine#23. + const envelope = await response.json() as { + verified?: boolean; + receipt?: Record; + }; + const r = envelope.receipt ?? {}; // Return subset based on which flow tool was called if (toolName === 'flow_status') { return { content: [{ type: 'text', text: JSON.stringify({ - verified: receipt.verified, - createdAt: receipt.createdAt, + verified: envelope.verified ?? false, + createdAt: r.createdAt, hash, }, null, 2) }] }; } if (toolName === 'flow_quality' || toolName === 'flow_governance') { + const facts = (r.facts ?? {}) as Record; return { content: [{ type: 'text', text: JSON.stringify({ - governance: receipt.governance, + governance: r.governance ?? null, + quality: { + confidence: facts.scaffold_confidence ?? null, + shadow_density: facts.shadow_density ?? null, + position_count: facts.position_count ?? null, + }, hash, }, null, 2) }] }; } - // flow_summary — full receipt - return { content: [{ type: 'text', text: JSON.stringify(receipt, null, 2) }] }; + // flow_summary — full envelope (verified + receipt) + return { content: [{ type: 'text', text: JSON.stringify(envelope, null, 2) }] }; } if (toolName === 'scaffold_status') { diff --git a/test/gateway.test.ts b/test/gateway.test.ts index 34f36a4..bb776a3 100644 --- a/test/gateway.test.ts +++ b/test/gateway.test.ts @@ -267,7 +267,8 @@ describe('handleMcpRequest', () => { tenantId: req.headers.get('X-Gateway-Tenant-Id') ?? '', }; return new Response(JSON.stringify({ - verified: true, createdAt: '2026-04-03', + verified: true, + receipt: { hash: 'abc', createdAt: '2026-04-03' }, }), { headers: { 'Content-Type': 'application/json' } }); }, connect: () => { throw new Error('not implemented'); }, @@ -280,6 +281,58 @@ describe('handleMcpRequest', () => { expect(capturedHeaders.tenantId).toBe('tenant-1'); }); + + // Regression: tarotscript#199 sibling / engine#23. + // Gateway previously read receipt fields off the envelope root, silently + // returning `{ hash }`-only echoes when nothing else happened to live there. + it('flow_governance unwraps the worker envelope (engine#23)', async () => { + const env = makeEnv({ + TAROTSCRIPT: { + fetch: async () => new Response(JSON.stringify({ + verified: true, + receipt: { + hash: 'abc', + createdAt: '2026-04-03', + governance: { determinism: 'locked', capped: true }, + facts: { scaffold_confidence: 'high', shadow_density: 0.2, position_count: 6 }, + }, + }), { headers: { 'Content-Type': 'application/json' } }), + connect: () => { throw new Error('not implemented'); }, + } as unknown as Fetcher, + }); + + const sessionId = await getSession(env); + const req = rpcRequest('tools/call', { name: 'flow_governance', arguments: { hash: 'abc' } }, { 'MCP-Session-Id': sessionId }); + const res = await handleMcpRequest(req, env); + const body = await res.json() as any; + const payload = JSON.parse(body.result.content[0].text); + + expect(payload.governance).toEqual({ determinism: 'locked', capped: true }); + expect(payload.quality.confidence).toBe('high'); + expect(payload.hash).toBe('abc'); + }); + + it('flow_status surfaces createdAt from the receipt envelope (engine#23)', async () => { + const env = makeEnv({ + TAROTSCRIPT: { + fetch: async () => new Response(JSON.stringify({ + verified: true, + receipt: { hash: 'abc', createdAt: '2026-04-17T00:00:00Z' }, + }), { headers: { 'Content-Type': 'application/json' } }), + connect: () => { throw new Error('not implemented'); }, + } as unknown as Fetcher, + }); + + const sessionId = await getSession(env); + const req = rpcRequest('tools/call', { name: 'flow_status', arguments: { hash: 'abc' } }, { 'MCP-Session-Id': sessionId }); + const res = await handleMcpRequest(req, env); + const body = await res.json() as any; + const payload = JSON.parse(body.result.content[0].text); + + expect(payload.verified).toBe(true); + expect(payload.createdAt).toBe('2026-04-17T00:00:00Z'); + expect(payload.hash).toBe('abc'); + }); }); describe('security', () => { From 4d5890e389c3efb93e6b2a2921be9b7f6489e72e Mon Sep 17 00:00:00 2001 From: Kurt Overmier Date: Fri, 17 Apr 2026 08:03:29 -0500 Subject: [PATCH 2/2] chore: scrub private-repo cross-refs from code comments and test names Remove references to internal tracking identifiers from a public-OSS artifact. Descriptive prose that documents the regression guard stays; the private-repo identifiers that named the specific tracking tickets do not. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/gateway.ts | 3 --- test/gateway.test.ts | 9 ++++----- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/gateway.ts b/src/gateway.ts index 55e06a0..5c5f2b3 100644 --- a/src/gateway.ts +++ b/src/gateway.ts @@ -237,9 +237,6 @@ async function proxyRestToolCall( } // Worker envelope: { verified, receipt: { hash, createdAt, governance, facts, ... } } - // Earlier code read these fields off the envelope root (receipt.governance), - // silently getting undefined and returning `{ hash }`-only echoes. - // tarotscript#199 sibling: engine#23. const envelope = await response.json() as { verified?: boolean; receipt?: Record; diff --git a/test/gateway.test.ts b/test/gateway.test.ts index bb776a3..0516551 100644 --- a/test/gateway.test.ts +++ b/test/gateway.test.ts @@ -282,10 +282,9 @@ describe('handleMcpRequest', () => { expect(capturedHeaders.tenantId).toBe('tenant-1'); }); - // Regression: tarotscript#199 sibling / engine#23. - // Gateway previously read receipt fields off the envelope root, silently - // returning `{ hash }`-only echoes when nothing else happened to live there. - it('flow_governance unwraps the worker envelope (engine#23)', async () => { + // Regression: gateway previously read receipt fields off the envelope root, + // silently returning `{ hash }`-only echoes when nothing happened to live there. + it('flow_governance unwraps the worker envelope', async () => { const env = makeEnv({ TAROTSCRIPT: { fetch: async () => new Response(JSON.stringify({ @@ -312,7 +311,7 @@ describe('handleMcpRequest', () => { expect(payload.hash).toBe('abc'); }); - it('flow_status surfaces createdAt from the receipt envelope (engine#23)', async () => { + it('flow_status surfaces createdAt from the receipt envelope', async () => { const env = makeEnv({ TAROTSCRIPT: { fetch: async () => new Response(JSON.stringify({