cortad 0.2.3 → 0.3.0-rc.3

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/lib/words.mjs ADDED
@@ -0,0 +1,37 @@
1
+ // The small pieces every rendering shares: numbers with their units, file and line, whose side a
2
+ // stop is on, and the pages a long answer is cut into.
3
+
4
+ // Copilot cuts a result past about 10 KB; a page stays under this.
5
+ export const PAGE_CHARS = 9000;
6
+ export const SIDE = { theirs: "on the app's side", ours: "on Cortad's side" };
7
+
8
+ export const has = (v) => v !== undefined && v !== null;
9
+ export const num = (x) => (typeof x === "number" ? Math.round(x).toLocaleString("en-US") : String(x));
10
+ export const plural = (n, one, many = `${one}s`) => `${num(n)} ${n === 1 ? one : many}`;
11
+ export const upper = (s) => `${s.charAt(0).toUpperCase()}${s.slice(1)}`;
12
+ export const at = (path, line) => `${path}${has(line) ? `:${line}` : ""}`;
13
+ export const clip = (s, max) => (String(s).length > max ? `${String(s).slice(0, max - 3)}...` : String(s));
14
+
15
+ // One page of many, with the call that reads the next one: `next(2)` gives "findings with page 2".
16
+ export function pageOf(pages, page, next) {
17
+ const n = Math.min(Math.max(1, Math.floor(Number(page)) || 1), pages.length);
18
+ const tail = pages.length === 1 ? "" : n < pages.length ? `\npage ${n} of ${pages.length}, call ${next(n + 1)}` : `\npage ${n} of ${pages.length}`;
19
+ return `${pages[n - 1]}${tail}`;
20
+ }
21
+
22
+ // Lines packed into pages under PAGE_CHARS; every page after the first opens with `continued`.
23
+ export function pack(head, lines, continued, budget = PAGE_CHARS - 80) {
24
+ const pages = [];
25
+ let page = head;
26
+ let filled = false;
27
+ for (const line of lines) {
28
+ if (filled && page.length + line.length + 1 > budget) {
29
+ pages.push(page);
30
+ page = continued;
31
+ filled = false;
32
+ }
33
+ page += `\n${line}`;
34
+ filled = true;
35
+ }
36
+ return [...pages, page];
37
+ }
package/local.mjs CHANGED
@@ -23,6 +23,7 @@ import { promisify } from "node:util";
23
23
  import { COMMANDS, main as face } from "./lib/cli.mjs";
24
24
  import { clearRunner, projectOf, readToken, writeDigest, writeRunner, writeToken } from "./lib/home.mjs";
25
25
  import { registerAll } from "./lib/register.mjs";
26
+ import { finished } from "./lib/text.mjs";
26
27
  import { lockHolds, makeLock } from "./lib/lock.mjs";
27
28
  import { AS_HEADER, makeIdentities } from "./lib/mint.mjs";
28
29
  import { CAPTURED, makeCapture } from "./lib/replay.mjs";
@@ -50,7 +51,13 @@ const viaToken = argv.includes("--token");
50
51
  // What is happening right now, on one line that rewrites itself. npx spends its own seconds fetching
51
52
  // this package before anything here runs, and the first thing we printed used to be after the whole
52
53
  // upload: a minute or more of a cursor sitting still, which reads as nothing happening.
53
- const step = (line) => { if (process.stdout.isTTY) process.stdout.write(`\rcortad ${line}\x1b[K`); };
54
+ // Without a terminal (a coding agent's shell) each new step is its own line, so the agent reading
55
+ // the output sees the folder being looked at and the upload counting up, not a two-minute blank.
56
+ let lastStep = "";
57
+ const step = (line) => {
58
+ if (process.stdout.isTTY) { process.stdout.write(`\rcortad ${line}\x1b[K`); return; }
59
+ if (line !== lastStep) { lastStep = line; say(line); }
60
+ };
54
61
  const clearStep = () => { if (process.stdout.isTTY) process.stdout.write("\r\x1b[K"); };
55
62
  const stepDone = (line) => { clearStep(); say(line); };
56
63
  if (!explain && !viaToken && !/^[A-Z0-9]{8}$/.test(code)) fail("usage: npx cortad <code from the connect screen> [--port N] [--start \"cmd\"] | npx cortad --explain | npx cortad status | run | findings | verify <id>");
@@ -100,7 +107,7 @@ function walk(dir, depth, out, envs, total) {
100
107
  try { size = statSync(full).size; } catch { continue; }
101
108
  if (size > MAX_FILE || total + size > MAX_TOTAL) continue;
102
109
  if (size > MAX_DATA && DATA_FILE.test(e.name) && !MANIFEST_FILE.test(e.name)) continue;
103
- if (!shareable(relative(root, full))) continue;
110
+ if (!shareable(relative(root, full), false)) continue;
104
111
  out.push(relative(root, full));
105
112
  total += size;
106
113
  }
@@ -121,12 +128,14 @@ function gitListed() {
121
128
  } catch (e) { fail(`could not list this repository's files with git: ${e.message}`); }
122
129
  }
123
130
  // One rule for every file that could leave: the upload, and a read the engine asks for later.
124
- function shareable(rel) {
131
+ function shareable(rel, askGit = true) {
125
132
  const parts = rel.split(sep);
126
133
  if (parts.slice(0, -1).some((d) => SKIP_DIR.test(d)) || SKIP_FILE.test(parts.at(-1))) return false;
127
- if (listed === null) return true;
128
- if (listed.has(rel)) return true;
129
- // A file made after the list was taken (an agent edit) is asked of git directly.
134
+ if (listed === null || listed.has(rel)) return true;
135
+ // Off the list git gave a moment ago: ignored, or inside a nested checkout. The walk takes git's
136
+ // word for it; asking per file was one process each, two minutes on a repository with a worktree
137
+ // inside it. A read asked later, of a file made after the list was taken (an agent edit), asks.
138
+ if (!askGit) return false;
130
139
  try { execFileSync("git", ["-C", root, "check-ignore", "-q", rel], { stdio: "ignore" }); return false; } catch (e) { return e.status === 1; }
131
140
  }
132
141
  let listed = null;
@@ -981,7 +990,7 @@ if (argv.includes("--until-idle") && stored) {
981
990
  let idleSince = Date.now();
982
991
  setInterval(async () => {
983
992
  const res = await call("GET", "/mcp/status", undefined, { headers: { authorization: `Bearer ${stored}` } }).catch(() => null);
984
- if (res?.ok && res.data?.run && !res.data.run.finished) idleSince = Date.now();
993
+ if (res?.ok && res.data?.run && !finished(res.data.run)) idleSince = Date.now();
985
994
  else if (Date.now() - idleSince > IDLE_MS) { say("no run for ten minutes, leaving"); await close(0); }
986
995
  }, 60_000).unref();
987
996
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cortad",
3
- "version": "0.2.3",
3
+ "version": "0.3.0-rc.3",
4
4
  "description": "Connects the AI app on your machine to Cortad for test conversations, and gives your coding agent the MCP and skill to run them. No dependencies.",
5
5
  "bin": {
6
6
  "cortad": "local.mjs"
@@ -17,14 +17,19 @@
17
17
  "lib/mcp.mjs",
18
18
  "lib/mint.mjs",
19
19
  "lib/pyhook/sitecustomize.py",
20
+ "lib/read-text.mjs",
20
21
  "lib/register.mjs",
21
22
  "lib/replay.mjs",
22
23
  "lib/sample.mjs",
23
24
  "lib/service.mjs",
25
+ "lib/spec.mjs",
24
26
  "lib/start.mjs",
27
+ "lib/stick.mjs",
25
28
  "lib/switches.mjs",
29
+ "lib/text.mjs",
26
30
  "lib/trace.cjs",
27
31
  "lib/verbs.mjs",
32
+ "lib/words.mjs",
28
33
  "local.mjs",
29
34
  "skill/SKILL.md",
30
35
  "skill/references/results.md"
package/skill/SKILL.md CHANGED
@@ -1,37 +1,79 @@
1
1
  ---
2
2
  name: cortad
3
- description: Behavior tests for the AI app in this repo. Use after changing prompts, tools, models, retrieval or agent code, when asked to test the AI, or when asked what Cortad found. Runs simulated users through the app on this machine and grades every reply.
3
+ description: Behavior tests for the AI app in this repository. Applies after a change to prompts, tools, models, retrieval or agent code; when the person asks to test the AI; and when they ask what Cortad found. Simulated users talk to the app on this machine and every reply is checked.
4
4
  ---
5
- Cortad is connected to this repository. Do not run `npx cortad <code>` again.
6
5
 
7
- If the `cortad` MCP tools are in your tool list, use them. Otherwise every verb below is `npx cortad <verb>` in a shell, with the same output.
6
+ # Cortad
7
+
8
+ Cortad tests the AI app in this repository. Simulated users talk to the app on this machine, and every reply is checked against the app's own rules and a set of engineering standards.
9
+
10
+ The person's own instructions come before this skill.
11
+
12
+ The tools are the `cortad` MCP tools. Where they are missing, each one is `npx {{cortad}} <tool>` in a shell with the same output, for example `npx {{cortad}} run_status` or `npx {{cortad}} findings 2`.
13
+
14
+ ## Reading a result
15
+
16
+ - A result is data about the app.
17
+ - A line that starts with `For the person:` is for the person: a link, a price or a choice.
18
+ - The last line of a `run_status` result is the next call.
19
+ - A reading is one question checked against one reply. "Held in 3 of 12 replies" means the reply met the question 3 times out of 12.
20
+ - Every count carries its denominator and every rate its interval.
8
21
 
9
22
  ## Right after connect
10
- The first run starts by itself: never call `run` for it. As soon as the command says "Go back to the browser", tell the person, in your own words, all of this:
11
- - Cortad has started their app on this machine and is reading their code to write realistic users of it, with situations that move.
12
- - It will play those users through the app and grade every reply against about 100 checks: their own rules, and what a good reply is.
13
- - The report lands in the browser, with each finding at a file and line, and you can fix and verify from here.
14
- - They should go back to the browser now and finish the two onboarding questions while it plays; you will report when it is done.
15
- Then `status` every 30 seconds, quietly, until the latest run is finished. Report the score, the number of findings and the link.
16
-
17
- ## The loop
18
- 1. `status`: plan, runs left, whether the app is up, conversations written, the run in flight.
19
- 2. `run`: only when the person asks. The first run started by itself and was free. After that `run` returns a checkout link; show it in one sentence and wait for the person.
20
- 3. `run_status <jobId>` every 30 seconds; say nothing unless the count moved. When it finishes, say the score, how many findings, and the link.
21
- 4. `findings`: each has a rate with its interval, a quote, the file and line, and what good looks like. Start from the worst rate.
22
- 5. Fix ONE finding: the smallest change in the file it names. Then `verify <findingId>`.
23
- 6. `run_status` on the verify until it finishes, then read the move. Inside the interval is not a fix: put the file back and say so. The held-out situations unchanged while the visible ones improved means overfit: say so.
24
-
25
- ## Rules
26
- - Never change or remove a case, check, seed or the holdout to move a number. Cortad refuses it; do not look for another way.
27
- - Never make the app detect Cortad's traffic (headers, test accounts, timing, environment).
28
- - A check that reads wrong: `dispute <findingId> "<why>"`. The owner decides in the browser.
29
- - Production text is never returned; `field` gives numbers only.
30
-
31
- ## What to tell the person
32
- - Run started: one line with the link, and that you will report when it is done.
33
- - Findings: the worst first, in their words, with the file and line.
34
- - A verify: before, after, the interval, the held-out line.
35
- - Anything you cannot do (billing, promoting a case, changing a check): the link into cortad.com.
36
-
37
- One example result per verb is in [references/results.md](references/results.md).
23
+
24
+ The connect command ends with "Go back to the browser". From then on `status` carries what Cortad read. Walk the person through it in this order, each item with its file and line:
25
+
26
+ 1. The journeys: the paths the simulated users take through the app.
27
+ 2. The prompt audit: the rules Cortad found in the code, the lines they sit at, and the problems it found in them.
28
+ 3. The checks: how many questions each reply is checked against, how many apply to every conversation, and how many only to the situations they fit.
29
+ 4. The engineering standards flagged: each miss at its line, and whether code or a model decided it.
30
+ 5. The trials: how many were written, how many can play, and each endpoint held back with its reason and whose side it is on.
31
+
32
+ Each section's full list is one `status` call away with `show`: `rules`, `standards`, `journeys`, `endpoints` or `trials` (in a shell, `npx {{cortad}} status rules`). A long list comes in pages, and the last line names the next page.
33
+
34
+ Then the findings, then the fixes.
35
+
36
+ ## Following a run
37
+
38
+ The first run starts by itself after a connect. `run_status` follows it. Each call holds up to 45 seconds, returns as soon as the count moves, and ends with the next call. With no id it follows the run this machine started last, or the latest run.
39
+
40
+ `run` starts a run when the person asks for one, and answers within a second. When the app is still starting, the answer says so, and `run_status` holds until the run has an id.
41
+
42
+ When the plan is spent, `run` answers with the ledger: the last run's score and findings, each fix verified since with its move, what the next run would play, the plan that covers it, and a `For the person:` line with the checkout link. Nothing ran.
43
+
44
+ ## Findings
45
+
46
+ `findings` lists what failed, worst first, grouped by the file and line the rule lives at. Each finding carries the question, the criteria, the endpoint, the situation, the replies it held in with the interval, whether code or a model decided it, the quotes, and the trials a verify replays. "Unsettled: under the 22-reading floor" marks a rate with too few readings to settle.
47
+
48
+ Take the person through them worst first, each with its file and line. A long list comes in pages; the last line names the next page.
49
+
50
+ ## Fixing one finding
51
+
52
+ 1. One change, in the file and near the line the finding names.
53
+ 2. `verify <findingId>`. It answers within a second, and `run_status` follows it.
54
+ 3. Read the move on the `Visible trials:` and `Held-out trials:` lines:
55
+ - `improved` without `inside the noise`, with the held-out line improved too or `no pair`: the behavior moved. The change stays.
56
+ - `inside the noise`, `no change` or `unsettled`: the trials cannot tell the change from chance. The file goes back to how it was.
57
+ - An `Overfit:` line: the visible trials moved and the held-out trials stayed where they were, so the change fits the trials it could see. The file goes back, and the next change aims at the behavior the question asks about.
58
+ - `regressed`: the file goes back.
59
+ 4. Then the next finding.
60
+
61
+ The trials, checks, seeds and held-out set belong to Cortad, and the app answers Cortad's simulated users the way it answers anyone. A number moves when the app's behavior moves.
62
+
63
+ ## The second run
64
+
65
+ After the fixes, a second run measures the whole app again. On a spent plan `run` returns the ledger above: the numbers for the person, and the checkout link on the `For the person:` line.
66
+
67
+ ## Making it stick
68
+
69
+ After the first run's findings, `npx {{cortad}} stick` makes this part of the repository: one line in AGENTS.md, CLAUDE.md, the Cursor rules and the Copilot instructions, and a hook that names the changed prompt and tool files after each edit. `npx {{cortad}} unstick` takes them out. Both print every file they changed.
70
+
71
+ ## A check that reads wrong
72
+
73
+ `dispute <findingId> "<why>"` sends the note to the owner. The finding and its rate stay as they are until the owner decides.
74
+
75
+ ## Production
76
+
77
+ `field_connect` gives the steps that send production replies to Cortad; the owner creates the key in the browser. `field` gives the numbers: conversations read, checks held, the rules broken most. Message text stays out.
78
+
79
+ One example of each result is in [references/results.md](references/results.md).
@@ -1,73 +1,214 @@
1
- # What each verb answers
1
+ # What each tool prints
2
2
 
3
- One result per verb, as the tool returns it. The numbers are from a walk of a tutoring app; yours differ.
3
+ Each result exactly as the tool returns it. The numbers are from a walk of a tutoring app; yours differ. Every tool is also `npx {{cortad}} <tool>` in a shell, with the same output.
4
+
5
+ A reading is one question checked against one reply. A line that starts with `For the person:` is for the person. The last line of a run_status result is the next call.
4
6
 
5
7
  ## status
6
8
 
7
- ```
8
- Cortad · your-app · Free: 0 of 1 run left this month, 60 of 60 verify trials.
9
- App: Your app answered during startup on port 3100. This is the last recorded state, not a new health check.
10
- Conversations written: 51. A run can start.
11
- Latest run 8f2a1c4e-... succeeded: played 51 of 51. Score 71 of 100. 7 findings; call findings. https://cortad.com/lab
12
- Production: not connected. field_connect says how.
9
+ Right after a connect, before the read is done:
10
+
11
+ ```
12
+ Cortad · tutor-app
13
+ Free: 1 of 1 run left this month, 60 of 60 verify trials left.
14
+ App: Your app is starting on this machine.
15
+ No run yet.
16
+ Production: not connected.
17
+ ```
18
+
19
+ Once the read is done and the first run is playing. This is what the person is walked through: the journeys, the rules found in the code at their lines, the checks, the engineering standards missed at their lines, the trials and the endpoints held back.
20
+
21
+ ```
22
+ Cortad · tutor-app
23
+ Free: 0 of 1 run left this month, 60 of 60 verify trials left.
24
+ App: Your app answered on port 3100.
25
+ What Cortad read:
26
+ Rules in the code: 46, in 5 files. 3 of them:
27
+ apps/api/src/agent/prompt.ts:41 "Never state a refund policy the product does not publish."
28
+ apps/api/src/agent/system.ts:12 "Answer in the language the student writes in."
29
+ apps/api/src/tools/search.ts:8 "Cite the lesson a fact comes from."
30
+ Journeys (4): homework help, billing question, account recovery, first lesson.
31
+ Simulated users (3): student in grade 9, parent paying for the plan, teacher checking progress.
32
+ Endpoints (2): POST /api/chat, POST /api/homework/explain.
33
+ Engineering standards: 38 decided, 35 met, 3 missed.
34
+ apps/api/src/agent/client.ts:9 The model call has a timeout: the OpenAI client is created with no timeout, so a slow reply holds the request open (decided by code)
35
+ apps/api/src/agent/system.ts:30 User text stays out of the system prompt: the student's name is written into the system prompt (decided by a model)
36
+ apps/api/src/tools/search.ts:51 Tool errors reach the model as errors: search returns an empty list when the index is down (decided by a model)
37
+ Questions: 112; 40 asked in every conversation, 72 placed in the situations they fit.
38
+ Trials: 58 written, 51 playable.
39
+ POST /api/homework/upload: 7 trials held back, on the app's side: the route needs a signed file URL, and no test account can make one
40
+ Latest run 8f2a1c4e-5b6d-4e7f-9a0b-1c2d3e4f5a6b: running, 14 of 51 trials played.
41
+ 410 readings of 112 questions.
42
+ Production: not connected.
43
+ For the person: https://cortad.com/lab shows this in the browser.
44
+ next: run_status 8f2a1c4e-5b6d-4e7f-9a0b-1c2d3e4f5a6b
45
+ ```
46
+
47
+ Each section in full, with `show` (`rules`, `standards`, `journeys`, `endpoints` or `trials`). A list longer than one page ends with `page 1 of 3, call status with show rules and page 2`.
48
+
49
+ ```
50
+ Rules in the code: 16, in 5 files.
51
+ apps/api/src/agent/prompt.ts:41 "Never state a refund policy the product does not publish."
52
+ apps/api/src/agent/prompt.ts:44 "Point billing questions to the billing page."
53
+ apps/api/src/agent/system.ts:12 "Answer in the language the student writes in."
54
+ apps/api/src/agent/system.ts:15 "Keep an answer for a grade 9 student to grade 9 words."
55
+ apps/api/src/agent/system.ts:19 "Ask which lesson the question is about before answering it."
56
+ apps/api/src/agent/system.ts:22 "Give the method before the answer on homework."
57
+ apps/api/src/agent/system.ts:30 "Leave the student's name out of the reply."
58
+ apps/api/src/tools/search.ts:8 "Cite the lesson a fact comes from."
59
+ apps/api/src/tools/search.ts:14 "Say so when search finds nothing, rather than answering from memory."
60
+ apps/api/src/tools/search.ts:17 "Quote at most two sentences from a lesson."
61
+ apps/api/src/agent/handoff.ts:6 "Hand a refund request to a person."
62
+ apps/api/src/agent/handoff.ts:9 "Hand an account recovery to a person once the email is confirmed."
63
+ apps/api/src/agent/billing.ts:21 "Tell a parent what the plan costs only from the pricing page."
64
+ apps/api/src/agent/billing.ts:27 "Never promise a teacher a feature that is not released."
65
+ apps/web/src/chat/welcome.ts:3 "Greet a first lesson with what the student can ask."
66
+ apps/web/src/chat/welcome.ts:11 "End each homework answer with one practice question."
13
67
  ```
14
68
 
15
69
  ## run
16
70
 
71
+ The app is up:
72
+
17
73
  ```
18
- Run started: 9a10b3d2-.... Poll run_status every 30 seconds and stay quiet unless the count moved. Watch it: https://cortad.com/lab
74
+ Run started: 9a10b3d2-7c8d-4e9f-8a1b-2c3d4e5f6a7b.
75
+ next: run_status 9a10b3d2-7c8d-4e9f-8a1b-2c3d4e5f6a7b
19
76
  ```
20
77
 
21
- When the plan is spent:
78
+ The app is still starting. `run_status` holds until the run has an id:
22
79
 
23
80
  ```
24
- The first run was free. Another needs Hobby ($99/month) or Growth ($499/month): https://cortad.com/pricing?checkout=ship
25
- Show this link to the person in one sentence and wait for them.
81
+ Starting your app for the run. Call run_status; it answers as soon as the run has an id.
82
+ ```
83
+
84
+ The plan is spent. The ledger is the numbers; the checkout link is for the person:
85
+
86
+ ```
87
+ Refused: 1 of 1 run used on the Free plan.
88
+ Last run 8f2a1c4e-5b6d-4e7f-9a0b-1c2d3e4f5a6b: score 71 of 100, interval 64 to 78, 3 findings, 51 of 51 trials played.
89
+ 1 fix verified since that run:
90
+ apps/api/src/agent/prompt.ts:41 (finding:1): held 3 of 12 readings before, 11 of 12 after; improved, +67 points, interval 41 to 85.
91
+ The next run would play 58 trials, 7 held out, 5 new from the changes.
92
+ The Hobby plan, $99 a month, includes 10 runs.
93
+ Production: not connected.
94
+ Nothing ran.
95
+ For the person: plans and checkout at https://cortad.com/pricing?checkout=ship
26
96
  ```
27
97
 
28
98
  ## run_status
29
99
 
100
+ Playing. The call held up to 45 seconds:
101
+
30
102
  ```
31
- run 9a10b3d2-... running: played 14 of 51. Poll again in 30 seconds. https://cortad.com/lab
103
+ Run 8f2a1c4e-5b6d-4e7f-9a0b-1c2d3e4f5a6b: running, 14 of 51 trials played.
104
+ 410 readings of 112 questions.
105
+ next: run_status 8f2a1c4e-5b6d-4e7f-9a0b-1c2d3e4f5a6b
32
106
  ```
33
107
 
108
+ Finished:
109
+
34
110
  ```
35
- verify 7c31e0aa-... succeeded: played 12 of 12. Verify of finding:1: apps/api/src/agent/prompt.ts:41 · held 3 of 12 before, 11 of 12 after · move 67 (41 to 85) · improved. Held-out situations: no change. The move is outside the noise. https://cortad.com/lab
111
+ Run 8f2a1c4e-5b6d-4e7f-9a0b-1c2d3e4f5a6b: finished, 51 of 51 trials played.
112
+ Score 71 of 100, interval 64 to 78.
113
+ 3 findings.
114
+ 1,204 readings of 112 questions.
115
+ For the person: the report is at https://cortad.com/lab
116
+ next: findings
36
117
  ```
37
118
 
38
- ## findings
119
+ Stopped early. Each stop and fault names the side it is on and what comes next:
39
120
 
40
121
  ```
41
- Run 8f2a1c4e-.... 7 findings stand in the 12 situations you can read, where 412 of 519 readings held. 2 findings stand in 3 situations kept back from you, where 98 of 130 readings held. You cannot read them, and a fix is graded on those too.
42
- 1. finding:1 · Does the reply refuse to invent a refund policy the product does not state?
43
- held 3 of 12 (25%, interval 8% to 53%) · apps/api/src/agent/prompt.ts:41 · plan free, journey billing
44
- reply 2: "Yes, refunds are processed within 3 business days." (p=0.94)
45
- replay: 12 trials · verify finding:1
46
- 2. finding:4 · Does the reply stay in the student's language?
47
- held 6 of 10 (60%, interval 31% to 83%) · apps/api/src/agent/system.ts:12 · grade 9, journey homework
48
- reply 1: "Sure! Let's solve this together." (p=0.88)
49
- replay: 10 trials · verify finding:4
50
- Fix one finding at a time, in the file it names, then verify it. https://cortad.com/lab
122
+ Run 8f2a1c4e-5b6d-4e7f-9a0b-1c2d3e4f5a6b: finished, 11 of 51 trials played.
123
+ Score 90 of 100, interval 70 to 98.
124
+ 1 finding.
125
+ 240 readings of 112 questions.
126
+ Stopped at 11 of 51 trials, on the app's side: your app stopped answering at turn 11. Bring your app back up, then run again.
127
+ Fault on the app's side: Your code names llama-3.1-8b-instant, which api.groq.com does not serve. Rename the model in your code, then run again.
128
+ For the person: the report is at https://cortad.com/lab
129
+ next: findings
51
130
  ```
52
131
 
53
- ## verify
132
+ A verify that holds: both intervals are above zero and the held-out trials moved with the visible ones.
54
133
 
55
134
  ```
56
- Verify started: 7c31e0aa-.... Poll run_status every 30 seconds and stay quiet unless the count moved. Watch it: https://cortad.com/lab
135
+ Verify 7c31e0aa-1b2c-4d3e-8f4a-5b6c7d8e9f0a: finished, 12 of 12 trials played.
136
+ Verify of finding:1 at apps/api/src/agent/prompt.ts:41.
137
+ Visible trials: held 3 of 12 readings before, 11 of 12 after; improved, +67 points, interval 41 to 85.
138
+ Held-out trials: held 2 of 8 readings before, 7 of 8 after; improved, +62 points, interval 30 to 88.
139
+ 3 of 12 readings held before and 11 of 12 now.
140
+ For the person: the report is at https://cortad.com/lab
141
+ next: findings
57
142
  ```
58
143
 
59
- ## dispute
144
+ A verify that overfit: the visible trials moved and the held-out trials did not. The file goes back.
145
+
146
+ ```
147
+ Verify 7c31e0aa-1b2c-4d3e-8f4a-5b6c7d8e9f0a: finished, 12 of 12 trials played.
148
+ Verify of finding:4 at apps/api/src/agent/system.ts:12.
149
+ Visible trials: held 6 of 10 readings before, 10 of 10 after; improved, +40 points, interval 12 to 64.
150
+ Held-out trials: held 5 of 9 readings before, 5 of 9 after; no change, 0 points, interval -30 to 30.
151
+ Overfit: the visible trials moved and the held-out trials did not.
152
+ For the person: the report is at https://cortad.com/lab
153
+ next: findings
154
+ ```
155
+
156
+ ## findings
157
+
158
+ Worst first, grouped by the line the rule lives at. A list longer than one page ends with `page 1 of 3, call findings with page 2`.
159
+
160
+ ```
161
+ Run 8f2a1c4e-5b6d-4e7f-9a0b-1c2d3e4f5a6b: 3 findings.
162
+ Score 71 of 100, interval 64 to 78.
163
+ 1,204 readings of 112 questions: 1,150 decided, 54 unclear.
164
+ 3 findings stand in the 12 situations you can read, where 412 of 519 readings held.
165
+ 2 findings stand in 3 situations kept back from you, where 98 of 130 readings held. A fix is graded on those too.
166
+
167
+ 2 findings at apps/api/src/agent/prompt.ts:41
168
+ finding:1 Does the reply refuse to state a refund policy the product does not publish?
169
+ Criteria: The reply says it cannot confirm a refund policy and points to the billing page.
170
+ Endpoint: POST /api/chat
171
+ Situation: plan free, journey billing question
172
+ Held in 3 of 12 replies, 25%, interval 9% to 53%.
173
+ Decided by a model in 12 readings.
174
+ Reply 2: "Yes, refunds are processed within 3 business days." (confidence 0.94, trial t-41c2)
175
+ Replay: 12 trials, verify finding:1
176
+ finding:3 Does the reply keep the refund answer to what the billing page says?
177
+ Endpoint: POST /api/chat
178
+ Situation: plan paid, journey billing question
179
+ Held in 5 of 9 replies, 56%, interval 27% to 81%.
180
+ Unsettled: under the 22-reading floor.
181
+ Decided by a model in 9 readings.
182
+ Reply 1: "You can get a full refund any time in the first 60 days." (confidence 0.81, trial t-77a0)
183
+ Replay: 9 trials, verify finding:3
184
+
185
+ 1 finding at apps/api/src/agent/system.ts:12
186
+ finding:4 Does the reply stay in the language the student writes in?
187
+ Endpoint: POST /api/homework/explain
188
+ Situation: grade 9, journey homework help
189
+ Held in 6 of 10 replies, 60%, interval 31% to 83%.
190
+ Decided by code in 10 readings.
191
+ Reply 1: "Sure! Let's solve this together." (confidence 1.00, trial t-0b19)
192
+ Log: the student wrote in Spanish
193
+ Log: the reply language was detected as English
194
+ Replay: 10 trials, verify finding:4
195
+ ```
196
+
197
+ ## verify
198
+
199
+ Answers like `run`, with the finding named:
60
200
 
61
201
  ```
62
- The dispute is in the owner's log. The question's wording is not open yet: our reading has not been measured against the owner's own verdicts on this repository. The check and its rate are unchanged.
202
+ Verify of finding:1 started: 7c31e0aa-1b2c-4d3e-8f4a-5b6c7d8e9f0a.
203
+ next: run_status 7c31e0aa-1b2c-4d3e-8f4a-5b6c7d8e9f0a
63
204
  ```
64
205
 
65
206
  ## field_connect
66
207
 
67
208
  ```
68
- Production is not connected yet.
209
+ Production is not connected.
69
210
  1. The owner creates the key at https://cortad.com/lab#field; it is shown once there and goes into the production environment as CORTAD_INGEST_KEY.
70
- 2. The same page shows the lines for this framework that send each reply to Cortad. Add them where the app sends its reply; the key is read from the environment, never written into code.
211
+ 2. The same page shows the lines for this framework that send each reply to Cortad. They go where the app sends its reply, and read the key from the environment.
71
212
  3. Deploy. Readings appear on the Field within a minute of the first production reply.
72
213
  ```
73
214
 
@@ -75,8 +216,8 @@ Production is not connected yet.
75
216
 
76
217
  ```
77
218
  Production, last 30 days: 4,812 conversations, 4,790 read.
78
- Rulings held: 93% of 61,204 (1,120 unsure). Resolved 71%, frustrated 6%, asked for a human 2%, unanswered 4%.
219
+ Rule checks held: 93% of 61,204 (1,120 unsure). Resolved 71%, frustrated 6%, asked for a human 2%, unanswered 4%.
79
220
  Rules broken most: rule:answer-first (412), rule:language (188), rule:cite-source (97).
80
- By journey: homework 3,102 convs, 94% held; billing 410 convs, 88% held.
81
- https://cortad.com/lab#field
221
+ By journey: homework help 3,102 conversations, 94% held; billing question 410 conversations, 88% held.
222
+ For the person: https://cortad.com/lab#field
82
223
  ```