Measure a codebase's real conventions, then have them written up as a standards document
Style Codex works in two halves. First the measurement: you paste or drop source files
and the browser counts what the code actually does — indentation character and
width, quote style, semicolons, brace placement, naming case per declaration kind,
comment style, line length, function length, nesting depth, blank-line rhythm, import
layout — and reports, per category, the variants it saw, the majority, the
conformance percentage and the files and lines that deviate. Then the metered pass:
that measurement goes to the model, which returns one JSON object — a
document_title, a preamble, sections of
imperative rules, an undecided list for the categories the
code genuinely disagrees on, and unverified for anything the measurement
could not vouch for. The result is checkable rather than plausible: every measured
category must be accounted for exactly once, every asserted_variant is
compared against the measured majority and every conformance_claim against
the measured percentage. Every code step below is shown in cURL, Python, JavaScript,
Go, Java, Ruby, PHP and C#; pick a language once and the whole page follows.
Basics
Base URL https://api.skillsafe.ai/v1/app-api, app slug
style-codex. Every request sends
Authorization: Bearer <token> and JSON bodies with
Content-Type: application/json. Responses are wrapped in an envelope:
{"ok":true,"data":{…}} on success and
{"ok":false,"error":{"code","message","status","details"}} on failure —
so a client can branch on ok before it looks at anything else. The model
that writes the document, its alias and the publisher markup are not hard-coded here:
/estimate reports model, model_alias and
markup_bps for the exact input you are about to send. Credits are in units
of 1/10 000 of a US dollar, so 10 000 credits is $1.00.
POST /guest GET /me POST /estimate POST /run GET /jobs/{id} POST /run-stream POST /collections/codices/query POST /collections/codices/similar
Error codes
| HTTP | code | What it means and what to do |
|---|---|---|
400 | validation_error | The body is missing a required field or a field has the wrong type. error.details names it. POST /guest in particular needs slug in the body — sending it as an X-App-Slug header returns 400 slug is required. A where entry that is a bare value instead of an operator object fails here too. |
401 | unauthorized | No token, a malformed token, or a token that has expired. Mint a new guest token or sign in again. |
402 | payment_required | Not enough credits: the balance cannot cover this run's minimum. Call /estimate first and compare min_credits against /me's credits. |
404 | not_found | Unknown job id, unknown collection, or a record that belongs to another subject. Guest identities are per-token: a new guest token cannot see the previous guest's documents. |
409 | conflict | An Idempotency-Key was reused with a different body. Change the attempt counter in the key when the input changes. |
429 | rate_limited | Too many requests. Back off and retry; do not tight-loop. Semantic search has its own limit of 30 requests/minute per IP. |
5xx | server_error | Transient. Retry with the same Idempotency-Key so the retry cannot bill twice. |
/run and /run-stream.
/guest, /me and /estimate are free, so a client
can price a run, check the balance and prove the model binding without spending
anything.
Step 1 · Get a token
Two ways in. If you already use the app in a browser, open
the token page at
/tokens.html and press Copy shell export — it hands
you the exact export SKILLSAFE_TOKEN="…" line, with no DevTools and
no browser console involved. For a fully scripted client,
POST /guest mints a guest token with no browser at all. The slug goes
in the body — {"slug":"style-codex"}; an
X-App-Slug header is not read and the call comes back 400
slug is required. Guest tokens can call /me and the free
/estimate; a personal token is what bills document runs to your own
account.
# Option A — take the token this browser already has: open
# https://style-codex.skillsafe.ai/tokens.html, press "Copy shell export",
# and paste the line it gives you.
export SKILLSAFE_TOKEN="aut_xxxxxxxxxxxxxxxxxxxx"
# Option B — mint a guest token with no browser at all. The slug goes in the
# BODY; an X-App-Slug header returns 400 "slug is required".
curl -s -X POST https://api.skillsafe.ai/v1/app-api/guest \
-H 'Content-Type: application/json' \
-d '{"slug":"style-codex"}'
# => {"ok":true,"data":{"token":"aut_...","subject_type":"guest","credits":0}}
import os, json, urllib.request
BASE = "https://api.skillsafe.ai/v1/app-api"
SLUG = "style-codex"
def call(path, body=None, token=None, method=None):
data = json.dumps(body).encode() if body is not None else None
req = urllib.request.Request(BASE + path, data=data,
method=method or ("POST" if data else "GET"))
req.add_header("Content-Type", "application/json")
req.add_header("User-Agent", "style-codex-client/1.0")
if token:
req.add_header("Authorization", "Bearer " + token)
with urllib.request.urlopen(req) as r:
env = json.loads(r.read())
if not env.get("ok"):
err = env.get("error") or {}
raise RuntimeError("%s: %s" % (err.get("code"), err.get("message")))
return env["data"]
# Option A: the token from /tokens.html, kept in your environment.
token = os.environ.get("SKILLSAFE_TOKEN")
# Option B: a fresh guest token, no browser involved. slug in the body.
if not token:
token = call("/guest", {"slug": SLUG})["token"]
print(token[:12] + "...")
const BASE = "https://api.skillsafe.ai/v1/app-api";
const SLUG = "style-codex";
async function call(path, { body, token, method } = {}) {
const res = await fetch(BASE + path, {
method: method || (body ? "POST" : "GET"),
headers: {
"Content-Type": "application/json",
...(token ? { Authorization: "Bearer " + token } : {}),
},
body: body ? JSON.stringify(body) : undefined,
});
const json = await res.json();
if (!json.ok) throw Object.assign(new Error(json.error.message), json.error);
return json.data;
}
// Option A: paste the token from /tokens.html (or read it from your own config).
const TOKEN = "YOUR_TOKEN";
let token = TOKEN;
// Option B: mint a guest token — the slug goes in the body, not in a header.
if (token === "YOUR_TOKEN") token = (await call("/guest", { body: { slug: SLUG } })).token;
console.log(token.slice(0, 12) + "...");
package main
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"os"
)
const base = "https://api.skillsafe.ai/v1/app-api"
const slug = "style-codex"
type envelope struct {
OK bool `json:"ok"`
Data json.RawMessage `json:"data"`
Error *struct {
Code string `json:"code"`
Message string `json:"message"`
Status int `json:"status"`
} `json:"error"`
}
func call(path, token string, body any, out any) error {
var rdr io.Reader
method := "GET"
if body != nil {
b, _ := json.Marshal(body)
rdr = bytes.NewReader(b)
method = "POST"
}
req, _ := http.NewRequest(method, base+path, rdr)
req.Header.Set("Content-Type", "application/json")
if token != "" {
req.Header.Set("Authorization", "Bearer "+token)
}
res, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
var env envelope
if err := json.NewDecoder(res.Body).Decode(&env); err != nil {
return err
}
if !env.OK && env.Error != nil {
return errors.New(env.Error.Code + ": " + env.Error.Message)
}
if out != nil {
return json.Unmarshal(env.Data, out)
}
return nil
}
func main() {
token := os.Getenv("SKILLSAFE_TOKEN")
if token == "" {
// slug travels in the body; an X-App-Slug header is rejected with 400.
var guest struct{ Token string `json:"token"` }
if err := call("/guest", "", map[string]string{"slug": slug}, &guest); err != nil {
panic(err)
}
token = guest.Token
}
fmt.Println(token[:12] + "...")
}
import java.net.URI;
import java.net.http.*;
public class StyleCodex {
static final String BASE = "https://api.skillsafe.ai/v1/app-api";
static final String SLUG = "style-codex";
static final HttpClient HTTP = HttpClient.newHttpClient();
static String call(String path, String token, String jsonBody) throws Exception {
HttpRequest.Builder b = HttpRequest.newBuilder(URI.create(BASE + path))
.header("Content-Type", "application/json");
if (token != null) b.header("Authorization", "Bearer " + token);
b = jsonBody == null ? b.GET()
: b.POST(HttpRequest.BodyPublishers.ofString(jsonBody));
HttpResponse<String> res = HTTP.send(b.build(), HttpResponse.BodyHandlers.ofString());
return res.body(); // {"ok":true,"data":...} or {"ok":false,"error":{...}}
}
public static void main(String[] args) throws Exception {
String token = System.getenv("SKILLSAFE_TOKEN");
if (token == null) {
// POST /guest with the slug in the BODY — a header is not accepted.
System.out.println(call("/guest", null, "{\"slug\":\"" + SLUG + "\"}"));
} else {
System.out.println(token.substring(0, 12) + "...");
}
}
}
require "json"
require "net/http"
BASE = URI("https://api.skillsafe.ai/v1/app-api")
SLUG = "style-codex"
def call(path, body: nil, token: nil, method: nil)
uri = URI(BASE.to_s + path)
req = (method || (body ? "POST" : "GET")) == "POST" ?
Net::HTTP::Post.new(uri) : Net::HTTP::Get.new(uri)
req["Content-Type"] = "application/json"
req["Authorization"] = "Bearer #{token}" if token
req.body = JSON.generate(body) if body
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
env = JSON.parse(res.body)
raise "#{env['error']['code']}: #{env['error']['message']}" unless env["ok"]
env["data"]
end
# slug in the body — an X-App-Slug header comes back 400 "slug is required".
token = ENV["SKILLSAFE_TOKEN"] || call("/guest", body: { slug: SLUG })["token"]
puts token[0, 12] + "..."
<?php
const BASE = "https://api.skillsafe.ai/v1/app-api";
const SLUG = "style-codex";
function call(string $path, ?array $body = null, ?string $token = null): array {
$headers = ["Content-Type: application/json"];
if ($token) { $headers[] = "Authorization: Bearer " . $token; }
$ch = curl_init(BASE . $path);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => $headers,
]);
if ($body !== null) {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
}
$env = json_decode(curl_exec($ch), true);
curl_close($ch);
if (empty($env["ok"])) {
throw new RuntimeException($env["error"]["code"] . ": " . $env["error"]["message"]);
}
return $env["data"];
}
// slug in the body, never as a header.
$token = getenv("SKILLSAFE_TOKEN") ?: call("/guest", ["slug" => SLUG])["token"];
echo substr($token, 0, 12) . "...\n";
using System;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json;
using System.Threading.Tasks;
class StyleCodex {
const string Base = "https://api.skillsafe.ai/v1/app-api";
const string Slug = "style-codex";
static readonly HttpClient Http = new HttpClient();
static async Task<JsonElement> Call(string path, object body = null, string token = null) {
var req = new HttpRequestMessage(body == null ? HttpMethod.Get : HttpMethod.Post, Base + path);
if (token != null) req.Headers.Add("Authorization", "Bearer " + token);
if (body != null) req.Content = JsonContent.Create(body);
var res = await Http.SendAsync(req);
var doc = JsonDocument.Parse(await res.Content.ReadAsStringAsync());
var root = doc.RootElement;
if (!root.GetProperty("ok").GetBoolean()) {
var err = root.GetProperty("error");
throw new Exception(err.GetProperty("code").GetString() + ": " +
err.GetProperty("message").GetString());
}
return root.GetProperty("data");
}
static async Task Main() {
var token = Environment.GetEnvironmentVariable("SKILLSAFE_TOKEN");
if (token == null) {
// The slug belongs in the body; a header is rejected with 400.
var guest = await Call("/guest", new { slug = Slug });
token = guest.GetProperty("token").GetString();
}
Console.WriteLine(token.Substring(0, 12) + "...");
}
}
Step 2 · Check who you are and what you can spend
GET /me returns subject_type (user or
guest), subject_id and credits. Compare
credits against /estimate's min_credits
before submitting a run — a 402 payment_required after
submit is a client bug, not a user problem.
curl -s https://api.skillsafe.ai/v1/app-api/me \
-H "Authorization: Bearer $SKILLSAFE_TOKEN"
# => {"ok":true,"data":{"subject_type":"user","subject_id":"usr_...","credits":184213}}
#
# subject_type is "user" for a personal token and "guest" for a guest one.
# credits is in credit units: 10 000 credits = $1.00.
me = call("/me", token=token)
print(me["subject_type"], me["credits"], "credits",
"= $%.2f" % (me["credits"] / 10000))
const me = await call("/me", { token });
console.log(me.subject_type, me.credits, "credits =",
"$" + (me.credits / 10000).toFixed(2));
var me struct {
SubjectType string `json:"subject_type"`
SubjectID string `json:"subject_id"`
Credits int64 `json:"credits"`
}
if err := call("/me", token, nil, &me); err != nil {
panic(err)
}
fmt.Printf("%s %d credits = $%.2f\n", me.SubjectType, me.Credits, float64(me.Credits)/10000)
// GET /me — {"ok":true,"data":{"subject_type":"user","credits":184213}}
String me = call("/me", token, null);
System.out.println(me);
me = call("/me", token: token)
puts "#{me['subject_type']} #{me['credits']} credits = $#{'%.2f' % (me['credits'] / 10000.0)}"
$me = call("/me", null, $token);
printf("%s %d credits = $%.2f\n", $me["subject_type"], $me["credits"], $me["credits"] / 10000);
var me = await Call("/me", null, token);
var credits = me.GetProperty("credits").GetInt64();
Console.WriteLine($"{me.GetProperty("subject_type").GetString()} {credits} credits = ${credits / 10000.0:F2}");
Step 3 · Price the run — free, and it proves the model binding
POST /estimate takes the same body as /run. It
creates no job and it costs nothing — no credits are held, none are
charged, nothing is queued. It returns model, model_alias,
markup_bps, hold_credits, min_credits and
sponsor_enabled. Present hold_credits as
reserved, never as the price: the hold is a reservation against the
full output cap, and the amount actually charged when the run settles is
usually far lower — a standards document rarely fills the cap.
# /estimate is free: no job is created, no credits are held, nothing is charged.
# Use it to show a price and to prove the model binding before you spend anything.
curl -s -X POST https://api.skillsafe.ai/v1/app-api/estimate \
-H "Authorization: Bearer $SKILLSAFE_TOKEN" \
-H 'Content-Type: application/json' \
-d @codex-input.json
# => {"ok":true,"data":{"model":"...","model_alias":"...","markup_bps":1000,
# "hold_credits":4260,"min_credits":310,
# "sponsor_enabled":false}}
#
# hold_credits reserves the FULL output cap. The settled charge is usually
# much smaller — never show the hold as the price.
est = call("/estimate", body=codex_input, token=token)
print("model", est["model"], "alias", est["model_alias"], "markup", est["markup_bps"])
print("reserved up to $%.4f (a cap, not the price)" % (est["hold_credits"] / 10000))
if me["credits"] < est["min_credits"]:
raise SystemExit("balance below the model minimum — top up before running")
const est = await call("/estimate", { body: codexInput, token });
console.log(est.model, est.model_alias, est.markup_bps, est.sponsor_enabled);
console.log("reserved up to $" + (est.hold_credits / 10000).toFixed(4) + " (a cap, not the price)");
if (me.credits < est.min_credits) throw new Error("balance below the model minimum");
var est struct {
Model string `json:"model"`
ModelAlias string `json:"model_alias"`
MarkupBps int `json:"markup_bps"`
HoldCredits int64 `json:"hold_credits"`
MinCredits int64 `json:"min_credits"`
SponsorEnabled bool `json:"sponsor_enabled"`
}
if err := call("/estimate", token, codexInput, &est); err != nil {
panic(err)
}
// HoldCredits is a reservation against the full output cap, not the price.
fmt.Printf("%s (%s) markup %d bps, reserve $%.4f\n",
est.Model, est.ModelAlias, est.MarkupBps, float64(est.HoldCredits)/10000)
// POST /estimate with the same body you would send to /run. Free, no job,
// no charge. hold_credits reserves the full output cap; the settled charge
// is normally far lower.
String est = call("/estimate", token, codexInputJson);
System.out.println(est);
est = call("/estimate", body: codex_input, token: token)
puts "#{est['model']} (#{est['model_alias']}) markup #{est['markup_bps']} bps"
puts "reserved up to $#{'%.4f' % (est['hold_credits'] / 10000.0)} (a cap, not the price)"
$est = call("/estimate", $codex_input, $token);
printf("%s (%s) markup %d bps, reserve $%.4f\n",
$est["model"], $est["model_alias"], $est["markup_bps"], $est["hold_credits"] / 10000);
// hold_credits is the reservation against the output cap, not the final price.
var est = await Call("/estimate", codexInput, token);
Console.WriteLine(est.GetProperty("model").GetString() + " / " +
est.GetProperty("model_alias").GetString() + " markup " +
est.GetProperty("markup_bps").GetInt32() + " bps");
// hold_credits reserves the full output cap; the settled charge is usually lower.
Step 4 · Run the writing pass and poll for it
POST /run returns {"job_id": …}; poll
GET /jobs/{job_id} until status is succeeded or
failed, then read data.output.output — the standards
document as a JSON string. Always send Idempotency-Key,
derived from the input plus an attempt counter: a network blip or a retry after a
malformed reply must never bill the same document twice. Reusing the key for a retry of
the same input returns the same job instead of starting a second one, which is
exactly what prevents double-billing; bump the attempt counter only when the input
itself changes.
# Metered. Always send Idempotency-Key: a retry with the same key returns the
# same job instead of billing the same document twice.
KEY="style-codex:$(shasum -a 256 codex-input.json | cut -c1-16):a1"
JOB=$(curl -s -X POST https://api.skillsafe.ai/v1/app-api/run \
-H "Authorization: Bearer $SKILLSAFE_TOKEN" \
-H 'Content-Type: application/json' \
-H "Idempotency-Key: $KEY" \
-d @codex-input.json | python3 -c 'import sys,json;print(json.load(sys.stdin)["data"]["job_id"])')
# Poll until terminal.
while true; do
OUT=$(curl -s "https://api.skillsafe.ai/v1/app-api/jobs/$JOB" \
-H "Authorization: Bearer $SKILLSAFE_TOKEN")
STATUS=$(printf '%s' "$OUT" | python3 -c 'import sys,json;print(json.load(sys.stdin)["data"]["status"])')
[ "$STATUS" = "succeeded" ] || [ "$STATUS" = "failed" ] && break
sleep 2
done
printf '%s' "$OUT" | python3 -c 'import sys,json;print(json.load(sys.stdin)["data"]["output"]["output"])'
# => {"document_title":"...","preamble":"...","sections":[...],
# "undecided":[...],"unverified":[]}
import hashlib, time
def idem_key(inp, attempt=1):
seed = json.dumps(inp, sort_keys=True)
return "style-codex:%s:a%d" % (hashlib.sha256(seed.encode()).hexdigest()[:16], attempt)
def run_codex(inp, token, attempt=1):
data = json.dumps(inp).encode()
req = urllib.request.Request(BASE + "/run", data=data, method="POST")
req.add_header("Content-Type", "application/json")
req.add_header("Authorization", "Bearer " + token)
# Same input, same key: a retry re-attaches to the job instead of billing twice.
req.add_header("Idempotency-Key", idem_key(inp, attempt))
with urllib.request.urlopen(req) as r:
job_id = json.loads(r.read())["data"]["job_id"]
while True:
job = call("/jobs/" + job_id, token=token)
if job["status"] in ("succeeded", "failed"):
break
time.sleep(2)
if job["status"] == "failed":
raise RuntimeError(job.get("error") or "run failed")
return json.loads(job["output"]["output"])
doc = run_codex(codex_input, token)
print(doc["document_title"], "-", len(doc["sections"]), "sections,",
len(doc["undecided"]), "undecided")
open("CODING_STANDARDS.json", "w").write(json.dumps(doc, indent=2))
import { createHash } from "node:crypto";
import { writeFileSync } from "node:fs";
function idemKey(inp, attempt = 1) {
const seed = JSON.stringify(inp);
return `style-codex:${createHash("sha256").update(seed).digest("hex").slice(0, 16)}:a${attempt}`;
}
async function runCodex(inp, token, attempt = 1) {
const res = await fetch(BASE + "/run", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
// Reuse the key on a retry of the same input — it cannot bill twice.
"Idempotency-Key": idemKey(inp, attempt),
},
body: JSON.stringify(inp),
});
const { ok, data, error } = await res.json();
if (!ok) throw new Error(error.message);
let job;
do {
await new Promise((r) => setTimeout(r, 2000));
job = await call("/jobs/" + data.job_id, { token });
} while (job.status !== "succeeded" && job.status !== "failed");
if (job.status === "failed") throw new Error(job.error || "run failed");
return JSON.parse(job.output.output);
}
const doc = await runCodex(codexInput, token);
console.log(doc.document_title, "-", doc.sections.length + " sections");
writeFileSync("CODING_STANDARDS.json", JSON.stringify(doc, null, 2));
import (
"crypto/sha256"
"encoding/hex"
"time"
)
func idemKey(inp map[string]any, attempt int) string {
b, _ := json.Marshal(inp)
sum := sha256.Sum256(b)
return fmt.Sprintf("style-codex:%s:a%d", hex.EncodeToString(sum[:])[:16], attempt)
}
// POST /run with the Idempotency-Key header, then poll GET /jobs/{id} every two
// seconds until status is "succeeded" or "failed". job.Output.Output holds the
// standards document as a JSON string. Reusing the key for a retry of the same
// input returns the same job, so a retry cannot bill the document twice.
func runCodex(inp map[string]any, token string) (string, error) {
b, _ := json.Marshal(inp)
req, _ := http.NewRequest("POST", base+"/run", bytes.NewReader(b))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Idempotency-Key", idemKey(inp, 1))
res, err := http.DefaultClient.Do(req)
if err != nil {
return "", err
}
defer res.Body.Close()
var env envelope
json.NewDecoder(res.Body).Decode(&env)
var started struct{ JobID string `json:"job_id"` }
json.Unmarshal(env.Data, &started)
for {
var job struct {
Status string `json:"status"`
Output struct{ Output string `json:"output"` } `json:"output"`
}
if err := call("/jobs/"+started.JobID, token, nil, &job); err != nil {
return "", err
}
if job.Status == "succeeded" {
return job.Output.Output, nil
}
if job.Status == "failed" {
return "", errors.New("run failed")
}
time.Sleep(2 * time.Second)
}
}
// POST /run must carry Idempotency-Key, derived from the input plus an attempt
// counter, so a network retry cannot bill the same document twice.
String key = "style-codex:" + sha256Hex(codexInputJson).substring(0, 16) + ":a1";
HttpRequest run = HttpRequest.newBuilder(URI.create(BASE + "/run"))
.header("Content-Type", "application/json")
.header("Authorization", "Bearer " + token)
.header("Idempotency-Key", key)
.POST(HttpRequest.BodyPublishers.ofString(codexInputJson))
.build();
String started = HTTP.send(run, HttpResponse.BodyHandlers.ofString()).body();
// started => {"ok":true,"data":{"job_id":"job_..."}}
// then poll GET /jobs/{job_id} until status is succeeded or failed, and read
// data.output.output — {"document_title":...,"sections":[...],"undecided":[...]}
// as a JSON string.
require "digest"
def idem_key(inp, attempt = 1)
seed = JSON.generate(inp)
"style-codex:#{Digest::SHA256.hexdigest(seed)[0, 16]}:a#{attempt}"
end
def run_codex(inp, token)
uri = URI(BASE.to_s + "/run")
req = Net::HTTP::Post.new(uri)
req["Content-Type"] = "application/json"
req["Authorization"] = "Bearer #{token}"
# Same input, same key — the retry re-attaches instead of billing again.
req["Idempotency-Key"] = idem_key(inp)
req.body = JSON.generate(inp)
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}", token: token)
return JSON.parse(job["output"]["output"]) if job["status"] == "succeeded"
raise "run failed" if job["status"] == "failed"
sleep 2
end
end
doc = run_codex(codex_input, token)
puts "#{doc['document_title']} - #{doc['sections'].length} sections"
File.write("CODING_STANDARDS.json", JSON.pretty_generate(doc))
function idem_key(array $inp, int $attempt = 1): string {
$seed = json_encode($inp);
return "style-codex:" . substr(hash("sha256", $seed), 0, 16) . ":a" . $attempt;
}
function run_codex(array $inp, string $token): array {
$ch = curl_init(BASE . "/run");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($inp),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"Authorization: Bearer " . $token,
// Retrying with the same key returns the same job — no double billing.
"Idempotency-Key: " . idem_key($inp),
],
]);
$job_id = json_decode(curl_exec($ch), true)["data"]["job_id"];
curl_close($ch);
while (true) {
$job = call("/jobs/" . $job_id, null, $token);
if ($job["status"] === "succeeded") { return json_decode($job["output"]["output"], true); }
if ($job["status"] === "failed") { throw new RuntimeException("run failed"); }
sleep(2);
}
}
$doc = run_codex($codex_input, $token);
echo $doc["document_title"] . " - " . count($doc["sections"]) . " sections\n";
file_put_contents("CODING_STANDARDS.json", json_encode($doc, JSON_PRETTY_PRINT));
using System.Security.Cryptography;
using System.Text;
static string IdemKey(object inp, int attempt = 1) {
var seed = JsonSerializer.Serialize(inp);
var hash = Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(seed))).ToLowerInvariant();
return $"style-codex:{hash[..16]}:a{attempt}";
}
var req = new HttpRequestMessage(HttpMethod.Post, Base + "/run") {
Content = JsonContent.Create(codexInput)
};
req.Headers.Add("Authorization", "Bearer " + token);
// Same input, same key: a retry cannot bill the document a second time.
req.Headers.Add("Idempotency-Key", IdemKey(codexInput));
var started = JsonDocument.Parse(await (await Http.SendAsync(req)).Content.ReadAsStringAsync());
var jobId = started.RootElement.GetProperty("data").GetProperty("job_id").GetString();
// Poll GET /jobs/{jobId} every two seconds; on "succeeded", data.output.output is
// {"document_title":...,"preamble":...,"sections":[...],"undecided":[...],
// "unverified":[...]} as a JSON string.
Step 5 · Or stream it
POST /run-stream is the same call over server-sent events, which is what
the web app uses so it can show the document arriving. The frame name arrives
on the event: line — job,
delta, done — and is not a type
field inside the payload; a parser that looks for payload.type will see
nothing. Concatenate every delta payload's text to rebuild
the JSON, and read charged_credits and truncated from the
done frame. If truncated is true the output cap was reduced
to fit the balance: a document cut off mid-section will fail the coverage check, so say
so rather than shipping it.
# Server-sent events. Frame names arrive on the `event:` line, NOT as a "type"
# field in the payload — `delta` carries text chunks, `job` the job id, `done`
# the settlement (charged_credits, truncated).
curl -N -X POST https://api.skillsafe.ai/v1/app-api/run-stream \
-H "Authorization: Bearer $SKILLSAFE_TOKEN" \
-H 'Content-Type: application/json' \
-H "Idempotency-Key: $KEY" \
-d @codex-input.json
# event: job
# data: {"job_id":"job_..."}
# event: delta
# data: {"text":"{\"document_title\":\"Ledger service coding stan"}
# ...
# event: done
# data: {"status":"succeeded","charged_credits":964,"truncated":false}
def run_stream(inp, token, attempt=1, on_delta=None):
data = json.dumps(inp).encode()
req = urllib.request.Request(BASE + "/run-stream", data=data, method="POST")
req.add_header("Content-Type", "application/json")
req.add_header("Authorization", "Bearer " + token)
req.add_header("Idempotency-Key", idem_key(inp, attempt))
raw, event = "", None
with urllib.request.urlopen(req) as r:
for line in r:
line = line.decode().rstrip("\n")
# The frame NAME is here, on the event: line — not inside the payload.
if line.startswith("event:"):
event = line[6:].strip()
elif line.startswith("data:"):
payload = json.loads(line[5:].strip() or "{}")
if event == "delta":
raw += payload.get("text", "")
if on_delta:
on_delta(payload.get("text", ""))
elif event == "done":
return json.loads(raw), payload
raise RuntimeError("stream ended without a done frame")
doc, settle = run_stream(codex_input, token)
print(doc["document_title"], "charged", settle["charged_credits"])
if settle.get("truncated"):
print("output cap was reduced — the document is incomplete")
if doc["unverified"]:
print("not backed by measurement:", "; ".join(doc["unverified"]))
async function runStream(inp, token, onDelta, attempt = 1) {
const res = await fetch(BASE + "/run-stream", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
"Idempotency-Key": idemKey(inp, attempt),
},
body: JSON.stringify(inp),
});
const reader = res.body.getReader();
const dec = new TextDecoder();
let buf = "", raw = "", event = null;
for (;;) {
const { value, done } = await reader.read();
if (done) break;
buf += dec.decode(value, { stream: true });
const lines = buf.split("\n");
buf = lines.pop();
for (const line of lines) {
// Frame name on the event: line; the payload has no "type" field.
if (line.startsWith("event:")) event = line.slice(6).trim();
else if (line.startsWith("data:")) {
const payload = JSON.parse(line.slice(5).trim() || "{}");
if (event === "delta") { raw += payload.text || ""; onDelta?.(payload.text || ""); }
else if (event === "done") return { doc: JSON.parse(raw), settle: payload };
}
}
}
throw new Error("stream ended without a done frame");
}
let chars = 0;
const { doc, settle } = await runStream(codexInput, token, (t) => { chars += t.length; });
console.log(doc.document_title, "charged", settle.charged_credits, "-", chars, "chars");
if (settle.truncated) console.warn("output cap reduced — document incomplete");
// POST /run-stream and read the SSE frames. The frame name is on the `event:`
// line — there is no "type" field in the payload. `delta` payloads carry
// {"text":"..."} and concatenate into the standards-document JSON.
req, _ := http.NewRequest("POST", base+"/run-stream", bytes.NewReader(bodyBytes))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Idempotency-Key", idemKey(inp, 1))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
sc := bufio.NewScanner(res.Body)
sc.Buffer(make([]byte, 1<<20), 1<<20)
var raw strings.Builder
event := ""
for sc.Scan() {
line := sc.Text()
switch {
case strings.HasPrefix(line, "event:"):
event = strings.TrimSpace(line[6:])
case strings.HasPrefix(line, "data:"):
payload := strings.TrimSpace(line[5:])
if event == "delta" {
var d struct{ Text string `json:"text"` }
json.Unmarshal([]byte(payload), &d)
raw.WriteString(d.Text)
} else if event == "done" {
fmt.Println("settled:", payload)
fmt.Println("document:", raw.String())
return
}
}
}
// POST /run-stream with BodyHandlers.ofLines() and fold the SSE frames yourself.
// The frame name is the event: line; the JSON payload carries no "type" field.
HttpRequest stream = HttpRequest.newBuilder(URI.create(BASE + "/run-stream"))
.header("Content-Type", "application/json")
.header("Authorization", "Bearer " + token)
.header("Idempotency-Key", key)
.POST(HttpRequest.BodyPublishers.ofString(codexInputJson))
.build();
StringBuilder raw = new StringBuilder();
String[] event = { "" };
HTTP.send(stream, HttpResponse.BodyHandlers.ofLines()).body().forEach(line -> {
if (line.startsWith("event:")) {
event[0] = line.substring(6).trim();
} else if (line.startsWith("data:") && event[0].equals("delta")) {
// parse {"text":"..."} with your JSON library and append it
raw.append(extractText(line.substring(5).trim()));
}
});
System.out.println(raw); // {"document_title":...,"sections":[...],"undecided":[...]}
def run_stream(inp, token, attempt = 1)
uri = URI(BASE.to_s + "/run-stream")
req = Net::HTTP::Post.new(uri)
req["Content-Type"] = "application/json"
req["Authorization"] = "Bearer #{token}"
req["Idempotency-Key"] = idem_key(inp, attempt)
req.body = JSON.generate(inp)
raw = ""
event = nil
settle = nil
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
# Frame name lives on the event: line, not in the payload.
if line.start_with?("event:")
event = line[6..].strip
elsif line.start_with?("data:")
payload = JSON.parse(line[5..].strip.empty? ? "{}" : line[5..].strip)
raw << payload.fetch("text", "") if event == "delta"
settle = payload if event == "done"
end
end
end
end
end
[JSON.parse(raw), settle]
end
doc, settle = run_stream(codex_input, token)
puts "#{doc['document_title']} charged #{settle['charged_credits']}"
// POST /run-stream with a write callback; the frame name arrives on `event:`
// and never as a "type" field inside the JSON payload.
$raw = "";
$event = "";
$ch = curl_init(BASE . "/run-stream");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($codex_input),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"Authorization: Bearer " . $token,
"Idempotency-Key: " . idem_key($codex_input),
],
CURLOPT_WRITEFUNCTION => function ($ch, $chunk) use (&$raw, &$event) {
foreach (explode("\n", $chunk) as $line) {
$line = rtrim($line);
if (str_starts_with($line, "event:")) {
$event = trim(substr($line, 6));
} elseif (str_starts_with($line, "data:") && $event === "delta") {
$payload = json_decode(trim(substr($line, 5)), true) ?: [];
$raw .= $payload["text"] ?? "";
}
}
return strlen($chunk);
},
]);
curl_exec($ch);
curl_close($ch);
$doc = json_decode($raw, true);
echo $doc["document_title"] . "\n";
var sreq = new HttpRequestMessage(HttpMethod.Post, Base + "/run-stream") {
Content = JsonContent.Create(codexInput)
};
sreq.Headers.Add("Authorization", "Bearer " + token);
sreq.Headers.Add("Idempotency-Key", IdemKey(codexInput));
using var sres = await Http.SendAsync(sreq, HttpCompletionOption.ResponseHeadersRead);
using var reader = new StreamReader(await sres.Content.ReadAsStreamAsync());
var raw = new StringBuilder();
string? evt = null, line;
while ((line = await reader.ReadLineAsync()) != null) {
// The frame name is the event: line — the payload has no "type" field.
if (line.StartsWith("event:")) {
evt = line[6..].Trim();
} else if (line.StartsWith("data:")) {
var payload = JsonDocument.Parse(line[5..].Trim() is { Length: > 0 } s ? s : "{}");
if (evt == "delta" && payload.RootElement.TryGetProperty("text", out var t))
raw.Append(t.GetString());
else if (evt == "done")
Console.WriteLine("settled: " + payload.RootElement);
}
}
Console.WriteLine(raw.ToString());
Step 6 · Read past documents — and search them by meaning
Past documents are stored in a declared collection named codices. Exactly eight
fields are declared, and only these are filterable or sortable — anything else in the
document round-trips fine but is rejected by where with
Unknown field:
| Field | Type | What it holds |
|---|---|---|
name | string | The project name the run was given. Embedded. |
audience | string | Who the document was written for. Embedded. |
languages | string | The measured languages, comma-joined, by display label — "JavaScript/TypeScript, Python", not "js, py". Embedded. |
rules_summary | string | A one-line summary of what the document covered. Embedded. |
rule_count | number | Rules in the document. |
conformance_avg | number | Mean conformance across measured categories. |
deviations | number | Total deviating lines counted. |
ran_at | timestamp | When the run happened. The sort key you almost always want. |
The document body — document_md, conformance_map,
result, context, meta — is stored on every record
and returned by every read, but is not declared, so it cannot appear in
where or sort. The four embedded fields above are what
similar searches. Records are scoped to the calling subject, and each
POST /guest mints a new guest identity, so reuse one token across
writes and reads.
1. Every
where entry must be an operator object. Write
{"languages":{"contains":"Python"}}, not {"languages":"Python"}
— the bare-value shorthand is rejected with a 400
validation_error, it is not quietly accepted. Operators:
eq ne lt lte gt gte in contains.
2.
languages is a comma-joined list, so filter it with
contains. A record for a mixed repo holds
"JavaScript/TypeScript, Python", so {"eq":"Python"} matches only
the runs that measured Python and nothing else — it returns a confidently short list
rather than an error, which is the way this one usually goes unnoticed.
3. The sort key is
sort, and it is an object.
{"sort":{"field":"ran_at","dir":"desc"}}. A body that says
order_by instead is silently ignored — no error, no
warning — and the query falls back to created_at desc, which is why
results sometimes look almost-but-not-quite right. If the ordering surprises you, check
that key first.
Semantic search is
POST /collections/codices/similar with
{"text": "the one where the tabs kept losing", "limit": 8} — each hit
carries a cosine score. It is rate-limited to 30 requests per
minute per IP and costs roughly ten times a filtered query, so debounce it and
prefer where whenever an exact match would do.
# Filtered query: codices that measured Python, newest first. `languages` is a
# comma-joined list, so `contains` is the right operator and `eq` would only
# match a Python-ONLY run. Note the operator objects and
# the `sort` object — `order_by` would be ignored silently.
curl -s -X POST https://api.skillsafe.ai/v1/app-api/collections/codices/query \
-H "Authorization: Bearer $SKILLSAFE_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"where":{"languages":{"contains":"Python"}},
"sort":{"field":"ran_at","dir":"desc"},"limit":20}'
# Semantic search over title + preamble + rule statements
# (30 requests/minute per IP; roughly 10x the cost of a filtered query):
curl -s -X POST https://api.skillsafe.ai/v1/app-api/collections/codices/similar \
-H "Authorization: Bearer $SKILLSAFE_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"text":"the one where the tabs kept losing","limit":8}'
res = call("/collections/codices/query", body={
# Operator objects only — {"languages": "Python"} is a 400.
"where": {"languages": {"contains": "Python"}},
# The key is "sort"; "order_by" is ignored and you silently get created_at desc.
"sort": {"field": "ran_at", "dir": "desc"},
"limit": 20,
}, token=token)
for rec in res["records"]:
d = rec["doc"]
print(d["ran_at"], d["document_title"], "-", d["rule_count"], "rules")
hits = call("/collections/codices/similar",
body={"text": "the one where the tabs kept losing", "limit": 8},
token=token) # 30 req/min per IP — debounce it
for rec in hits["records"]:
print("%.2f" % rec.get("score", 0), rec["doc"]["document_title"])
const res = await call("/collections/codices/query", {
token,
body: {
// Operator object, not a bare value.
where: { languages: { contains: "Python" } },
// "sort" — an "order_by" key is ignored and falls back to created_at desc.
sort: { field: "ran_at", dir: "desc" },
limit: 20,
},
});
for (const rec of res.records) {
const d = rec.doc;
console.log(d.ran_at, d.document_title, "-", d.rule_count, "rules");
}
// 30 requests/minute per IP, ~10x the cost of a filtered query.
const hits = await call("/collections/codices/similar", {
token,
body: { text: "the one where the tabs kept losing", limit: 8 },
});
for (const rec of hits.records) console.log(rec.score, rec.doc.document_title);
// POST /collections/codices/query with an operator object per where field.
// A bare value is a 400; "order_by" instead of "sort" is silently ignored.
query := map[string]any{
"where": map[string]any{"languages": map[string]any{"contains": "Python"}},
"sort": map[string]string{"field": "ran_at", "dir": "desc"},
"limit": 20,
}
var res struct {
Records []struct {
RecordID string `json:"record_id"`
Doc map[string]any `json:"doc"`
} `json:"records"`
}
if err := call("/collections/codices/query", token, query, &res); err != nil {
panic(err)
}
for _, r := range res.Records {
fmt.Println(r.Doc["ran_at"], r.Doc["document_title"], r.Doc["rule_count"])
}
// POST /collections/codices/query — operator objects in where, and the sort
// key must be "sort" (an "order_by" key is ignored, not rejected).
String q = "{\"where\":{\"languages\":{\"contains\":\"Python\"}}," +
"\"sort\":{\"field\":\"ran_at\",\"dir\":\"desc\"},\"limit\":20}";
System.out.println(call("/collections/codices/query", token, q));
// Semantic search: POST /collections/codices/similar {"text":"...","limit":8}
// — 30 requests per minute per IP.
res = call("/collections/codices/query", body: {
# Operator object per field; a bare value is rejected.
"where" => { "languages" => { "contains" => "Python" } },
# "sort", not "order_by" — the latter is silently ignored.
"sort" => { "field" => "ran_at", "dir" => "desc" },
"limit" => 20,
}, token: token)
res["records"].each do |rec|
d = rec["doc"]
puts "#{d['ran_at']} #{d['document_title']} - #{d['rule_count']} rules"
end
hits = call("/collections/codices/similar",
body: { "text" => "the one where the tabs kept losing", "limit" => 8 },
token: token) # 30 req/min per IP
$res = call("/collections/codices/query", [
// Operator object — ["languages" => "Python"] would be a 400.
"where" => ["languages" => ["contains" => "Python"]],
// "sort" is the key; "order_by" is ignored without an error.
"sort" => ["field" => "ran_at", "dir" => "desc"],
"limit" => 20,
], $token);
foreach ($res["records"] as $rec) {
$d = $rec["doc"];
echo "{$d['ran_at']} {$d['document_title']} - {$d['rule_count']} rules\n";
}
// 30 requests/minute per IP.
$hits = call("/collections/codices/similar",
["text" => "the one where the tabs kept losing", "limit" => 8], $token);
var q = new {
// Operator object per where field; a bare value is rejected with a 400.
where = new { languages = new { contains = "Python" } },
// The key is "sort"; "order_by" is silently ignored (created_at desc).
sort = new { field = "ran_at", dir = "desc" },
limit = 20
};
var res = await Call("/collections/codices/query", q, token);
foreach (var rec in res.GetProperty("records").EnumerateArray()) {
var d = rec.GetProperty("doc");
Console.WriteLine($"{d.GetProperty("ran_at")} {d.GetProperty("document_title")}");
}
// Semantic search — 30 requests/minute per IP, ~10x a filtered query.
var hits = await Call("/collections/codices/similar",
new { text = "the one where the tabs kept losing", limit = 8 }, token);
The input schema
These are the exact fields the app submits. Almost all of the value is in
style_profile: the measurement is done before the run, in the
browser, and it is what the reply is held to. Each entry in
style_profile.categories is one measured convention — the variants
seen with their counts, the majority with its percentage and whether it was a tie, the
conformance percentage, the deviation count and a few real deviating lines with file and
line number. Categories the code could not decide are still sent; they belong in
undecided, not in silence. not_measured names the categories
the analyzer did not attempt at all, so the writing pass can say so instead of guessing.
| Field | Type | Meaning |
|---|---|---|
project_name | string | What the document is about. May be empty. |
audience | string | Who will read it — a team, new contributors, a review bot. May be empty. |
strictness | string | One of descriptive, balanced, prescriptive. Describes how far the document is allowed to go beyond reporting what the code already does. |
extra_instructions | string | Free-form direction for this run. May be empty. |
current_document | string | Markdown of an existing standards document to revise rather than replace. May be empty. |
refine_note | string | What to change about current_document. Only meaningful alongside it. May be empty. |
style_profile.languages | array | The languages detected across the submitted files, e.g. ["JavaScript/TypeScript"]. |
style_profile.totals | object | files, lines, code_lines, comment_lines, blank_lines — how much code the measurement rests on. |
style_profile.metrics | object | comment_density_pct, median_line_length, p95_line_length, function_count — the shape numbers the threshold rules are drawn from. |
style_profile.categories[] | array | One entry per measured convention: id (the key a rule cites), label, kind (convention or a threshold kind), threshold, variants (name → count), majority ({name, count, total, pct, tied}), conformance_pct, deviations, a free-text note and top_deviations ({file, line, variant, text}). |
style_profile.not_measured | array | Category ids the analyzer did not attempt. These are not subject to the coverage rule — nothing may claim them. |
code_excerpt | string | A slice of the real source for texture. When it is long it is clipped from the middle — both ends kept — because the head and the tail of a file both carry meaning. Context, not evidence: the categories are computed from the full submission, so a pattern absent from the excerpt is still measured. |
current_datetime | string | The caller's local time, weekday included. |
retry_note | string | Optional, and normally absent. The web app adds it on exactly one occasion: the first reply did not parse as the JSON contract below, and it is retrying once with a note saying so. The system prompt defines it, so a rule-abiding client may send it — but if you do, reuse the same Idempotency-Key input hash with an incremented attempt counter rather than minting a fresh key, or the retry bills as a second run. |
A complete body
Trimmed to one category so the shape is readable. A real profile carries every category the analyzer measured, each with its own variants, majority and deviations.
{
"project_name": "string, may be empty",
"audience": "string, may be empty",
"strictness": "descriptive | balanced | prescriptive",
"extra_instructions": "string, may be empty",
"current_document": "Markdown of an existing standards document to revise, may be empty",
"refine_note": "what to change about current_document, may be empty",
"style_profile": {
"languages": ["JavaScript/TypeScript"],
"totals": {"files": 6, "lines": 2140, "code_lines": 1680, "comment_lines": 210, "blank_lines": 250},
"metrics": {"comment_density_pct": 11.1, "median_line_length": 34, "p95_line_length": 96, "function_count": 84},
"categories": [
{"id":"indent_char","label":"Indentation character","kind":"convention","threshold":null,
"variants":{"space":1580,"tab":12},
"majority":{"name":"space","count":1580,"total":1592,"pct":99.2,"tied":false},
"conformance_pct":99.2,"deviations":12,"note":"",
"top_deviations":[{"file":"legacy/util.js","line":44,"variant":"tab","text":"..."}]}
],
"not_measured": ["import_grouping"]
},
"code_excerpt": "a slice of the real source, clipped from the MIDDLE when long, both ends kept",
"current_datetime": "2026-08-07T10:15:00+00:00 (Friday)"
}
The output contract
The reply is one JSON object and nothing else — no prose before
it, no commentary after it, no code fences around it. Parse defensively anyway: strip a
stray fence, take the span from the first { to the matching last
}, and re-ask once with the same idempotency seed and a bumped attempt
counter if it still does not parse.
{
"document_title": "string",
"preamble": "string",
"sections": [
{"heading":"string","intro":"string, optional",
"rules":[{"category":"an id from style_profile.categories",
"statement":"imperative, testable",
"asserted_variant":"the exact majority.name string, copied verbatim",
"conformance_claim": 99.2,
"rationale":"string","enforcement":"string","exceptions":"string, optional"}]}
],
"undecided": [{"category":"an id","why":"string"}],
"unverified": ["strings"]
}
| Field | Constraint |
|---|---|
document_title | The document's own title. Non-empty. |
preamble | What this document is, what it was measured from, and how to read a conformance figure. Non-empty. |
sections[].heading | A grouping of related rules — layout, naming, comments, structure. intro is optional. |
rules[].category | An id from style_profile.categories, exactly as written. An id that was never measured — or one from not_measured — is a failure. |
rules[].statement | Imperative and testable: something a reviewer or a linter could pass or fail a file on. Not "prefer consistency". |
rules[].asserted_variant | The measured majority.name string, copied verbatim. This is the field the drift check compares. |
rules[].conformance_claim | The percentage the rule claims the codebase already meets, checked against the measured conformance_pct within a 2-point tolerance. |
rules[].rationale, enforcement | Why the rule exists, and how it is enforced — formatter setting, lint rule, review checklist. exceptions is optional. |
undecided[] | {category, why} for a measured category the code genuinely disagrees on — a tie, or conformance too low to call a convention. This is where an honest "no majority" goes; it counts as coverage. |
unverified[] | Strings. Anything asserted that the measurement could not vouch for. An empty array is a claim that the whole document is grounded — and the app checks it. |
The coverage rule
sections[].rules[].category and undecided[].category: it must
match the set of measured category ids one-for-one — never zero times, never
twice. A category the code has a clear majority on gets a rule; a category it disagrees
on gets an undecided entry saying why. Silence is not an option, and
neither is saying it twice in two different sections.
The app checks this mechanically after every run and reports both failure modes by name: a measured category that no statement covers, and a measured category that two statements cover. Both are real bugs in a standards document — the first leaves a convention undocumented, the second lets two rules drift apart and contradict each other later. Categories listed in
style_profile.not_measured are outside the rule: nothing may claim them,
and their absence is not a gap.
Two more checks run alongside it.
asserted_variant is compared against the
measured majority.name, and conformance_claim against the
measured conformance_pct with a 2-point tolerance. When a
claim drifts, the app does not silently rewrite it: the claimed figure is
displayed as written, beside the measured one, so you can see that the document and the
measurement disagree and decide which is wrong. A number quietly corrected is a number
you can no longer audit.
A complete reply
{
"document_title": "Ledger service coding standards",
"preamble": "Measured from 6 JavaScript/TypeScript files (2,140 lines) on 2026-08-07. Every rule below cites the category it was measured from and the share of the codebase that already conforms; a rule at 99.2% describes what the code does, a rule at 61% describes where it is heading.",
"sections": [
{
"heading": "Layout",
"intro": "How lines and blocks are shaped.",
"rules": [
{
"category": "indent_char",
"statement": "Indent with spaces. Do not commit tab characters for indentation.",
"asserted_variant": "space",
"conformance_claim": 99.2,
"rationale": "1,580 of 1,592 indented lines already use spaces; the 12 exceptions are confined to legacy/util.js.",
"enforcement": "indent_style = space in .editorconfig; the formatter rewrites on save.",
"exceptions": "Makefiles, where tabs are required by the tool."
}
]
}
],
"undecided": [
{
"category": "quote_style",
"why": "Single and double quotes are within a percentage point of each other (812 vs 799). The codebase has no majority to describe, and picking one here would be an unmeasured preference."
}
],
"unverified": []
}
unverified, in plain words, rather than being asserted with a made-up
percentage next to it. A confident number with nothing behind it is the one failure mode
a standards document cannot survive.
Free before you pay
Most of Style Codex costs nothing and needs no account. The measurement engine runs
entirely in the browser: the language detection, the per-category counts, the majority
and conformance figures, the deviation lists with file and line numbers. So does
everything derived from them — the generated standards document, the generated
.editorconfig and the deviations CSV are all produced client-side, with no
network call and no charge. Only the writing pass is metered: the one
step that turns the measurement into prose. Your source never leaves the page unless you
run that step.
/estimate is worth calling: you can show a real price for
the one paid step while the free half of the app has already given the user the numbers,
the .editorconfig and the CSV. And because the measurement is deterministic
— the same files always produce the same profile — a CI job can gate a repo
on conformance figures without an API call at all.