static-fire 0.44.0 → 0.54.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/guide.md CHANGED
@@ -14,8 +14,12 @@ safe default. How you talk with the user is yours to decide. The second part is
14
14
  format and command.
15
15
 
16
16
  Run every command as `static-fire <command>`, or `npx static-fire <command>` when `static-fire` is not
17
- installed. A command finds the project by looking in the current folder and each folder above it for
18
- `static-fire.yaml`.
17
+ installed.
18
+
19
+ **Which project a command uses.** A command uses the nearest project above the current folder (a folder
20
+ with `static-fire.yaml`, the current one included). Outside any project it uses the home, the one project
21
+ the user keeps all their tasks and results in, and prints `using the home project: <folder>` on stderr.
22
+ `static-fire home` prints the home; `static-fire home <folder>` sets it.
19
23
 
20
24
  ## The rule above all: realism
21
25
 
@@ -25,6 +29,19 @@ score is worth something only when it predicts how a configuration does on the u
25
29
  difference from the real job is a known gap. Write each gap of a task in the project's `README.md` (make
26
30
  the file when the first gap comes up), so the user can see what the score does not cover.
27
31
 
32
+ ## What spends
33
+
34
+ These start agent runs or judge calls, which cost money or use up the user's plan:
35
+
36
+ - `static-fire run`: agent runs, and judge calls to grade each trial.
37
+ - `static-fire rerun <batch>`: the failed trials of the batch again, with their judge calls.
38
+ - `static-fire regrade`: judge calls only, for each trial it grades again.
39
+ - On the dashboard: New run on the Runs page, and Regrade on the Results page.
40
+
41
+ The rule: get the user's approval before each one starts, every time, with what it will run (step 6). An
42
+ approval covers the one command it was given for. `run --plan` and `regrade --plan` print the plan and
43
+ spend nothing.
44
+
28
45
  ## The path
29
46
 
30
47
  ### 1. Learn the goal, the examples and what good looks like
@@ -53,12 +70,28 @@ Do not invent the user's taste: ask what good looks like.
53
70
 
54
71
  ### 2. Set up the project and settle the block list
55
72
 
56
- **Why.** A project is the folder that holds the tasks, the configurations and the records. A trial runs with
57
- no permission prompts, so nobody is there to approve a tool that sends mail, posts a message, spends money
58
- or deploys. The block list stops those tools in trials.
73
+ **Why.** A project is the folder that holds the tasks, the configurations and the records. Most users need
74
+ one: the home. A trial runs with no permission prompts, so nobody is there to approve a tool that sends
75
+ mail, posts a message, spends money or deploys. The block list stops those tools in trials.
76
+
77
+ **Do.** Run `static-fire init --json`. It never waits for input and never changes a file that exists.
78
+
79
+ - With no home, outside any project, it makes the project in `~/static-fire` and saves it as the home.
80
+ `static-fire init <folder> --json` makes it in that folder instead, and saves that as the home.
81
+ - With a home, outside any project, it writes nothing and reports the home. `static-fire init <folder>`
82
+ then makes a second project in that folder, with its own records and dashboard. The home does not change.
83
+ - Inside a project, it writes nothing and reports that project.
59
84
 
60
- **Do.** In the folder the user chose (an empty folder is best), run `static-fire init --json`. It never
61
- overwrites a file. It makes:
85
+ Tell the user where the home is, or will be, before you add their task to it. `static-fire home` prints it,
86
+ or says none is set, and writes nothing; with none set, the home will be `~/static-fire` unless the user
87
+ names another folder. The report's `project` is the project's folder, `created` says if this run made it,
88
+ `home` is the home's folder (or null), `isHome` says if `project` is the home, and `homeSaved` says if this
89
+ run saved the home.
90
+
91
+ **Safe default.** Put the task in the home. Make a second project only when the user asks for a separate
92
+ one.
93
+
94
+ A new project has:
62
95
 
63
96
  - `configs/<harness>.yaml`: one agent configuration for each harness it found on the `PATH` (`claude`,
64
97
  `codex`, `agy`, `pi`), with a default model.
@@ -67,15 +100,27 @@ overwrites a file. It makes:
67
100
  run until one is added (see the reference).
68
101
  - `evals/`, `suites/`, `static-fire.yaml`, a `.gitignore` for `runs/` and `.cache/`, and `AGENTS.md`.
69
102
 
70
- The JSON report has, for each harness: `found` and `path`, `blockable`, `tools` (each with `name`, `kind`,
71
- `foundIn` and `block`, the entries that block it) and `notes`. It also has `judge`, `written`, `kept` and
72
- `notes`. Run `static-fire init --json` again in the project at any time: it changes nothing and lists the
103
+ The JSON report also has, for each harness: `found` and `path`, `blockable`, `tools` (each with `name`,
104
+ `kind`, `foundIn` and `block`, the entries that block it) and `notes`. It also has `judge`, `written`, `kept`
105
+ and `notes`. Run `static-fire init --json` again in the project at any time: it changes nothing and lists the
73
106
  tools again.
74
107
 
75
- **Settle the block list.** For each tool in the report, decide if it acts on an outside service: sends or
76
- posts something, changes data in an account, spends money, or deploys. Propose the block list to the user
77
- with a short reason for each entry, and show the tools you propose to leave on. The user approves or changes
78
- it. Then write the `block` entries under `trials.blockedTools.<harness>` in `static-fire.yaml`.
108
+ **Settle the block list.** `init` finds the tools; it does not choose and does not ask. For each tool in the
109
+ report, decide if it acts on an outside service: sends or posts something, changes data in an account,
110
+ spends money, or deploys. Then the user decides. Before they decide, they need to see:
111
+
112
+ - Every tool found, for each harness, with its kind and where it was found. They cannot judge a list they
113
+ have not seen.
114
+ - Which tools you propose to block, each with its reason.
115
+ - Which tools stay on.
116
+ - Their choices: approve your list, change it, or block nothing.
117
+
118
+ Blocking nothing is a valid choice. Its facts: a trial then runs with every tool of the user's setup and no
119
+ permission prompts, which is the closest to how their agents really run. And a tool that acts on an outside
120
+ service can then act in a trial, with nobody there to stop it.
121
+
122
+ Write the entries of the user's choice under `trials.blockedTools.<harness>` in `static-fire.yaml`, copied
123
+ from each tool's `block` list.
79
124
 
80
125
  **Facts.**
81
126
 
@@ -92,7 +137,7 @@ Run `static-fire init --json` again after the user installs a harness.
92
137
  result of a fast "yes" from the user must be safe. Tools that only read (search, docs, the local file
93
138
  system) can stay on, because the real job has them.
94
139
 
95
- **Configurations.** Show the user the configurations `init` wrote, and change them to what they want to
140
+ **Configurations.** Show the user the configurations in the project, and change them to what they want to
96
141
  compare: other models, other effort levels, more than one configuration per harness. A configuration is a
97
142
  small YAML file (see the reference). `init` writes a current model id for each harness it found. For another
98
143
  model, take its id from the harness itself (its model list or its documentation). Static Fire does not check
@@ -126,20 +171,29 @@ comment on each part, and an empty `evals/<id>/inputs/`. Put the example inputs
126
171
  error and every grading of the task is invalid.
127
172
  - A check is cheap to test before any trial: write a sample output by hand and run the check's command or
128
173
  pattern on it yourself. A `regex` is a JavaScript regular expression.
129
- - Give each criterion a level: `floor` (most configurations pass), `strong` (good ones pass) or `ceiling`
130
- (only the best pass). A mix makes the scores spread out.
174
+ - Test each derived evidence script the same way before the first trial: run it on a sample output, see
175
+ that it ends well inside its 60-second limit, and open the PNG it writes. Every file in the task's
176
+ `grader/` folder is part of the grading version, so a fix after a trial costs a regrade (step 5).
177
+ - Give each criterion a level: `floor` (most configurations should pass), `strong` (good ones pass) or
178
+ `ceiling` (only the best pass). A mix makes the scores spread out. These three names are the ones the
179
+ dashboard shows.
131
180
  - `static-fire tasks` warns under 15 criteria in all, under 3 in a measure, or when a level is missing.
132
181
  These are advice. With 15 criteria, one criterion moves the score by about 7 points.
133
182
  - A gate is a criterion outside the measures: when one fails, the task score is 0. "The output file exists
134
183
  and is not empty" is always a gate, added by the engine.
135
184
 
136
- **Safe default.** Turn each thing the user said about good and bad output into one or more criteria. Show
137
- the user the criteria in plain words and let them correct them before you run anything. Write each gap
138
- between the task and the real job in the project's `README.md`.
185
+ **Confirm the criteria with the user.** The criteria decide every score, so the user approves them as their
186
+ own step, apart from any approval to run. Show each criterion in plain words with its level, and explain the
187
+ levels in plain words with their names: floor, strong, ceiling. Offer to change any criterion, and get the
188
+ user's yes on the criteria before you go on.
189
+
190
+ **Safe default.** Turn each thing the user said about good and bad output into one or more criteria. Write
191
+ each gap between the task and the real job in the project's `README.md`.
139
192
 
140
193
  ### 4. Check the task and put it in a suite
141
194
 
142
- **Why.** A task with an error cannot run. The dashboard has a button only for the tasks that are in a suite.
195
+ **Why.** A task with an error cannot run. A task counts toward the Agent Performance Index only when it is in a
196
+ suite.
143
197
 
144
198
  **Do.** Run `static-fire tasks`. Each error stops the task from running: fix every one. Warnings are advice.
145
199
  Then add the task to a suite, a file `suites/<id>.yaml` (see the reference), and run `static-fire suites`,
@@ -153,8 +207,8 @@ suite fits.
153
207
  **Why.** The first trial shows that the task works: the agent finds its inputs, writes the output where the
154
208
  task expects it, and each verdict is right for the right reason. It is the least spend that shows this.
155
209
 
156
- **Before you run.** A trial spends money: the agent run, and the judge calls that grade it. Tell the user
157
- what will run and get their approval first.
210
+ **Before you run.** A trial spends: the agent run, and the judge calls that grade it. Show the user its plan
211
+ (add `--plan` to the command below, as in step 6) and get their approval first.
158
212
 
159
213
  **Do.** Run one trial of one configuration, usually the one the user works with:
160
214
  `static-fire run --tasks <task> --configs <config> --trials 1`. A trial can take several minutes (the limit
@@ -177,37 +231,52 @@ the task works. Show the user the output and the verdicts, so they can say wheth
177
231
  their own view.
178
232
 
179
233
  **Facts.** A change to the prompt, the output path, or a file in `inputs/` or `project/` makes a new task
180
- hash: earlier trials no longer share a chart with new ones. A change to a criterion, `context`, a reference
181
- or the judge makes a new grading version: earlier trials are left out of the view until a
182
- `static-fire regrade` grades them again with judge calls only. So settle the task before the batch.
234
+ hash: earlier trials no longer share a chart with new ones. A change to a criterion, `context`, a reference,
235
+ any file in `grader/` (a check's helper script or a derived evidence script) or the judge makes a new
236
+ grading version: earlier trials are left out of the view until a `static-fire regrade` grades them again.
237
+ That regrade spends judge calls and needs the user's approval like any other spend. So settle the task, its
238
+ grader scripts included, before the batch.
183
239
 
184
240
  ### 6. Work out the batch cost, get approval, run the batch
185
241
 
186
- **Why.** The batch is where most of the money goes. The user approves it knowing what it costs.
242
+ **Why.** The batch is where most of the spend goes. The user approves it knowing what it uses.
243
+
244
+ **The plan in runs comes first.** Add `--plan` to the `run` command you mean to start, for example
245
+ `static-fire run --suites <suite> --configs <a>,<b> --trials 3 --plan --json`. It starts nothing and writes
246
+ nothing. It gives the trials, the agent runs for each configuration (a human configuration needs none),
247
+ the judge calls for the judge as a range, and what the range leaves out (`judgeCalls.notCounted`). These
248
+ counts are what a user on a subscription can judge: their plan counts runs and usage, not dollars. `static-fire regrade <flags> --plan --json` gives the plan of a
249
+ regrade the same way. `rerun` has no plan: it runs the failed trials of the batch again, each with its
250
+ grading.
187
251
 
188
252
  **Facts.**
189
253
 
190
- - Static Fire gives no cost estimate and enforces no limit. You work out the cost.
191
- - The cost of a configuration is known only once it has run. Configurations on other models can cost very
192
- different amounts for the same task.
193
- - A batch runs `--trials` trials (3 by default) of each configuration in `--configs` on each task. Each
194
- trial that runs is also graded: its judge cost is in `grades.judgeCostUsd`.
254
+ - Judge calls are often the larger part of a batch, in count and in cost. Their number per trial comes
255
+ from the judge criteria, put in groups: criteria with the same evidence and the same derived evidence
256
+ script are one group; each `isolate` criterion, each other set of evidence, each other derived script and
257
+ the judged gates make more groups. Each group gets one judge call per vote. With 3 votes (the default,
258
+ `--votes`), 2 calls run first and the third only when they disagree. That is the range: the low end when
259
+ every first pair agrees, the high end when none does.
260
+ - Outside the range: a judge answer that does not parse is tried once more, and the plan says that this
261
+ call is not counted. A trial with no output, or with a derived evidence script that fails, gets fewer calls.
262
+ - Dollar figures are a second fact, and they are API prices, not what a subscription is charged. The cost
263
+ of a configuration is known only once it has run, and configurations on other models can cost very
264
+ different amounts for the same task. A trial's judge cost is in `grades.judgeCostUsd`.
195
265
  - Claude Code and Pi report their own cost (`costSource: reported`). Codex and Antigravity costs are
196
266
  estimated from tokens and the engine's price list (`estimated`). A model that the price list does not have
197
267
  gives no cost (`unknown`, shown as `?` in the `run` output). A project can have its own `pricing.yaml`.
198
268
  - Cost that is not in the harness's own report, such as image generation by an outside service, is not
199
- counted.
200
- - With several configurations, you can run one trial of each one first (real numbers for each, more spend
201
- before the batch), or scale from what you have. Say which numbers are measured and which are a guess.
269
+ counted. Static Fire enforces no spending limit.
202
270
 
203
- **Safe default.** The user sees the batch cost before the batch, with what is measured and what is guessed
204
- made clear, and approves it. Nothing runs before that approval.
271
+ **Safe default.** The user sees the plan in runs before the batch, with the dollar figures you have as API
272
+ prices, what is measured made clear from what is scaled up, and approves it. Nothing runs before that
273
+ approval. A batch that starts prints the same plan first.
205
274
 
206
275
  **Do.** Run the batch, for example `static-fire run --suites <suite> --configs <a>,<b> --trials 3`, or
207
276
  `--configs all` for every configuration. It runs 4 trials at a time by default (`--concurrency`). A batch can
208
277
  take a long time, so start it in the background and follow it with `static-fire list` or on the dashboard's
209
278
  Runs page. When trials fail for a reason outside the task (a usage limit, a login),
210
- `static-fire rerun <batch>` runs the failed ones again in the same batch.
279
+ `static-fire rerun <batch>` runs the failed ones again in the same batch, after the user approves it.
211
280
 
212
281
  ### 7. Open the dashboard
213
282
 
@@ -219,14 +288,17 @@ not exit. It prints `Static Fire → http://localhost:4477 (records: <path>)`. G
219
288
  `--port <n>` picks another port. You can start it at any time; trials show up as they are graded.
220
289
 
221
290
  **What the user sees.** The Results page shows the score of each configuration against its cost per task, as
222
- a chart or a table, for the Workflow Index (all suites), one suite, or one task. With one task in one suite,
223
- each of the three shows the same score. Each point is the mean of the valid trials with its 95% confidence
224
- interval. Opening a trial shows each verdict with its reason. The Runs page lists each batch and its trials,
291
+ a chart or a table, for the Agent Performance Index or one task. The index covers the suites the user turns on with
292
+ the suite buttons. With none picked, it covers the best set the data supports, and one line says which suites it
293
+ covers and why each other suite is left out. `static-fire show` uses the same set and prints the same line. With one
294
+ task in one suite, the index and the task show the same score. Each point is the mean of the valid trials with its 95% confidence
295
+ interval. Opening a trial shows its output, with each verdict and its reason in the Verdicts tab. The Runs page lists each batch and its trials,
225
296
  and can start a new run. The Compare page shows what changed between two grading versions of a task.
226
297
 
227
- Before any trial is graded, the Results page says "No trials in this view". A task that is in no suite has no
228
- button on the Results page. The address `http://localhost:4477/?scope=task:<task>` still shows it, and so
229
- does `static-fire show --tasks <task>`, as text.
298
+ Before any trial is graded, the Results page says "No trials in this view". The Task section of the Filter menu
299
+ lists every task of the project, also a task that is in no suite; `static-fire show --tasks <task>` shows one
300
+ task as text. The Tasks page shows each suite and task: its description, what the agent gets and how it is
301
+ graded.
230
302
 
231
303
  ## Reference
232
304
 
@@ -402,12 +474,16 @@ The contract for a script:
402
474
  - **Input.** Argument 1 and `$OUTPUT` are the output file. `$TASK_DIR` is the task folder, so the script can
403
475
  read other task files. `$WORKSPACE` is the working folder, and the script runs in it. The time limit is 60
404
476
  seconds.
477
+ - **At the same time.** Gradings run at the same time: up to `--concurrency` (4 by default) in a batch or a
478
+ regrade, each in its own trial folder. In one grading, every script it needs runs at once. So a script
479
+ writes only to its argument 2 path and keeps nothing in a fixed path that another run could share.
405
480
  - **Output.** Write one PNG to the path in argument 2 (also `$DERIVED`). A non-zero exit, a time-out, no file
406
481
  or a file that is not a PNG makes the verdict an error, so the grading is invalid.
407
482
  - **Where it stays.** `runs/trials/<trial-id>/gradings/<grading-id>/_derived/<script>.png`, with the
408
483
  script's output next to it as `.log`.
409
484
  - **Sharing.** Criteria that name the same script share one image, and one judge call when their other
410
- evidence is the same. A new script name or new script content gives a new grading version.
485
+ evidence is the same. A new script name or new script content gives a new grading version: test it before
486
+ the first trial (step 3).
411
487
 
412
488
  An example script, for an image output:
413
489
 
@@ -484,7 +560,7 @@ tasks:
484
560
  | Field | What |
485
561
  |---|---|
486
562
  | `title` | Shown on the dashboard ("Thumbnails Index") |
487
- | `description` | Optional. Shown with the suite's charts |
563
+ | `description` | Optional. Shown on the Tasks page |
488
564
  | `tasks` | The task ids |
489
565
  | `weights` | Optional. Task id to weight; 1 by default |
490
566
 
@@ -511,16 +587,17 @@ records the list it ran with.
511
587
 
512
588
  | Command | What |
513
589
  |---|---|
514
- | `static-fire init [--json]` | Make a project in the current folder, or list the outside tools again in a project |
590
+ | `static-fire init [<folder>] [--json]` | Make the home (`~/static-fire`, or `<folder>`), or report the home or the current project; with a home, `<folder>` makes a second project. Lists the harnesses and outside tools |
515
591
  | `static-fire guide` | This guide |
592
+ | `static-fire home [<folder>]` | Show the home, the project a command uses outside any project, or save a project folder as the home |
516
593
  | `static-fire new task <id>` | A new task folder with a commented `task.yaml` |
517
594
  | `static-fire tasks` | Each task, with its errors and warnings |
518
595
  | `static-fire configs` | Each configuration and its label |
519
596
  | `static-fire judges` | Each judge; the default is marked |
520
597
  | `static-fire suites` | Each suite and its tasks |
521
- | `static-fire run` | One batch. `--tasks <a,b>` or `--suites <a,b>`, `--configs <a,b>` or `all` (all by default), `--trials <n>` (3), `--judge <id>`, `--votes <n>` (3), `--concurrency <n>` (4) |
598
+ | `static-fire run` | One batch. `--tasks <a,b>` or `--suites <a,b>`, `--configs <a,b>` or `all` (all by default), `--trials <n>` (3), `--judge <id>`, `--votes <n>` (3), `--concurrency <n>` (4). `--plan` prints the plan and starts nothing; `--plan --json` for JSON |
522
599
  | `static-fire rerun <batch>` | The failed trials of a batch again, as new trials. `--concurrency <n>` |
523
- | `static-fire regrade` | Grade stored outputs again under the current grading, with judge calls only. `--tasks`, `--configs`, `--batch <id>`, `--judge`, `--votes`, `--only-invalid`, `--concurrency` |
600
+ | `static-fire regrade` | Grade stored outputs again under the current grading, with judge calls only. `--tasks`, `--configs`, `--batch <id>`, `--judge`, `--votes`, `--only-invalid`, `--concurrency`, `--plan` (with `--json`) |
524
601
  | `static-fire list` | Each batch, newest first, with its trial counts |
525
602
  | `static-fire show` | The scores as text. `--tasks` or `--suites`, `--judge`, `--grading <hash>`, `--since <date>`, `--until <date>`, `--json` |
526
603
  | `static-fire serve [--port <n>]` | The dashboard, at port 4477 by default |
@@ -543,5 +620,5 @@ A flag that a command does not take is an error. A list is comma-separated: `--c
543
620
  questions.
544
621
  - **The copy check reads text only.** An image output that copies a published original is not caught.
545
622
  - **Cost** is reported by Claude Code and Pi, estimated for Codex and Antigravity, and unknown for a model
546
- that has no price. Judge cost is not part of a configuration's cost. There is no estimate before a batch
547
- and no spending limit.
623
+ that has no price. Judge cost is not part of a configuration's cost. Before a batch there is the plan in
624
+ runs (`--plan`), not a dollar estimate. There is no spending limit.
package/src/server.ts CHANGED
@@ -7,14 +7,18 @@ import type { ProjectPaths } from "./core/project";
7
7
  import { projectCatalog } from "./core/view";
8
8
  import { PAGES } from "../web/pages";
9
9
 
10
- /** Serves the dashboard of `project`: its store, and its configs, judges, suites and tasks. */
11
- export function serve(port: number, project: ProjectPaths) {
10
+ /**
11
+ * Serves the dashboard of `project`: its store, and its configs, judges, suites and tasks. The page is bundled
12
+ * once, minified and cached, unless `development` asks for Bun's development mode (bundled on request, with hot
13
+ * reload), which is for work on the engine (`static-fire serve --dev`, `bun run dev`).
14
+ */
15
+ export function serve(port: number, project: ProjectPaths, { development = false }: { development?: boolean } = {}) {
12
16
  const { root, dbPath, cache } = project;
13
17
  const store = openStore(dbPath);
14
18
  const api = apiRoutes({ store, dbPath, catalog: () => projectCatalog(root), root, cacheDir: cache });
15
19
  const server = Bun.serve({
16
20
  port,
17
- development: process.env.NODE_ENV !== "production",
21
+ development,
18
22
  idleTimeout: 0,
19
23
  // Every page path serves the one page; it shows the page its path names (web/pages.ts).
20
24
  routes: { ...Object.fromEntries(PAGES.map((p) => [p.path, index])), ...api.routes },