Skip to content

fix(gateway): unwrap receipt envelope in flow_* proxy (closes #37)#40

Merged
stackbilt-admin merged 2 commits intomainfrom
fix/37-flow-tool-envelope-unwrap
Apr 17, 2026
Merged

fix(gateway): unwrap receipt envelope in flow_* proxy (closes #37)#40
stackbilt-admin merged 2 commits intomainfrom
fix/37-flow-tool-envelope-unwrap

Conversation

@stackbilt-admin
Copy link
Copy Markdown
Member

Summary

  • proxyRestToolCall was reading receipt fields (governance, createdAt, facts) off the tarotscript response root, but the /run worker returns them nested under envelope.receipt. Every read resolved to undefined, so flow_quality / flow_governance / flow_status silently fell through to { hash }-only echoes.
  • Fix unwraps the envelope once and reads from envelope.receipt. flow_quality and flow_governance now return real governance + quality signals (determinism profile, capping, scaffold confidence, shadow density, position count). flow_status correctly surfaces verified + createdAt. flow_summary returns the full envelope so callers see verification state alongside the receipt.
  • Two regression tests against a fixture matching the production envelope shape.

Why this, not the alternatives

The triage comment on #37 framed this as "fix in tarotscript" vs "remove from gateway manifest." Neither was right — the receipt store is correct on the backend; it was a gateway-side unwrap bug. This PR closes #37 with a ~20-line gateway fix and no changes needed downstream.

Test plan

  • npm run typecheck — clean
  • npm test — 127/127 pass (including 2 new regression tests)
  • Smoke test in dev: flow_createflow_governance / flow_quality return populated data, not { hash } alone

Closes #37

🤖 Generated with Claude Code

Kurt Overmier and others added 2 commits April 17, 2026 07:51
`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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
@stackbilt-admin stackbilt-admin merged commit 45b6b32 into main Apr 17, 2026
1 check passed
@stackbilt-admin stackbilt-admin deleted the fix/37-flow-tool-envelope-unwrap branch April 17, 2026 13:04
stackbilt-admin pushed a commit that referenced this pull request Apr 17, 2026
stackbilt-admin added a commit that referenced this pull request Apr 17, 2026
#33)

* fix(oauth): rescue legacy grants via grant.scope fallback in resolveAuth (#29)

The C-1a remediation (commit 256ba06, 2026-04-10) removed the hardcoded
['generate','read'] path in resolveAuth and started enforcing the actual
scopes threaded through props.scopes. PR #32 fixed the grant-creation
side so empty-scope OAuth initiates get routed through the consent page
and mint grants with populated scopes.

This closes the last gap: the READ side. Any grant minted before C-1a
carries empty props.scopes (the pre-C-1a path wrote the grant top-level
scope but never populated props), so every authenticated request from
that legacy cohort still hits a (none) scopes error at tool dispatch
- exactly the UX symptom #29 and #30 describe.

resolveAuth now calls a new resolveLegacyGrantScopes helper when
oauthProps.scopes is empty. The helper extracts the bearer, calls
the OAuth provider's unwrapToken (which denormalizes the top-level
grant.scope onto the token record), and returns it as the effective
scope set. Successful rescues are logged so we can measure the
legacy cohort shrinking over time and know when to retire both
this fallback and the future backfill script.

What this does NOT rescue:

- Grants minted between C-1a (2026-04-10) and #32 (2026-04-11 20:43Z)
  where grant.scope and props.scopes are both empty. These users must
  disconnect and reauth through the #32 consent page - nothing exists
  on the server to fall back to.

Architecture:

The @cloudflare/workers-oauth-provider library transitively imports
cloudflare:* protocol modules that vitest's node ESM loader cannot
resolve, so getOAuthApi is dynamically imported inside the fallback
helper. Every other unit test that transitively imports gateway.ts
continues to work without touching the library.

OAuthProvider configuration was extracted to src/oauth-config.ts so
gateway.ts can pass the same options to getOAuthApi without a
circular import back through index.ts.

In-flight safety: deploy this READ fallback first. The backfill
script that rewrites stale KV grants comes in a separate PR and
must run AFTER this deploy is live - a partial backfill against
workers running old code will 500 in-flight sessions on mixed blob
versions.

Tests (6 new, 131/131 total):

- props.scopes present -> uses them directly, never touches unwrapToken
- props.scopes empty + grant.scope populated -> fallback rescues,
  session created with rescued scopes, tool call succeeds
- both empty -> scopes stay empty, tool call rejects with (none)
  error, which is the UX signal pointing users to reauth
- unwrapToken returns null -> graceful empty fallback
- unwrapToken throws (KV outage) -> caught, logged, empty fallback
- no bearer header -> fallback is inert

Related: #28 (closed by #32), #29 (this PR), #30 (should resolve
after this deploys + users reconnect).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* ci: refresh for current main (post-#40)

---------

Co-authored-by: Codebeast <codebeast@stackbilt.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Kurt Overmier <kurt@stackbilt.dev>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

flow_quality and flow_governance return only {hash} — handlers appear stubbed

1 participant