cortad 0.3.0-rc.12 → 0.3.0-rc.13

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/read-text.mjs CHANGED
@@ -16,7 +16,9 @@ const ruleLine = (e) => ` ${at(e.path, e.line)} "${clip(e.text, 300)}"`;
16
16
  const headOf = (label, parts) => (parts.some(Boolean) ? `${label}: ${parts.filter(Boolean).join(", ")}.` : `${label}:`);
17
17
  const standardsHead = (m) => headOf("Engineering standards", [has(m.decided) && `${num(m.decided)} decided`, has(m.met) && `${num(m.met)} met`, has(m.missed) && `${num(m.missed)} missed`]);
18
18
  const standardLine = (x) => ` ${x.path ? `${at(x.path, x.line)} ` : ""}${x.standard}${x.detail ? `: ${clip(x.detail, 240)}` : ""}${x.decidedBy ? ` (decided by ${x.decidedBy === "code" ? "code" : "a model"})` : ""}`;
19
- const trialsHead = (t) => headOf("Trials", [has(t.written) && `${num(t.written)} written`, has(t.playable) && `${num(t.playable)} playable`]);
19
+ // Which trials can be played is decided when a run knocks: a read that said "21 playable, none held
20
+ // back" was followed by a run that held 28 back, and three agents called the counts contradictory.
21
+ const trialsHead = (t) => headOf("Trials", [has(t.written) && `${num(t.written)} written`]);
20
22
  const heldWhy = (h) => `${SIDE[h.side] ? `, ${SIDE[h.side]}` : ""}${h.why ? `: ${clip(h.why, 300)}` : ""}`;
21
23
  const named = (label, group, max = 12) => {
22
24
  if (!group) return null;
package/lib/text.mjs CHANGED
@@ -77,7 +77,10 @@ function runLines(d, prefix = "") {
77
77
  // to every agent that got it. What was measured leads, then the rate on what was measured.
78
78
  if (done && has(d.questionsAsked) && has(d.questionsOf) && d.questionsOf > 0) lines.push(coverageLine(d));
79
79
  if (done && d.blocked) lines.push(d.blocked);
80
- if (done && typeof d.score === "number") lines.push(scoreLine(d));
80
+ if (done && d.notCounted) lines.push(d.notCounted);
81
+ // A verify is a move, not a score: "100 of 100" on a replay whose run before decided nothing read
82
+ // as a clean bill to bloom's agent, beside "zero verified fixes".
83
+ if (done && typeof d.score === "number" && d.kind !== "verify") lines.push(scoreLine(d));
81
84
  if (done && d.halted) lines.push(haltLine(d.halted));
82
85
  if (done && has(d.readingsHeld) && has(d.readings) && d.readings > 0) lines.push(`The question readings held in ${num(d.readingsHeld)} of ${num(d.readings)}, ${Math.round((100 * d.readingsHeld) / d.readings)}%; the score is the case checks' pass rate on the checks measured.`);
83
86
  if (done && has(d.findings)) lines.push(`${plural(d.findings, "finding")}.`);
@@ -136,7 +139,7 @@ export function runText(d) {
136
139
  export function startedText(d, kind, findingId) {
137
140
  const verify = (d.kind ?? kind) === "verify";
138
141
  const what = verify ? `Verify${findingId && !d.joined ? ` of ${findingId}` : ""}` : "Run";
139
- return `${what} ${d.joined ? "already playing" : "started"}: ${d.jobId}.\nnext: run_status ${d.jobId}`;
142
+ return `${what} ${d.joined ? "already playing" : "started"}: ${d.jobId}.${d.note ? `\n${d.note}` : ""}\nnext: run_status ${d.jobId}`;
140
143
  }
141
144
 
142
145
  export const waitingText = (p, now, limitMs) =>
package/local.mjs CHANGED
@@ -712,9 +712,14 @@ async function freePortAbove(port) {
712
712
  for (let next = port + 1; next < port + 30; next++) if (!lost.has(next) && !(await listenerOn(next))) return next;
713
713
  return null;
714
714
  }
715
+ // The port a bind failure names, in the app's own words: uvicorn's "('127.0.0.1', 8105): address
716
+ // already in use", Node's "EADDRINUSE: address already in use :::3000".
717
+ const portInError = (said) => Number(/(\d{4,5})\)?:?\s*address already in use/i.exec(said)?.[1] ?? /(?:EADDRINUSE|address already in use)\D{0,40}?(\d{4,5})\b/i.exec(said)?.[1] ?? 0);
715
718
  async function freed(cmd, lifted) {
716
719
  const m = /(--port[= ]|-p |\bPORT=)(\d{4,5})\b/.exec(cmd);
717
- const named = m ? Number(m[2]) : Number(lifted.PORT) || 0;
720
+ // The app's own env file names its port too: yunqiao's PORT=8105 sat in .env, and a stale copy
721
+ // on that port was reported as "your app stopped" instead of moved past.
722
+ const named = m ? Number(m[2]) : Number(lifted.PORT) || Number(envExports(envFiles).PORT) || 0;
718
723
  if (!named || (!lost.has(named) && !(await listenerOn(named)))) return { cmd, lifted };
719
724
  const port = await freePortAbove(named);
720
725
  if (!port) return { cmd, lifted };
@@ -749,7 +754,7 @@ async function start(waitMs) {
749
754
  await stopApp(mine.pid);
750
755
  // The port this start named was taken between the check and the bind: the next free one, at
751
756
  // most three times, before the failure is theirs to read.
752
- const named = /(?:--port[= ]|-p |\bPORT=)(\d{4,5})\b/.exec(cmd)?.[1] ?? lifted.PORT;
757
+ const named = /(?:--port[= ]|-p |\bPORT=)(\d{4,5})\b/.exec(cmd)?.[1] ?? lifted.PORT ?? envExports(envFiles).PORT ?? (portInError(seen) || undefined);
753
758
  if (/EADDRINUSE|address already in use/i.test(seen) && named && lost.size < 3) { lost.add(Number(named)); return start(waitMs); }
754
759
  return { port: null, exited: 1, tail: tail() };
755
760
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cortad",
3
- "version": "0.3.0-rc.12",
3
+ "version": "0.3.0-rc.13",
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"
@@ -35,7 +35,7 @@ Engineering standards: 38 decided, 35 met, 3 missed.
35
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
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
37
  Questions: 112; 40 asked in every conversation, 72 placed in the situations they fit.
38
- Trials: 58 written, 51 playable.
38
+ Trials: 58 written.
39
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
40
  Latest run 8f2a1c4e-5b6d-4e7f-9a0b-1c2d3e4f5a6b: running, 14 of 51 trials played.
41
41
  410 readings of 112 questions.