@workos/quickstudy 0.6.1 → 0.7.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@workos/quickstudy",
3
- "version": "0.6.1",
3
+ "version": "0.7.0",
4
4
  "description": "An open-source, company-agnostic eval harness for measuring whether coding agents can complete real integration tasks, across an agent x feature x framework x surface matrix.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -9,7 +9,7 @@ export const EMBEDDED_BUILD_INFO: {
9
9
  // (the `x-release-please-version` marker anchors the replacement). It is
10
10
  // restored as-is after a binary build (scripts/build-binary.ts), so the
11
11
  // committed source `--version` stays in lockstep with package.json.
12
- version: "0.6.1", // x-release-please-version
12
+ version: "0.7.0", // x-release-please-version
13
13
  revision: null,
14
14
  dirty: null,
15
15
  sourceHash: null,
@@ -17,7 +17,7 @@ export function renderDiagnosisText(diagnosis: DiagnosisJson, resultsDir: string
17
17
  lines.push(`diagnosis: run ${diagnosis.run_id}`);
18
18
  lines.push(` model: ${diagnosis.model.requested}${diagnosis.model.resolved !== null ? ` (resolved: ${diagnosis.model.resolved})` : ""}`);
19
19
  lines.push(
20
- ` pairs: ${diagnosis.pairs_diagnosed} diagnosed, ${diagnosis.pairs_skipped} skipped (all passing)` +
20
+ ` pairs: ${diagnosis.pairs_diagnosed} diagnosed, ${diagnosis.pairs_skipped} skipped (passing or control)` +
21
21
  (diagnosis.errors.length > 0 ? `, ${diagnosis.errors.length} errored` : ""),
22
22
  );
23
23
 
@@ -30,6 +30,7 @@ import {
30
30
  type PairBundle,
31
31
  } from "./prompt.ts";
32
32
  import { selectDiagnosablePairs, type DiagnosablePair } from "./select.ts";
33
+ import { controlExperiments } from "../report/report.ts";
33
34
  import {
34
35
  DIAGNOSIS_DISCLAIMER,
35
36
  DIAGNOSIS_PROMPT_VERSION,
@@ -218,10 +219,13 @@ export async function runDiagnose(options: RunDiagnoseOptions): Promise<Diagnosi
218
219
 
219
220
  const run = mustResolveRun(store, options.runId);
220
221
  const attempts = store.listAttempts(run.id);
221
- const selection = selectDiagnosablePairs(attempts);
222
+ // Controls check the instrument, not an agent: the oracle passes and the
223
+ // no-op fails by construction, so their pairs carry no diagnosable cause.
224
+ const controls = controlExperiments(run);
225
+ const selection = selectDiagnosablePairs(attempts, { controls });
222
226
  log(
223
227
  `diagnose: run ${run.id} — ${selection.pairs.length} diagnosable pair(s), ` +
224
- `${selection.skipped} passing pair(s) skipped`,
228
+ `${selection.skipped} passing pair(s) skipped, ${selection.controls} control pair(s) excluded`,
225
229
  );
226
230
 
227
231
  const findings: DiagnosisFinding[] = [];
@@ -281,7 +285,7 @@ export async function runDiagnose(options: RunDiagnoseOptions): Promise<Diagnosi
281
285
  prompt_version: DIAGNOSIS_PROMPT_VERSION,
282
286
  disclaimer: DIAGNOSIS_DISCLAIMER,
283
287
  pairs_diagnosed: selection.pairs.length - errors.length,
284
- pairs_skipped: selection.skipped,
288
+ pairs_skipped: selection.skipped + selection.controls,
285
289
  findings,
286
290
  errors,
287
291
  };
@@ -47,6 +47,19 @@ export interface PairSelection {
47
47
  pairs: DiagnosablePair[];
48
48
  /** Passing-only pairs — skipped, never sent to the model. */
49
49
  skipped: number;
50
+ /**
51
+ * Control pairs (the positive-control oracle, the negative-control no-op) —
52
+ * excluded before classification, never sent to the model. A control's
53
+ * outcome is fixed by construction: asked why the no-op "failed", a model
54
+ * confidently reports a broken harness. The report's scorer health watches
55
+ * the oracle instead.
56
+ */
57
+ controls: number;
58
+ }
59
+
60
+ export interface SelectOptions {
61
+ /** Experiment ids the run recorded as controls (RunConfig.roles other than "agent"). */
62
+ controls?: ReadonlySet<string>;
50
63
  }
51
64
 
52
65
  /** Bundle ordering: failed first, then errors, then incomplete. */
@@ -58,10 +71,11 @@ const CLASS_ORDER: Record<Exclude<AttemptClass, "passed">, number> = {
58
71
 
59
72
  /**
60
73
  * Group attempts into pairs, classify, and select each diagnosable pair's
61
- * bundle. Deterministic: pairs sort by eval id then experiment id (the
62
- * report's order); within a class, attempts sort by ULID (id) order.
74
+ * bundle. Control experiments are excluded first (see PairSelection.controls).
75
+ * Deterministic: pairs sort by eval id then experiment id (the report's
76
+ * order); within a class, attempts sort by ULID (id) order.
63
77
  */
64
- export function selectDiagnosablePairs(attempts: AttemptRecord[]): PairSelection {
78
+ export function selectDiagnosablePairs(attempts: AttemptRecord[], options: SelectOptions = {}): PairSelection {
65
79
  const byPair = new Map<string, AttemptRecord[]>();
66
80
  for (const attempt of attempts) {
67
81
  const key = `${attempt.evalId}|${attempt.experimentId}`;
@@ -72,8 +86,13 @@ export function selectDiagnosablePairs(attempts: AttemptRecord[]): PairSelection
72
86
 
73
87
  const pairs: DiagnosablePair[] = [];
74
88
  let skipped = 0;
89
+ let controls = 0;
75
90
 
76
91
  for (const group of byPair.values()) {
92
+ if (options.controls?.has(group[0]!.experimentId)) {
93
+ controls += 1;
94
+ continue;
95
+ }
77
96
  const report = collectPairs(group)[0]!;
78
97
  if (report.failed + report.errors + report.incomplete === 0) {
79
98
  skipped += 1;
@@ -106,5 +125,5 @@ export function selectDiagnosablePairs(attempts: AttemptRecord[]): PairSelection
106
125
  }
107
126
 
108
127
  pairs.sort((a, b) => a.evalId.localeCompare(b.evalId) || a.experimentId.localeCompare(b.experimentId));
109
- return { pairs, skipped };
128
+ return { pairs, skipped, controls };
110
129
  }
@@ -85,6 +85,7 @@ export interface DiagnosisJson {
85
85
  /** Fixed self-labeling: hypotheses with evidence links, not verdicts; single-sweep scope. */
86
86
  disclaimer: string;
87
87
  pairs_diagnosed: number;
88
+ /** Pairs never sent to the model: passing-only pairs and control (oracle / no-op) pairs. */
88
89
  pairs_skipped: number;
89
90
  findings: DiagnosisFinding[];
90
91
  errors: PairDiagnosisError[];
@@ -454,7 +454,7 @@ function roleOf(run: RunRecord, experimentId: string): ExperimentRole {
454
454
  }
455
455
 
456
456
  /** The run's control experiments, from the roles it recorded. */
457
- function controlExperiments(run: RunRecord): Set<string> {
457
+ export function controlExperiments(run: RunRecord): Set<string> {
458
458
  return new Set(run.config.experimentIds.filter((id) => roleOf(run, id) !== "agent"));
459
459
  }
460
460