← Promote Desk
API
Tokens

Run Promote Desk from your own code

Both metered lanes are available over HTTP, and each returns one JSON object. The division of labour: the page reads the evidence, computes every drift delta with its 95% half-width and budget verdict, the arena win rate with its Wilson interval, every stage's status, the verdict in gate order with one remediation, and the export format the map gives - all free, in promokit.js. The run writes the report or the plan; the page then holds the reply to its own gate (promoreply.js).

Base URL and the response shape

Every endpoint lives under https://api.skillsafe.ai/v1/app-api, and every response has one shape:

{ "ok": true,  "data":  { ... } }
{ "ok": false, "error": { "code": "...", "message": "..." } }

The token is minted for this app (the guest call takes {"slug":"promote-desk"}), so no slug header is needed afterwards. Send it as Authorization: Bearer …. The run body is the input object, sent bare.

StatusCodeMeaning
400validation_errorThe body is not a JSON object, or a required key (task, run, evals, facts, deploy, gate) is missing.
401unauthorizedNo token, or a stale one. Mint a guest token or sign in again.
402insufficient_creditsThe balance is under min_credits. Price with /estimate first.
403forbiddenA guest token tried to run: both lanes are metered and need a personal sign-in.
404not_foundUnknown job id.
429rate_limitedBack off and retry.
5xxserver_errorTransient. Retry with the same Idempotency-Key so a retry never double-bills.

The task field comes first

One app, one model, two lanes - task picks the lane. An unknown or missing task is answered as gate. The export lane is meant to run only after a PROMOTE; the page will not send it otherwise.

taskStageReturns
gatedecideThe promotion report: verdict (PROMOTE, REJECT or RERUN), uncertain, four stages (stage, status, evidence), evidence, remediation (one fix, empty for PROMOTE) and caveats.
exportshipThe export plan: format, packaging, runtime, why, steps (step, command), smoke_test, watch_for (signature, cause) and note.

The input

FieldRequiredWhat
taskyesgate or export. If it is missing or neither, do gate and set "task": "gate".
runyesthe checkpoint and base model names, the target-task metric, the deployment (production or local), the forgetting-ladder rungs already tried, and whether replay rows reuse the drift suite's phrasing.
evalsyesthe drift table as the page read it: benchmark, baseline, checkpoint, delta, n, half-width, seed rerun, budget verdict.
factsyesevery stage's status line, the expected verdict and what decided it, the page's remediation, and every finding tagged [fix], [check] or [info].
deployyesfor export: hardware, workload, packaging, model size, the page's format and packaging pick with the allowed alternatives, the runtime, the smoke-test gate and approximate sizes. none for gate.
gateyesfor export, the gate's result; none for gate.
retry_notenooptional; if present, your previous reply was unusable - follow it.

The rules the model applies

House rules

The replies

gate

  1. verdict - copy the expected verdict from the facts: PROMOTE, REJECT or RERUN (RERUN only when stage 2 still waits on a seed rerun - the report cannot end yet).
  2. uncertain - true only when the expected verdict in the facts carries the word uncertain; otherwise false - if the numbers look tighter than that, say so in a caveat instead.
  3. stages - exactly four entries, stages 1 to 4 in order: stage (the number), status (exactly one of PASS, FAIL, HARD FAIL, RERUN, UNCERTAIN, NOT RUN, NOT APPLICABLE - the token whose meaning matches the stage line, not its exact wording: "RERUN pending" is RERUN, "REJECT (uncertain)" is UNCERTAIN, "NOT RUN (production target)" is NOT RUN), evidence (one or two sentences, at most 45 words, with the numbers that matter).
  4. evidence - one paragraph, at most 70 words: for REJECT or RERUN, cite the specific stage and number that decided it; for PROMOTE, say every stage cleared and cite the tightest numbers.
  5. remediation - for REJECT or RERUN, exactly one fix, at most 35 words, following the page's remediation unless it contradicts the rules; "" for PROMOTE.
  6. caveats - at most 4 short strings: sample-size limits, instruction-familiar scores, single-run lever attribution, anything the person should know before trusting the verdict. [] when none.
{
  "task": "gate",
  "verdict": "PROMOTE | REJECT | RERUN",
  "uncertain": false,
  "stages": [ { "stage": 1, "status": "PASS", "evidence": "one or two sentences" } ],
  "evidence": "one paragraph",
  "remediation": "one fix, or empty",
  "caveats": [ "a caveat" ]
}

The page holds it to its own gate: the verdict and the uncertain flag must match, each stage status must be the one the page computed, a PROMOTE carries no remediation and a REJECT or RERUN exactly one, and every number in a sentence must be in the input.

export

  1. format - the page's format pick, written as its key (fp8, w8a8, awq-int4, gguf-q4_k_m, gguf-q8_0, nvfp4 or bf16-merged); another key only if it is one of the allowed alternatives and you say why.
  2. packaging - merged or lora-only, following the page's packaging pick.
  3. runtime - the runtime the artifact is served and smoke-tested in.
  4. why - one or two sentences, at most 45 words: the hardware and workload rule that picks this format.
  5. steps - 3 to 6 entries in order, each step (one short sentence) and command (the command or code for it, with placeholders such as CHECKPOINT_DIR, MERGED_DIR, OUT_DIR - never invent paths or model names).
  6. smoke_test - 3 to 5 short strings: the smoke-test procedure for this format and runtime, ending with the gate (byte match or grader agreement).
  7. watch_for - 2 to 4 entries: signature (what the failure looks like) and cause.
  8. note - one sentence, at most 30 words: what to run first.
{
  "task": "export",
  "format": "fp8",
  "packaging": "merged",
  "runtime": "vLLM",
  "why": "one or two sentences",
  "steps": [ { "step": "one sentence", "command": "the command" } ],
  "smoke_test": [ "a step" ],
  "watch_for": [ { "signature": "what it looks like", "cause": "why" } ],
  "note": "one sentence"
}

The page holds it to the format map: the format must be its pick or an allowed alternative, never INT4 for long-context, code or math, never NVFP4 on GB10; the runtime must fit the format; GGUF Q4_K_M must build an imatrix; the smoke test must pin greedy decoding with a fixed seed and use the right gate - byte match for a lossless export, task-grader agreement for a lossy one.

Worked examples - every lane, every sample

These are the page's four free samples. Each input is exactly what the page sends; each reply is the saved reply the sample replays, shipped as the validating model wrote it; the last line is the page's own check of it. The export lane runs only for the two promoted samples.

A code model headed to production - gate

The gate says PROMOTE - every stage cleared.

Input:

{
 "task": "gate",
 "run": "Checkpoint: support-coder-r16-ckpt-2400; base model: Qwen2.5-Coder 7B Instruct\nTarget task: ticket-to-patch pass@1 41.0 -> 53.5 (+12.5 pts)\nDeployment: production (canary passed)\nForgetting-ladder rungs already tried: none\nReplay rows reuse the drift suite's instruction phrasing: no",
 "evals": "benchmark | baseline | checkpoint | delta (pts) | n | 95% half-width (pts) | seed rerun | budget verdict\nmmlu-subset | 66.4 | 66.1 | -0.3 | 1400 | 2.5 | - | noise\ngsm8k-subset | 79.2 | 78.8 | -0.4 | 1319 | 2.2 | - | noise\nifeval | 72.0 | 72.5 | +0.5 | 541 | 3.8 | - | noise\nhumaneval-plus | 61.6 | 62.2 | +0.6 | 164 | 7.4 | - | noise\ndomain-adjacent | 58.0 | 57.4 | -0.6 | 1300 | 2.7 | - | noise",
 "facts": "Stage 1 data quality: PASS - goldens leakage 0 overlapping IDs; dedup 37 rows removed; label noise pass\nStage 2 capability drift: PASS\nStage 3 paired arena (judge): PASS - 171 wins, 58 ties, 71 losses of 300; win rate 66.7% (ties count half), 95% Wilson CI 61.2-71.8%, threshold 55.0% with the CI excluding 50%\nStage 4 canary: PASS\nExpected verdict: PROMOTE - every stage cleared\nFindings (3):\n[info] dedup (stage 1): 37 duplicate rows removed before training.\n[check] small_n (stage 2): humaneval-plus has n=164, under the 200-item pragmatic floor.\n[info] floor_n (stage 2): Suites at the 200-item floor resolve about +/-6 pts at 70%; the strict size for a 2.5 pt half-width is about 1,291 items.",
 "deploy": "none",
 "gate": "none"
}

Reply:

{
 "task": "gate",
 "verdict": "PROMOTE",
 "uncertain": false,
 "stages": [
  {
   "stage": 1,
   "status": "PASS",
   "evidence": "Zero overlapping golden IDs leaked into training, 37 duplicate rows were removed, and the label-noise check passed."
  },
  {
   "stage": 2,
   "status": "PASS",
   "evidence": "All five benchmarks moved at most 0.6 pts, within the 1-point noise band; the largest was domain-adjacent at -0.6 pts (n=1300, +/-2.7 pts). humaneval-plus was +0.6 pts but at n=164, under the 200-item floor (+/-7.4 pts)."
  },
  {
   "stage": 3,
   "status": "PASS",
   "evidence": "171 wins, 58 ties, 71 losses of 300 (ties count half) gave a 66.7% win rate; the 95% Wilson interval 61.2-71.8% clears the 55.0% threshold and excludes 50%."
  },
  {
   "stage": 4,
   "status": "PASS",
   "evidence": "The production canary rollout passed."
  }
 ],
 "evidence": "All four gating stages passed: stage 1 found zero golden leakage, stage 2's worst drift was domain-adjacent at -0.6 pts, within the 1-point noise band, stage 3's arena win rate was 66.7% (95% CI 61.2-71.8%, excluding 50%), and stage 4's production canary passed, so the checkpoint promotes.",
 "remediation": "",
 "caveats": [
  "humaneval-plus was evaluated at n=164, under the 200-item pragmatic floor (+/-7.4 pts)."
 ]
}

The page's check: kept - nothing to flag

A code model headed to production - export

The gate says PROMOTE - every stage cleared. The format map picks fp8.

Input:

{
 "task": "export",
 "run": "Checkpoint: support-coder-r16-ckpt-2400; base model: Qwen2.5-Coder 7B Instruct\nTarget task: ticket-to-patch pass@1 41.0 -> 53.5 (+12.5 pts)\nDeployment: production (canary passed)\nForgetting-ladder rungs already tried: none\nReplay rows reuse the drift suite's instruction phrasing: no",
 "evals": "benchmark | baseline | checkpoint | delta (pts) | n | 95% half-width (pts) | seed rerun | budget verdict\nmmlu-subset | 66.4 | 66.1 | -0.3 | 1400 | 2.5 | - | noise\ngsm8k-subset | 79.2 | 78.8 | -0.4 | 1319 | 2.2 | - | noise\nifeval | 72.0 | 72.5 | +0.5 | 541 | 3.8 | - | noise\nhumaneval-plus | 61.6 | 62.2 | +0.6 | 164 | 7.4 | - | noise\ndomain-adjacent | 58.0 | 57.4 | -0.6 | 1300 | 2.7 | - | noise",
 "facts": "Stage 1 data quality: PASS - goldens leakage 0 overlapping IDs; dedup 37 rows removed; label noise pass\nStage 2 capability drift: PASS\nStage 3 paired arena (judge): PASS - 171 wins, 58 ties, 71 losses of 300; win rate 66.7% (ties count half), 95% Wilson CI 61.2-71.8%, threshold 55.0% with the CI excluding 50%\nStage 4 canary: PASS\nExpected verdict: PROMOTE - every stage cleared\nFindings (3):\n[info] dedup (stage 1): 37 duplicate rows removed before training.\n[check] small_n (stage 2): humaneval-plus has n=164, under the 200-item pragmatic floor.\n[info] floor_n (stage 2): Suites at the 200-item floor resolve about +/-6 pts at 70%; the strict size for a 2.5 pt half-width is about 1,291 items.",
 "deploy": "Hardware: Hopper datacenter GPU (H100, H200)\nWorkload: Code\nPackaging asked: Decide for me\nModel size: 7.6B parameters\nPage's format pick: FP8 (W8A8-FP8) (fp8) - FP8 is the default on Hopper-class GPUs: near-bf16 quality at about half the memory.\nAlternatives allowed: W8A8 INT8 (w8a8)\nPackaging pick: merged - Merged folds the adapter into the base: a larger artifact, no base-model dependency at serve time.\nRuntime for the smoke test: vLLM\nSmoke-test gate: task-grader verdict agreement with the pre-export outputs (byte match is expected to fail on a lossy export)\nApproximate artifact sizes: FP8 (W8A8-FP8) ~7.7 GB; W8A8 INT8 ~7.7 GB; Merged bf16 safetensors ~15.2 GB",
 "gate": "Verdict: PROMOTE - every stage cleared\nStage 1 data quality: PASS - goldens leakage 0 overlapping IDs; dedup 37 rows removed; label noise pass\nStage 2 capability drift: PASS\nStage 3 paired arena (judge): PASS - 171 wins, 58 ties, 71 losses of 300; win rate 66.7% (ties count half), 95% Wilson CI 61.2-71.8%, threshold 55.0% with the CI excluding 50%\nStage 4 canary: PASS"
}

Reply:

{
 "task": "export",
 "format": "fp8",
 "packaging": "merged",
 "runtime": "vLLM",
 "why": "FP8 is the default pick for Hopper-class GPUs, and the code workload must stay on FP8 or W8A8 rather than INT4, so FP8 fits both the hardware and the workload here.",
 "steps": [
  {
   "step": "Merge the LoRA adapter into the base model to produce a standalone merged checkpoint.",
   "command": "python merge_adapter.py --base_model BASE_MODEL_DIR --adapter CHECKPOINT_DIR --out MERGED_DIR"
  },
  {
   "step": "Quantize the merged weights to FP8 for Hopper-class serving.",
   "command": "python quantize_fp8.py --model MERGED_DIR --out OUT_DIR --dtype fp8"
  },
  {
   "step": "Serve the FP8 checkpoint with vLLM.",
   "command": "vllm serve OUT_DIR --quantization fp8"
  },
  {
   "step": "Smoke-test the exported checkpoint by comparing its outputs with the pre-export outputs.",
   "command": "python smoke_test.py --model OUT_DIR --prompts GOLDEN_PROMPTS_FILE --seed SEED --temperature 0"
  }
 ],
 "smoke_test": [
  "Load the FP8 checkpoint in vLLM, the real target runtime.",
  "Replay the same 3 to 5 reference prompts used before export with deterministic decoding at temperature 0, keeping the same fixed seed as before export.",
  "Compare the outputs against the pre-export outputs.",
  "Because FP8 is a lossy export, accept task-grader verdict agreement with the pre-export outputs rather than a byte match."
 ],
 "watch_for": [
  {
   "signature": "Output comes out scrambled or does not break into separate turns the way it should.",
   "cause": "Mismatch between the chat template used at export time and the one used to serve the model."
  },
  {
   "signature": "Output reads fluently but ignores the expected response structure or makes no sense.",
   "cause": "The lm_head layer was quantized incorrectly."
  }
 ],
 "note": "Merge the LoRA adapter into the base model first, before quantizing to FP8."
}

The page's check: kept - nothing to flag

A 3B recipe model for laptops - gate

The gate says PROMOTE - every stage cleared.

Input:

{
 "task": "gate",
 "run": "Checkpoint: recipes-lora-ckpt-900; base model: Llama 3.2 3B Instruct\nTarget task: recipe-format exact match 52.0 -> 81.0 (+29.0 pts)\nDeployment: local only\nForgetting-ladder rungs already tried: none\nReplay rows reuse the drift suite's instruction phrasing: no",
 "evals": "benchmark | baseline | checkpoint | delta (pts) | n | 95% half-width (pts) | seed rerun | budget verdict\nmmlu-subset | 60.2 | 59.8 | -0.4 | 500 | 4.3 | - | noise\ngsm8k-subset | 72.1 | 68.9 | -3.2 | 500 | 4.0 | 71.5 (delta -0.6) | pass after rerun\nifeval | 68.4 | 69.0 | +0.6 | 541 | 3.9 | - | noise\ndomain-adjacent | 64.0 | 63.6 | -0.4 | 300 | 5.4 | - | noise",
 "facts": "Stage 1 data quality: PASS - goldens leakage 0 overlapping IDs; dedup 4 rows removed; label noise pass\nStage 2 capability drift: PASS\nStage 3 paired arena (deterministic): PASS - 30 wins, 40 ties, 10 losses of 80; win rate 62.5% (ties count half), 95% Wilson CI 51.5-72.3%, threshold 55.0% with the CI excluding 50%\nStage 4 canary: NOT APPLICABLE - local-only deployment stops at stage 3\nExpected verdict: PROMOTE - every stage cleared\nFindings (3):\n[info] dedup (stage 1): 4 duplicate rows removed before training.\n[info] floor_n (stage 2): Suites at the 200-item floor resolve about +/-6 pts at 70%; the strict size for a 2.5 pt half-width is about 1,291 items.\n[info] arena_det_small (stage 3): The deterministic arena covers 80 goldens - dogfood scale; read the CI width as evidence quality.",
 "deploy": "none",
 "gate": "none"
}

Reply:

{
 "task": "gate",
 "verdict": "PROMOTE",
 "uncertain": false,
 "stages": [
  {
   "stage": 1,
   "status": "PASS",
   "evidence": "Zero overlapping golden IDs leaked into training, 4 duplicate rows were removed, and the label-noise check passed."
  },
  {
   "stage": 2,
   "status": "PASS",
   "evidence": "gsm8k-subset dropped 3.2 pts initially (+/-4.0 pts, n=500), in the 2-5 pt rerun band; the seed-variation rerun returned 71.5 (delta -0.6 pts), within the 1-point noise band, so it passes. The other three benchmarks moved at most 0.6 pts, also noise."
  },
  {
   "stage": 3,
   "status": "PASS",
   "evidence": "30 wins, 40 ties, 10 losses of 80 (deterministic arena, ties count half) gave a 62.5% win rate; the 95% Wilson interval 51.5-72.3% clears the 55.0% threshold and excludes 50%, though 80 goldens is dogfood scale."
  },
  {
   "stage": 4,
   "status": "NOT APPLICABLE",
   "evidence": "This is a local-only deployment, which correctly stops at stage 3; no canary is run."
  }
 ],
 "evidence": "All three applicable stages passed: stage 1 found zero golden leakage, stage 2's gsm8k-subset rerun landed at a -0.6-point delta after an initial 3.2-point drop, within the 1-point noise band, and stage 3's deterministic arena reached a 62.5% win rate (95% CI 51.5-72.3%, excluding 50%). Stage 4 does not apply to this local-only deployment, so promotion is correct.",
 "remediation": "",
 "caveats": [
  "The deterministic arena covers only 80 goldens, dogfood scale; read the 51.5-72.3% CI width as evidence quality.",
  "The gsm8k-subset pass relies on a single seed-variation rerun, not a repeated average."
 ]
}

The page's check: kept - nothing to flag

A 3B recipe model for laptops - export

The gate says PROMOTE - every stage cleared. The format map picks gguf-q4_k_m.

Input:

{
 "task": "export",
 "run": "Checkpoint: recipes-lora-ckpt-900; base model: Llama 3.2 3B Instruct\nTarget task: recipe-format exact match 52.0 -> 81.0 (+29.0 pts)\nDeployment: local only\nForgetting-ladder rungs already tried: none\nReplay rows reuse the drift suite's instruction phrasing: no",
 "evals": "benchmark | baseline | checkpoint | delta (pts) | n | 95% half-width (pts) | seed rerun | budget verdict\nmmlu-subset | 60.2 | 59.8 | -0.4 | 500 | 4.3 | - | noise\ngsm8k-subset | 72.1 | 68.9 | -3.2 | 500 | 4.0 | 71.5 (delta -0.6) | pass after rerun\nifeval | 68.4 | 69.0 | +0.6 | 541 | 3.9 | - | noise\ndomain-adjacent | 64.0 | 63.6 | -0.4 | 300 | 5.4 | - | noise",
 "facts": "Stage 1 data quality: PASS - goldens leakage 0 overlapping IDs; dedup 4 rows removed; label noise pass\nStage 2 capability drift: PASS\nStage 3 paired arena (deterministic): PASS - 30 wins, 40 ties, 10 losses of 80; win rate 62.5% (ties count half), 95% Wilson CI 51.5-72.3%, threshold 55.0% with the CI excluding 50%\nStage 4 canary: NOT APPLICABLE - local-only deployment stops at stage 3\nExpected verdict: PROMOTE - every stage cleared\nFindings (3):\n[info] dedup (stage 1): 4 duplicate rows removed before training.\n[info] floor_n (stage 2): Suites at the 200-item floor resolve about +/-6 pts at 70%; the strict size for a 2.5 pt half-width is about 1,291 items.\n[info] arena_det_small (stage 3): The deterministic arena covers 80 goldens - dogfood scale; read the CI width as evidence quality.",
 "deploy": "Hardware: Laptop, desktop or CPU - llama.cpp\nWorkload: Generic chat\nPackaging asked: Decide for me\nModel size: 3.2B parameters\nPage's format pick: GGUF Q4_K_M + imatrix (gguf-q4_k_m) - Edge/llama.cpp serving: GGUF Q4_K_M built from an imatrix optimizes for footprint.\nAlternatives allowed: GGUF Q8_0 (gguf-q8_0)\nPackaging pick: merged - Merged folds the adapter into the base: a larger artifact, no base-model dependency at serve time.\nRuntime for the smoke test: llama.cpp\nSmoke-test gate: task-grader verdict agreement with the pre-export outputs (byte match is expected to fail on a lossy export)\nApproximate artifact sizes: GGUF Q4_K_M + imatrix ~1.9 GB; GGUF Q8_0 ~3.4 GB; Merged bf16 safetensors ~6.4 GB",
 "gate": "Verdict: PROMOTE - every stage cleared\nStage 1 data quality: PASS - goldens leakage 0 overlapping IDs; dedup 4 rows removed; label noise pass\nStage 2 capability drift: PASS\nStage 3 paired arena (deterministic): PASS - 30 wins, 40 ties, 10 losses of 80; win rate 62.5% (ties count half), 95% Wilson CI 51.5-72.3%, threshold 55.0% with the CI excluding 50%\nStage 4 canary: NOT APPLICABLE - local-only deployment stops at stage 3"
}

Reply:

{
 "task": "export",
 "format": "gguf-q4_k_m",
 "packaging": "merged",
 "runtime": "llama.cpp",
 "why": "GGUF Q4_K_M built from an imatrix is the edge and llama.cpp pick for laptop, desktop or CPU hardware, and the generic chat workload has no restriction against this quantization level.",
 "steps": [
  {
   "step": "Merge the LoRA adapter into the base model to produce a standalone merged checkpoint.",
   "command": "python merge_adapter.py --base_model BASE_MODEL_DIR --adapter CHECKPOINT_DIR --out MERGED_DIR"
  },
  {
   "step": "Build llama.cpp with its default configuration and install the repo's gguf-py and sentencepiece.",
   "command": "cmake -B build && cmake --build build --config Release && pip install -e gguf-py && pip install sentencepiece"
  },
  {
   "step": "Convert the merged checkpoint to f16 GGUF.",
   "command": "python convert_hf_to_gguf.py MERGED_DIR --outtype f16 --outfile OUT_DIR/model-f16.gguf"
  },
  {
   "step": "Generate an importance matrix from a domain calibration corpus.",
   "command": "./llama-imatrix -m OUT_DIR/model-f16.gguf -f CALIBRATION_CORPUS -o OUT_DIR/imatrix.dat"
  },
  {
   "step": "Quantize to Q4_K_M using the imatrix.",
   "command": "./llama-quantize --imatrix OUT_DIR/imatrix.dat OUT_DIR/model-f16.gguf OUT_DIR/model-Q4_K_M.gguf Q4_K_M"
  }
 ],
 "smoke_test": [
  "Load the Q4_K_M GGUF checkpoint in llama.cpp, the real target runtime.",
  "Replay the same 3 to 5 reference prompts used before export with deterministic decoding at temperature 0, keeping the same fixed seed as before export, using llama-completion rather than llama-cli.",
  "Compare the outputs against the pre-export outputs.",
  "Because this export is lossy, accept task-grader verdict agreement with the pre-export outputs rather than a byte match."
 ],
 "watch_for": [
  {
   "signature": "Output comes out scrambled or does not break into separate turns the way it should.",
   "cause": "Mismatch between the chat template used at export time and the one used to serve the model."
  },
  {
   "signature": "Output reads fluently but ignores the expected response structure or makes no sense.",
   "cause": "The lm_head layer was quantized incorrectly."
  }
 ],
 "note": "Merge the LoRA adapter into the base model first, then build llama.cpp before converting and quantizing to GGUF."
}

The page's check: kept - nothing to flag

Task up 9 pts, domain down 11.5 - gate

The gate says REJECT - decided by stage 2 HARD FAIL on domain-adjacent.

Input:

{
 "task": "gate",
 "run": "Checkpoint: legal-qa-r64-ckpt-3000; base model: Llama 3.1 8B Instruct\nTarget task: clause-extraction F1 70.2 -> 79.1 (+8.9 pts)\nDeployment: local only\nForgetting-ladder rungs already tried: none\nReplay rows reuse the drift suite's instruction phrasing: no",
 "evals": "benchmark | baseline | checkpoint | delta (pts) | n | 95% half-width (pts) | seed rerun | budget verdict\nmmlu-subset | 68.2 | 67.6 | -0.6 | 1400 | 2.4 | - | noise\ngsm8k-subset | 81.0 | 80.4 | -0.6 | 1319 | 2.1 | - | noise\nifeval | 74.1 | 73.8 | -0.3 | 541 | 3.7 | - | noise\ndomain-adjacent | 64.0 | 52.5 | -11.5 | 1300 | 2.7 | - | HARD FAIL",
 "facts": "Stage 1 data quality: PASS - goldens leakage 0 overlapping IDs; dedup 0 rows removed; label noise pass\nStage 2 capability drift: HARD FAIL\nStage 3 paired arena (judge): PASS - 118 wins, 30 ties, 52 losses of 200; win rate 66.5% (ties count half), 95% Wilson CI 59.7-72.7%, threshold 55.0% with the CI excluding 50%\nStage 4 canary: NOT APPLICABLE - local-only deployment stops at stage 3\nExpected verdict: REJECT - decided by stage 2 HARD FAIL on domain-adjacent\nTop remediation (the page's pick): Rung 1 of the forgetting ladder: swap the replay-mix fraction (swap rows, do not add them; start toward 20%).\nFindings (3):\n[fix] hard_fail (stage 2): domain-adjacent dropped 11.5 pts (+/-2.7 pts at n=1300) - over the 5 pt budget: HARD FAIL, no exception for task gains.\n[info] floor_n (stage 2): Suites at the 200-item floor resolve about +/-6 pts at 70%; the strict size for a 2.5 pt half-width is about 1,291 items.\n[info] task_gain (stage 2): The target task gained 8.9 pts - task improvement never buys back a drift-budget breach.",
 "deploy": "none",
 "gate": "none"
}

Reply:

{
 "task": "gate",
 "verdict": "REJECT",
 "uncertain": false,
 "stages": [
  {
   "stage": 1,
   "status": "PASS",
   "evidence": "Data quality passed: 0 overlapping golden IDs, 0 rows removed in dedup, and label noise checks passed."
  },
  {
   "stage": 2,
   "status": "HARD FAIL",
   "evidence": "Domain-adjacent dropped 11.5 pts (n=1300, +/-2.7 pts half-width), over the 5 pt budget: HARD FAIL, no exception for the 8.9 pt task gain."
  },
  {
   "stage": 3,
   "status": "PASS",
   "evidence": "Paired arena: 118 wins, 30 ties, 52 losses of 200; win rate 66.5% (ties count half), 95% Wilson CI 59.7-72.7%, above the 55.0% threshold excluding 50%."
  },
  {
   "stage": 4,
   "status": "NOT APPLICABLE",
   "evidence": "Local-only deployment stops at stage 3, so the canary rollout does not apply."
  }
 ],
 "evidence": "Stage 2 capability drift is a HARD FAIL: domain-adjacent dropped 11.5 pts (+/-2.7 pts half-width, n=1300), over the 5 pt budget with no exception for the checkpoint's 8.9 pt task gain. Stage 1 passed and stage 3 passed with a 66.5% win rate, but stage 2 governs and REJECT is the result; stage 4 does not apply to this local-only deployment.",
 "remediation": "Apply rung 1 of the forgetting ladder: swap the replay-mix fraction by swapping rows, not adding them, moving toward 20%.",
 "caveats": [
  "The 8.9 pt task gain does not offset the stage 2 drift-budget breach.",
  "Suites near the 200-item floor resolve only about +/-6 pts; a 2.5 pt half-width needs about 1,291 items.",
  "After applying rung 1, a before/after comparison is a single-run hypothesis, not proof of fix."
 ]
}

The page's check: kept - nothing to flag

GSM8K down 3.1, no rerun yet - gate

The gate says RERUN - decided by stage 2 needs a seed-variation rerun on gsm8k-subset.

Input:

{
 "task": "gate",
 "run": "Checkpoint: triage-r32-ckpt-1500; base model: Mistral 7B Instruct v0.3\nTarget task: triage accuracy 74.0 -> 83.0 (+9.0 pts)\nDeployment: local only\nForgetting-ladder rungs already tried: none\nReplay rows reuse the drift suite's instruction phrasing: no",
 "evals": "benchmark | baseline | checkpoint | delta (pts) | n | 95% half-width (pts) | seed rerun | budget verdict\nmmlu-subset | 62.5 | 62.0 | -0.5 | 1400 | 2.5 | - | noise\ngsm8k-subset | 52.1 | 49.0 | -3.1 | 1319 | 2.7 | - | rerun-seed\nifeval | 60.3 | 60.9 | +0.6 | 541 | 4.1 | - | noise\ndomain-adjacent | 71.0 | 70.6 | -0.4 | 1300 | 2.5 | - | noise",
 "facts": "Stage 1 data quality: PASS - goldens leakage 0 overlapping IDs; dedup 9 rows removed; label noise pass\nStage 2 capability drift: RERUN pending\nStage 3 paired arena (judge): PASS - 126 wins, 34 ties, 40 losses of 200; win rate 71.5% (ties count half), 95% Wilson CI 64.9-77.3%, threshold 55.0% with the CI excluding 50%\nStage 4 canary: NOT APPLICABLE - local-only deployment stops at stage 3\nExpected verdict: RERUN - decided by stage 2 needs a seed-variation rerun on gsm8k-subset\nTop remediation (the page's pick): Rerun gsm8k-subset with seed variation and enter the rerun score.\nFindings (3):\n[info] dedup (stage 1): 9 duplicate rows removed before training.\n[fix] rerun_pending (stage 2): gsm8k-subset dropped 3.1 pts (+/-2.7 pts at n=1319) - in the 2-5 pt band: rerun with seed variation before deciding; only <=1 pt on the rerun passes.\n[info] floor_n (stage 2): Suites at the 200-item floor resolve about +/-6 pts at 70%; the strict size for a 2.5 pt half-width is about 1,291 items.",
 "deploy": "none",
 "gate": "none"
}

Reply:

{
 "task": "gate",
 "verdict": "RERUN",
 "uncertain": false,
 "stages": [
  {
   "stage": 1,
   "status": "PASS",
   "evidence": "Zero overlapping golden IDs leaked into training, 9 duplicate rows were removed, and the label-noise check passed."
  },
  {
   "stage": 2,
   "status": "RERUN",
   "evidence": "gsm8k-subset dropped 3.1 pts (+/-2.7 pts, n=1319), in the 2-5 point band that requires a seed-variation rerun before deciding; only a rerun landing at <=1 pt will pass. The other three benchmarks moved at most 0.6 pts, within noise."
  },
  {
   "stage": 3,
   "status": "PASS",
   "evidence": "126 wins, 34 ties, 40 losses of 200 (ties count half) gave a 71.5% win rate; the 95% Wilson interval 64.9-77.3% clears the 55.0% threshold and excludes 50%."
  },
  {
   "stage": 4,
   "status": "NOT APPLICABLE",
   "evidence": "This is a local-only deployment, which correctly stops at stage 3; no canary is run."
  }
 ],
 "evidence": "Stage 2 decided the verdict: gsm8k-subset dropped 3.1 pts (+/-2.7 pts, n=1319), landing in the 2-5 point band that requires a seed-variation rerun before the checkpoint can be judged. Stage 1 passed and stage 3's arena reached 71.5% (95% CI 64.9-77.3%), but the report cannot end until the gsm8k-subset rerun is entered.",
 "remediation": "Rerun gsm8k-subset with seed variation and enter the rerun score.",
 "caveats": [
  "The gsm8k-subset rerun must land at <=1 pt to pass; anything else resolves to hard fail, not another rerun."
 ]
}

The page's check: kept - nothing to flag

The drift-table CSV for the third sample

benchmark,baseline,checkpoint,delta_pts,n,half_width_pts,rerun,verdict
mmlu-subset,68.2,67.6,-0.6,1400,2.4,,pass
gsm8k-subset,81.0,80.4,-0.6,1319,2.1,,pass
ifeval,74.1,73.8,-0.3,541,3.7,,pass
domain-adjacent,64.0,52.5,-11.5,1300,2.7,,hard

Build the input yourself

Load the engine in node (global.window = global; require("./promokit.js"); require("./promoreply.js")), then:

const K = PromoKit, R = PromoReply;
const f = K.form({ name: "ckpt-2400", base: "the base model", evals: fs.readFileSync("drift.csv", "utf8"), taskline: "task 61.0 -> 69.0",
  leak: "0", noise: "pass", mode: "judge", wins: "130", ties: "20", losses: "50", margin: "5", target: "local", rung: "0",
  hw: "hopper", work: "chat", pack: "auto", params: "8" });
// f.rows: [{name, base, ck, n, rerun, j: {drop, hw, band, status}}]  f.s1..f.s4: {status}  f.verdict: PROMOTE | REJECT | RERUN  f.uncertain
// f.remedy: the one fix   f.exp: {format, alt, pack, sizes, runtime, gate}   f.findings: [{code, level, stage, text}]
K.driftCSV(f)                                    // benchmark, baseline, checkpoint, delta, n, half-width, rerun, verdict
const b = K.buildInput(f, "gate");               // b.ok is false, with b.note, until a drift row is read (export: until PROMOTE)
const wire = K.toWire(b.input);                  // POST this to /estimate and /run
// then: const reply = R.normalize(R.parseReply(job.output.output), "gate");
// R.check(reply, b)                             // {status: kept or flagged, notes}
// R.report("ckpt-2400", b, reply)               // promotion-report.md

Get a token for the steps below from /tokens.html.

Step 1 — a tiny client helper

One function that sends JSON and unwraps the response. Paste a personal token from /tokens.html, or leave it empty and mint a guest token in step 2.

BASE="https://api.skillsafe.ai/v1/app-api"
TOKEN="paste-your-token-here"   # from https://promote-desk.skillsafe.ai/tokens.html
api() { # api METHOD PATH [JSON-BODY]
  curl -s -X "$1" "$BASE$2" -H "Content-Type: application/json" \
    ${TOKEN:+-H "Authorization: Bearer $TOKEN"} ${3:+--data "$3"}
}

Step 2 — a token

A guest token is enough for /me and /estimate. A run is metered, so it needs a personal token: sign in on /tokens.html and copy it from there.

# a guest token (enough for /me and /estimate); a run needs your personal token from /tokens.html
TOKEN=$(curl -s -X POST "$BASE/guest" -H "Content-Type: application/json" -d '{"slug":"promote-desk"}' | jq -r '.data.token')

Step 3 — GET /me

Who the token is (subject_type: user or guest) and the credit balance.

api GET /me | jq '.data | {subject_type, credits}' 

Step 4 — POST /estimate

Free. The body is the run input itself, the bare object (never wrapped in {"input": ...}). hold_credits is a reservation, not the price; min_credits is what the balance must cover; model_alias reads gpt-terra.

# input.json: the run input object - {"task": "gate", "run": "Checkpoint: ...", "evals": "benchmark | baseline | checkpoint | ...", "facts": "Stage 1 data quality: PASS ...", "deploy": "none", "gate": "none"}
# - see the input table
api POST /estimate "$(cat input.json)" | jq '.data | {hold_credits, min_credits, model, model_alias, markup_bps, warnings}' 

Step 5 — POST /run, then poll the job

Send an Idempotency-Key built from the lane and the input: a retry with the same key never bills twice. The page uses promote-desk:<task>:<inputHash>:a<attempt> - the lane is part of the key, so a report and an export plan of the same checkpoint never collide - and its one automatic reformat retry is a new attempt number, named in the page as one extra run. A gate reply carries verdict, stages and remediation; an export plan format, steps and smoke_test; the code below prints either.

KEY="promote-desk:gate:$(shasum -a 256 input.json | cut -c1-16):a1"
JOB=$(curl -s -X POST "$BASE/run" -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" \
  -H "Idempotency-Key: $KEY" --data @input.json | jq -r '.data.job_id')
while :; do
  J=$(api GET "/jobs/$JOB"); S=$(echo "$J" | jq -r '.data.status')
  case "$S" in succeeded|failed|cancelled) break;; esac; sleep 2
done
echo "$J" | jq -r '.data.output.output' > reply.json   # one JSON object, the reply contract
echo "$J" | jq '.data | {charged_credits, truncated}'
jq -r '.verdict // .format, .evidence // .why, ((.stages // .steps)[] | "\(.stage // .step): \(.status // .command)")' reply.json
jq -r '.remediation // .note // empty' reply.json   # the one fix (gate) or what to run first (export)

Step 6 — POST /run-stream

The same run as Server-Sent Events, with the same input.json. Browsers receive progress ticks rather than text deltas; the final done event carries the reply.

# Server-Sent Events: tick/delta events while the job runs, then a final event with the job
curl -N -s -X POST "$BASE/run-stream" -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" \
  -H "Idempotency-Key: promote-desk:gate:$(shasum -a 256 input.json | cut -c1-16):a1" --data @input.json