Skip to content

fix(den-api): roll expired usage buckets forward on inference status reads - #3648

Open
benjaminshafii wants to merge 2 commits into
devfrom
fix/stale-inference-usage-bucket
Open

benjaminshafii wants to merge 2 commits into
devfrom
fix/stale-inference-usage-bucket

Conversation

@benjaminshafii

Copy link
Copy Markdown
Member

Problem

A user report (Victor, via openwork-web-app feedback) showed /dashboard/inference stuck at 0% remaining with a reset time days in the past. Verified against prod data: his five-hour bucket expired 2026-08-05 14:21 UTC ("Resets 10:21 PM" in UTC+8), but the dashboard kept reporting that exhausted, expired bucket four days later.

Root cause: getActiveUsageBuckets reads inference_org_limit_policies.current_bucket_id verbatim. Buckets only roll forward lazily in the inference proxy (ensureBucket) on an actual request, so status reads can serve an expired activity-based bucket indefinitely. Enforcement was never wrong — the next request would have started a fresh window — but the dashboard told users they were locked out.

Fix

getInferenceStatus already treats status reads as a repair point (member key repair). This reuses the existing syncInferenceLimitPolicies there, so expired buckets are rolled forward before getActiveUsageBuckets reports them. 8-line diff, no new logic — anchored windows chain from anchor_at, activity-based windows restart at now, both exactly as the proxy already does.

Tests

  • New: ee/apps/den-api/test/inference-status-stale-bucket.test.ts — seeds an org whose five-hour policy points at an exhausted bucket that expired 4 days ago, then asserts the status read reports a window containing now with usedAmount 0.
  • bun test test/inference-status-stale-bucket.test.ts (den-api, local MySQL via DATABASE_URL, schema pushed with drizzle-kit): 1 pass.
  • Red/green proven: with the fix stashed, the same test fails (windowEndAt in the past).
  • tsc --noEmit on den-api: clean.
  • Sanity: test/delete-organization.test.ts passes; test/member-removal-rejoin.test.ts has 1 pre-existing failure that reproduces identically on pristine origin/dev (role "admin" vs "member", unrelated).

Validation lane

Local (bun + dockerized MySQL). Daytona not used for this server-side unit-level fix; coverage follows the existing ee/apps/den-api/test/** real-DB pattern rather than a new evals/specs app-driving spec, since the change is a single den-api status-read repair with no desktop surface. No testkit tape for that reason — repro steps above are exact.

…reads

The dashboard read the policy's current bucket verbatim, so an exhausted
activity-based five-hour bucket kept showing 0% remaining with a reset
time in the past for days after it expired. Reuse
syncInferenceLimitPolicies during getInferenceStatus so status reads
never report a stale window.
@vercel

vercel Bot commented Aug 9, 2026 •

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
openwork-app Ready Ready Preview Sep 5, 2026 7:56pm UTC
openwork-den Ready Ready Preview Sep 5, 2026 7:56pm UTC
openwork-den-worker-proxy Ready Ready Preview Sep 5, 2026 7:56pm UTC
2 Skipped Deployments
Project Deployment Actions Updated
openwork-diagnostics Skipped Skipped Sep 5, 2026 7:56pm UTC
openwork-landing Skipped Skipped Sep 5, 2026 7:56pm UTC

@diff-warden diff-warden Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warden security clearance: clear. No blocking findings (security or desktop↔den sync) in this diff (8cb0fafb498769cbe302101201b3c01bdbcd690a). Automated clearance satisfies the required-review gate only — a human still reviews and merges. Analysis run

benjaminshafii added a commit that referenced this pull request Aug 9, 2026
…bjects on desktop

Smoke health endpoint ran the image with DATABASE_URL pointing at
127.0.0.1 inside the container, so the DB was never reachable. den-api
binds :8788, then Better Auth's oauth-provider seedResources queries
oauthResource during createAuthContext and the unhandled rejection kills
the process — so docker port never resolved and the failure surfaced as
'No such container'. Pre-existing on dev (verified by building the
origin/dev image and reproducing the identical DrizzleQueryError) and on
PRs #3648/#3640; dev pushes look green only because the step is gated on
pull_request.

Not fixed by suppressing the crash: better-auth caches the context
promise (auth/base.mjs:8 initFn(options), awaited per request), so a
rejected init stays rejected for the process lifetime. Swallowing it
would yield a container that passes health checks with permanently dead
auth, when crashing lets the orchestrator restart into a healthy state.
The test premise was wrong, not the app.

The den-api smoke now stands up mysql:8.4 on a dedicated network, waits
for a real mysqladmin ping, applies the schema with the image's own
bootstrap.js (the asset the preceding step already asserts exists), and
only then boots den-api against it — so the smoke additionally proves
migrations apply, including 0057. Containers are no longer --rm, so logs
survive a crash and get dumped on failure. Other matrix images keep the
original path.

Also fixes Warden's third finding: the desktop parser rejected the new
'script' config object type, silently dropping saved scripts from the
Connect inventory.
benjaminshafii added a commit that referenced this pull request Aug 10, 2026
…sable, behind an org flag (#3645)

* feat(codemode): vendor @openwork/codemode confined interpreter with input bindings

Vendored from anomalyco/opencode@38e10eb (MIT; NOTICE + UPSTREAM.md record
provenance and divergences). Drops the OpenAPI adapter. Adds a local
divergence: CodeMode.execute({ bindings }) seeds read-only root-scope
values through the plain-data boundary, enabling saved parameterized
scripts. 244 upstream + bindings tests pass via bun test.

* feat(den): execute_capability_script + saved script capabilities behind org flag

- codemodeScripts org capability (metadata.capabilities, default off),
  wired like mcpConnections: admin routes, GET /v1/org, rollout helper.
- /mcp/agent registers execute_capability_script only when enabled: builds
  a per-call tool tree (tools.den.<op> via invokeMcpOperation in-process +
  tools.<connection>.<tool> via executeExternalCapability, tool policy
  applied at build), runs the confined interpreter with host limits, and
  search_capabilities matches now carry scriptPath.
- New 'script' config object type: executeMarketplaceCapability executes
  saved scripts with Ajv-validated typed input, least-authority tree
  restricted to declared requiredCapabilities, and fail-closed
  capability_unavailable (providerCallAttempted: false) when a declared
  tool is missing or policy-disabled.
- codemode_run receipts for every run (source adhoc|plugin:<id>) +
  GET /v1/codemode-runs (admin: org-wide, member: own) with safe MCP tag.
- drizzle migration 0057: codemode_run table + object_type enum value.

* feat(den-web): Codemode scripts admin toggle + Script runs dashboard

- den-admin-panel: codemodeScripts capability checkbox beside Connect.
- New Script runs screen (nav gated by capabilities.codemodeScripts):
  status, truncated source, tool calls, duration, when.

* test(evals): codemode-scripts voiceover spec + testkit fixes

- codemode-scripts.slow.test.ts: the approved 7-frame narration as
  observable assertions — org flag gating, deterministic rail probes,
  a real model turn answering idle-workers as one script step, parallel
  cross-connection fan-out with witness proof, save through the
  capability rail, teammate discovery + typed re-run, receipts, dashboard
  vision check, and fail-closed tool-policy proof.
- testkit: locally booted Den seeds DEN_BOOTSTRAP_ADMIN_EMAILS so specs
  can flip /v1/admin capabilities; MySQL probe honors
  OPENWORK_EVAL_MYSQL_URL instead of hardcoding 3306.

* fix(codemode): close OAuth-review and prototype-pollution gaps, repair lockfile churn

Warden findings on #3645:
- buildExternalMcpToolTree listed tools for connections pending admin OAuth
  issuer review, constructing the OAuth provider (and possible token refresh)
  against a configuration an admin had blocked. Filtering is now the exported
  pure predicate isCodemodeEligibleConnection, which mirrors
  probeExternalMcpConnection: skip allDisabled AND oauthIssuerReviewRequiredAt.
- restrictCodemodeToolTree used `tools[ns] ??= {}`, so a connection named
  __proto__ wrote a tool definition onto Object.prototype. Namespaces now
  reserve __proto__/constructor/prototype, and the tree is accumulated in Maps
  and materialized with Object.fromEntries (defineProperty semantics).

CI repair — the earlier pnpm install churned the lockfile:
- restored the xlsx CDN tarball integrity hash (ERR_PNPM_MISSING_TARBALL_INTEGRITY
  broke openwork-tests on both runners under --frozen-lockfile)
- pinned codemode's acorn to 8.16.0, the version already resolved in the
  workspace, removing an unrelated 8.16.0 -> 8.18.0 bump; lockfile is now
  additions only (167/0, was 184/15)
- reverted an unrelated msgpackr-extract allowBuilds entry; pnpm-workspace.yaml
  is now identical to dev
- Dockerfile.den copies packages/codemode (den-api depends on it)

Simplification: deleted buildCodemodeManifest, which was dead outside its own
test — both tree builders already return their own manifest.

* fix(deps): declare msgpackr-extract in allowBuilds

effect -> msgpackr -> msgpackr-extract ships an optional native accelerator
with a build script. Without an explicit allowBuilds decision, CI's
`pnpm install --frozen-lockfile` fails with ERR_PNPM_IGNORED_BUILDS.
msgpackr falls back to pure JS, so this is declared false rather than
compiling native code in every CI run and image build.

Reverting this in the previous commit was my error: it is a direct
consequence of adding effect, not unrelated churn.

* chore: trigger Warden analysis after draft to ready

The PR was opened as a draft, so Warden skipped it and never re-analyzed
the head that fixes its two findings (OAuth-review bypass, prototype
pollution). Warden does not support the ready_for_review event by design
— .github/workflows/warden.yml notes that draft-to-ready PRs get analyzed
on their next push — so this empty commit is that push.

* fix(ci): smoke den-api against a real migrated MySQL; accept script objects on desktop

Smoke health endpoint ran the image with DATABASE_URL pointing at
127.0.0.1 inside the container, so the DB was never reachable. den-api
binds :8788, then Better Auth's oauth-provider seedResources queries
oauthResource during createAuthContext and the unhandled rejection kills
the process — so docker port never resolved and the failure surfaced as
'No such container'. Pre-existing on dev (verified by building the
origin/dev image and reproducing the identical DrizzleQueryError) and on
PRs #3648/#3640; dev pushes look green only because the step is gated on
pull_request.

Not fixed by suppressing the crash: better-auth caches the context
promise (auth/base.mjs:8 initFn(options), awaited per request), so a
rejected init stays rejected for the process lifetime. Swallowing it
would yield a container that passes health checks with permanently dead
auth, when crashing lets the orchestrator restart into a healthy state.
The test premise was wrong, not the app.

The den-api smoke now stands up mysql:8.4 on a dedicated network, waits
for a real mysqladmin ping, applies the schema with the image's own
bootstrap.js (the asset the preceding step already asserts exists), and
only then boots den-api against it — so the smoke additionally proves
migrations apply, including 0057. Containers are no longer --rm, so logs
survive a crash and get dumped on failure. Other matrix images keep the
original path.

Also fixes Warden's third finding: the desktop parser rejected the new
'script' config object type, silently dropping saved scripts from the
Connect inventory.

* fix(ci): pin the smoke MySQL image to an immutable digest

Warden flagged mysql:8.4 as a mutable tag: a registry-side replacement
would execute unreviewed code inside the PR workflow, sharing the
smoke test's docker network. Pinned to the current multi-arch index
digest (verified to contain linux/amd64 for CI runners, and confirmed to
pull, boot, and pass the same mysqladmin readiness probe the step uses).

Worth noting the tag had already moved: the digest cached locally from
an earlier pull no longer matched the registry's 8.4, which is exactly
the drift being closed.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 5, 2026 •

Copy link
Copy Markdown

Deploying openwork with  Cloudflare Pages  Cloudflare Pages

Latest commit: c87cddd
Status: ✅  Deploy successful!
Preview URL: https://86f0e8ac.openwork.pages.dev
Branch Preview URL: https://fix-stale-inference-usage-bu.openwork.pages.dev

View logs

@vercel

vercel Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Deployment failed for project openwork-diagnostics with the following error:

Too many requests - try again in 5 minutes (more than 120, code: "api-deployments-flood-pro").

Learn More: https://vercel.com/docs/limits#rate-limits

@vercel
vercel Bot temporarily deployed to Preview – openwork-landing September 5, 2026 19:56 Inactive
@vercel

vercel Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Deployment failed for project openwork-app with the following error:

Too many requests - try again in 5 minutes (more than 120, code: "api-deployments-flood-pro").

Learn More: https://vercel.com/docs/limits#rate-limits

@vercel

vercel Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Deployment failed for project openwork-den with the following error:

Too many requests - try again in 5 minutes (more than 120, code: "api-deployments-flood-pro").

Learn More: https://vercel.com/docs/limits#rate-limits

@diff-warden diff-warden Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warden security clearance: clear. No blocking findings (security or desktop↔den sync) in this diff (c87cddd9e592f44bfc2e3031d053afbfdbc6d52c). Automated clearance satisfies the required-review gate only — a human still reviews and merges. Analysis run

This branch was successfully deployed

3 active (outdated) and 1 inactive deployments
Preview – openwork-landing — c87cddd9 Deployed Sep 5, 2026 by vercel[bot]
Preview – openwork-den-worker-proxy — 8cb0fafb Deployed Aug 9, 2026 by vercel[bot]
Preview – openwork-den — 8cb0fafb Deployed Aug 9, 2026 by vercel[bot]
Preview – openwork-app — 8cb0fafb Deployed Aug 9, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants