Drive Workers Clinic from your own code
Everything the web page does is available over HTTP: paste a Cloudflare Workers project in, get the
same structured production-readiness review back. The natural use is a CI job that re-reviews the
Worker whenever wrangler.jsonc or anything under src/ changes, and fails
the build when readiness comes back not-ready or a finding arrives at
priority: "critical". The same call run across a fleet of Workers gives you twelve
identical checks per service, diffable row by row.
Base URL and the envelope
Every endpoint lives under https://api.skillsafe.ai/v1/app-api and every response uses
the same envelope, so one helper covers the whole API:
{ "ok": true, "data": { ... } }
{ "ok": false, "error": { "code": "...", "message": "...", "status": 402, "details": { ... } } }
Send your app slug as X-App-Slug: workers-clinic and your token as
Authorization: Bearer … on every call.
Error codes
| code | status | what to do |
|---|---|---|
unauthorized | 401 | The token is missing, malformed or expired. Get a new one from the token page. |
payment_required | 402 | The balance is below min_credits. Call /estimate first, compare hold_credits against the balance from /me, and top up before submitting. |
forbidden | 403 | The token is valid but not for this app, or a guest token tried a metered run. Check the X-App-Slug header, and sign in for a personal token. |
not_found | 404 | Unknown job id, unknown collection, or the app slug does not exist. A reviews collection that is not declared on the release you are calling also lands here. |
conflict | 409 | The same Idempotency-Key was replayed with a different body. Bump the attempt suffix, or send the original input back unchanged. |
validation_error | 422 | The input object is missing a required field — files is the usual one — or a field is the wrong type. prescan_facts must be an object with resources and flags arrays, not a bare array. |
rate_limited | 429 | Too many requests. Back off and retry; do not tight-loop the job poller — two seconds between polls is plenty. |
internal | 5xx | A server-side failure. Retry with the SAME Idempotency-Key so you are not billed twice. |
1. A tiny client
One helper that adds the headers, unwraps data and raises on error.
# Every call is the same three things: the base URL, your bearer token,
# and a JSON body. Keep the token in a shell variable.
BASE="https://api.skillsafe.ai/v1/app-api"
SLUG="workers-clinic"
TOKEN="YOUR_TOKEN" # from https://workers-clinic.skillsafe.ai/tokens.html
call() { # call <path> [json-body]
if [ -n "$2" ]; then
curl -sS -X POST "$BASE/$1" \
-H "Authorization: Bearer $TOKEN" \
-H "X-App-Slug: $SLUG" \
-H "Content-Type: application/json" \
-d "$2"
else
curl -sS "$BASE/$1" -H "Authorization: Bearer $TOKEN" -H "X-App-Slug: $SLUG"
fi
}
import json, os, urllib.error, urllib.request
BASE = "https://api.skillsafe.ai/v1/app-api"
SLUG = "workers-clinic"
TOKEN = os.environ.get("SKILLSAFE_TOKEN", "YOUR_TOKEN") # from https://workers-clinic.skillsafe.ai/tokens.html
def call(path, body=None):
"""Returns the unwrapped `data`, or raises with the API error code."""
data = json.dumps(body).encode() if body is not None else None
req = urllib.request.Request(f"{BASE}/{path}", data=data, method="POST" if body is not None else "GET")
req.add_header("Authorization", f"Bearer {TOKEN}")
req.add_header("X-App-Slug", SLUG)
if body is not None:
req.add_header("Content-Type", "application/json")
try:
with urllib.request.urlopen(req) as r:
payload = json.load(r)
except urllib.error.HTTPError as e:
payload = json.load(e)
if not payload.get("ok"):
err = payload.get("error", {})
raise RuntimeError(f"{err.get('code')}: {err.get('message')}")
return payload["data"]
const BASE = "https://api.skillsafe.ai/v1/app-api";
const SLUG = "workers-clinic";
const TOKEN = "YOUR_TOKEN"; // from https://workers-clinic.skillsafe.ai/tokens.html
async function call(path, body) {
const res = await fetch(`${BASE}/${path}`, {
method: body ? "POST" : "GET",
headers: {
Authorization: `Bearer ${TOKEN}`,
"X-App-Slug": SLUG,
...(body ? { "Content-Type": "application/json" } : {}),
},
body: body ? JSON.stringify(body) : undefined,
});
const payload = await res.json();
if (!payload.ok) throw new Error(`${payload.error.code}: ${payload.error.message}`);
return payload.data;
}
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
const (
base = "https://api.skillsafe.ai/v1/app-api"
slug = "workers-clinic"
)
var token = os.Getenv("SKILLSAFE_TOKEN") // from https://workers-clinic.skillsafe.ai/tokens.html
type envelope struct {
OK bool `json:"ok"`
Data json.RawMessage `json:"data"`
Error struct {
Code string `json:"code"`
Message string `json:"message"`
} `json:"error"`
}
func call(path string, body any) (json.RawMessage, error) {
method := http.MethodGet
var rdr io.Reader
if body != nil {
method = http.MethodPost
b, _ := json.Marshal(body)
rdr = bytes.NewReader(b)
}
req, _ := http.NewRequest(method, base+"/"+path, rdr)
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("X-App-Slug", slug)
if body != nil {
req.Header.Set("Content-Type", "application/json")
}
res, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var env envelope
if err := json.NewDecoder(res.Body).Decode(&env); err != nil {
return nil, err
}
if !env.OK {
return nil, fmt.Errorf("%s: %s", env.Error.Code, env.Error.Message)
}
return env.Data, nil
}
import java.net.URI;
import java.net.http.*;
public class Clinic {
static final String BASE = "https://api.skillsafe.ai/v1/app-api";
static final String SLUG = "workers-clinic";
static final String TOKEN = System.getenv().getOrDefault("SKILLSAFE_TOKEN", "YOUR_TOKEN");
static final HttpClient HTTP = HttpClient.newHttpClient();
static String call(String path, String jsonBody) throws Exception {
HttpRequest.Builder b = HttpRequest.newBuilder(URI.create(BASE + "/" + path))
.header("Authorization", "Bearer " + TOKEN)
.header("X-App-Slug", SLUG);
if (jsonBody != null) {
b.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody));
} else {
b.GET();
}
HttpResponse<String> res = HTTP.send(b.build(), HttpResponse.BodyHandlers.ofString());
// The envelope is always {"ok":...,"data":...} or {"ok":false,"error":...}.
return res.body();
}
}
require "json"
require "net/http"
require "uri"
BASE = "https://api.skillsafe.ai/v1/app-api"
SLUG = "workers-clinic"
TOKEN = ENV.fetch("SKILLSAFE_TOKEN", "YOUR_TOKEN") # from https://workers-clinic.skillsafe.ai/tokens.html
def call(path, body = nil)
uri = URI("#{BASE}/#{path}")
req = body ? Net::HTTP::Post.new(uri) : Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer #{TOKEN}"
req["X-App-Slug"] = SLUG
if body
req["Content-Type"] = "application/json"
req.body = JSON.generate(body)
end
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
payload = JSON.parse(res.body)
raise "#{payload['error']['code']}: #{payload['error']['message']}" unless payload["ok"]
payload["data"]
end
<?php
const BASE = "https://api.skillsafe.ai/v1/app-api";
const SLUG = "workers-clinic";
define("TOKEN", getenv("SKILLSAFE_TOKEN") ?: "YOUR_TOKEN"); // from /tokens.html
function call(string $path, ?array $body = null) {
$ch = curl_init(BASE . "/" . $path);
$headers = ["Authorization: Bearer " . TOKEN, "X-App-Slug: " . SLUG];
if ($body !== null) {
$headers[] = "Content-Type: application/json";
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
}
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$payload = json_decode(curl_exec($ch), true);
curl_close($ch);
if (empty($payload["ok"])) {
throw new RuntimeException($payload["error"]["code"] . ": " . $payload["error"]["message"]);
}
return $payload["data"];
}
using System.Net.Http.Json;
using System.Text.Json;
static class Clinic
{
const string Base = "https://api.skillsafe.ai/v1/app-api";
const string Slug = "workers-clinic";
static readonly string Token =
Environment.GetEnvironmentVariable("SKILLSAFE_TOKEN") ?? "YOUR_TOKEN";
static readonly HttpClient Http = new();
public static async Task<JsonElement> Call(string path, object? body = null)
{
var req = new HttpRequestMessage(body is null ? HttpMethod.Get : HttpMethod.Post, $"{Base}/{path}");
req.Headers.Add("Authorization", $"Bearer {Token}");
req.Headers.Add("X-App-Slug", Slug);
if (body is not null) req.Content = JsonContent.Create(body);
var res = await Http.SendAsync(req);
var payload = await res.Content.ReadFromJsonAsync<JsonElement>();
if (!payload.GetProperty("ok").GetBoolean())
{
var e = payload.GetProperty("error");
throw new Exception($"{e.GetProperty("code")}: {e.GetProperty("message")}");
}
return payload.GetProperty("data");
}
}
2. Get a token
The easiest route is the token page: it shows the token this browser already holds, with Copy token and Copy shell export buttons, and a sign-in button for a personal token. You never need to open the developer console.
A guest token can call /me and /estimate. Reviewing a
Worker is metered, so /run and /run-stream need a personal
token from signing in — a guest token that posts a run gets a 403 forbidden. Each
POST /guest also mints a new guest subject, which matters later: the
reviews collection is scoped to the calling subject, so a fresh guest token sees an
empty history.
# A guest token is enough for /me and /estimate. Running a review is metered and
# needs a personal token: open the token page and press "Sign in".
#
# https://workers-clinic.skillsafe.ai/tokens.html
#
# That page also gives you a ready-made shell export:
# export SKILLSAFE_TOKEN="..."
#
# To mint a guest token from the command line instead:
curl -sS -X POST "https://api.skillsafe.ai/v1/app-api/guest" -H "X-App-Slug: workers-clinic"
# Open https://workers-clinic.skillsafe.ai/tokens.html and press "Copy token".
# The token page exists so you never have to dig a token out of the browser
# yourself; it also prints the `export SKILLSAFE_TOKEN=...` line.
#
# A guest token, which can call /me and /estimate but cannot run:
guest = call("guest")
TOKEN = guest["token"]
// Open https://workers-clinic.skillsafe.ai/tokens.html and press "Copy token".
// A guest token can call /me and /estimate but cannot run a metered review.
const guest = await call("guest");
// Use guest.token as the bearer for subsequent calls.
// Open https://workers-clinic.skillsafe.ai/tokens.html and press "Copy token".
// Or mint a guest token, which can call /me and /estimate but cannot run:
raw, err := call("guest", map[string]any{})
if err != nil {
panic(err)
}
var guest struct {
Token string `json:"token"`
}
_ = json.Unmarshal(raw, &guest)
// Open https://workers-clinic.skillsafe.ai/tokens.html and press "Copy token".
// A guest token can call /me and /estimate but cannot run a metered review.
String guest = call("guest", "{}");
System.out.println(guest);
# Open https://workers-clinic.skillsafe.ai/tokens.html and press "Copy token".
# A guest token can call /me and /estimate but cannot run a metered review.
guest = call("guest", {})
puts guest["token"]
<?php
// Open https://workers-clinic.skillsafe.ai/tokens.html and press "Copy token".
// A guest token can call /me and /estimate but cannot run a metered review.
$guest = call("guest", []);
echo $guest["token"];
// Open https://workers-clinic.skillsafe.ai/tokens.html and press "Copy token".
// A guest token can call /me and /estimate but cannot run a metered review.
var guest = await Clinic.Call("guest", new { });
Console.WriteLine(guest.GetProperty("token").GetString());
3. Check the session and the balance
GET /me tells you whether the token is a guest or a person, and what the balance is.
subject_type is user for a personal token and guest for a
guest one — branch on it before you spend a call finding out the hard way. Compare
credits against hold_credits from the next step before you run, so a
shortfall surfaces as your own clear message rather than a 402. The web app does exactly this: if
the balance is under the reservation it says so and offers a top-up link instead of submitting.
call me
# {"ok":true,"data":{"subject_type":"user","username":"you","credits":51234}}
#
# subject_type is "guest" for a guest token; a guest can /estimate but not /run.
me = call("me")
print(me["subject_type"], me.get("username"), me.get("credits"))
if me["subject_type"] != "user":
raise SystemExit("A personal token is required to run a review - see /tokens.html")
const me = await call("me");
console.log(me.subject_type, me.username, me.credits);
if (me.subject_type !== "user") {
throw new Error("A personal token is required to run a review - see /tokens.html");
}
raw, err := call("me", nil)
if err != nil {
panic(err)
}
var me struct {
SubjectType string `json:"subject_type"`
Username string `json:"username"`
Credits int `json:"credits"`
}
_ = json.Unmarshal(raw, &me)
fmt.Println(me.SubjectType, me.Username, me.Credits)
System.out.println(call("me", null));
// {"ok":true,"data":{"subject_type":"user","username":"you","credits":51234}}
me = call("me")
puts "#{me['subject_type']} #{me['credits']}"
abort "A personal token is required to run a review" unless me["subject_type"] == "user"
<?php
$me = call("me");
echo $me["subject_type"], " ", $me["credits"], PHP_EOL;
if ($me["subject_type"] !== "user") {
throw new RuntimeException("A personal token is required to run a review");
}
var me = await Clinic.Call("me");
Console.WriteLine(me.GetProperty("subject_type").GetString());
Console.WriteLine(me.GetProperty("credits").GetInt32());
4. Price the run — free
The input object is exactly what the app's own form submits:
| field | type | meaning |
|---|---|---|
files | string, required | The pasted project — wrangler.jsonc or wrangler.toml, the Worker entrypoint, Durable Object and WorkerEntrypoint classes, generated or hand-written types, sometimes package.json. Put a // file: src/index.ts marker line above each file so they can be told apart; a single file with no marker is accepted. This is the review's only evidence. If you clipped the middle of a file, say so with a // [... clipped N characters ...] marker — the review will not infer anything about what was cut. |
target | string | The deploy target to review against: production, staging, preview or unknown. It changes what is acceptable, not what is true — a stale compatibility_date on a preview Worker is a note, on a production Worker fronting customer traffic it is a finding. |
focus | string | general, config, bindings, runtime, types, security or observability. It reorders the findings and shapes focus_areas; it never lets a whole category be skipped, and a high-severity finding from elsewhere is never suppressed. |
context | string, optional | Free text about traffic, exposure, history and deadlines — "public API behind a Cloudflare route, 2k req/s at peak, first review before it takes real traffic". Used for prioritisation. It is treated as a claim, not as evidence. |
prescan_facts | object, optional but strongly recommended | {resources: [{id,label}], flags: [{id,label}]} — see below. |
retry_note | string, optional | Send only on a retry, when a previous reply failed to parse or came back truncated. The instruction is obeyed exactly. This is the lane the web page uses for its one automatic reformat retry. |
What prescan_facts is for
Before it sends anything, the browser app runs a deterministic client-side parse of the paste: it
reads wrangler.jsonc as real JSONC and wrangler.toml as real TOML, pulls
out the worker name, entrypoint, compatibility_date, compatibility flags, named
environments and every declared binding by type, then walks the source for a fixed list of
anti-patterns. What it establishes goes into resources; what it objects to goes into
flags, each with a stable id.
Those facts are then handed to the model as things it must reconcile. Every
flags id has to come back exactly once in coverage_check — either
addressed: true with a finding covering it, or addressed: false with a
one-sentence note saying why the parser was wrong in context. resources are treated as
established fact and are not contradicted: if the prescan says
compatibility_date 2023-05-18, that is the date.
This is the single biggest quality difference between the web app and a naive API call.
A caller who omits prescan_facts gets a review with an empty
coverage_check — nothing to hold the model to, and no way to tell a fact it
weighed from a fact it never saw. A caller who supplies it gets every flag either confirmed with a
finding id or explicitly set aside with a reason. Ids are yours to choose, but the app's own shapes
are worth copying, because they encode where the problem is:
config:compat-date-stale compatibility_date is more than a year behind
config:compat-date-missing no compatibility_date at all
config:node-import:node:crypto a Node built-in imported without nodejs_compat
config:var-secret:SESSION_SECRET something credential-shaped in plain `vars`
bindings:undeclared:RATE_LIMIT_KV env.X is read but nothing declares it
bindings:unused:UPLOADS declared but never read
bindings:rest-api:src/index.ts:34 the Cloudflare REST API called from inside a Worker
bindings:hyperdrive-missing a direct Postgres/MySQL connection, no Hyperdrive
runtime:global-state:responseCache a module-level mutable holding request data
runtime:unbounded-body:src/index.ts:27 await upstream.text() with no size bound
runtime:floating-promise:src/index.ts:31 a promise neither awaited nor handed to waitUntil
runtime:ctx-destructured:src/index.ts:14 `const { waitUntil } = ctx`
runtime:passthrough-on-exception:src/index.ts:15
security:math-random:src/index.ts:21 Math.random() used to build an identifier
security:hardcoded-secret:UPSTREAM_TOKEN a credential literal in source
security:timing-unsafe-compare:SESSION_SECRET
types:any:src/index.ts:23 an `any` annotation
types:hand-written-env Env declared by hand instead of `wrangler types`
observability:disabled observability.enabled is not true
observability:no-sampling enabled with no explicit head_sampling_rate
label is one human sentence describing the fact; it is what the model reads, so make it
concrete ("Module-level Map responseCache caches response bodies across
requests") rather than generic ("global state").
/estimate
POST /estimate creates no job and charges nothing. It returns the model
binding — model, model_alias, markup_bps — and the
reservation: hold_credits is what gets held, min_credits is the balance
you must clear to start, and sponsor_enabled says whether the app is covering the run.
The hold is priced at the full output cap, so the charged_credits that comes
back on the finished job is normally far lower — a review that reserves a couple of thousand credits
typically settles for a few hundred. Comparing hold_credits against the
credits from GET /me before you submit is how you turn a 402 into a
message of your own.
INPUT='{"files": "// file: wrangler.jsonc\n{\n \"name\": \"edge-api\",\n \"main\": \"src/index.ts\",\n \"compatibility_date\": \"2023-05-18\",\n \"kv_namespaces\": [{ \"binding\": \"CACHE\", \"id\": \"9d1f0e7c4b2a4f9e\" }],\n \"vars\": { \"UPSTREAM_BASE\": \"https://origin.internal.example.com\" }\n}\n\n// file: src/index.ts\ninterface Env {\n CACHE: KVNamespace;\n UPSTREAM_BASE: string;\n}\n\nconst responseCache = new Map();\n\nexport default {\n async fetch(request: Request, env: Env, ctx: ExecutionContext) {\n const traceId = \"req-\" + Math.random().toString(36).slice(2);\n const url = new URL(request.url);\n if (responseCache.has(url.pathname)) return new Response(responseCache.get(url.pathname));\n const upstream = await fetch(env.UPSTREAM_BASE + url.pathname);\n const body = await upstream.text();\n responseCache.set(url.pathname, body);\n return new Response(body);\n }\n};", "target": "production", "focus": "general", "context": "Public API behind a Cloudflare route, ~2k req/s at peak. First review before it takes real traffic.", "prescan_facts": {"resources": [{"id": "config:file:wrangler.jsonc", "label": "wrangler.jsonc read as JSONC"}, {"id": "config:compat:2023-05-18", "label": "compatibility_date 2023-05-18"}, {"id": "config:binding:kv:CACHE", "label": "KV namespace binding CACHE"}], "flags": [{"id": "config:compat-date-stale", "label": "compatibility_date 2023-05-18 is more than a year behind"}, {"id": "runtime:global-state:responseCache", "label": "Module-level Map responseCache caches response bodies across requests"}, {"id": "security:math-random:src/index.ts:21", "label": "Math.random() builds the trace identifier"}]}}'
call estimate "$INPUT"
# {"ok":true,"data":{"model":"gpt-5.6-terra","model_alias":"gpt-terra",
# "markup_bps":1000,"hold_credits":2140,"min_credits":310,"sponsor_enabled":false}}
#
# estimate is FREE. It creates no job and charges nothing. hold_credits is what
# gets RESERVED at the full output cap; the charge afterwards is normally much lower.
WRANGLER = """// file: wrangler.jsonc
{
"name": "edge-api",
"main": "src/index.ts",
"compatibility_date": "2023-05-18",
"kv_namespaces": [{ "binding": "CACHE", "id": "9d1f0e7c4b2a4f9e" }],
"vars": { "UPSTREAM_BASE": "https://origin.internal.example.com" }
}
// file: src/index.ts
interface Env {
CACHE: KVNamespace;
UPSTREAM_BASE: string;
}
const responseCache = new Map();
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
const traceId = "req-" + Math.random().toString(36).slice(2);
const url = new URL(request.url);
if (responseCache.has(url.pathname)) return new Response(responseCache.get(url.pathname));
const upstream = await fetch(env.UPSTREAM_BASE + url.pathname);
const body = await upstream.text();
responseCache.set(url.pathname, body);
return new Response(body);
}
};"""
INPUT = {
"files": WRANGLER,
"target": "production",
"focus": "general",
"context": "Public API behind a Cloudflare route, ~2k req/s at peak. First review before it takes real traffic.",
"prescan_facts": {
"resources": [
{"id": "config:file:wrangler.jsonc", "label": "wrangler.jsonc read as JSONC"},
{"id": "config:compat:2023-05-18", "label": "compatibility_date 2023-05-18"},
{"id": "config:binding:kv:CACHE", "label": "KV namespace binding CACHE"}
],
"flags": [
{"id": "config:compat-date-stale",
"label": "compatibility_date 2023-05-18 is more than a year behind"},
{"id": "runtime:global-state:responseCache",
"label": "Module-level Map responseCache caches response bodies across requests"},
{"id": "security:math-random:src/index.ts:21",
"label": "Math.random() builds the trace identifier"}
]
}
}
est = call("estimate", INPUT)
print(est["model"], est["model_alias"], est["markup_bps"])
print(est["hold_credits"], est["min_credits"], est["sponsor_enabled"])
# estimate is free: no job is created and nothing is charged. Check the balance
# against the reservation before you spend a run.
me = call("me")
if not est["sponsor_enabled"] and me.get("credits", 0) < est["hold_credits"]:
raise SystemExit(f"balance {me['credits']} is under the {est['hold_credits']}-credit reservation")
const WRANGLER = `// file: wrangler.jsonc
{
"name": "edge-api",
"main": "src/index.ts",
"compatibility_date": "2023-05-18",
"kv_namespaces": [{ "binding": "CACHE", "id": "9d1f0e7c4b2a4f9e" }],
"vars": { "UPSTREAM_BASE": "https://origin.internal.example.com" }
}
// file: src/index.ts
interface Env {
CACHE: KVNamespace;
UPSTREAM_BASE: string;
}
const responseCache = new Map();
export default {
async fetch(request, env, ctx) {
const traceId = "req-" + Math.random().toString(36).slice(2);
const url = new URL(request.url);
if (responseCache.has(url.pathname)) return new Response(responseCache.get(url.pathname));
const upstream = await fetch(env.UPSTREAM_BASE + url.pathname);
const body = await upstream.text();
responseCache.set(url.pathname, body);
return new Response(body);
}
};`;
const INPUT = {
files: WRANGLER,
target: "production",
focus: "general",
context:
"Public API behind a Cloudflare route, ~2k req/s at peak. First review before it takes real traffic.",
prescan_facts: {
resources: [
{ id: "config:file:wrangler.jsonc", label: "wrangler.jsonc read as JSONC" },
{ id: "config:compat:2023-05-18", label: "compatibility_date 2023-05-18" },
],
flags: [
{
id: "config:compat-date-stale",
label: "compatibility_date 2023-05-18 is more than a year behind",
},
{
id: "runtime:global-state:responseCache",
label: "Module-level Map responseCache caches response bodies across requests",
},
{
id: "security:math-random:src/index.ts:21",
label: "Math.random() builds the trace identifier",
},
],
},
};
const est = await call("estimate", INPUT);
console.log(est.model, est.model_alias, est.markup_bps);
console.log(est.hold_credits, est.min_credits, est.sponsor_enabled);
// estimate is free: no job is created and nothing is charged.
const me = await call("me");
if (!est.sponsor_enabled && me.credits < est.hold_credits) {
throw new Error(`balance ${me.credits} is under the ${est.hold_credits}-credit reservation`);
}
files := "// file: wrangler.jsonc\n{\n \"name\": \"edge-api\",\n \"main\": \"src/index.ts\",\n" +
" \"compatibility_date\": \"2023-05-18\",\n" +
" \"kv_namespaces\": [{ \"binding\": \"CACHE\", \"id\": \"9d1f0e7c4b2a4f9e\" }]\n}\n\n" +
"// file: src/index.ts\nconst responseCache = new Map();\n\nexport default {\n" +
" async fetch(request, env, ctx) {\n" +
" const traceId = \"req-\" + Math.random().toString(36).slice(2);\n" +
" const body = await (await fetch(env.UPSTREAM_BASE)).text();\n" +
" responseCache.set(request.url, body);\n return new Response(body);\n }\n};"
input := map[string]any{
"files": files,
"target": "production",
"focus": "general",
"context": "Public API behind a Cloudflare route, ~2k req/s at peak.",
"prescan_facts": map[string]any{
"resources": []any{
map[string]string{"id": "config:compat:2023-05-18", "label": "compatibility_date 2023-05-18"},
},
"flags": []any{
map[string]string{"id": "config:compat-date-stale", "label": "compatibility_date is more than a year behind"},
map[string]string{"id": "runtime:global-state:responseCache", "label": "Module-level Map responseCache holds request data"},
map[string]string{"id": "security:math-random:src/index.ts:21", "label": "Math.random() builds the trace identifier"},
},
},
}
raw, err := call("estimate", input)
if err != nil {
panic(err)
}
fmt.Println(string(raw)) // estimate is free - no job, no charge
// {"model":"gpt-5.6-terra","model_alias":"gpt-terra","markup_bps":1000,
// "hold_credits":2140,"min_credits":310,"sponsor_enabled":false}
String input = """
{
"files": "// file: wrangler.jsonc\\n{\\n \\"name\\": \\"edge-api\\",\\n \\"main\\": \\"src/index.ts\\",\\n \\"compatibility_date\\": \\"2023-05-18\\"\\n}\\n\\n// file: src/index.ts\\nconst responseCache = new Map();\\nexport default { async fetch(request, env, ctx) { return new Response(\\"ok\\"); } };",
"target": "production",
"focus": "general",
"context": "Public API behind a Cloudflare route, ~2k req/s at peak.",
"prescan_facts": {
"resources": [
{ "id": "config:compat:2023-05-18", "label": "compatibility_date 2023-05-18" }
],
"flags": [
{ "id": "config:compat-date-stale", "label": "compatibility_date is more than a year behind" },
{ "id": "runtime:global-state:responseCache", "label": "Module-level Map responseCache holds request data" },
{ "id": "security:math-random:src/index.ts:21", "label": "Math.random() builds the trace identifier" }
]
}
}
""";
System.out.println(call("estimate", input));
// estimate is free: no job is created and nothing is charged.
// The data object carries model, model_alias, markup_bps, hold_credits,
// min_credits and sponsor_enabled.
files = <<~FILES
// file: wrangler.jsonc
{ "name": "edge-api", "main": "src/index.ts", "compatibility_date": "2023-05-18" }
// file: src/index.ts
const responseCache = new Map();
export default {
async fetch(request, env, ctx) {
const traceId = "req-" + Math.random().toString(36).slice(2);
return new Response(traceId);
}
};
FILES
input = {
"files" => files,
"target" => "production",
"focus" => "general",
"context" => "Public API behind a Cloudflare route, ~2k req/s at peak.",
"prescan_facts" => {
"resources" => [{ "id" => "config:compat:2023-05-18", "label" => "compatibility_date 2023-05-18" }],
"flags" => [
{ "id" => "config:compat-date-stale", "label" => "compatibility_date is more than a year behind" },
{ "id" => "runtime:global-state:responseCache", "label" => "Module-level Map responseCache holds request data" },
{ "id" => "security:math-random:src/index.ts:21", "label" => "Math.random() builds the trace identifier" }
]
}
}
est = call("estimate", input)
puts "#{est['model']} hold=#{est['hold_credits']} min=#{est['min_credits']}"
# estimate is free: no job is created and nothing is charged.
<?php
$files = "// file: wrangler.jsonc\n"
. "{ \"name\": \"edge-api\", \"main\": \"src/index.ts\", \"compatibility_date\": \"2023-05-18\" }\n\n"
. "// file: src/index.ts\n"
. "const responseCache = new Map();\n"
. "export default { async fetch(request, env, ctx) { return new Response(\"ok\"); } };";
$input = [
"files" => $files,
"target" => "production",
"focus" => "general",
"context" => "Public API behind a Cloudflare route, ~2k req/s at peak.",
"prescan_facts" => [
"resources" => [["id" => "config:compat:2023-05-18", "label" => "compatibility_date 2023-05-18"]],
"flags" => [
["id" => "config:compat-date-stale", "label" => "compatibility_date is more than a year behind"],
["id" => "runtime:global-state:responseCache", "label" => "Module-level Map responseCache holds request data"],
["id" => "security:math-random:src/index.ts:21", "label" => "Math.random() builds the trace identifier"],
],
],
];
$est = call("estimate", $input);
echo $est["model"], " ", $est["hold_credits"], " ", $est["min_credits"], PHP_EOL;
// estimate is free: no job is created and nothing is charged.
var files = """
// file: wrangler.jsonc
{ "name": "edge-api", "main": "src/index.ts", "compatibility_date": "2023-05-18" }
// file: src/index.ts
const responseCache = new Map();
export default {
async fetch(request, env, ctx) {
const traceId = "req-" + Math.random().toString(36).slice(2);
return new Response(traceId);
}
};
""";
var input = new
{
files,
target = "production",
focus = "general",
context = "Public API behind a Cloudflare route, ~2k req/s at peak.",
prescan_facts = new
{
resources = new[] { new { id = "config:compat:2023-05-18", label = "compatibility_date 2023-05-18" } },
flags = new[]
{
new { id = "config:compat-date-stale", label = "compatibility_date is more than a year behind" },
new { id = "runtime:global-state:responseCache", label = "Module-level Map responseCache holds request data" },
new { id = "security:math-random:src/index.ts:21", label = "Math.random() builds the trace identifier" }
}
}
};
var est = await Clinic.Call("estimate", input);
Console.WriteLine(est.GetProperty("hold_credits").GetInt32());
Console.WriteLine(est.GetProperty("sponsor_enabled").GetBoolean());
// estimate is free: no job is created and nothing is charged.
5. Run it, then poll
POST /run takes {"input": {…}} and returns a job_id; poll
GET jobs/{job_id} until status is succeeded or
failed. The review JSON is the string at data.output.output.
Always send an Idempotency-Key. Derive it from the input, as the web
app does — it uses workers-clinic:<hash>:<length>:a<attempt>, e.g.
workers-clinic:1a2b3c4d:1812:a1. A retried request carrying the same key
returns the same job instead of billing a second run, which is exactly what makes a CI retry after a
dropped connection safe: a retry with a fresh key double-bills. Replaying a key
with a different body is a 409 conflict, so bump the attempt suffix whenever
the input actually changed — which is also how the app distinguishes its automatic reformat retry
(:a2) from a deliberate re-run.
# Always send an Idempotency-Key derived from the input. A retried request with
# the same key returns the SAME job instead of billing a second run.
KEY="workers-clinic:$(printf '%s' "$INPUT" | shasum -a 256 | cut -c1-16):a1"
JOB=$(curl -sS -X POST "$BASE/run" \
-H "Authorization: Bearer $TOKEN" \
-H "X-App-Slug: $SLUG" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $KEY" \
-d "{\"input\": $INPUT}" | python3 -c 'import sys,json;print(json.load(sys.stdin)["data"]["job_id"])')
# Poll until the job reaches a terminal status. Two seconds is plenty - a tight
# loop earns a 429.
while :; do
OUT=$(call "jobs/$JOB")
STATUS=$(printf '%s' "$OUT" | python3 -c 'import sys,json;print(json.load(sys.stdin)["data"]["status"])')
[ "$STATUS" = "succeeded" ] && break
[ "$STATUS" = "failed" ] && echo "$OUT" && exit 1
sleep 2
done
printf '%s' "$OUT" | python3 -c 'import sys,json;print(json.load(sys.stdin)["data"]["output"]["output"])'
import hashlib, time
# Always send an Idempotency-Key derived from the input: a retried request with
# the same key returns the SAME job instead of billing a second run. A retry with
# a FRESH key is a second review and a second charge.
digest = hashlib.sha256(json.dumps(INPUT, sort_keys=True).encode()).hexdigest()[:16]
key = f"workers-clinic:{digest}:a1"
def submit(body, idem):
req = urllib.request.Request(f"{BASE}/run", data=json.dumps({"input": body}).encode(), method="POST")
req.add_header("Authorization", f"Bearer {TOKEN}")
req.add_header("X-App-Slug", SLUG)
req.add_header("Content-Type", "application/json")
req.add_header("Idempotency-Key", idem)
with urllib.request.urlopen(req) as r:
return json.load(r)["data"]["job_id"]
job_id = submit(INPUT, key)
while True:
job = call(f"jobs/{job_id}")
if job["status"] == "succeeded":
break
if job["status"] == "failed":
raise RuntimeError(job.get("error"))
time.sleep(2)
review = json.loads(job["output"]["output"])
print(review["readiness"], "|", review["verdict"])
print(review["worker_name"], review["entrypoint"], review["compat_date"])
print(len(review["findings"]), "findings", len(review["checks"]), "checks")
import { createHash } from "node:crypto";
// Always send an Idempotency-Key derived from the input: a retried request with
// the same key returns the SAME job instead of billing a second run.
const digest = createHash("sha256").update(JSON.stringify(INPUT)).digest("hex").slice(0, 16);
const key = `workers-clinic:${digest}:a1`;
const started = await fetch(`${BASE}/run`, {
method: "POST",
headers: {
Authorization: `Bearer ${TOKEN}`,
"X-App-Slug": SLUG,
"Content-Type": "application/json",
"Idempotency-Key": key,
},
body: JSON.stringify({ input: INPUT }),
}).then((r) => r.json());
let job = started.data;
while (job.status !== "succeeded" && job.status !== "failed") {
await new Promise((r) => setTimeout(r, 2000));
job = await call(`jobs/${job.job_id}`);
}
if (job.status === "failed") throw new Error(JSON.stringify(job.error));
const review = JSON.parse(job.output.output);
console.log(review.readiness, review.worker_name, review.findings.length, "findings");
// Always send an Idempotency-Key derived from the input: a retried request with
// the same key returns the SAME job instead of billing a second run.
body, _ := json.Marshal(map[string]any{"input": input})
sum := sha256.Sum256(body)
key := fmt.Sprintf("workers-clinic:%x:a1", sum[:8])
req, _ := http.NewRequest(http.MethodPost, base+"/run", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("X-App-Slug", slug)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", key)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
var started struct {
Data struct {
JobID string `json:"job_id"`
} `json:"data"`
}
_ = json.NewDecoder(res.Body).Decode(&started)
for {
raw, err := call("jobs/"+started.Data.JobID, nil)
if err != nil {
panic(err)
}
var job struct {
Status string `json:"status"`
Output struct {
Output string `json:"output"`
} `json:"output"`
}
_ = json.Unmarshal(raw, &job)
if job.Status == "succeeded" {
fmt.Println(job.Output.Output) // the review JSON, as a string
break
}
if job.Status == "failed" {
panic("run failed")
}
time.Sleep(2 * time.Second)
}
// Always send an Idempotency-Key derived from the input: a retried request with
// the same key returns the SAME job instead of billing a second run.
var payload = "{\"input\": " + input + "}";
var digest = java.security.MessageDigest.getInstance("SHA-256")
.digest(payload.getBytes(java.nio.charset.StandardCharsets.UTF_8));
var key = "workers-clinic:" + java.util.HexFormat.of().formatHex(digest).substring(0, 16) + ":a1";
var start = HttpRequest.newBuilder(URI.create(BASE + "/run"))
.header("Authorization", "Bearer " + TOKEN)
.header("X-App-Slug", SLUG)
.header("Content-Type", "application/json")
.header("Idempotency-Key", key)
.POST(HttpRequest.BodyPublishers.ofString(payload))
.build();
String started = HTTP.send(start, HttpResponse.BodyHandlers.ofString()).body();
// Parse job_id out of `started`, then poll GET jobs/{job_id} every two seconds
// until status is "succeeded" or "failed"; the review JSON is data.output.output.
System.out.println(started);
require "digest"
# Always send an Idempotency-Key derived from the input: a retried request with
# the same key returns the SAME job instead of billing a second run.
payload = JSON.generate({ "input" => input })
digest = Digest::SHA256.hexdigest(payload)[0, 16]
key = "workers-clinic:#{digest}:a1"
uri = URI("#{BASE}/run")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer #{TOKEN}"
req["X-App-Slug"] = SLUG
req["Content-Type"] = "application/json"
req["Idempotency-Key"] = key
req.body = payload
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
job_id = JSON.parse(res.body)["data"]["job_id"]
loop do
job = call("jobs/#{job_id}")
break puts(job["output"]["output"]) if job["status"] == "succeeded"
raise "run failed" if job["status"] == "failed"
sleep 2
end
<?php
// Always send an Idempotency-Key derived from the input: a retried request with
// the same key returns the SAME job instead of billing a second run.
$payload = json_encode(["input" => $input]);
$digest = substr(hash("sha256", $payload), 0, 16);
$key = "workers-clinic:{$digest}:a1";
$ch = curl_init(BASE . "/run");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer " . TOKEN,
"X-App-Slug: " . SLUG,
"Content-Type: application/json",
"Idempotency-Key: " . $key,
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$jobId = json_decode(curl_exec($ch), true)["data"]["job_id"];
curl_close($ch);
while (true) {
$job = call("jobs/" . $jobId);
if ($job["status"] === "succeeded") { echo $job["output"]["output"]; break; }
if ($job["status"] === "failed") { throw new RuntimeException("run failed"); }
sleep(2);
}
using System.Security.Cryptography;
using System.Text;
// Always send an Idempotency-Key derived from the input: a retried request with
// the same key returns the SAME job instead of billing a second run.
var payload = JsonSerializer.Serialize(new { input });
var digest = Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(payload)))[..16].ToLowerInvariant();
var key = $"workers-clinic:{digest}:a1";
var run = new HttpRequestMessage(HttpMethod.Post, "https://api.skillsafe.ai/v1/app-api/run");
run.Headers.Add("Authorization", "Bearer YOUR_TOKEN");
run.Headers.Add("X-App-Slug", "workers-clinic");
run.Headers.Add("Idempotency-Key", key);
run.Content = new StringContent(payload, Encoding.UTF8, "application/json");
// POST it, read data.job_id, then poll GET jobs/{job_id} every two seconds until
// status is "succeeded" or "failed"; the review JSON is data.output.output.
6. Or stream it
POST /run-stream is the same call over server-sent events: an event: job
with the job id, a run of event: delta frames each carrying a chunk of the JSON review
in {"text": "…"}, and a terminal event: done carrying
status, charged_credits and the truncated flag. Send the same
Idempotency-Key you would send to /run.
The app's own page uses this endpoint, and the trick it uses is worth stealing: because the model
emits the object's keys in a fixed order, you can drive a staged progress list purely by watching
for the top-level JSON keys as they arrive in the accumulated text. No parsing, no partial
JSON repair — a substring search for "checks" is enough to mark the checks stage done
and light up the next one. The stages the page advances through are:
Read the project (no key - it starts immediately)
Establish the Worker, its entrypoint and its readiness "worker_name" "readiness"
Map every binding against every env read "bindings"
Work the twelve checks "checks"
Write the findings with corrected fragments "findings"
Reconcile the prescan "coverage_check"
Produce the hardened config and the verification commands "hardened_config" "commands"
Choose focus areas and summarize "focus_areas" "summary"
The other reason to stream is recovery. If the connection dies mid-flight you still hold every byte
that arrived, and the sections that completed are readable — the app keeps whatever parsed rather
than discarding the run. A truncated: true on the done event means the
reply hit the output cap: what you hold is a prefix, not a review, and the fix is a retry with a
retry_note asking for fewer, denser findings, not an attempt to repair the JSON.
# Server-sent events. Each `delta` carries a chunk of the JSON review; the final
# `done` event carries the status, charged_credits and the truncated flag.
curl -N -X POST "$BASE/run-stream" \
-H "Authorization: Bearer $TOKEN" \
-H "X-App-Slug: $SLUG" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $KEY" \
-H "Accept: text/event-stream" \
-d "{\"input\": $INPUT}"
# event: job {"job_id":"job_..."}
# event: delta {"text":"{\"review_name\":\"edge-api"}
# event: delta {"text":" - production readiness review\",\"readiness\":\"not-ready\","}
# event: delta {"text":"\"checks\":[{\"check\":\"compatibility_date\","}
# event: done {"status":"succeeded","charged_credits":517,"truncated":false}
# Server-sent events: the review arrives in chunks, so a UI can show progress.
req = urllib.request.Request(f"{BASE}/run-stream",
data=json.dumps({"input": INPUT}).encode(), method="POST")
req.add_header("Authorization", f"Bearer {TOKEN}")
req.add_header("X-App-Slug", SLUG)
req.add_header("Content-Type", "application/json")
req.add_header("Idempotency-Key", key)
req.add_header("Accept", "text/event-stream")
# The same staged progress the web page shows: watch for the top-level keys as
# they arrive. No partial-JSON parsing is needed to know where the model is.
STAGES = [
("Establish the Worker and its readiness", ['"worker_name"', '"readiness"']),
("Map the bindings", ['"bindings"']),
("Work the twelve checks", ['"checks"']),
("Write the findings", ['"findings"']),
("Reconcile the prescan", ['"coverage_check"']),
("Hardened config and commands", ['"hardened_config"', '"commands"']),
("Focus areas and summary", ['"focus_areas"', '"summary"']),
]
raw = ""
done = {}
event = None
announced = set()
with urllib.request.urlopen(req) as stream:
for line in stream:
line = line.decode().rstrip("\n")
if line.startswith("event: "):
event = line[7:]
elif line.startswith("data: ") and event == "delta":
raw += json.loads(line[6:]).get("text", "")
for label, keys in STAGES:
if label not in announced and all(k in raw for k in keys):
announced.add(label)
print("...", label)
elif line.startswith("data: ") and event == "done":
done = json.loads(line[6:])
review = json.loads(raw[raw.index("{"):raw.rindex("}") + 1])
print(review["readiness"], len(review["findings"]), "findings",
"TRUNCATED" if done.get("truncated") else f"charged {done.get('charged_credits')}")
// Server-sent events: the review arrives in chunks, so a UI can show progress.
const res = await fetch(`${BASE}/run-stream`, {
method: "POST",
headers: {
Authorization: `Bearer ${TOKEN}`,
"X-App-Slug": SLUG,
"Content-Type": "application/json",
"Idempotency-Key": key,
Accept: "text/event-stream",
},
body: JSON.stringify({ input: INPUT }),
});
// The staged progress list the app itself uses: each stage is done once its
// top-level keys have appeared in the accumulated text.
const STAGES = [
["Establish the Worker and its readiness", ['"worker_name"', '"readiness"']],
["Map the bindings", ['"bindings"']],
["Work the twelve checks", ['"checks"']],
["Write the findings", ['"findings"']],
["Reconcile the prescan", ['"coverage_check"']],
["Hardened config and commands", ['"hardened_config"', '"commands"']],
["Focus areas and summary", ['"focus_areas"', '"summary"']],
];
const reader = res.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
let raw = "";
let done = {};
let event = null;
const announced = new Set();
while (true) {
const chunk = await reader.read();
if (chunk.done) break;
buffer += decoder.decode(chunk.value, { stream: true });
const lines = buffer.split("\n");
buffer = lines.pop();
for (const line of lines) {
if (line.startsWith("event: ")) event = line.slice(7);
else if (line.startsWith("data: ") && event === "delta") {
raw += JSON.parse(line.slice(6)).text ?? "";
for (const [label, keys] of STAGES) {
if (!announced.has(label) && keys.every((k) => raw.includes(k))) {
announced.add(label);
console.log("...", label);
}
}
} else if (line.startsWith("data: ") && event === "done") {
done = JSON.parse(line.slice(6));
}
}
}
const review = JSON.parse(raw.slice(raw.indexOf("{"), raw.lastIndexOf("}") + 1));
console.log(review.readiness, review.findings.length, "findings", done.charged_credits);
// Server-sent events: the review arrives in chunks, so a UI can show progress.
req, _ = http.NewRequest(http.MethodPost, base+"/run-stream", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("X-App-Slug", slug)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", key)
req.Header.Set("Accept", "text/event-stream")
res, _ = http.DefaultClient.Do(req)
defer res.Body.Close()
var raw strings.Builder
var event string
sc := bufio.NewScanner(res.Body)
sc.Buffer(make([]byte, 0, 64*1024), 4*1024*1024)
for sc.Scan() {
line := sc.Text()
switch {
case strings.HasPrefix(line, "event: "):
event = strings.TrimPrefix(line, "event: ")
case strings.HasPrefix(line, "data: ") && event == "delta":
var d struct {
Text string `json:"text"`
}
_ = json.Unmarshal([]byte(strings.TrimPrefix(line, "data: ")), &d)
raw.WriteString(d.Text)
// Staged progress without parsing: the top-level keys arrive in order.
if strings.Contains(raw.String(), "\"coverage_check\"") {
// the prescan reconciliation has started
}
case strings.HasPrefix(line, "data: ") && event == "done":
fmt.Println(strings.TrimPrefix(line, "data: ")) // status, charged_credits, truncated
}
}
fmt.Println(raw.String())
// Server-sent events: the review arrives in chunks, so a UI can show progress.
var stream = HttpRequest.newBuilder(URI.create(BASE + "/run-stream"))
.header("Authorization", "Bearer " + TOKEN)
.header("X-App-Slug", SLUG)
.header("Content-Type", "application/json")
.header("Idempotency-Key", key)
.header("Accept", "text/event-stream")
.POST(HttpRequest.BodyPublishers.ofString("{\"input\": " + input + "}"))
.build();
StringBuilder raw = new StringBuilder();
String[] event = { null };
HTTP.send(stream, HttpResponse.BodyHandlers.ofLines()).body().forEach(line -> {
if (line.startsWith("event: ")) event[0] = line.substring(7);
else if (line.startsWith("data: ") && "delta".equals(event[0])) {
raw.append(line.substring(6)); // each data line is {"text":"..."} - decode and append .text
// A staged progress display only needs substring checks on the accumulated
// text: "bindings", "checks", "findings", "coverage_check", "summary".
}
});
System.out.println(raw);
# Server-sent events: the review arrives in chunks, so a UI can show progress.
uri = URI("#{BASE}/run-stream")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer #{TOKEN}"
req["X-App-Slug"] = SLUG
req["Content-Type"] = "application/json"
req["Idempotency-Key"] = key
req["Accept"] = "text/event-stream"
req.body = JSON.generate({ "input" => input })
STAGES = [
["Map the bindings", ['"bindings"']],
["Work the twelve checks", ['"checks"']],
["Write the findings", ['"findings"']],
["Reconcile the prescan", ['"coverage_check"']],
["Focus areas and summary", ['"focus_areas"', '"summary"']]
].freeze
raw = +""
event = nil
seen = {}
Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(req) do |res|
res.read_body do |chunk|
chunk.each_line do |line|
line = line.chomp
if line.start_with?("event: ") then event = line[7..]
elsif line.start_with?("data: ") && event == "delta"
raw << (JSON.parse(line[6..])["text"] || "")
STAGES.each do |label, keys|
next if seen[label]
next unless keys.all? { |k| raw.include?(k) }
seen[label] = true
puts "... #{label}"
end
end
end
end
end
end
review = JSON.parse(raw[raw.index("{")..raw.rindex("}")])
puts "#{review['readiness']} #{review['findings'].length} findings"
<?php
// Server-sent events: the review arrives in chunks, so a UI can show progress.
$raw = "";
$event = null;
$ch = curl_init(BASE . "/run-stream");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(["input" => $input]));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer " . TOKEN,
"X-App-Slug: " . SLUG,
"Content-Type: application/json",
"Idempotency-Key: " . $key,
"Accept: text/event-stream",
]);
curl_setopt($ch, CURLOPT_WRITEFUNCTION, function ($ch, $chunk) use (&$raw, &$event) {
foreach (explode("\n", $chunk) as $line) {
if (str_starts_with($line, "event: ")) {
$event = substr($line, 7);
} elseif (str_starts_with($line, "data: ") && $event === "delta") {
$raw .= json_decode(substr($line, 6), true)["text"] ?? "";
}
}
return strlen($chunk);
});
curl_exec($ch);
curl_close($ch);
$review = json_decode(substr($raw, strpos($raw, "{")), true);
echo $review["readiness"], PHP_EOL;
// Server-sent events: the review arrives in chunks, so a UI can show progress.
var stream = new HttpRequestMessage(HttpMethod.Post, "https://api.skillsafe.ai/v1/app-api/run-stream");
stream.Headers.Add("Authorization", "Bearer YOUR_TOKEN");
stream.Headers.Add("X-App-Slug", "workers-clinic");
stream.Headers.Add("Idempotency-Key", key);
stream.Headers.Add("Accept", "text/event-stream");
stream.Content = new StringContent(payload, Encoding.UTF8, "application/json");
using var res = await Http.SendAsync(stream, HttpCompletionOption.ResponseHeadersRead);
using var reader = new StreamReader(await res.Content.ReadAsStreamAsync());
var raw = new StringBuilder();
string? evt = null;
while (await reader.ReadLineAsync() is { } line)
{
if (line.StartsWith("event: ")) evt = line[7..];
else if (line.StartsWith("data: ") && evt == "delta")
{
var d = JsonSerializer.Deserialize<JsonElement>(line[6..]);
if (d.TryGetProperty("text", out var t)) raw.Append(t.GetString());
// Staged progress: check raw for "checks", "findings", "coverage_check".
}
}
Console.WriteLine(raw.ToString());
7. Parse the review and check the reconciliation
data.output.output is a string holding one JSON object, so unwrap it twice: the
envelope, then the review. Be tolerant in the same way the app is — strip a stray code fence, then
slice from the first { to the last } before parsing.
Three invariants are worth enforcing on your side, because the app enforces them too:
- Every
prescan_facts.flagsid appears exactly once incoverage_check, and no id appears that you did not send. A flag missing from the reconciliation means a fact you established was quietly skipped — treat that as a failed run, not a passing one, and retry with aretry_notenaming the missing ids. checkshas all twelve entries, in the documented order. Fewer means the reply was cut short or the model improvised; the app renders the table by index, so a short array is a defect.- Every
focus_areas[].finding_idsentry names a realfindings[].id. The app silently drops unknown ids rather than showing a dangling reference, which is convenient in a UI and misleading in a script — check it explicitly.
It is also worth applying the same coercions the app applies, so your consumer never sees a value
outside the documented enums: an unrecognised category becomes config,
an unrecognised severity, likelihood or priority becomes
medium, an unrecognised readiness becomes needs-work, an
unrecognised check status becomes unknown, and a missing
worker_name, entrypoint, compat_date or
deploy_target becomes the string "unknown". Findings with neither a
problem nor a fix are dropped; if that empties the array, the reply is not
a review — the app treats it as a parse failure and retries once with a retry_note.
# The review JSON is a string inside the envelope, so unwrap it twice.
REVIEW=$(printf '%s' "$OUT" | python3 -c 'import sys,json;print(json.load(sys.stdin)["data"]["output"]["output"])')
printf '%s' "$REVIEW" | python3 -c '
import sys, json
r = json.load(sys.stdin)
print(r["readiness"], "|", r["verdict"])
print(r["worker_name"], r["entrypoint"], r["compat_date"], r["deploy_target"])
for c in r["checks"]:
print(f" {c[\"status\"]:8} {c[\"check\"]:28} {c[\"evidence\"]}")
for b in r["bindings"]:
print(f" {b[\"binding\"]:16} {b[\"type\"]:18} declared={b[\"declared\"]} used={b[\"used\"]}")
for f in r["findings"]:
print(f" {f[\"id\"]} {f[\"priority\"]:8} {f[\"category\"]:14} {f[\"resource\"]}")
print(r["hardened_config"])
print("\n".join(r["commands"]))
'
# Every prescan flag id must come back exactly once in coverage_check.
printf '%s' "$REVIEW" | python3 -c '
import sys, json
seen = [c["id"] for c in json.load(sys.stdin)["coverage_check"]]
want = ["config:compat-date-stale", "runtime:global-state:responseCache",
"security:math-random:src/index.ts:21"]
missing = [i for i in want if seen.count(i) != 1]
if missing:
raise SystemExit("unreconciled prescan flags: " + ", ".join(missing))
print("coverage_check reconciles")
'
import re
CHECKS = ["compatibility_date", "nodejs_compat", "Generated types", "Secrets",
"Bindings declared and used", "Response streaming", "Promise handling",
"Bindings over REST", "Hyperdrive", "Observability",
"Request-scoped state", "Error handling"]
def parse_result(text):
"""Tolerant of a stray code fence, exactly like the web app."""
t = re.sub(r"^```[a-z]*\s*", "", str(text or "").strip())
t = re.sub(r"```\s*$", "", t)
i, j = t.find("{"), t.rfind("}")
if i < 0 or j <= i:
raise ValueError("no JSON object found")
return json.loads(t[i:j + 1])
review = parse_result(job["output"]["output"])
# 1. Every prescan flag id appears exactly once in coverage_check, and nothing else does.
sent = [f["id"] for f in INPUT["prescan_facts"]["flags"]]
seen = [c["id"] for c in review["coverage_check"]]
missing = [i for i in sent if seen.count(i) != 1]
invented = [i for i in seen if i not in sent]
if missing or invented:
raise RuntimeError(f"coverage_check drift: missing={missing} invented={invented}")
# 2. All twelve checks, in order.
got = [c["check"] for c in review["checks"]]
if got != CHECKS:
raise RuntimeError(f"checks drift: {got}")
# 3. Every focus_areas finding id exists in findings.
ids = {f["id"] for f in review["findings"]}
for area in review["focus_areas"]:
for fid in area["finding_ids"]:
if fid not in ids:
raise RuntimeError(f"focus_areas references unknown finding {fid}")
# A truncated reply is a prefix, not a review. Retry, do not repair.
if job.get("truncated"):
INPUT["retry_note"] = (
"The previous reply was truncated. Return the same twelve checks but at most "
"eight findings, each with a shorter snippet, and keep hardened_config complete."
)
# ... resubmit with an incremented attempt suffix in the Idempotency-Key.
print(review["readiness"], "|", review["verdict"])
for c in review["checks"]:
print(f"{c['status']:8} {c['check']:28} {c['evidence']}")
for b in review["bindings"]:
flagged = "" if b["declared"] and b["used"] else " <-- look at this one"
print(f"{b['binding']:16} {b['type']:18} {b['note']}{flagged}")
for f in review["findings"]:
print(f["id"], f["priority"], f["category"], f["resource"])
print(" ", f["fix"])
print(review["hardened_config"]) # a complete wrangler.jsonc, secrets named not echoed
print("\n".join(review["commands"])) # ends with a real deploy dry run
const CHECKS = [
"compatibility_date", "nodejs_compat", "Generated types", "Secrets",
"Bindings declared and used", "Response streaming", "Promise handling",
"Bindings over REST", "Hyperdrive", "Observability",
"Request-scoped state", "Error handling",
];
function parseResult(text) {
// Tolerant of a stray code fence, exactly like the web app.
const t = String(text ?? "").trim().replace(/^```[a-z]*\s*/i, "").replace(/```\s*$/, "");
const i = t.indexOf("{");
const j = t.lastIndexOf("}");
if (i < 0 || j <= i) throw new Error("no JSON object found");
return JSON.parse(t.slice(i, j + 1));
}
const review = parseResult(job.output.output);
// 1. Every prescan flag id appears exactly once in coverage_check.
const sent = INPUT.prescan_facts.flags.map((f) => f.id);
const seen = review.coverage_check.map((c) => c.id);
const missing = sent.filter((id) => seen.filter((s) => s === id).length !== 1);
const invented = seen.filter((id) => !sent.includes(id));
if (missing.length || invented.length) {
throw new Error(`coverage_check drift: missing=${missing} invented=${invented}`);
}
// 2. All twelve checks, in order.
const got = review.checks.map((c) => c.check);
if (got.join("|") !== CHECKS.join("|")) throw new Error(`checks drift: ${got}`);
// 3. Every focus_areas finding id exists in findings.
const ids = new Set(review.findings.map((f) => f.id));
for (const area of review.focus_areas) {
for (const fid of area.finding_ids) {
if (!ids.has(fid)) throw new Error(`focus_areas references unknown finding ${fid}`);
}
}
// A truncated reply is a prefix, not a review. Retry, do not repair.
if (job.truncated) {
INPUT.retry_note =
"The previous reply was truncated. Return the same twelve checks but at most eight findings.";
}
console.log(review.readiness, "|", review.verdict);
for (const c of review.checks) console.log(c.status.padEnd(8), c.check, "-", c.evidence);
for (const b of review.bindings) {
console.log(b.binding.padEnd(16), b.type, `declared=${b.declared} used=${b.used}`, b.note);
}
for (const f of review.findings) console.log(f.id, f.priority, f.category, f.resource);
console.log(review.hardened_config);
console.log(review.commands.join("\n"));
type check struct {
Check string `json:"check"`
Status string `json:"status"`
Evidence string `json:"evidence"`
Requirement string `json:"requirement"`
}
type binding struct {
Binding string `json:"binding"`
Type string `json:"type"`
Declared bool `json:"declared"`
Used bool `json:"used"`
Note string `json:"note"`
}
type finding struct {
ID string `json:"id"`
Category string `json:"category"`
Severity string `json:"severity"`
Likelihood string `json:"likelihood"`
Priority string `json:"priority"`
Resource string `json:"resource"`
Problem string `json:"problem"`
Impact string `json:"impact"`
Fix string `json:"fix"`
Snippet string `json:"snippet"`
}
type review struct {
ReviewName string `json:"review_name"`
Readiness string `json:"readiness"`
Verdict string `json:"verdict"`
WorkerName string `json:"worker_name"`
Entrypoint string `json:"entrypoint"`
CompatDate string `json:"compat_date"`
DeployTarget string `json:"deploy_target"`
Bindings []binding `json:"bindings"`
Checks []check `json:"checks"`
Findings []finding `json:"findings"`
HardenedConfig string `json:"hardened_config"`
Commands []string `json:"commands"`
CoverageCheck []struct {
ID string `json:"id"`
Addressed bool `json:"addressed"`
Note string `json:"note"`
} `json:"coverage_check"`
}
var r review
if err := json.Unmarshal([]byte(job.Output.Output), &r); err != nil {
panic(err)
}
// Every prescan flag id must come back exactly once in coverage_check.
count := map[string]int{}
for _, c := range r.CoverageCheck {
count[c.ID]++
}
for _, id := range []string{
"config:compat-date-stale",
"runtime:global-state:responseCache",
"security:math-random:src/index.ts:21",
} {
if count[id] != 1 {
panic("unreconciled prescan flag: " + id)
}
}
if len(r.Checks) != 12 {
panic(fmt.Sprintf("expected twelve checks, got %d", len(r.Checks)))
}
fmt.Println(r.Readiness, r.WorkerName, r.CompatDate, len(r.Findings), "findings")
fmt.Println(r.HardenedConfig)
// The review JSON is a string inside data.output.output - parse it, then check
// the three invariants before you trust it:
//
// 1. every prescan_facts.flags id appears exactly once in coverage_check, and
// no id appears that the prescan did not send;
// 2. checks has all twelve entries, in the documented order;
// 3. every focus_areas[].finding_ids entry names an id present in findings.
//
// A `truncated` job is a prefix, not a review: resubmit with a retry_note such as
// "The previous reply was truncated. Return the same twelve checks but at most
// eight findings" and an incremented attempt suffix on the Idempotency-Key.
String reviewJson = /* data.output.output */ call("jobs/" + jobId, null);
System.out.println(reviewJson);
// checks[] is always the same twelve entries in the same order, so a table can be
// rendered by index without searching for a check by name. bindings[] is the
// interesting join: declared=false means the code reads a binding nothing
// declares, used=false means config carries one nothing reads.
CHECKS = [
"compatibility_date", "nodejs_compat", "Generated types", "Secrets",
"Bindings declared and used", "Response streaming", "Promise handling",
"Bindings over REST", "Hyperdrive", "Observability",
"Request-scoped state", "Error handling"
].freeze
review = JSON.parse(job["output"]["output"])
# 1. Every prescan flag id appears exactly once in coverage_check.
sent = input["prescan_facts"]["flags"].map { |f| f["id"] }
seen = review["coverage_check"].map { |c| c["id"] }
missing = sent.reject { |id| seen.count(id) == 1 }
invented = seen - sent
raise "coverage_check drift: #{missing} / #{invented}" unless missing.empty? && invented.empty?
# 2. All twelve checks, in order.
got = review["checks"].map { |c| c["check"] }
raise "checks drift: #{got}" unless got == CHECKS
# 3. Every focus_areas finding id exists in findings.
ids = review["findings"].map { |f| f["id"] }
review["focus_areas"].each do |area|
area["finding_ids"].each { |fid| raise "unknown finding #{fid}" unless ids.include?(fid) }
end
review["checks"].each { |c| puts format("%-8s %s", c["status"], c["check"]) }
review["bindings"].each { |b| puts "#{b['binding']} #{b['type']} declared=#{b['declared']} used=#{b['used']}" }
review["findings"].each { |f| puts "#{f['id']} #{f['priority']} #{f['category']} #{f['resource']}" }
puts review["hardened_config"]
puts review["commands"].last # a real deploy dry run
<?php
$CHECKS = [
"compatibility_date", "nodejs_compat", "Generated types", "Secrets",
"Bindings declared and used", "Response streaming", "Promise handling",
"Bindings over REST", "Hyperdrive", "Observability",
"Request-scoped state", "Error handling",
];
$review = json_decode($job["output"]["output"], true);
// 1. Every prescan flag id appears exactly once in coverage_check.
$sent = array_column($input["prescan_facts"]["flags"], "id");
$seen = array_column($review["coverage_check"], "id");
$counts = array_count_values($seen);
foreach ($sent as $id) {
if (($counts[$id] ?? 0) !== 1) {
throw new RuntimeException("unreconciled prescan flag: " . $id);
}
}
// 2. All twelve checks, in order.
if (array_column($review["checks"], "check") !== $CHECKS) {
throw new RuntimeException("checks drift");
}
// 3. Every focus_areas finding id exists in findings.
$ids = array_column($review["findings"], "id");
foreach ($review["focus_areas"] as $area) {
foreach ($area["finding_ids"] as $fid) {
if (!in_array($fid, $ids, true)) {
throw new RuntimeException("focus_areas references unknown finding " . $fid);
}
}
}
foreach ($review["checks"] as $c) {
printf("%-8s %s\n", $c["status"], $c["check"]);
}
echo $review["hardened_config"], PHP_EOL;
var review = JsonSerializer.Deserialize<JsonElement>(reviewJson);
// 1. Every prescan flag id appears exactly once in coverage_check.
var seen = review.GetProperty("coverage_check")
.EnumerateArray()
.Select(c => c.GetProperty("id").GetString())
.ToList();
foreach (var id in new[]
{
"config:compat-date-stale",
"runtime:global-state:responseCache",
"security:math-random:src/index.ts:21"
})
{
if (seen.Count(s => s == id) != 1)
throw new Exception($"unreconciled prescan flag: {id}");
}
// 2. All twelve checks, in order.
if (review.GetProperty("checks").GetArrayLength() != 12)
throw new Exception("expected twelve checks");
// 3. Every focus_areas finding id exists in findings.
var ids = review.GetProperty("findings")
.EnumerateArray()
.Select(f => f.GetProperty("id").GetString())
.ToHashSet();
foreach (var area in review.GetProperty("focus_areas").EnumerateArray())
foreach (var fid in area.GetProperty("finding_ids").EnumerateArray())
if (!ids.Contains(fid.GetString()))
throw new Exception($"focus_areas references unknown finding {fid}");
foreach (var c in review.GetProperty("checks").EnumerateArray())
Console.WriteLine($"{c.GetProperty("status")} {c.GetProperty("check")}");
Console.WriteLine(review.GetProperty("hardened_config").GetString());
The output contract
data.output.output is a JSON string holding one object. This is exactly what the web app
parses, so anything that renders here will render there. Trimmed to two checks, two findings and
short strings:
{
"review_name": "edge-api — production readiness review",
"readiness": "not-ready",
"verdict": "A module-level Map caches response bodies across requests, so one user's response can be served to another.",
"worker_name": "edge-api",
"entrypoint": "src/index.ts",
"compat_date": "2023-05-18",
"deploy_target": "production, internet-facing",
"exec_summary": "edge-api is a thin read-through cache in front of an internal origin...\n\nThe shape of the problem is request-scoped state...\n\nDo the cache first; the rest is config work.",
"assumptions": [
"The Worker is deployed with `wrangler deploy` from this config; no named environment was pasted."
],
"open_questions": [
"Is the upstream response ever user-specific? If so the module-level cache is a data-leak, not just a correctness bug."
],
"bindings": [
{ "binding": "CACHE", "type": "KV namespace", "declared": true, "used": false,
"note": "declared in wrangler.jsonc but nothing in src/index.ts reads env.CACHE" },
{ "binding": "UPSTREAM_BASE", "type": "plain var", "declared": true, "used": true,
"note": "read once in fetch to build the upstream URL" }
],
"checks": [
{ "check": "compatibility_date", "status": "fail",
"evidence": "wrangler.jsonc sets \"compatibility_date\": \"2023-05-18\"",
"requirement": "A compatibility_date within the last few months, bumped deliberately." },
{ "check": "Request-scoped state", "status": "fail",
"evidence": "src/index.ts:16 `const responseCache = new Map()` at module scope, written per request",
"requirement": "No module-level mutable holding request data; use the KV binding or the Cache API." }
],
"findings": [
{
"id": "WC-001",
"category": "runtime",
"severity": "high",
"likelihood": "high",
"priority": "critical",
"resource": "src/index.ts:16",
"problem": "`responseCache` is a module-level Map written on every request (runtime:global-state:responseCache).",
"impact": "Module scope is shared by every request an isolate serves, so a body cached for one path or one user is returned to the next caller of that path.",
"fix": "Delete the Map and read through the CACHE KV binding, or use the runtime Cache API keyed on the request.",
"snippet": "const hit = await env.CACHE.get(url.pathname);\nif (hit) return new Response(hit);"
},
{
"id": "WC-002",
"category": "config",
"severity": "medium",
"likelihood": "high",
"priority": "high",
"resource": "wrangler.jsonc",
"problem": "compatibility_date is 2023-05-18 (config:compat-date-stale).",
"impact": "The Worker runs on a runtime two years behind the one you test against locally.",
"fix": "Bump compatibility_date to today's date and re-run the test suite before deploying.",
"snippet": "\"compatibility_date\": \"2026-08-12\""
}
],
"coverage_check": [
{ "id": "config:compat-date-stale", "addressed": true, "note": "WC-002." },
{ "id": "runtime:global-state:responseCache", "addressed": true, "note": "WC-001." },
{ "id": "security:math-random:src/index.ts:21", "addressed": false,
"note": "The value is only a log trace id, never a token or a cache key, so Math.random is adequate here." }
],
"hardened_config": "{\n // Bumped: the old date pinned a two-year-old runtime.\n \"compatibility_date\": \"2026-08-12\",\n \"name\": \"edge-api\",\n \"main\": \"src/index.ts\",\n \"observability\": { \"enabled\": true, \"head_sampling_rate\": 0.1 },\n \"kv_namespaces\": [{ \"binding\": \"CACHE\", \"id\": \"9d1f0e7c4b2a4f9e\" }],\n \"vars\": { \"UPSTREAM_BASE\": \"https://origin.internal.example.com\" }\n // Secrets: npx wrangler secret put UPSTREAM_TOKEN\n}",
"commands": [
"npx wrangler types # regenerate Env from the config",
"npx tsc --noEmit # the generated Env must typecheck",
"npx wrangler deploy --dry-run # config validation without deploying"
],
"quick_wins": [
"Add \"observability\": { \"enabled\": true, \"head_sampling_rate\": 0.1 } to wrangler.jsonc."
],
"focus_areas": [
{ "area": "Request-scoped state", "why": "The one issue that can serve one caller's data to another.",
"finding_ids": ["WC-001"] },
{ "area": "Config freshness", "why": "The runtime you deploy onto is not the one you test against.",
"finding_ids": ["WC-002"] }
],
"summary": "One paragraph you can paste into the pull request."
}
Every key
| key | type | meaning |
|---|---|---|
review_name | string | Short title naming the Worker and the target reviewed against. Defaults to "Untitled Workers review" if the model omits it. |
readiness | enum | ready, needs-work or not-ready. The single value a CI gate should branch on. |
verdict | string | One sentence naming the single thing that decides the readiness. |
worker_name | string | The name from the config, or "unknown" if no config was pasted. Never guessed. |
entrypoint | string | The main from the config, e.g. "src/index.ts", or "unknown". |
compat_date | string | The compatibility_date the config shows, or "unknown". This is the paste's value, not a recommendation — the recommendation lives in hardened_config. |
deploy_target | string | The target input echoed back in words, e.g. "production, internet-facing". Free text, not an enum — do not branch on it; branch on the target you sent. |
exec_summary | string | Two or three short paragraphs separated by blank lines: what the Worker does, what shape it is in, what to do first. Plain text — Markdown inside it renders literally. |
assumptions | string[] | What had to be assumed because it was not pasted — which environment deploys, whether a secret was pushed, what a clipped region contained. |
open_questions | string[] | Questions whose answers would change the review or its ordering. |
bindings | object[] | {binding, type, declared, used, note}, one row per binding the config declares or the code reads. declared and used are booleans; note is one clause. The interesting rows are the asymmetric ones: declared: false, used: true is an env.X read with nothing behind it, declared: true, used: false is config carrying weight for nothing. |
checks | object[] | {check, status, evidence, requirement}. Always the same twelve check strings in the same fixed order — render by index, do not search by name. evidence quotes or names the exact line, file and construct; requirement is what would make it pass, in one sentence. |
findings | object[] | {id, category, severity, likelihood, priority, resource, problem, impact, fix, snippet}. Ids are sequential WC-001, WC-002, … Always at least one entry — a genuinely clean Worker gets the highest-value remaining improvement at low priority and readiness: "ready". One finding per distinct problem, not one per occurrence. resource is path/to/file.ts:LINE where possible, otherwise the config key. snippet is a pasteable corrected TypeScript or JSONC fragment, or "" rather than a guess. |
coverage_check | object[] | {id, addressed, note}. Exactly one entry per prescan_facts.flags id, and no ids the prescan did not send. addressed: true means a finding covers it and note carries the finding id; addressed: false means it was deliberately set aside, with the one-sentence reason in note. Empty if you sent no prescan_facts. |
hardened_config | string | A complete, valid wrangler.jsonc for this Worker with the config-level findings applied: a current compatibility_date, the compatibility flags it needs, observability with an explicit sampling rate, and every binding the code actually reads. JSONC comments are part of the value and are what makes it readable. No secret values — a secret appears as a comment naming the wrangler secret put command that sets it. Trimmed; "" if no config could be produced. |
commands | string[] | Ordered shell commands that verify the result, each with a trailing comment. Read-only, and the last one is a real deploy dry run — typically npx wrangler types, npx tsc --noEmit, npx wrangler deploy --dry-run. |
quick_wins | string[] | Changes worth under fifteen minutes each, phrased as instructions. |
focus_areas | object[] | {area, why, finding_ids}, two to four themes. Every id in finding_ids must exist in findings — the app filters unknown ids out silently, so check them yourself. |
summary | string | One paragraph, written to be pasted into a pull request. |
The enums
| field | values | notes |
|---|---|---|
readiness | ready, needs-work, not-ready | not-ready means at least one finding would leak data, drop writes or break at runtime under real traffic. needs-work means named problems remain but nothing fails today. ready means the Worker can take traffic and what is left is improvement work. An unrecognised value coerces to needs-work. |
findings[].category | config, bindings, runtime, types, security, observability, architecture | config is the wrangler file, bindings is the declaration/use join and anything reaching a service the wrong way, runtime is behaviour under load — streaming, promises, module-level state — and architecture is the shape of the Worker itself. An unrecognised value coerces to config. |
findings[].severityfindings[].likelihood | low, medium, high | Severity is how bad it is if it happens; likelihood is how reachable it is from the code as pasted. Both coerce to medium if unrecognised. |
findings[].priority | critical, high, medium, low | Severity by likelihood, adjusted for target: a leaked credential on a production Worker is critical; the same pattern in a preview Worker with a test key is medium. This is the field a CI gate should count. Coerces to medium. |
checks[].status | pass, fail, weak, unknown | unknown is a legitimate answer — and the required one — when the paste does not contain the evidence: a check whose evidence was never pasted is not a passing check. weak means the thing exists but does not do its job, such as observability.enabled with no head_sampling_rate. Coerces to unknown. |
bindings[].declaredbindings[].used | true, false | Booleans, coerced from whatever arrives. A secret read from env that is absent from the config is not a missing binding — it is the correct pattern, and the note asks you to confirm wrangler secret put was run. |
The twelve checks
checks always carries these twelve check strings, in this order, on every
run — so a table can be rendered by index and two reviews of the same Worker are diffable row by
row:
1. compatibility_date present, and recent enough for the target
2. nodejs_compat set when a Node built-in is imported, absent when nothing needs it
3. Generated types Env from `wrangler types`, no `any`, no `as unknown as` double casts
4. Secrets nothing credential-shaped in source or plain vars; compared with
crypto.subtle.timingSafeEqual, never ===
5. Bindings declared and used every env.X read is declared, every declaration is read, and
Durable Object classes use this.env
6. Response streaming no unbounded .text() / .json() / .arrayBuffer() - 128 MB, no swap
7. Promise handling every promise awaited, returned, voided or given to ctx.waitUntil;
ctx never destructured
8. Bindings over REST in-process and service bindings instead of the Cloudflare REST API
9. Hyperdrive external PostgreSQL or MySQL goes through a Hyperdrive binding
10. Observability observability.enabled true with an explicit head_sampling_rate,
and one structured JSON object per log event
11. Request-scoped state no module-level mutables or caches holding request data
12. Error handling explicit try/catch with structured error responses, never
ctx.passThroughOnException()
The review never echoes a secret value. If the paste contains a hardcoded token, an API key or a
credential in plain vars, the finding names the identifier and says to move it to
wrangler secret put and rotate it — the value itself does not appear in
problem, snippet or hardened_config.
Your saved reviews
Every run the app completes is written to the reviews collection, so a review follows
the user across devices. It is declared acl_read: owner and
acl_write: user: rows are scoped to the calling subject, which means a script must
reuse one token across the run and the query or it will see an empty collection. Each
POST /guest mints a new guest subject, so guest tokens are not a way to share
history.
| field | type | meaning |
|---|---|---|
uid | string | The app's own id for the run — stable across a re-render, and the key the local mirror and the account rows are merged on. |
title | string | review_name from the reply. |
readiness | string | ready, needs-work or not-ready. |
verdict | string | The one-sentence verdict. |
worker_name | string | The Worker the review was about, or unknown. |
compat_date | string | The compatibility_date the paste showed. Handy for "which of our Workers are still on a 2023 runtime?". |
input_hash | string | Hash of the submitted input — the cheap way to tell whether a Worker actually changed between runs before spending credits on a re-review. |
findings_count | number | findings.length. |
critical_count | number | How many findings came back priority: "critical" or "high". |
ran_at | timestamp | ISO-8601, set when the run completed. The natural sort key. |
Those ten fields are declared, and therefore filterable and sortable. The rest of the document — the
whole review, its meta and the input it ran against — round-trips intact under entry
but is not indexed. Documents are capped at 64 KB, and the app drops entry.input before
it drops the review itself, so a very large paste may not come back with its row.
embed is ["title", "worker_name", "verdict", "readiness"], so
POST /collections/reviews/similar with a text or a
record_id finds past reviews that read like this one — useful for "have we seen this
failure shape before?" across a fleet of Workers.
Two shapes catch people out. Sorting takes sort, an object
({"field": "ran_at", "dir": "desc"}); an order_by array is silently
ignored, and you get insertion order with no error. And every where entry must be an
operator object — eq, ne, lt, lte,
gt, gte, in (up to 20 values) or contains. The
bare-value shorthand {"readiness": "not-ready"} is rejected. Records come back wrapped:
data.records[].doc holds the fields, alongside a record_id — never read the
fields flat off the record.
# Your saved reviews, newest first. Note `sort` is an object, not an array.
call collections/reviews/query '{"sort":{"field":"ran_at","dir":"desc"},"limit":10}'
# Only the ones that came back not-ready with something critical in them.
call collections/reviews/query '{"where":{"readiness":{"eq":"not-ready"},"critical_count":{"gte":1}},
"sort":{"field":"ran_at","dir":"desc"},"limit":20}'
# Which Workers are still pinned to an old runtime?
call collections/reviews/query '{"where":{"compat_date":{"contains":"2023"}},"limit":50}'
# Past reviews that read like this one, over embed = title, worker_name, verdict, readiness.
call collections/reviews/similar '{"text":"module-level cache shared across requests","limit":5}'
# Every `where` entry must be an operator object - the bare-value shorthand
# ({"readiness": "not-ready"}) is rejected. Sorting takes `sort`, an object.
recent = call("collections/reviews/query", {
"where": {"readiness": {"eq": "not-ready"}, "critical_count": {"gte": 1}},
"sort": {"field": "ran_at", "dir": "desc"},
"limit": 20,
})
for rec in recent["records"]:
d = rec["doc"] # the fields nest under .doc, not flat
print(d["ran_at"], d["worker_name"], d["readiness"], d["critical_count"], "critical/high")
# Has this Worker actually changed since the last review? If input_hash matches,
# the answer is no and the credits are better spent elsewhere.
last = recent["records"][0]["doc"] if recent["records"] else None
if last and last["input_hash"] == digest:
print("unchanged since", last["ran_at"], "- skipping the re-review")
# Nearest neighbours over embed = ["title", "worker_name", "verdict", "readiness"].
similar = call("collections/reviews/similar",
{"text": "module-level cache shared across requests", "limit": 5})
for rec in similar["records"]:
print(rec["doc"]["title"], "-", rec["doc"]["verdict"])
const recent = await call("collections/reviews/query", {
where: { readiness: { eq: "not-ready" }, critical_count: { gte: 1 } },
sort: { field: "ran_at", dir: "desc" }, // an object; order_by is ignored
limit: 20,
});
for (const rec of recent.records) {
const d = rec.doc; // the fields nest under .doc, not flat
console.log(d.ran_at, d.worker_name, d.readiness, d.critical_count);
}
// Which Workers are still pinned to an old runtime?
const stale = await call("collections/reviews/query", {
where: { compat_date: { contains: "2023" } },
limit: 50,
});
console.log(stale.records.map((r) => `${r.doc.worker_name} ${r.doc.compat_date}`));
// Nearest neighbours over embed = ["title", "worker_name", "verdict", "readiness"].
const similar = await call("collections/reviews/similar", {
text: "module-level cache shared across requests",
limit: 5,
});
console.log(similar.records.map((r) => r.doc.title));
query := map[string]any{
"where": map[string]any{
"readiness": map[string]any{"eq": "not-ready"},
"critical_count": map[string]any{"gte": 1},
},
"sort": map[string]string{"field": "ran_at", "dir": "desc"}, // object, not array
"limit": 20,
}
raw, err := call("collections/reviews/query", query)
if err != nil {
panic(err)
}
var out struct {
Records []struct {
RecordID string `json:"record_id"`
Doc map[string]any `json:"doc"`
} `json:"records"`
}
_ = json.Unmarshal(raw, &out)
for _, r := range out.Records {
fmt.Println(r.Doc["ran_at"], r.Doc["worker_name"], r.Doc["critical_count"])
}
// collections/reviews/similar takes {"text": "...", "limit": 5} and ranks over
// the declared embed fields: title, worker_name, verdict and readiness.
String query = """
{"where":{"readiness":{"eq":"not-ready"},"critical_count":{"gte":1}},
"sort":{"field":"ran_at","dir":"desc"},"limit":20}
""";
String reviews = call("collections/reviews/query", query);
System.out.println(reviews);
// {"ok":true,"data":{"records":[{"record_id":"...","doc":{"title":"...","readiness":"not-ready",...}}]}}
// The fields nest under .doc - never read them flat off the record.
String similar = call("collections/reviews/similar",
"{\"text\":\"module-level cache shared across requests\",\"limit\":5}");
System.out.println(similar);
recent = call("collections/reviews/query", {
"where" => { "readiness" => { "eq" => "not-ready" }, "critical_count" => { "gte" => 1 } },
"sort" => { "field" => "ran_at", "dir" => "desc" }, # an object, not an array
"limit" => 20,
})
recent["records"].each do |r|
d = r["doc"] # the fields nest under .doc, not flat
puts "#{d['ran_at']} #{d['worker_name']} #{d['readiness']}"
end
# Nearest neighbours over embed = ["title", "worker_name", "verdict", "readiness"].
similar = call("collections/reviews/similar",
{ "text" => "module-level cache shared across requests", "limit" => 5 })
similar["records"].each { |r| puts r["doc"]["title"] }
<?php
$recent = call("collections/reviews/query", [
"where" => ["readiness" => ["eq" => "not-ready"], "critical_count" => ["gte" => 1]],
"sort" => ["field" => "ran_at", "dir" => "desc"], // an object, not an array
"limit" => 20,
]);
foreach ($recent["records"] as $rec) {
$d = $rec["doc"]; // the fields nest under .doc, not flat
echo $d["ran_at"], " ", $d["worker_name"], " ", $d["readiness"], PHP_EOL;
}
// Nearest neighbours over embed = ["title", "worker_name", "verdict", "readiness"].
$similar = call("collections/reviews/similar",
["text" => "module-level cache shared across requests", "limit" => 5]);
foreach ($similar["records"] as $rec) {
echo $rec["doc"]["title"], PHP_EOL;
}
var query = new
{
where = new { readiness = new { eq = "not-ready" }, critical_count = new { gte = 1 } },
sort = new { field = "ran_at", dir = "desc" }, // an object, not an array
limit = 20,
};
var recent = await Clinic.Call("collections/reviews/query", query);
foreach (var rec in recent.GetProperty("records").EnumerateArray())
{
var d = rec.GetProperty("doc"); // the fields nest under .doc, not flat
Console.WriteLine($"{d.GetProperty("ran_at")} {d.GetProperty("worker_name")}");
}
// Nearest neighbours over embed = ["title", "worker_name", "verdict", "readiness"].
var similar = await Clinic.Call("collections/reviews/similar",
new { text = "module-level cache shared across requests", limit = 5 });
Console.WriteLine(similar.GetProperty("records").GetArrayLength());
A CI recipe
This is the natural use: a job that re-reviews the Worker whenever wrangler.jsonc or
anything under src/** changes, and fails the build when readiness comes
back not-ready or any finding arrives at priority: "critical". Everything
else — needs-work, a handful of high findings — is a warning, because a
gate that fires on every review is a gate people turn off.
Four things make it cheap and safe to run on every push. Concatenate the tracked files into
files with // file: markers, so the review sees the same paths your
reviewers do. Derive the Idempotency-Key from that string, so a re-run of the same
commit — a flaky runner, a manual re-trigger — replays the original job instead of billing again.
Send prescan_facts if you have any deterministic linting of your own, so the model has
to reconcile it. And call /estimate first: it is free, and comparing
hold_credits against the /me balance turns a mid-pipeline 402 into a clear
message before anything is reserved.
# .github/workflows/workers-clinic.yml
name: Workers Clinic
on:
pull_request:
paths:
- "wrangler.jsonc"
- "wrangler.toml"
- "src/**"
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Review the Worker
env:
SKILLSAFE_TOKEN: ${{ secrets.SKILLSAFE_TOKEN }}
run: python3 .github/scripts/workers_clinic.py
#!/bin/sh
# Fail the build on not-ready or on any critical finding.
set -eu
BASE="https://api.skillsafe.ai/v1/app-api"
SLUG="workers-clinic"
TOKEN="$SKILLSAFE_TOKEN" # a repository secret; see /tokens.html
# 1. Build `files` from the tracked config and sources, with // file: markers.
FILES=$(for f in wrangler.jsonc wrangler.toml $(git ls-files 'src/*.ts'); do
[ -f "$f" ] || continue
printf '// file: %s\n' "$f"
cat "$f"
printf '\n\n'
done)
# 2. One JSON body, and an Idempotency-Key derived from it: a re-run of the same
# commit replays the original job instead of billing a second review.
INPUT=$(FILES="$FILES" python3 -c '
import json, os
print(json.dumps({"input": {
"files": os.environ["FILES"],
"target": "production",
"focus": "general",
"context": "CI gate on every pull request that touches wrangler config or src/.",
"prescan_facts": {"resources": [], "flags": []}
}}))')
KEY="workers-clinic:$(printf '%s' "$INPUT" | shasum -a 256 | cut -c1-16):a1"
# 3. Price it first - /estimate is free and creates no job.
printf '%s' "$INPUT" > /tmp/wc-input.json
curl -sS -X POST "$BASE/estimate" \
-H "Authorization: Bearer $TOKEN" -H "X-App-Slug: $SLUG" \
-H "Content-Type: application/json" -d @/tmp/wc-input.json
# 4. Run, poll, gate.
JOB=$(curl -sS -X POST "$BASE/run" \
-H "Authorization: Bearer $TOKEN" -H "X-App-Slug: $SLUG" \
-H "Content-Type: application/json" -H "Idempotency-Key: $KEY" \
-d @/tmp/wc-input.json | python3 -c 'import sys,json;print(json.load(sys.stdin)["data"]["job_id"])')
while :; do
OUT=$(curl -sS "$BASE/jobs/$JOB" -H "Authorization: Bearer $TOKEN" -H "X-App-Slug: $SLUG")
STATUS=$(printf '%s' "$OUT" | python3 -c 'import sys,json;print(json.load(sys.stdin)["data"]["status"])')
[ "$STATUS" = "succeeded" ] && break
[ "$STATUS" = "failed" ] && echo "$OUT" && exit 1
sleep 2
done
printf '%s' "$OUT" | python3 -c '
import sys, json
r = json.loads(json.load(sys.stdin)["data"]["output"]["output"])
crit = [f for f in r["findings"] if f["priority"] == "critical"]
for f in r["findings"]:
lvl = "error" if f["priority"] == "critical" else "warning"
print(f"::{lvl} file={f[\"resource\"].split(\":\")[0]}::{f[\"id\"]} {f[\"problem\"]} -- {f[\"fix\"]}")
print(r["summary"])
if r["readiness"] == "not-ready" or crit:
raise SystemExit(f"::error::Workers Clinic: {r[\"readiness\"]} ({len(crit)} critical)")
'
#!/usr/bin/env python3
"""CI gate: fail on not-ready or on any critical finding."""
import glob, hashlib, json, os, subprocess, sys, time, urllib.error, urllib.request
BASE = "https://api.skillsafe.ai/v1/app-api"
SLUG = "workers-clinic"
TOKEN = os.environ.get("SKILLSAFE_TOKEN", "YOUR_TOKEN") # a repository secret
def call(path, body=None, idem=None):
data = json.dumps(body).encode() if body is not None else None
req = urllib.request.Request(f"{BASE}/{path}", data=data,
method="POST" if body is not None else "GET")
req.add_header("Authorization", f"Bearer {TOKEN}")
req.add_header("X-App-Slug", SLUG)
if body is not None:
req.add_header("Content-Type", "application/json")
if idem:
req.add_header("Idempotency-Key", idem)
try:
with urllib.request.urlopen(req) as r:
payload = json.load(r)
except urllib.error.HTTPError as e:
payload = json.load(e)
if not payload.get("ok"):
err = payload.get("error", {})
raise RuntimeError(f"{err.get('code')}: {err.get('message')}")
return payload["data"]
def collect():
"""The tracked config and sources, with // file: markers, exactly as a
reviewer would paste them."""
tracked = subprocess.run(["git", "ls-files", "src"], capture_output=True, text=True).stdout.split()
paths = [p for p in ("wrangler.jsonc", "wrangler.toml", "package.json") if os.path.exists(p)]
paths += [p for p in tracked if p.endswith((".ts", ".js"))]
paths += sorted(glob.glob("worker-configuration.d.ts"))
parts = []
for p in paths:
with open(p, encoding="utf-8") as fh:
parts.append(f"// file: {p}\n{fh.read().rstrip()}\n")
return "\n".join(parts)
files = collect()
payload = {
"files": files,
"target": "production",
"focus": "general",
"context": "CI gate on every pull request that touches wrangler config or src/.",
# If you have deterministic checks of your own, pass them here and the review
# must reconcile every id in coverage_check.
"prescan_facts": {"resources": [], "flags": []},
}
# The key is derived from the input, so a re-run of the same commit replays the
# original job rather than billing a second review.
digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode()).hexdigest()[:16]
key = f"workers-clinic:{digest}:a1"
est = call("estimate", {"input": payload}) # free: no job, no charge
me = call("me")
if not est["sponsor_enabled"] and me.get("credits", 0) < est["hold_credits"]:
sys.exit(f"::error::balance {me.get('credits')} is under the "
f"{est['hold_credits']}-credit reservation - top up before this gate can run")
job_id = call("run", {"input": payload}, idem=key)["job_id"]
while True:
job = call(f"jobs/{job_id}")
if job["status"] in ("succeeded", "failed"):
break
time.sleep(2)
if job["status"] == "failed":
sys.exit(f"::error::run failed: {job.get('error')}")
review = json.loads(job["output"]["output"])
critical = [f for f in review["findings"] if f["priority"] == "critical"]
for c in review["checks"]:
if c["status"] in ("fail", "weak"):
print(f"::warning::{c['check']}: {c['evidence']}")
for f in review["findings"]:
level = "error" if f["priority"] == "critical" else "warning"
path = f["resource"].split(":")[0]
print(f"::{level} file={path}::{f['id']} {f['problem']} -- {f['fix']}")
print(review["summary"])
if review["readiness"] == "not-ready" or critical:
sys.exit(f"::error::Workers Clinic: {review['readiness']} "
f"({len(critical)} critical finding(s))")
if review["readiness"] == "needs-work":
print("::warning::Workers Clinic: needs work, but nothing blocking")
#!/usr/bin/env node
// CI gate: fail on not-ready or on any critical finding.
import { createHash } from "node:crypto";
import { execFileSync } from "node:child_process";
import { readFileSync, existsSync } from "node:fs";
const BASE = "https://api.skillsafe.ai/v1/app-api";
const SLUG = "workers-clinic";
const TOKEN = "YOUR_TOKEN"; // read this from your CI secret store; see /tokens.html
async function call(path, body, idem) {
const res = await fetch(`${BASE}/${path}`, {
method: body ? "POST" : "GET",
headers: {
Authorization: `Bearer ${TOKEN}`,
"X-App-Slug": SLUG,
...(body ? { "Content-Type": "application/json" } : {}),
...(idem ? { "Idempotency-Key": idem } : {}),
},
body: body ? JSON.stringify(body) : undefined,
});
const payload = await res.json();
if (!payload.ok) throw new Error(`${payload.error.code}: ${payload.error.message}`);
return payload.data;
}
// The tracked config and sources, with // file: markers.
const tracked = execFileSync("git", ["ls-files", "src"], { encoding: "utf8" })
.split("\n")
.filter((p) => p.endsWith(".ts") || p.endsWith(".js"));
const paths = ["wrangler.jsonc", "wrangler.toml", "package.json"].filter(existsSync).concat(tracked);
const files = paths
.map((p) => `// file: ${p}\n${readFileSync(p, "utf8").trimEnd()}\n`)
.join("\n");
const payload = {
files,
target: "production",
focus: "general",
context: "CI gate on every pull request that touches wrangler config or src/.",
prescan_facts: { resources: [], flags: [] },
};
// Derived key: a re-run of the same commit replays instead of re-billing.
const digest = createHash("sha256").update(JSON.stringify(payload)).digest("hex").slice(0, 16);
const key = `workers-clinic:${digest}:a1`;
const est = await call("estimate", { input: payload }); // free: no job, no charge
const me = await call("me");
if (!est.sponsor_enabled && me.credits < est.hold_credits) {
console.log(`::error::balance ${me.credits} is under the ${est.hold_credits}-credit reservation`);
process.exit(1);
}
let job = await call("run", { input: payload }, key);
while (job.status !== "succeeded" && job.status !== "failed") {
await new Promise((r) => setTimeout(r, 2000));
job = await call(`jobs/${job.job_id}`);
}
if (job.status === "failed") {
console.log(`::error::run failed: ${JSON.stringify(job.error)}`);
process.exit(1);
}
const review = JSON.parse(job.output.output);
const critical = review.findings.filter((f) => f.priority === "critical");
for (const c of review.checks) {
if (c.status === "fail" || c.status === "weak") {
console.log(`::warning::${c.check}: ${c.evidence}`);
}
}
for (const f of review.findings) {
const level = f.priority === "critical" ? "error" : "warning";
console.log(`::${level} file=${f.resource.split(":")[0]}::${f.id} ${f.problem} -- ${f.fix}`);
}
console.log(review.summary);
if (review.readiness === "not-ready" || critical.length) {
console.log(`::error::Workers Clinic: ${review.readiness} (${critical.length} critical)`);
process.exit(1);
}
if (review.readiness === "needs-work") {
console.log("::warning::Workers Clinic: needs work, but nothing blocking");
}
// CI gate: fail on not-ready or on any critical finding.
files := collectTrackedFiles() // "// file: path\n" + contents, joined
payload := map[string]any{
"files": files,
"target": "production",
"focus": "general",
"context": "CI gate on every pull request that touches wrangler config or src/.",
"prescan_facts": map[string]any{"resources": []any{}, "flags": []any{}},
}
body, _ := json.Marshal(map[string]any{"input": payload})
sum := sha256.Sum256(body)
key := fmt.Sprintf("workers-clinic:%x:a1", sum[:8]) // replay, never re-bill
if _, err := call("estimate", map[string]any{"input": payload}); err != nil { // free
panic(err)
}
req, _ := http.NewRequest(http.MethodPost, base+"/run", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("X-App-Slug", slug)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", key)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
var started struct {
Data struct{ JobID string `json:"job_id"` } `json:"data"`
}
_ = json.NewDecoder(res.Body).Decode(&started)
var r review
for {
raw, err := call("jobs/"+started.Data.JobID, nil)
if err != nil {
panic(err)
}
var job struct {
Status string `json:"status"`
Output struct{ Output string `json:"output"` } `json:"output"`
}
_ = json.Unmarshal(raw, &job)
if job.Status == "failed" {
fmt.Println("::error::run failed")
os.Exit(1)
}
if job.Status == "succeeded" {
if err := json.Unmarshal([]byte(job.Output.Output), &r); err != nil {
panic(err)
}
break
}
time.Sleep(2 * time.Second)
}
critical := 0
for _, f := range r.Findings {
level := "warning"
if f.Priority == "critical" {
level = "error"
critical++
}
fmt.Printf("::%s::%s %s -- %s\n", level, f.ID, f.Problem, f.Fix)
}
if r.Readiness == "not-ready" || critical > 0 {
fmt.Printf("::error::Workers Clinic: %s (%d critical)\n", r.Readiness, critical)
os.Exit(1)
}
// CI gate: fail on not-ready or on any critical finding.
//
// 1. Concatenate the tracked wrangler config and src/**.ts into `files`, each
// preceded by a "// file: path" marker line.
// 2. Derive the Idempotency-Key from the serialised body:
// workers-clinic:<sha256-prefix>:a1
// A re-run of the same commit then replays the original job instead of
// billing a second review.
// 3. POST /estimate first - it is free, creates no job, and comparing
// hold_credits against the /me balance turns a mid-pipeline 402 into a clear
// message before anything is reserved.
// 4. POST /run with that key, poll GET jobs/{id} every two seconds, then gate.
String reviewJson = /* data.output.output from the finished job */ "";
// Fail when readiness is "not-ready", or when any findings[].priority is
// "critical". Emit the rest as warnings - a gate that fires on every review is a
// gate that gets switched off:
//
// ::error file=src/index.ts::WC-001 <problem> -- <fix>
// ::warning file=wrangler.jsonc::WC-002 <problem> -- <fix>
//
// checks[] with status "fail" or "weak" make good warnings too, and because the
// twelve entries are always in the same order you can diff them run to run.
System.out.println(reviewJson);
#!/usr/bin/env ruby
# CI gate: fail on not-ready or on any critical finding.
require "digest"
require "json"
paths = ["wrangler.jsonc", "wrangler.toml", "package.json"].select { |p| File.exist?(p) }
paths += `git ls-files src`.split.grep(/\.(ts|js)\z/)
files = paths.map { |p| "// file: #{p}\n#{File.read(p).rstrip}\n" }.join("\n")
payload = {
"files" => files,
"target" => "production",
"focus" => "general",
"context" => "CI gate on every pull request that touches wrangler config or src/.",
"prescan_facts" => { "resources" => [], "flags" => [] }
}
# Derived key: a re-run of the same commit replays instead of re-billing.
key = "workers-clinic:#{Digest::SHA256.hexdigest(JSON.generate(payload))[0, 16]}:a1"
est = call("estimate", { "input" => payload }) # free: no job, no charge
me = call("me")
if !est["sponsor_enabled"] && me["credits"].to_i < est["hold_credits"].to_i
abort "::error::balance #{me['credits']} is under the #{est['hold_credits']}-credit reservation"
end
job = post_run(payload, key) # POST /run with the Idempotency-Key header
loop do
job = call("jobs/#{job['job_id']}")
break if %w[succeeded failed].include?(job["status"])
sleep 2
end
abort "::error::run failed" if job["status"] == "failed"
review = JSON.parse(job["output"]["output"])
critical = review["findings"].select { |f| f["priority"] == "critical" }
review["checks"].each do |c|
puts "::warning::#{c['check']}: #{c['evidence']}" if %w[fail weak].include?(c["status"])
end
review["findings"].each do |f|
level = f["priority"] == "critical" ? "error" : "warning"
puts "::#{level} file=#{f['resource'].split(':').first}::#{f['id']} #{f['problem']} -- #{f['fix']}"
end
puts review["summary"]
if review["readiness"] == "not-ready" || !critical.empty?
abort "::error::Workers Clinic: #{review['readiness']} (#{critical.length} critical)"
end
<?php
// CI gate: fail on not-ready or on any critical finding.
$paths = array_values(array_filter(["wrangler.jsonc", "wrangler.toml", "package.json"], "file_exists"));
exec("git ls-files src", $tracked);
foreach ($tracked as $p) {
if (str_ends_with($p, ".ts") || str_ends_with($p, ".js")) { $paths[] = $p; }
}
$parts = [];
foreach ($paths as $p) {
$parts[] = "// file: " . $p . "\n" . rtrim(file_get_contents($p)) . "\n";
}
$payload = [
"files" => implode("\n", $parts),
"target" => "production",
"focus" => "general",
"context" => "CI gate on every pull request that touches wrangler config or src/.",
"prescan_facts" => ["resources" => [], "flags" => []],
];
// Derived key: a re-run of the same commit replays instead of re-billing.
$key = "workers-clinic:" . substr(hash("sha256", json_encode($payload)), 0, 16) . ":a1";
$est = call("estimate", ["input" => $payload]); // free: no job, no charge
$me = call("me");
if (!$est["sponsor_enabled"] && $me["credits"] < $est["hold_credits"]) {
fwrite(STDERR, "::error::balance is under the reservation\n");
exit(1);
}
$job = post_run($payload, $key); // POST /run with the Idempotency-Key header
while (!in_array($job["status"], ["succeeded", "failed"], true)) {
sleep(2);
$job = call("jobs/" . $job["job_id"]);
}
if ($job["status"] === "failed") { fwrite(STDERR, "::error::run failed\n"); exit(1); }
$review = json_decode($job["output"]["output"], true);
$critical = array_filter($review["findings"], fn($f) => $f["priority"] === "critical");
foreach ($review["findings"] as $f) {
$level = $f["priority"] === "critical" ? "error" : "warning";
printf("::%s file=%s::%s %s -- %s\n", $level, explode(":", $f["resource"])[0],
$f["id"], $f["problem"], $f["fix"]);
}
echo $review["summary"], PHP_EOL;
if ($review["readiness"] === "not-ready" || count($critical) > 0) {
printf("::error::Workers Clinic: %s (%d critical)\n", $review["readiness"], count($critical));
exit(1);
}
// CI gate: fail on not-ready or on any critical finding.
var paths = new List<string> { "wrangler.jsonc", "wrangler.toml", "package.json" }
.Where(File.Exists)
.Concat(Directory.EnumerateFiles("src", "*.ts", SearchOption.AllDirectories))
.ToList();
var files = string.Join("\n",
paths.Select(p => $"// file: {p}\n{File.ReadAllText(p).TrimEnd()}\n"));
var payload = new
{
files,
target = "production",
focus = "general",
context = "CI gate on every pull request that touches wrangler config or src/.",
prescan_facts = new { resources = Array.Empty<object>(), flags = Array.Empty<object>() },
};
// Derived key: a re-run of the same commit replays instead of re-billing.
var body = JsonSerializer.Serialize(new { input = payload });
var key = "workers-clinic:" +
Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(body)))[..16].ToLowerInvariant() + ":a1";
var est = await Clinic.Call("estimate", new { input = payload }); // free: no job, no charge
// POST /run with the Idempotency-Key header, poll GET jobs/{id} every two
// seconds, then parse data.output.output and gate on it:
//
// readiness == "not-ready" -> fail
// any findings[].priority == "critical" -> fail
// readiness == "needs-work" -> warn
// checks[].status in ("fail", "weak") -> warn
Console.WriteLine(est.GetProperty("hold_credits").GetInt32());
One last note on cost control: before you spend a run, query the reviews collection for
the newest row and compare its input_hash with the hash of what you are about to send.
If they match, nothing about the Worker changed since the last review and the credits are better
spent somewhere else — the app uses the same hash for exactly this purpose.