Skip to content

Codemode scripts: confined multi-tool scripts on Den, savable and reusable, behind an org flag - #3645

Merged
benjaminshafii merged 9 commits into
devfrom
feat/codemode-scripts
Aug 10, 2026
Merged

benjaminshafii merged 9 commits into
devfrom
feat/codemode-scripts

Conversation

@benjaminshafii

Copy link
Copy Markdown
Member

What

Organizations can flip on Codemode scripts (metadata.capabilities.codemodeScripts, default off) and get:

  • execute_capability_script on /mcp/agent — a confined JavaScript interpreter over the org's capabilities: tools.den.<operation> (Den REST catalog, in-process dispatch) and tools.<connection>.<tool> (external MCP connections, policy applied at tree build). One model step replaces N sequential execute_capability round trips; Promise.all fan-out works across connections; only the returned value enters model context.
  • Saved scripts — new script config object type on plugin-arch. executeMarketplaceCapability executes them with Ajv-validated typed input (bindings divergence in the vendored interpreter), a least-authority tree restricted to declared requiredCapabilities, and fail-closed capability_unavailable (providerCallAttempted: false) when a declared tool is missing or policy-disabled. Saved scripts inherit versioning, team grants, search discovery, and marketplace sharing.
  • Receipts — every run recorded in codemode_run (source adhoc | plugin:<id>, tool calls, duration, digest) + GET /v1/codemode-runs + a Script runs dashboard screen.
  • Vendored @openwork/codemode from anomalyco/opencode@38e10eb (MIT, NOTICE/UPSTREAM.md; OpenAPI adapter dropped; input-bindings divergence). 244 upstream+bindings tests pass.

Why

The two-tool facade (search_capabilities/execute_capability) makes every multi-step task N model round trips with N full payloads through context. This collapses chains into one call, makes the proven program a reusable org capability, and runs it fully server-side (cloud path from day one). Sibling groundwork for #3601 (its "replaceable port" runtime can be this adapter).

Verification (agent-first)

evals/specs/codemode-scripts.slow.test.ts — the approved voiceover narration encoded frame by frame; 16/16 evidence facts pass on a local stack (real Den boot, real Electron desktop, real Sonnet model turn, two mock MCP witnesses):

  1. Flag off → tool absent from tools/list; platform-admin flip → present
  2. Deterministic rail probes: namespaces, in-program $codemode.search, catalog dispatch
  3. Model turn: "which of my workers are idle?" answered as one script step (witnessed via session tool calls), reply names all seeded workers; vision-validated screenshot
  4. One script fans out over two org connections in parallel (both witnesses record the markers; combined result returned)
  5. Script saved through the capability rail (postPlugins with a script component) → config-objects?type=script shows it
  6. Teammate (separate member token) discovers it via search_capabilities and runs it by name with {days: 14} → status: executed, fresh data
  7. Receipts for every run (adhoc + saved) with tool calls + duration; Script runs dashboard vision-validated; disabling mock_echo via tool policy → saved script fails closed, provider witness records zero calls

Also ran:

  • packages/codemode: bun test → 244 pass
  • den-api: bun test (agent-codemode-script, codemode-tools, codemode-rollout, organization-capabilities, agent-mcp-routes, marketplace-codemode-scripts, marketplace-codemode-readiness, codemode-runs, marketplace-capabilities) → 65 pass, 0 fail
  • tsc --noEmit clean on den-api, den-web, evals; pnpm --filter @openwork-ee/den-api build exit 0
  • Known pre-existing: 2 failures in mcp-agent-config-policy.test.ts reproduce on clean origin/dev (stale expectations vs OpenAPI anyOf), not touched here

Evidence tape publishes to this PR next (fraimz roll 2026-08-09T16-11-01-887Z-codemode-scripts-…, 16 facts + chat and dashboard frames).

Notes for review

  • Authority: no new authorization paths. Script leaves delegate to the existing invokeMcpOperation / executeExternalCapability; member/policy checks run per call. Sandbox = vendored tree-walking interpreter (no eval/vm), plain-data boundary, blocked prototype members, host-owned limits (170s/50 calls/64KB output).
  • Hosted rollout: org flag default-off is the rollout control (platform admin flips per org). Worker-thread resource isolation for hosted multi-tenant is intentionally a fast-follow before broad hosted enablement.
  • Migration 0057 is additive (new table + enum value).

…nput 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.
…nd 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.
- den-admin-panel: codemodeScripts capability checkbox beside Connect.
- New Script runs screen (nav gated by capabilities.codemodeScripts):
  status, truncated source, tool calls, duration, when.
- 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.
@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 (UTC)
openwork-app Ready Ready Preview Aug 9, 2026 8:43pm
openwork-den Ready Ready Preview Aug 9, 2026 8:43pm
openwork-den-worker-proxy Ready Ready Preview Aug 9, 2026 8:43pm
openwork-diagnostics Ready Ready Preview Aug 9, 2026 8:43pm
openwork-landing Ready Ready Preview, v0 Aug 9, 2026 8:43pm

@benjaminshafii

benjaminshafii commented Aug 9, 2026 •

Copy link
Copy Markdown
Member Author

Photo roll — codemode-scripts-a-40-step-question-becomes-one-step-gets-saved-and-a-teammate-r — 2/2 frames passed · 13 facts

✅ 2/2 frames passed · 13 facts · 16 expectations passed · 0 failed

ℹ️ FACT — 1. With the org flag off, execute_capability_script is not even discoverable

tools/list before enabling: ["search_capabilities","execute_capability"]

  • ✅ PASS With the org flag off, execute_capability_script is not even discoverable — tools/list before enabling: ["search_capabilities","execute_capability"]

ℹ️ FACT — 2. Flipping the Codemode scripts org capability registers the script tool

tools/list after enabling: ["search_capabilities","execute_capability","execute_capability_script"]

  • ✅ PASS Flipping the Codemode scripts org capability registers the script tool — tools/list after enabling: ["search_capabilities","execute_capability","execute_capability_script"]

ℹ️ FACT — 3. The script runtime exposes the Den catalog and both org connections as namespaces

Object.keys(tools): [
"$codemode",
"den",
"drive_mock",
"gmail_mock"
]

  • ✅ PASS The script runtime exposes the Den catalog and both org connections as namespaces — Object.keys(tools): [
    "$codemode",
    "den",
    "drive_mock",
    "gmail_mock"
    ]

ℹ️ FACT — 4. In-program tool search finds the Den workers operation

search("list workers") paths: ["tools.den.getWorkers","tools.den.deleteWorkersById","tools.den.getWorkersById","tools.den.getWorkersByIdRuntime","tools.den.listAutomationRuns","tools.den.listAutomations","tools.den.patchWorkersById","tools.den.postWorkers","tools.den.postWorkersByIdActivityHeartbeat","tools.den.postWorkersByIdRu

  • ✅ PASS In-program tool search finds the Den workers operation — search("list workers") paths: ["tools.den.getWorkers","tools.den.deleteWorkersById","tools.den.getWorkersById","tools.den.getWorkersByIdRuntime","tools.den.listAutomationRuns","tools.den.listAutomations","tools.den.patchWorkersById","tools.den.postWorkers","tools.den.postWorkersByIdActivityHeartbeat","tools.den.postWorkersByIdRu

ℹ️ FACT — 5. A script can call a Den catalog operation and see the seeded workers

tools.den.getWorkers({}) → {
"workers": [
{
"id": "wrk_01kzkqv05ke6ya5s13nmbffpsx",
"orgId": "org_01kzkqtzwnef1s2n5wm8m79d0s",
"createdByUserId": "usr_01kzkqtztbekxa3vcmzyfvbt97",
"isMine": true,
"name": "builder-1",
"description": null,
"destination": "local",
"status": "

  • ✅ PASS A script can call a Den catalog operation and see the seeded workers — tools.den.getWorkers({}) → {
    "workers": [
    {
    "id": "wrk_01kzkqv05ke6ya5s13nmbffpsx",
    "orgId": "org_01kzkqtzwnef1s2n5wm8m79d0s",
    "createdByUserId": "usr_01kzkqtztbekxa3vcmzyfvbt97",
    "isMine": true,
    "name": "builder-1",
    "description": null,
    "destination": "local",
    "status": "

ℹ️ FACT — 6. The idle-workers question ran as a single script step, not one call per worker

Session tool calls: {"script":2,"single":0}; assistant reply: builder-1, builder-2, builder-3

  • ✅ PASS The idle-workers question ran as a single script step, not one call per worker — Session tool calls: {"script":2,"single":0}; assistant reply: builder-1, builder-2, builder-3

✅ PASS — 7. A chat conversation answers which workers are idle, naming builder workers

  • ✅ PASS A chat conversation answers which workers are idle, naming builder workers — The conversation response visibly says “builder-1, builder-2, builder-3.”
  • ✅ PASS No error banner or crash message is visible — No error banner, crash message, or failure notice is visible anywhere in the interface.

A chat conversation answers which workers are idle, naming builder workers

ℹ️ FACT — 8. One script reached both org connections in parallel and returned only the combined result

Result: {
"drive": "drive-1786295649138",
"gmail": "gmail-1786295649138"
}; Drive witness saw drive-1786295649138: true; Gmail witness saw gmail-1786295649138: true

  • ✅ PASS One script reached both org connections in parallel and returned only the combined result — Result: {
    "drive": "drive-1786295649138",
    "gmail": "gmail-1786295649138"
    }; Drive witness saw drive-1786295649138: true; Gmail witness saw gmail-1786295649138: true

ℹ️ FACT — 9. The idle-workers script is saved as an org capability through the capability rail

execute_capability(postPlugins) → {
"ok": true,
"item": {
"createdAt": "2026-08-09T17:14:09.206Z",
"createdByOrgMembershipId": "om_01kzkqtzwpen6a54mxvnda57pj",
"deletedAt": null,
"description": "Lists idle workers"

  • ✅ PASS The idle-workers script is saved as an org capability through the capability rail — execute_capability(postPlugins) → {
    "ok": true,
    "item": {
    "createdAt": "2026-08-09T17:14:09.206Z",
    "createdByOrgMembershipId": "om_01kzkqtzwpen6a54mxvnda57pj",
    "deletedAt": null,
    "description": "Lists idle workers"

ℹ️ FACT — 10. The agent saved the script as an org config object of type script

config-objects?type=script returned 1 row(s); idle-workers-report present: true

  • ✅ PASS The agent saved the script as an org config object of type script — config-objects?type=script returned 1 row(s); idle-workers-report present: true

ℹ️ FACT — 11. A teammate's agent discovers the saved script through capability search

Member search matches: [{"name":"plugin:plg_01kzkr90xpe64v2xh383fxqhrt:cob_01kzkr90xseaxa4b1ttmxzgdsc","kind":"script"},{"name":"deleteWorkersById"},{"name":"getWorkersById"},{"name":"getWorkersByIdRuntime"},{"name":"patchWorkersById"}]

  • ✅ PASS A teammate's agent discovers the saved script through capability search — Member search matches: [{"name":"plugin:plg_01kzkr90xpe64v2xh383fxqhrt:cob_01kzkr90xseaxa4b1ttmxzgdsc","kind":"script"},{"name":"deleteWorkersById"},{"name":"getWorkersById"},{"name":"getWorkersByIdRuntime"},{"name":"patchWorkersById"}]

ℹ️ FACT — 12. The teammate runs the saved script by name with typed input and gets fresh data from the same program

status: executed; value: ["builder-1","builder-2","builder-3"]

  • ✅ PASS The teammate runs the saved script by name with typed input and gets fresh data from the same program — status: executed; value: ["builder-1","builder-2","builder-3"]

ℹ️ FACT — 13. Every script run left a receipt with its tool calls and duration

runs: 7 (adhoc: 6, saved: 1); all carry toolCalls+durationMs: true

  • ✅ PASS Every script run left a receipt with its tool calls and duration — runs: 7 (adhoc: 6, saved: 1); all carry toolCalls+durationMs: true

✅ PASS — 14. A dashboard screen lists script runs with status and duration information

  • ✅ PASS A dashboard screen lists script runs with status and duration information — The “Script runs” page shows a table with STATUS and DURATION columns, including Succeeded/Failed statuses and durations such as 5 ms, 18 ms, and 6 ms.

A dashboard screen lists script runs with status and duration information

ℹ️ FACT — 15. Disabling a connection tool makes the dependent saved script fail closed without reaching the provider

Blocked run payload: {"error":"capability_unavailable","message":"Required capability tools.drive_mock.mock_echo (mcp:emc_01kzkqv060ee6rckf90tsaqsrx:mock_echo) is unavailable or disabled for this organization.","providerCallAttempted":false,"missing":[{"capabilityName":"mcp:emc_01kzkqv060ee6rckf90tsaqsrx:mock_echo","scriptPath":"tools.drive_mock.mock_echo"}]}; provider calls after policy flip: 0

  • ✅ PASS Disabling a connection tool makes the dependent saved script fail closed without reaching the provider — Blocked run payload: {"error":"capability_unavailable","message":"Required capability tools.drive_mock.mock_echo (mcp:emc_01kzkqv060ee6rckf90tsaqsrx:mock_echo) is unavailable or disabled for this organization.","providerCallAttempted":false,"missing":[{"capabilityName":"mcp:emc_01kzkqv060ee6rckf90tsaqsrx:mock_echo","scriptPath":"tools.drive_mock.mock_echo"}]}; provider calls after policy flip: 0

Roll created 2026-08-09T17:06:10.392Z · Source: evals/results/rolls/2026-08-09T17-06-10-392Z-codemode-scripts-a-40-step-question-becomes-one-step-gets-saved-and-a-teammate-r/roll.json · Repro: pnpm fraimz:publish -- --pr 3645 --roll 2026-08-09T17-06-10-392Z-codemode-scripts-a-40-step-question-becomes-one-step-gets-saved-and-a-teammate-r

Comment thread ee/apps/den-api/src/mcp/codemode-tools.ts
Comment thread ee/apps/den-api/src/mcp/codemode-tools.ts Outdated
@benjaminshafii
benjaminshafii marked this pull request as draft August 9, 2026 16:55
…r 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.
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.
@benjaminshafii

Copy link
Copy Markdown
Member Author

Review comments addressed + CI status

Both Warden findings fixed (fb0d27a):

  1. OAuth issuer review gate bypass (codemode-tools.ts:239) — buildExternalMcpToolTree listed tools for connections marked oauthIssuerReviewRequiredAt, constructing the OAuth provider (and possible token refresh) against a configuration an admin had explicitly blocked. Filtering is now the exported pure predicate isCodemodeEligibleConnection, mirroring probeExternalMcpConnection: skip allDisabled and oauthIssuerReviewRequiredAt. Unit-tested with fabricated rows for all three cases.
  2. Object.prototype pollution via connection namespace (codemode-tools.ts:102) — tools[ns] ??= {} meant a connection named __proto__ wrote a tool definition onto Object.prototype. Namespaces now reserve __proto__/constructor/prototype alongside den/$codemode, and the tree is accumulated in Maps and materialized via Object.fromEntries (defineProperty semantics), removing the ??= pattern entirely. Test asserts Object.prototype is unmutated. The second location Warden flagged (marketplace-capabilities.ts:1543) only calls restrictCodemodeToolTree, so it is covered by the same fix.

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

CI: 4 of 5 failures were mine and are fixed. They all traced to lockfile churn from my pnpm install:

  • restored the xlsx CDN tarball integrity hash (ERR_PNPM_MISSING_TARBALL_INTEGRITY broke openwork-tests on both runners)
  • pinned codemode's acorn to 8.16.0, the version already resolved in the workspace, removing an unrelated 8.16.0 to 8.18.0 bump on the hono-openapi chain — the lockfile is now additions only (167/0, was 184/15)
  • declared msgpackr-extract: false in allowBuilds (effect to msgpackr to msgpackr-extract ships an optional native accelerator; msgpackr falls back to pure JS)
  • Dockerfile.den now copies packages/codemode, which den-api depends on

openwork-tests (both runners), Build openwork-den-web, Build openwork-den-gateway, CodeQL, contracts, migrations, Helm are all green.

The one remaining red check, Build openwork-den-api, is pre-existing and not caused by this PR. Evidence:

  • The failing step is Smoke health endpoint, which runs the image with DATABASE_URL pointing at 127.0.0.1:3306 and no MySQL service — the DB is unreachable by design. The container exits 1 during boot inside Better Auth's oauth-provider seedResources / createAuthContext, on select ... from oauthResource where identifier = 'http://.../mcp/agent'. Nothing in that path is codemode.
  • I built the image from origin/dev locally and ran the identical smoke command: it exits 1 with the same DrizzleQueryError on oauthResource.
  • Unrelated PRs fix(den-api): roll expired usage buckets forward on inference status reads #3648 and fix(den-api): clamp config object projections to fit MySQL TEXT columns #3640 fail the same job at the same Smoke health endpoint step.
  • Pushes to dev look green only because that step is gated on if: github.event_name == 'pull_request', so it never runs outside PRs.

Net: the image builds fine; den-api just doesn't survive an unreachable DB at boot because that Better Auth plugin init is unguarded. Happy to fix it in a separate PR (guard seedResources / defer OAuth resource seeding), but it is out of scope here and would be an unrelated diff.

Verification tape re-published for the current head: 16/16 expectations, all 7 narrated frames.

@benjaminshafii
benjaminshafii marked this pull request as ready for review August 9, 2026 19:17
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.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 9, 2026 •

Copy link
Copy Markdown

Deploying openwork with  Cloudflare Pages  Cloudflare Pages

Latest commit: ea0613e
Status: ✅  Deploy successful!
Preview URL: https://b8726742.openwork.pages.dev
Branch Preview URL: https://feat-codemode-scripts.openwork.pages.dev

View logs

Comment thread ee/apps/den-api/src/mcp/agent.ts
diff-warden[bot]
diff-warden Bot previously approved these changes Aug 9, 2026

@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 (ea0613e356b2f75feabb1beac5ca042c886284c8). 1 advisory desktop↔den sync note(s) were posted as review comments; they do not affect clearance. Automated clearance satisfies the required-review gate only — a human still reviews and merges. Analysis run

…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.
Comment thread .github/workflows/publish-ee-images.yml Outdated
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.

This branch was successfully deployed

5 active deployments
Preview – openwork-den-worker-proxy — 5c7fa87c Deployed Aug 9, 2026 by vercel[bot]
Preview – openwork-den — 5c7fa87c Deployed Aug 9, 2026 by vercel[bot]
Preview – openwork-landing — 5c7fa87c Deployed Aug 9, 2026 by vercel[bot]
Preview – openwork-app — 5c7fa87c Deployed Aug 9, 2026 by vercel[bot]
Preview – openwork-diagnostics — 5c7fa87c 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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants