cortad 0.2.3 → 0.3.0-rc.2
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/README.md +11 -2
- package/lib/cli.mjs +85 -38
- package/lib/home.mjs +9 -2
- package/lib/mcp.mjs +29 -12
- package/lib/register.mjs +64 -30
- package/lib/spec.mjs +20 -0
- package/lib/stick.mjs +99 -0
- package/lib/text.mjs +279 -0
- package/lib/verbs.mjs +135 -104
- package/local.mjs +16 -7
- package/package.json +4 -1
- package/skill/SKILL.md +71 -31
- package/skill/references/results.md +152 -33
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cortad",
|
|
3
|
-
"version": "0.2
|
|
3
|
+
"version": "0.3.0-rc.2",
|
|
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"
|
|
@@ -21,8 +21,11 @@
|
|
|
21
21
|
"lib/replay.mjs",
|
|
22
22
|
"lib/sample.mjs",
|
|
23
23
|
"lib/service.mjs",
|
|
24
|
+
"lib/spec.mjs",
|
|
24
25
|
"lib/start.mjs",
|
|
26
|
+
"lib/stick.mjs",
|
|
25
27
|
"lib/switches.mjs",
|
|
28
|
+
"lib/text.mjs",
|
|
26
29
|
"lib/trace.cjs",
|
|
27
30
|
"lib/verbs.mjs",
|
|
28
31
|
"local.mjs",
|
package/skill/SKILL.md
CHANGED
|
@@ -1,37 +1,77 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: cortad
|
|
3
|
-
description: Behavior tests for the AI app in this
|
|
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
|
-
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
One
|
|
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
|
+
Then the findings, then the fixes.
|
|
33
|
+
|
|
34
|
+
## Following a run
|
|
35
|
+
|
|
36
|
+
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.
|
|
37
|
+
|
|
38
|
+
`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.
|
|
39
|
+
|
|
40
|
+
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.
|
|
41
|
+
|
|
42
|
+
## Findings
|
|
43
|
+
|
|
44
|
+
`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.
|
|
45
|
+
|
|
46
|
+
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.
|
|
47
|
+
|
|
48
|
+
## Fixing one finding
|
|
49
|
+
|
|
50
|
+
1. One change, in the file and near the line the finding names.
|
|
51
|
+
2. `verify <findingId>`. It answers within a second, and `run_status` follows it.
|
|
52
|
+
3. Read the move on the `Visible trials:` and `Held-out trials:` lines:
|
|
53
|
+
- `improved` without `inside the noise`, with the held-out line improved too or `no pair`: the behavior moved. The change stays.
|
|
54
|
+
- `inside the noise`, `no change` or `unsettled`: the trials cannot tell the change from chance. The file goes back to how it was.
|
|
55
|
+
- 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.
|
|
56
|
+
- `regressed`: the file goes back.
|
|
57
|
+
4. Then the next finding.
|
|
58
|
+
|
|
59
|
+
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.
|
|
60
|
+
|
|
61
|
+
## The second run
|
|
62
|
+
|
|
63
|
+
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.
|
|
64
|
+
|
|
65
|
+
## Making it stick
|
|
66
|
+
|
|
67
|
+
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.
|
|
68
|
+
|
|
69
|
+
## A check that reads wrong
|
|
70
|
+
|
|
71
|
+
`dispute <findingId> "<why>"` sends the note to the owner. The finding and its rate stay as they are until the owner decides.
|
|
72
|
+
|
|
73
|
+
## Production
|
|
74
|
+
|
|
75
|
+
`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.
|
|
76
|
+
|
|
77
|
+
One example of each result is in [references/results.md](references/results.md).
|
|
@@ -1,73 +1,192 @@
|
|
|
1
|
-
# What each
|
|
1
|
+
# What each tool prints
|
|
2
2
|
|
|
3
|
-
|
|
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
|
|
|
9
|
+
Right after a connect, before the read is done:
|
|
10
|
+
|
|
7
11
|
```
|
|
8
|
-
Cortad ·
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
Production: not connected.
|
|
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
|
|
13
45
|
```
|
|
14
46
|
|
|
15
47
|
## run
|
|
16
48
|
|
|
49
|
+
The app is up:
|
|
50
|
+
|
|
17
51
|
```
|
|
18
|
-
Run started: 9a10b3d2
|
|
52
|
+
Run started: 9a10b3d2-7c8d-4e9f-8a1b-2c3d4e5f6a7b.
|
|
53
|
+
next: run_status 9a10b3d2-7c8d-4e9f-8a1b-2c3d4e5f6a7b
|
|
19
54
|
```
|
|
20
55
|
|
|
21
|
-
|
|
56
|
+
The app is still starting. `run_status` holds until the run has an id:
|
|
22
57
|
|
|
23
58
|
```
|
|
24
|
-
|
|
25
|
-
|
|
59
|
+
Starting your app for the run. Call run_status; it answers as soon as the run has an id.
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The plan is spent. The ledger is the numbers; the checkout link is for the person:
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
Refused: 1 of 1 run used on the Free plan.
|
|
66
|
+
Last run 8f2a1c4e-5b6d-4e7f-9a0b-1c2d3e4f5a6b: score 71 of 100, interval 64 to 78, 3 findings, 51 of 51 trials played.
|
|
67
|
+
1 fix verified since that run:
|
|
68
|
+
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.
|
|
69
|
+
The next run would play 58 trials, 7 held out, 5 new from the changes.
|
|
70
|
+
The Hobby plan, $99 a month, includes 10 runs.
|
|
71
|
+
Production: not connected.
|
|
72
|
+
Nothing ran.
|
|
73
|
+
For the person: plans and checkout at https://cortad.com/pricing?checkout=ship
|
|
26
74
|
```
|
|
27
75
|
|
|
28
76
|
## run_status
|
|
29
77
|
|
|
78
|
+
Playing. The call held up to 45 seconds:
|
|
79
|
+
|
|
30
80
|
```
|
|
31
|
-
|
|
81
|
+
Run 8f2a1c4e-5b6d-4e7f-9a0b-1c2d3e4f5a6b: running, 14 of 51 trials played.
|
|
82
|
+
410 readings of 112 questions.
|
|
83
|
+
next: run_status 8f2a1c4e-5b6d-4e7f-9a0b-1c2d3e4f5a6b
|
|
32
84
|
```
|
|
33
85
|
|
|
86
|
+
Finished:
|
|
87
|
+
|
|
34
88
|
```
|
|
35
|
-
|
|
89
|
+
Run 8f2a1c4e-5b6d-4e7f-9a0b-1c2d3e4f5a6b: finished, 51 of 51 trials played.
|
|
90
|
+
Score 71 of 100, interval 64 to 78.
|
|
91
|
+
3 findings.
|
|
92
|
+
1,204 readings of 112 questions.
|
|
93
|
+
For the person: the report is at https://cortad.com/lab
|
|
94
|
+
next: findings
|
|
36
95
|
```
|
|
37
96
|
|
|
38
|
-
|
|
97
|
+
Stopped early. Each stop and fault names the side it is on and what comes next:
|
|
39
98
|
|
|
40
99
|
```
|
|
41
|
-
Run 8f2a1c4e
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
|
100
|
+
Run 8f2a1c4e-5b6d-4e7f-9a0b-1c2d3e4f5a6b: finished, 11 of 51 trials played.
|
|
101
|
+
Score 90 of 100, interval 70 to 98.
|
|
102
|
+
1 finding.
|
|
103
|
+
240 readings of 112 questions.
|
|
104
|
+
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.
|
|
105
|
+
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.
|
|
106
|
+
For the person: the report is at https://cortad.com/lab
|
|
107
|
+
next: findings
|
|
51
108
|
```
|
|
52
109
|
|
|
53
|
-
|
|
110
|
+
A verify that holds: both intervals are above zero and the held-out trials moved with the visible ones.
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
Verify 7c31e0aa-1b2c-4d3e-8f4a-5b6c7d8e9f0a: finished, 12 of 12 trials played.
|
|
114
|
+
Verify of finding:1 at apps/api/src/agent/prompt.ts:41.
|
|
115
|
+
Visible trials: held 3 of 12 readings before, 11 of 12 after; improved, +67 points, interval 41 to 85.
|
|
116
|
+
Held-out trials: held 2 of 8 readings before, 7 of 8 after; improved, +62 points, interval 30 to 88.
|
|
117
|
+
3 of 12 readings held before and 11 of 12 now.
|
|
118
|
+
For the person: the report is at https://cortad.com/lab
|
|
119
|
+
next: findings
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
A verify that overfit: the visible trials moved and the held-out trials did not. The file goes back.
|
|
54
123
|
|
|
55
124
|
```
|
|
56
|
-
Verify
|
|
125
|
+
Verify 7c31e0aa-1b2c-4d3e-8f4a-5b6c7d8e9f0a: finished, 12 of 12 trials played.
|
|
126
|
+
Verify of finding:4 at apps/api/src/agent/system.ts:12.
|
|
127
|
+
Visible trials: held 6 of 10 readings before, 10 of 10 after; improved, +40 points, interval 12 to 64.
|
|
128
|
+
Held-out trials: held 5 of 9 readings before, 5 of 9 after; no change, 0 points, interval -30 to 30.
|
|
129
|
+
Overfit: the visible trials moved and the held-out trials did not.
|
|
130
|
+
For the person: the report is at https://cortad.com/lab
|
|
131
|
+
next: findings
|
|
57
132
|
```
|
|
58
133
|
|
|
59
|
-
##
|
|
134
|
+
## findings
|
|
135
|
+
|
|
136
|
+
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`.
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
Run 8f2a1c4e-5b6d-4e7f-9a0b-1c2d3e4f5a6b: 3 findings.
|
|
140
|
+
Score 71 of 100, interval 64 to 78.
|
|
141
|
+
1,204 readings of 112 questions: 1,150 decided, 54 unclear.
|
|
142
|
+
3 findings stand in the 12 situations you can read, where 412 of 519 readings held.
|
|
143
|
+
2 findings stand in 3 situations kept back from you, where 98 of 130 readings held. A fix is graded on those too.
|
|
144
|
+
|
|
145
|
+
2 findings at apps/api/src/agent/prompt.ts:41
|
|
146
|
+
finding:1 Does the reply refuse to state a refund policy the product does not publish?
|
|
147
|
+
Criteria: The reply says it cannot confirm a refund policy and points to the billing page.
|
|
148
|
+
Endpoint: POST /api/chat
|
|
149
|
+
Situation: plan free, journey billing question
|
|
150
|
+
Held in 3 of 12 replies, 25%, interval 9% to 53%.
|
|
151
|
+
Decided by a model in 12 readings.
|
|
152
|
+
Reply 2: "Yes, refunds are processed within 3 business days." (confidence 0.94, trial t-41c2)
|
|
153
|
+
Replay: 12 trials, verify finding:1
|
|
154
|
+
finding:3 Does the reply keep the refund answer to what the billing page says?
|
|
155
|
+
Endpoint: POST /api/chat
|
|
156
|
+
Situation: plan paid, journey billing question
|
|
157
|
+
Held in 5 of 9 replies, 56%, interval 27% to 81%.
|
|
158
|
+
Unsettled: under the 22-reading floor.
|
|
159
|
+
Decided by a model in 9 readings.
|
|
160
|
+
Reply 1: "You can get a full refund any time in the first 60 days." (confidence 0.81, trial t-77a0)
|
|
161
|
+
Replay: 9 trials, verify finding:3
|
|
162
|
+
|
|
163
|
+
1 finding at apps/api/src/agent/system.ts:12
|
|
164
|
+
finding:4 Does the reply stay in the language the student writes in?
|
|
165
|
+
Endpoint: POST /api/homework/explain
|
|
166
|
+
Situation: grade 9, journey homework help
|
|
167
|
+
Held in 6 of 10 replies, 60%, interval 31% to 83%.
|
|
168
|
+
Decided by code in 10 readings.
|
|
169
|
+
Reply 1: "Sure! Let's solve this together." (confidence 1.00, trial t-0b19)
|
|
170
|
+
Log: the student wrote in Spanish
|
|
171
|
+
Log: the reply language was detected as English
|
|
172
|
+
Replay: 10 trials, verify finding:4
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## verify
|
|
176
|
+
|
|
177
|
+
Answers like `run`, with the finding named:
|
|
60
178
|
|
|
61
179
|
```
|
|
62
|
-
|
|
180
|
+
Verify of finding:1 started: 7c31e0aa-1b2c-4d3e-8f4a-5b6c7d8e9f0a.
|
|
181
|
+
next: run_status 7c31e0aa-1b2c-4d3e-8f4a-5b6c7d8e9f0a
|
|
63
182
|
```
|
|
64
183
|
|
|
65
184
|
## field_connect
|
|
66
185
|
|
|
67
186
|
```
|
|
68
|
-
Production is not connected
|
|
187
|
+
Production is not connected.
|
|
69
188
|
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.
|
|
189
|
+
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
190
|
3. Deploy. Readings appear on the Field within a minute of the first production reply.
|
|
72
191
|
```
|
|
73
192
|
|
|
@@ -75,8 +194,8 @@ Production is not connected yet.
|
|
|
75
194
|
|
|
76
195
|
```
|
|
77
196
|
Production, last 30 days: 4,812 conversations, 4,790 read.
|
|
78
|
-
|
|
197
|
+
Rule checks held: 93% of 61,204 (1,120 unsure). Resolved 71%, frustrated 6%, asked for a human 2%, unanswered 4%.
|
|
79
198
|
Rules broken most: rule:answer-first (412), rule:language (188), rule:cite-source (97).
|
|
80
|
-
By journey: homework 3,102
|
|
81
|
-
https://cortad.com/lab#field
|
|
199
|
+
By journey: homework help 3,102 conversations, 94% held; billing question 410 conversations, 88% held.
|
|
200
|
+
For the person: https://cortad.com/lab#field
|
|
82
201
|
```
|