polymath-society 0.2.20 → 0.2.21

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.
Files changed (38) hide show
  1. package/dist/cli.js +23915 -21642
  2. package/dist/engine/chat-loops.js +8 -1
  3. package/dist/engine/exp-allfacets.js +8 -1
  4. package/dist/engine/exp-person.js +8 -1
  5. package/dist/engine/exp-pipeline.js +8 -1
  6. package/dist/engine/ingest-export.js +7 -6
  7. package/dist/engine/peak-demos.js +8 -1
  8. package/dist/engine/person-dimension-summary.js +8 -1
  9. package/dist/engine/person-facet-lines.js +8 -1
  10. package/dist/engine/person-headline.js +8 -1
  11. package/dist/engine/person-report.js +8 -1
  12. package/dist/engine/person-self-image.js +8 -1
  13. package/dist/engine/public-report.js +15 -7
  14. package/dist/engine/run-analysis.js +8 -1
  15. package/dist/engine/run-tagger.js +8 -1
  16. package/dist/index.js +19647 -17969
  17. package/dist/pipeline/coding-agglomerate.js +826 -82
  18. package/dist/pipeline/coding-aggregate.js +307 -23
  19. package/dist/pipeline/coding-build.js +453 -95
  20. package/dist/pipeline/coding-coaching.js +479 -88
  21. package/dist/pipeline/coding-day-digest.js +308 -36
  22. package/dist/pipeline/coding-delegation.js +376 -52
  23. package/dist/pipeline/coding-expertise.js +356 -62
  24. package/dist/pipeline/coding-flow.js +288 -15
  25. package/dist/pipeline/coding-focus.js +371 -51
  26. package/dist/pipeline/coding-frontier-detail.js +344 -50
  27. package/dist/pipeline/coding-frontier.js +7 -6
  28. package/dist/pipeline/coding-gap-dist.js +288 -15
  29. package/dist/pipeline/coding-gap.js +507 -115
  30. package/dist/pipeline/coding-grade.js +342 -45
  31. package/dist/pipeline/coding-growth.js +17041 -0
  32. package/dist/pipeline/coding-nutshell.js +15 -7
  33. package/dist/pipeline/coding-projects.js +7 -6
  34. package/dist/pipeline/coding-walkthrough.js +317 -37
  35. package/dist/pipeline/coding-workbrief.js +16387 -0
  36. package/dist/web/app.js +2095 -1395
  37. package/dist/web/styles.css +1 -1
  38. package/package.json +1 -1
@@ -1,8 +1,8 @@
1
1
  import{createRequire as __cr}from'module';const require=__cr(import.meta.url);
2
2
 
3
3
  // ../../scripts/coding-aggregate.mts
4
- import { promises as fs2 } from "fs";
5
- import path2 from "path";
4
+ import { promises as fs3 } from "fs";
5
+ import path3 from "path";
6
6
 
7
7
  // ../../lib/agents/shared/cliAdapter.ts
8
8
  var CLAUDE_BIN = process.env.CLAUDE_BIN || "claude";
@@ -50,7 +50,7 @@ var LIBERAL = [
50
50
  var ALS = new AsyncLocalStorage();
51
51
 
52
52
  // ../coding-core/dist/messages.js
53
- import { promises as fs } from "fs";
53
+ import { promises as fs2 } from "fs";
54
54
 
55
55
  // ../coding-core/dist/injected.js
56
56
  var SYSTEM_REMINDER_RE = /<system-reminder>[\s\S]*?<\/system-reminder>/g;
@@ -176,6 +176,274 @@ function extractCodexEvents(raw) {
176
176
  return out;
177
177
  }
178
178
 
179
+ // ../coding-core/dist/cursor.js
180
+ import { promises as fs } from "fs";
181
+ var NODE_SQLITE = "node:sqlite";
182
+ async function openCursorSqlite(dbPath) {
183
+ try {
184
+ await fs.access(dbPath);
185
+ } catch {
186
+ return null;
187
+ }
188
+ let mod;
189
+ try {
190
+ mod = await import(NODE_SQLITE);
191
+ } catch {
192
+ return null;
193
+ }
194
+ const DatabaseSync = mod?.DatabaseSync;
195
+ if (!DatabaseSync)
196
+ return null;
197
+ let db;
198
+ try {
199
+ db = new DatabaseSync(dbPath, { readOnly: true });
200
+ } catch {
201
+ try {
202
+ db = new DatabaseSync(dbPath);
203
+ } catch {
204
+ return null;
205
+ }
206
+ }
207
+ const asText = (v) => v == null ? null : typeof v === "string" ? v : Buffer.from(v).toString("utf-8");
208
+ return {
209
+ prefix(prefix) {
210
+ try {
211
+ const rows = db.prepare("SELECT key, value FROM cursorDiskKV WHERE key LIKE ? ESCAPE '\\'").all(likePrefix(prefix));
212
+ return rows.map((r) => ({ key: r.key, value: asText(r.value) ?? "" }));
213
+ } catch {
214
+ return [];
215
+ }
216
+ },
217
+ get(key) {
218
+ try {
219
+ const row = db.prepare("SELECT value FROM cursorDiskKV WHERE key = ?").get(key);
220
+ return row ? asText(row.value) : null;
221
+ } catch {
222
+ return null;
223
+ }
224
+ },
225
+ close() {
226
+ try {
227
+ db.close();
228
+ } catch {
229
+ }
230
+ }
231
+ };
232
+ }
233
+ function likePrefix(prefix) {
234
+ return prefix.replace(/[\\%_]/g, (c) => "\\" + c) + "%";
235
+ }
236
+ var CURSOR_USER_QUERY_RE = /<user_query>\s*([\s\S]*?)\s*<\/user_query>/;
237
+ function cleanUserText(raw) {
238
+ let t = raw.replace(SYSTEM_REMINDER_RE, "");
239
+ const m = t.match(CURSOR_USER_QUERY_RE);
240
+ if (m)
241
+ t = m[1];
242
+ return t.trim();
243
+ }
244
+ var msFrom = (v) => {
245
+ if (typeof v === "number" && Number.isFinite(v))
246
+ return v;
247
+ if (typeof v === "string") {
248
+ const n = Date.parse(v);
249
+ return Number.isFinite(n) ? n : null;
250
+ }
251
+ return null;
252
+ };
253
+ function readCursorComposer(db, composerId) {
254
+ const cdRaw = db.get(`composerData:${composerId}`);
255
+ if (!cdRaw)
256
+ return null;
257
+ let cd;
258
+ try {
259
+ cd = JSON.parse(cdRaw);
260
+ } catch {
261
+ return null;
262
+ }
263
+ const headers = Array.isArray(cd.fullConversationHeadersOnly) ? cd.fullConversationHeadersOnly : [];
264
+ if (headers.length === 0)
265
+ return null;
266
+ const bubbles = /* @__PURE__ */ new Map();
267
+ for (const row of db.prefix(`bubbleId:${composerId}:`)) {
268
+ const bid = row.key.slice(`bubbleId:${composerId}:`.length);
269
+ try {
270
+ bubbles.set(bid, JSON.parse(row.value));
271
+ } catch {
272
+ }
273
+ }
274
+ const turns = [];
275
+ let clock = msFrom(cd.createdAt) ?? 0;
276
+ for (const h of headers) {
277
+ const b = bubbles.get(h.bubbleId);
278
+ if (!b)
279
+ continue;
280
+ const type = h.type === 1 ? 1 : 2;
281
+ const tool = type === 2 && b.toolFormerData && typeof b.toolFormerData.name === "string" ? b.toolFormerData.name : null;
282
+ const rawText = typeof b.text === "string" ? b.text : "";
283
+ const text = type === 1 ? cleanUserText(rawText) : rawText.replace(SYSTEM_REMINDER_RE, "").trim();
284
+ if (!text && !tool)
285
+ continue;
286
+ const t = msFrom(b.createdAt);
287
+ clock = Math.max(clock, t ?? clock + 1);
288
+ const model = b.modelInfo && typeof b.modelInfo.modelName === "string" ? b.modelInfo.modelName : null;
289
+ turns.push({ t: clock, type, text, tool, model });
290
+ }
291
+ if (turns.length === 0)
292
+ return null;
293
+ const gitRepo = Array.isArray(cd.trackedGitRepos) && cd.trackedGitRepos[0] && typeof cd.trackedGitRepos[0].repoPath === "string" ? cd.trackedGitRepos[0].repoPath : null;
294
+ return {
295
+ composerId,
296
+ createdAt: msFrom(cd.createdAt),
297
+ updatedAt: msFrom(cd.lastUpdatedAt),
298
+ name: typeof cd.name === "string" && cd.name.trim() ? cd.name.trim() : null,
299
+ gitRepo,
300
+ turns
301
+ };
302
+ }
303
+ function sniffCursorRaw(raw) {
304
+ for (const lineRaw of raw.split("\n")) {
305
+ const line = lineRaw.trim();
306
+ if (!line)
307
+ continue;
308
+ try {
309
+ const e = JSON.parse(line);
310
+ return (e.role === "user" || e.role === "assistant") && typeof e.message === "object" && e.message !== null && "content" in e.message && !("type" in e);
311
+ } catch {
312
+ return false;
313
+ }
314
+ }
315
+ return false;
316
+ }
317
+ function jsonlText(content) {
318
+ const parts = [];
319
+ const tools = [];
320
+ if (Array.isArray(content)) {
321
+ for (const item of content) {
322
+ if (!item || typeof item !== "object")
323
+ continue;
324
+ const it = item;
325
+ if (it.type === "text" && it.text)
326
+ parts.push(it.text);
327
+ else if (it.type === "tool_use" && it.name)
328
+ tools.push(it.name);
329
+ }
330
+ } else if (typeof content === "string")
331
+ parts.push(content);
332
+ return { text: parts.join("\n"), tools };
333
+ }
334
+ function parseCursorJsonl(raw, base) {
335
+ const turns = [];
336
+ let clock = base;
337
+ for (const lineRaw of raw.split("\n")) {
338
+ const line = lineRaw.trim();
339
+ if (!line)
340
+ continue;
341
+ let e;
342
+ try {
343
+ e = JSON.parse(line);
344
+ } catch {
345
+ continue;
346
+ }
347
+ if (e.role !== "user" && e.role !== "assistant")
348
+ continue;
349
+ const { text: rawText, tools } = jsonlText(e.message?.content);
350
+ const type = e.role === "user" ? 1 : 2;
351
+ const text = type === 1 ? cleanUserText(rawText) : rawText.replace(SYSTEM_REMINDER_RE, "").trim();
352
+ if (type === 2 && tools.length) {
353
+ for (const tool of tools) {
354
+ clock += 1;
355
+ turns.push({ t: clock, type: 2, text: "", tool, model: null });
356
+ }
357
+ }
358
+ if (!text)
359
+ continue;
360
+ clock += 1;
361
+ turns.push({ t: clock, type, text, tool: null, model: null });
362
+ }
363
+ if (turns.length === 0)
364
+ return null;
365
+ return { composerId: "", createdAt: base, updatedAt: clock, name: null, gitRepo: null, turns };
366
+ }
367
+ function cursorMessages(c) {
368
+ const out = [];
369
+ let aiStart = 0;
370
+ let aiParts = [];
371
+ const flushAI = () => {
372
+ if (!aiParts.length)
373
+ return;
374
+ out.push({ t: aiStart, role: "ai", text: aiParts.join("\n") });
375
+ aiParts = [];
376
+ };
377
+ for (const turn of c.turns) {
378
+ if (turn.type === 1) {
379
+ flushAI();
380
+ out.push({ t: turn.t, role: "user", text: turn.text });
381
+ } else {
382
+ if (aiParts.length === 0)
383
+ aiStart = turn.t;
384
+ if (turn.text)
385
+ aiParts.push(turn.text);
386
+ }
387
+ }
388
+ flushAI();
389
+ return out;
390
+ }
391
+ function composerIdFromFile(file) {
392
+ const m = file.match(/agent-transcripts\/([^/]+)\/[^/]+\.jsonl$/);
393
+ return m ? m[1] : null;
394
+ }
395
+ async function loadCursorComposer(file, raw, dbPath) {
396
+ const cid = composerIdFromFile(file);
397
+ if (cid && dbPath) {
398
+ const db = await openCursorSqlite(dbPath);
399
+ if (db) {
400
+ try {
401
+ const c = readCursorComposer(db, cid);
402
+ if (c)
403
+ return c;
404
+ } finally {
405
+ db.close();
406
+ }
407
+ }
408
+ }
409
+ return parseCursorJsonl(raw, 0);
410
+ }
411
+
412
+ // ../coding-core/dist/raw.js
413
+ import path from "path";
414
+ import os2 from "os";
415
+ var SOURCE_ROOT_ENV_VARS = [
416
+ "CLAUDE_PROJECTS_DIR",
417
+ "CODEX_SESSIONS_DIR",
418
+ "CURSOR_WORKSPACE_DIR",
419
+ "CURSOR_PROJECTS_DIR",
420
+ // Cursor's globalStorage dir (holds state.vscdb — the composer bubbles that
421
+ // carry a Cursor session's real per-turn timestamps + content). Same
422
+ // isolation contract: overriding any root disables every unset source.
423
+ "CURSOR_GLOBAL_DIR"
424
+ ];
425
+ var anyRootOverridden = () => SOURCE_ROOT_ENV_VARS.some((v) => !!process.env[v]);
426
+ function resolveSourceRoot(envVar, ...defaultSegments) {
427
+ const explicit = process.env[envVar];
428
+ if (explicit)
429
+ return explicit;
430
+ if (anyRootOverridden())
431
+ return null;
432
+ return path.join(os2.homedir(), ...defaultSegments);
433
+ }
434
+ var cursorGlobalRoot = () => resolveSourceRoot("CURSOR_GLOBAL_DIR", "Library", "Application Support", "Cursor", "User", "globalStorage");
435
+ function cursorGlobalDbPath() {
436
+ const root = cursorGlobalRoot();
437
+ return root === null ? null : path.join(root, "state.vscdb");
438
+ }
439
+ var MACHINE_CURSOR_FILE_RE = /^(.*[/\\]machines[/\\][^/\\]+[/\\]\.cursor)[/\\]projects[/\\]/;
440
+ function cursorDbPathForFile(file) {
441
+ const m = file.match(MACHINE_CURSOR_FILE_RE);
442
+ if (m)
443
+ return path.join(m[1], "globalStorage", "state.vscdb");
444
+ return cursorGlobalDbPath();
445
+ }
446
+
179
447
  // ../coding-core/dist/messages.js
180
448
  function extractText(content) {
181
449
  if (typeof content === "string")
@@ -243,12 +511,16 @@ function extractMessagesCodex(raw) {
243
511
  async function extractMessages(file) {
244
512
  let raw;
245
513
  try {
246
- raw = await fs.readFile(file, "utf-8");
514
+ raw = await fs2.readFile(file, "utf-8");
247
515
  } catch {
248
516
  return [];
249
517
  }
250
518
  if (sniffCodexRaw(raw))
251
519
  return extractMessagesCodex(raw);
520
+ if (sniffCursorRaw(raw)) {
521
+ const c = await loadCursorComposer(file, raw, cursorDbPathForFile(file));
522
+ return c ? cursorMessages(c) : [];
523
+ }
252
524
  const out = [];
253
525
  const userTexts = /* @__PURE__ */ new Set();
254
526
  const seenUuids = /* @__PURE__ */ new Set();
@@ -415,9 +687,13 @@ function throughputCeiling(dayWords, proof = 3) {
415
687
  return 0;
416
688
  return scores[Math.min(proof, scores.length) - 1];
417
689
  }
690
+ function throughputMeanDays(activeDays, spanHByDate, spanHourMin) {
691
+ const substantial = activeDays.filter((d) => (spanHByDate[d] || 0) >= spanHourMin);
692
+ return substantial.length ? substantial : activeDays;
693
+ }
418
694
 
419
695
  // ../../lib/agents/coding/profile.ts
420
- import path from "path";
696
+ import path2 from "path";
421
697
 
422
698
  // ../../lib/calibration/fingerprint.ts
423
699
  import { createHash } from "node:crypto";
@@ -567,7 +843,7 @@ var IDLE_CAP_MS = 12 * 6e4;
567
843
 
568
844
  // ../../lib/calibration/index.ts
569
845
  var DEFAULT_CALIBRATION = {
570
- version: "2026-07-16.2",
846
+ version: "2026-07-21.1",
571
847
  // = RUBRIC_FINGERPRINT of the shipped npm rubric (packages/coding-analyzer/
572
848
  // src/grade/criteria.ts). A unit test fails loud when the rubric changes
573
849
  // without this being updated (and re-pushed to the server).
@@ -663,13 +939,14 @@ var DEFAULT_CALIBRATION = {
663
939
  },
664
940
  codingBenchmarks: {
665
941
  flow: {
666
- // typical knowledge worker ≈ 35% of an 8h day focused; ~4h/day is the
667
- // recognized deep-work ceiling (Newport) 50% most sit far below it.
942
+ // typical knowledge worker ≈ 35% of an 8h day focused; the ~4h/day
943
+ // deep-work ceiling (Newport) sits nearer ~40% of a real (9-10h) top
944
+ // performer's day — 50% overstated it (owner call 2026-07-21).
668
945
  typicalPctOfDay: 0.35,
669
- topPctOfDay: 0.5,
946
+ topPctOfDay: 0.4,
670
947
  tag: "measured",
671
- source: "RescueTime 2019 (~2h48m focused/day) \xB7 Cal Newport, Deep Work (~4h/day deep-work ceiling)",
672
- line: "the best spend ~half the workday in true deep flow; ~4h/day is the human ceiling"
948
+ source: "RescueTime 2019 (~2h48m focused/day) \xB7 Cal Newport, Deep Work (~4h/day deep-work ceiling against a 9-10h day)",
949
+ line: "the best spend ~40% of the workday in true deep flow; ~4h/day is the human ceiling"
673
950
  },
674
951
  longestRun: {
675
952
  typicalHours: 0.67,
@@ -718,15 +995,15 @@ var BEST = DEFAULT_CALIBRATION.codingBenchmarks;
718
995
 
719
996
  // ../../lib/agents/coding/profile.ts
720
997
  var ROOT = process.cwd();
721
- var CODING_DIR = process.env.POLYMATH_DATA_DIR ? path.join(process.env.POLYMATH_DATA_DIR, "coding") : path.join(ROOT, ".data", "coding");
722
- var GRADES = path.join(CODING_DIR, "grades");
998
+ var CODING_DIR = process.env.POLYMATH_DATA_DIR ? path2.join(process.env.POLYMATH_DATA_DIR, "coding") : path2.join(ROOT, ".data", "coding");
999
+ var GRADES = path2.join(CODING_DIR, "grades");
723
1000
 
724
1001
  // ../../scripts/coding-aggregate.mts
725
1002
  var CODING = CODING_DIR;
726
1003
  var TZ = "America/Los_Angeles";
727
1004
  var readJson = async (f, d) => {
728
1005
  try {
729
- return JSON.parse(await fs2.readFile(path2.join(CODING, f), "utf8"));
1006
+ return JSON.parse(await fs3.readFile(path3.join(CODING, f), "utf8"));
730
1007
  } catch {
731
1008
  return d;
732
1009
  }
@@ -738,7 +1015,7 @@ async function throughputAvg(spanHourMin) {
738
1015
  const slots = Object.keys(d.slots || {}).map(Number);
739
1016
  spanH[d.date] = slots.length ? (Math.max(...slots) - Math.min(...slots) + 1) / 6 : 0;
740
1017
  }
741
- const sess = JSON.parse(await fs2.readFile(path2.join(CODING, "sessions.json"), "utf8")).filter((s) => !s.duplicateOf && s.klass === "interactive");
1018
+ const sess = JSON.parse(await fs3.readFile(path3.join(CODING, "sessions.json"), "utf8")).filter((s) => !s.duplicateOf && s.klass === "interactive");
742
1019
  const wordsByDay = {};
743
1020
  const firstSeen = createMessageDeduper();
744
1021
  for (const s of sess) {
@@ -756,7 +1033,7 @@ async function throughputAvg(spanHourMin) {
756
1033
  const dates = Object.keys(wordsByDay).filter((d) => wordsByDay[d] > 0);
757
1034
  const mean = (a) => a.length ? Math.round(a.reduce((x, y) => x + y, 0) / a.length) : 0;
758
1035
  const substantial = dates.filter((d) => (spanH[d] || 0) >= spanHourMin);
759
- const avgWords = mean(substantial.map((d) => wordsByDay[d]));
1036
+ const avgWords = mean(throughputMeanDays(dates, spanH, spanHourMin).map((d) => wordsByDay[d]));
760
1037
  const sens = {};
761
1038
  for (const h of [2, 3, 4, 5, 6]) {
762
1039
  const ds = dates.filter((d) => (spanH[d] || 0) >= h);
@@ -768,14 +1045,14 @@ async function throughputAvg(spanHourMin) {
768
1045
  return { spanHourMin, substantialDays: substantial.length, totalCodingDays: dates.length, avgWordsPerDay: avgWords, score, label, ceiling: throughputCeiling(dates.map((d) => wordsByDay[d])), daySensitivity: sens };
769
1046
  }
770
1047
  async function abilityCeiling() {
771
- const dir = path2.join(CODING, "grades");
772
- const files = (await fs2.readdir(dir).catch(() => [])).filter((f) => f.endsWith(".json"));
1048
+ const dir = path3.join(CODING, "grades");
1049
+ const files = (await fs3.readdir(dir).catch(() => [])).filter((f) => f.endsWith(".json"));
773
1050
  const collect = (key) => [];
774
1051
  const byKey = { generative: [], taste: [] };
775
1052
  for (const f of files) {
776
1053
  let j;
777
1054
  try {
778
- j = JSON.parse(await fs2.readFile(path2.join(dir, f), "utf8"));
1055
+ j = JSON.parse(await fs3.readFile(path3.join(dir, f), "utf8"));
779
1056
  } catch {
780
1057
  continue;
781
1058
  }
@@ -831,10 +1108,11 @@ async function flowStats() {
831
1108
  }
832
1109
  }
833
1110
  const peakWindow = bestSum > 0 ? `${hm(bestStart * 6)}\u2013${hm((bestStart + 3) * 6)}` : null;
834
- const perDay = conc.perDay || {};
1111
+ const perDayRaw = conc.perDay || {};
1112
+ const perDay = Array.isArray(perDayRaw) ? Object.fromEntries(perDayRaw.filter((r) => r?.date).map((r) => [r.date, r])) : perDayRaw;
835
1113
  const concOf = (date) => {
836
1114
  const v = perDay[date];
837
- return typeof v === "number" ? v : v?.peak ?? v?.max ?? v?.maxConcurrency ?? 1;
1115
+ return typeof v === "number" ? v : v?.peakConcurrency ?? v?.peak ?? v?.max ?? v?.maxConcurrency ?? 1;
838
1116
  };
839
1117
  const tiers = { "solo (\xD71)": { fracs: [] }, "light (\xD72\u20133)": { fracs: [] }, "heavy (\xD74+)": { fracs: [] } };
840
1118
  for (const d of days) {
@@ -850,9 +1128,15 @@ async function flowStats() {
850
1128
  const pctOfWorkDay = workDayHours > 0 ? Math.round(flowHoursPerDay / workDayHours * 100) / 100 : null;
851
1129
  const bestTier = [...byParallelism].sort((a, b) => b.avgFlowFraction - a.avgFlowFraction)[0];
852
1130
  const insight = peakWindow ? `Flow peaks ${peakWindow}; deepest on ${bestTier ? bestTier.tier : "\u2014"} days (${bestTier ? Math.round(bestTier.avgFlowFraction * 100) : 0}% in flow).` : "Not enough flow data yet.";
1131
+ const avgConcurrent = (hist) => {
1132
+ const total = Object.values(hist || {}).reduce((a, v) => a + Number(v || 0), 0);
1133
+ if (total <= 0) return 0;
1134
+ const weighted = Object.entries(hist || {}).reduce((a, [k, v]) => a + Number(k) * Number(v || 0), 0);
1135
+ return Math.round(weighted / total * 10) / 10;
1136
+ };
853
1137
  return {
854
1138
  workWindow: { typicalStart: starts.length ? hm(med(starts)) : null, typicalEnd: ends.length ? hm(med(ends)) : null, activeDays: days.length },
855
- parallelism: { maxConcurrent: conc.maxConcurrency || 0 },
1139
+ parallelism: { maxConcurrent: conc.maxConcurrency || 0, avgConcurrent: avgConcurrent(conc.levelHistogramMin || {}) },
856
1140
  flow: { fractionOfActiveTime: flow.totals?.flowFraction ?? null, pctOfWorkDay, workDayHours, flowHoursPerDay, totalFlowHours },
857
1141
  peakWindow,
858
1142
  flowByHour,
@@ -866,7 +1150,7 @@ async function main() {
866
1150
  const throughput = await throughputAvg(4);
867
1151
  const prev = await readJson("aggregate.json", {});
868
1152
  const out = { generatedAt: (/* @__PURE__ */ new Date()).toISOString(), ability, flow, throughput, ...prev.bigPicture ? { bigPicture: prev.bigPicture, ...prev.bigPictureSha ? { bigPictureSha: prev.bigPictureSha } : {} } : {} };
869
- await fs2.writeFile(path2.join(CODING, "aggregate.json"), JSON.stringify(out, null, 2));
1153
+ await fs3.writeFile(path3.join(CODING, "aggregate.json"), JSON.stringify(out, null, 2));
870
1154
  console.log("aggregate.json written" + (prev.bigPicture ? " (bigPicture preserved)" : " (no bigPicture present \u2014 run coding-nutshell.mts)"));
871
1155
  console.log(` Throughput AVG (start\u2192finish span \u2265${throughput.spanHourMin}h): ${throughput.score}/11 (${throughput.label}) \xB7 ${throughput.avgWordsPerDay} words/day over ${throughput.substantialDays}/${throughput.totalCodingDays} days \xB7 [peak ceiling was ${throughput.ceiling}]`);
872
1156
  console.log(` day sensitivity:`, throughput.daySensitivity);