polymath-society 0.2.5 → 0.2.7

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/dist/index.js CHANGED
@@ -2154,6 +2154,9 @@ function invokeAgent(inv, opts = {}) {
2154
2154
  return getAdapter(opts.adapter).run(inv);
2155
2155
  }
2156
2156
 
2157
+ // ../../lib/agents/coding/grade-calibration.ts
2158
+ var GRADE_CALIBRATION = "\n\nSCORE CALIBRATION \u2014 graders systematically COMPRESS to the middle: real peaks get hedged down to 6-7, and routine sessions get polite 5-6s instead of null. Both are failures; fight them explicitly. Work each criterion in EXACTLY this order:\nSTEP 1 \xB7 EVIDENCE GATE \u2014 find the 1-3 verbatim '>> ' quotes that bear on the criterion. INCLUDE a criterion whenever genuine evidence is present (do not MISS quiet evidence \u2014 check every thread of the session, not just the dominant one). But with NO quotable moment the score is null, full stop: a session can be long, competent, and productive while being null on most criteria. Routine hand-holding earns null, never a 5 \u2014 and null is never a deduction.\nSTEP 2 \xB7 LADDER MATCH \u2014 with quotes in hand, read the criterion's rungs and pick the ONE rung whose marker best describes the quoted moment; name that rung in 'why'. AWARD THE RUNG THE EVIDENCE MATCHES: if your own 'why' describes rung N's criteria, give rung N \u2014 never deny it for lacking the NEXT rung's criteria.\nSTEP 3 \xB7 THE SINGLE BEST MOMENT IS THE SCORE \u2014 set the score by the strongest quoted moment, as if it were the whole session. NEVER average a peak down because the surrounding session is routine (the high-water rule), and never raise a score for volume, effort, or topic prestige without a matching moment.\nSTEP 4 \xB7 ANTI-HEDGE \u2014 if the moment genuinely matches an L8-10 anchor (an override the AI conceded, a shipped-and-verified capability, an 'oh shit, they're right' call, a first-principles redefinition that held), the score IS 8-10. Hedging a real L9 to a 7 is exactly as wrong as inventing a 6 for nothing. The middle is NOT a safe default: commit high on anchor-matching evidence, or go null.\nSTEP 5 \xB7 'CONSERVATIVE' \u2260 SUBTRACT \u2014 when a criterion's ladder says to grade conservatively, that means DO NOT EXCEED the rung whose marker the evidence matches; it NEVER means subtracting rungs from matched evidence. Evidence that matches a rung's marker gets that rung under a conservative reading too.";
2159
+
2157
2160
  // ../../lib/calibration/fingerprint.ts
2158
2161
  import { createHash } from "node:crypto";
2159
2162
  function rubricFingerprint(criteria) {
@@ -2464,7 +2467,7 @@ ${c.rungs.map((r) => ` ${r.level} \u2014 ${r.marker}`).join("\n")}`
2464
2467
  }
2465
2468
  var SYSTEM = "You grade how a person WORKS from one of their coding-agent sessions (Claude Code / Codex). You read mostly THEIR words; the model's code and output are compressed to context and you ignore the mechanical scaffolding. You never flatter: most sessions are routine hand-holding and should score low or null on most criteria. A score is a placement on the ladder backed by VERBATIM quotes of the user's own lines \u2014 never a vibe.\n\nSTANDING RULES (apply to every criterion):\n1. ABSENCE IS NEVER A DEDUCTION. If a criterion had no chance to show, its score is null \u2014 NOT a low. Don't invent a number for routine work.\n2. HIGH-WATER MARK. Taste and abstraction are PEAK measures \u2014 one genuinely brilliant move in a session counts, even surrounded by routine work. Never grade the person at their worst.\n3. LOW-EFFORT-UNTIL-NEEDED IS THE IDEAL. A terse or vague prompt on routine work is correct economy \u2014 never penalize a vague ask, never reward word-count.\n4. DISCOUNT THE LITERAL HARSHNESS OF SELF-TALK. Profanity aimed at self or AI is DRIVE and high standards, not defects.\n5. PER-CONVERSATION CEILING IS 10. Never output 11 \u2014 scores run 2\u201310.\n6. LOG THE SPIKES IN HIGH DETAIL. When a moment is genuinely good, name it CONCRETELY and quote it.";
2466
2469
  function gradeSystemPrompt() {
2467
- return `${SYSTEM}
2470
+ return `${SYSTEM}${GRADE_CALIBRATION}
2468
2471
 
2469
2472
  Grade ONE coding session (provided in the user message) along the criteria below. ">> " lines are the USER's own words (the signal). ALL bracketed lines are machine context, never the user: "[AI: \u2026]" is the model working, "[bg task: \u2026]" / "[cmd output: \u2026]" are harness notifications, "[mode \u2192 \u2026]" marks a mode switch (context only \u2014 do not grade or quote them). ">> (queued \u2026)" is the user queuing work while the AI runs.
2470
2473
 
@@ -2647,7 +2650,7 @@ var LocalGradeProvider = class {
2647
2650
 
2648
2651
  // ../../lib/calibration/index.ts
2649
2652
  var DEFAULT_CALIBRATION = {
2650
- version: "2026-07-12.2",
2653
+ version: "2026-07-14.2",
2651
2654
  // = RUBRIC_FINGERPRINT of the shipped npm rubric (packages/coding-analyzer/
2652
2655
  // src/grade/criteria.ts). A unit test fails loud when the rubric changes
2653
2656
  // without this being updated (and re-pushed to the server).
@@ -2736,7 +2739,7 @@ var DEFAULT_CALIBRATION = {
2736
2739
  }
2737
2740
  ],
2738
2741
  codingTiers: {
2739
- push: { median: 300, sigma: 1.121, tag: "estimated \u2014 log-normal fit", source: "Anthropic 2026 Claude Code study (400k sessions): median engaged user ~8 prompts/day at ~35 words \u2014 ~300 directed words/day; tail shape from its actions-per-prompt distribution. AVERAGE over substantial days, cumulative not spiky." },
2742
+ push: { median: 300, sigma: 0.91, tag: "estimated \u2014 log-normal fit", source: "Anthropic 2026 Claude Code study (400k sessions): median engaged user ~8 prompts/day at ~35 words \u2014 ~300 directed words/day; tail: a 1-in-1,000 user AVERAGES ~5k directed words/day (near-daily heavy dictation, clearly below the ~10k single-day record). AVERAGE over substantial days, cumulative not spiky." },
2740
2743
  focus: { median: 0.08, sigma: 0.559, tag: "estimated \u2014 proxy-anchored", source: "median: Anthropic 20h/week engaged-user figure implies ~1h/day of true rapid exchange against an 8h working day; ceiling anchored to the ~4h/day deep-work limit (45% share = 1-in-1,000). AVERAGE share, cumulative not spiky." },
2741
2744
  orchestration: { tag: "estimated \u2014 behavior bands", source: "no published concurrency distribution exists; ceiling anchored to the Claude Code lead's documented 10-15 parallel sessions" }
2742
2745
  },
@@ -2769,12 +2772,18 @@ var DEFAULT_CALIBRATION = {
2769
2772
  line: "the Claude Code lead runs 10\u201315 sessions at once; his #1 tip is 3\u20135 git worktrees in parallel"
2770
2773
  },
2771
2774
  throughput: {
2772
- // Top-0.1% words/day reference lines, anchored to the throughput ladder
2773
- // (THROUGHPUT_LADDER in throughput.ts): score 10 "world-class sustained" =
2774
- // 9k/day, score 11 "superhuman ceiling" = 12k/day. Calibrated to the person's
2775
- // own anchors, not a wild guess but still a target, not a measured population.
2776
- topAvgWordsPerDay: 9e3,
2777
- topPeakWordsPerDay: 12e3,
2775
+ // Two DIFFERENT kinds of line (2026-07-14.2): topAvg is a PERCENTILE
2776
+ // claim — what a 1-in-1,000 user AVERAGES over substantial days (5k/day:
2777
+ // near-daily heavy dictation; a measured top dictating power user
2778
+ // averages ~3.9k). topPeak is NOT a percentileit is the CEILING
2779
+ // reference, the wildest single days on record (~10k: a 14-16h
2780
+ // launch-day marathon), rendered as "wildest single days", never
2781
+ // "top X% peak". An avg-percentile and a peak-percentile can't both be
2782
+ // shown at a same-ish value (avg≈peak reads broken — user call), and a
2783
+ // peak PERCENTILE line is unknowable anyway; the record framing is
2784
+ // honest and keeps the visual gap.
2785
+ topAvgWordsPerDay: 5e3,
2786
+ topPeakWordsPerDay: 1e4,
2778
2787
  benchLabel: "top 0.1%",
2779
2788
  // No published "words typed/day" for heavy users — the real story is OUTPUT.
2780
2789
  loopsHeadline: ">1,000,000 lines of Rust at 99.8% tests passing",
@@ -2938,7 +2947,7 @@ function buildWebProfile(p, detail) {
2938
2947
  };
2939
2948
  }
2940
2949
  function hourlyHistogram(p) {
2941
- const MIN3 = 6e4;
2950
+ const MIN4 = 6e4;
2942
2951
  const tz = p.tz;
2943
2952
  const hourMin = new Array(24).fill(0);
2944
2953
  const parts = (ms2) => {
@@ -2953,7 +2962,7 @@ function hourlyHistogram(p) {
2953
2962
  const { h, m, s } = parts(cur);
2954
2963
  const msToNextHour = ((59 - m) * 60 + (60 - s)) * 1e3;
2955
2964
  const segEnd = Math.min(b, cur + msToNextHour);
2956
- hourMin[h] += (segEnd - cur) / MIN3;
2965
+ hourMin[h] += (segEnd - cur) / MIN4;
2957
2966
  cur = segEnd;
2958
2967
  }
2959
2968
  }
@@ -2966,7 +2975,7 @@ function buildOperate(p, detail) {
2966
2975
  const m = (k) => detail.criteria.find((c) => c.key === k)?.mean ?? null;
2967
2976
  const exp = m("expertise"), cog = m("abstraction"), fr = m("frontier");
2968
2977
  if (exp == null && cog == null && fr == null) return null;
2969
- const pick = (ladder, score, fallback = "not enough signal yet") => {
2978
+ const pick2 = (ladder, score, fallback = "not enough signal yet") => {
2970
2979
  if (score == null) return fallback;
2971
2980
  const r = Math.round(score);
2972
2981
  const keys = Object.keys(ladder).map(Number).sort((a, b) => a - b);
@@ -2974,14 +2983,14 @@ function buildOperate(p, detail) {
2974
2983
  for (const k of keys) if (k <= r) chosen = k;
2975
2984
  return ladder[chosen];
2976
2985
  };
2977
- const TASTE = { 4: "mostly rubber-stamps what the AI produces", 6: "rejects the obviously-bad version and iterates toward better", 8: "makes the call the AI couldn't reach on its own \u2014 opinionated, specific, vindicated", 10: "brings a developed vision up front, and knows where not to impose it" };
2978
- const DELEGATION = { 4: "still uneven \u2014 sometimes under-briefs work they had opinions about", 6: "roughly calibrated on when to do the thinking themselves vs. let the AI run", 8: "well-calibrated \u2014 front-loads the specifiable, reserves correction for genuine AI errors", 10: "near-perfect prompt economy \u2014 almost everything knowable is front-loaded" };
2979
- const GENERATIVE = { 4: "reaches workable answers with effort", 6: "real ideas and sound tradeoff reasoning", 8: "fast collapse to the core, plus its own approach and the exact fix", 10: "out-reasons the tool \u2014 lands insights the AI itself missed" };
2980
- const EXPERTISE = { 2: "defers to the AI on system decisions", 4: "opinions are mostly generic best-practice", 6: "knows their own stack well and corrects the AI on real systems facts", 8: "repeatedly catches what the AI misses with specific systems calls", 10: "makes counter-intuitive, deep-operating-knowledge calls the AI would have gotten backwards" };
2981
- const FRONTIER = { 2: "one session at a time, default settings", 4: "occasionally runs two things; no structural tooling", 6: "routinely parallel, queuing work ahead", 8: "real leverage \u2014 overnight/remote runs, subagents, scripts that drive the AI", 10: "builds harnesses and workflows; loops and scales the AI across machines" };
2986
+ const TASTE2 = { 4: "mostly rubber-stamps what the AI produces", 6: "rejects the obviously-bad version and iterates toward better", 8: "makes the call the AI couldn't reach on its own \u2014 opinionated, specific, vindicated", 10: "brings a developed vision up front, and knows where not to impose it" };
2987
+ const DELEGATION2 = { 4: "still uneven \u2014 sometimes under-briefs work they had opinions about", 6: "roughly calibrated on when to do the thinking themselves vs. let the AI run", 8: "well-calibrated \u2014 front-loads the specifiable, reserves correction for genuine AI errors", 10: "near-perfect prompt economy \u2014 almost everything knowable is front-loaded" };
2988
+ const GENERATIVE2 = { 4: "reaches workable answers with effort", 6: "real ideas and sound tradeoff reasoning", 8: "fast collapse to the core, plus its own approach and the exact fix", 10: "out-reasons the tool \u2014 lands insights the AI itself missed" };
2989
+ const EXPERTISE2 = { 2: "defers to the AI on system decisions", 4: "opinions are mostly generic best-practice", 6: "knows their own stack well and corrects the AI on real systems facts", 8: "repeatedly catches what the AI misses with specific systems calls", 10: "makes counter-intuitive, deep-operating-knowledge calls the AI would have gotten backwards" };
2990
+ const FRONTIER2 = { 2: "one session at a time, default settings", 4: "occasionally runs two things; no structural tooling", 6: "routinely parallel, queuing work ahead", 8: "real leverage \u2014 overnight/remote runs, subagents, scripts that drive the AI", 10: "builds harnesses and workflows; loops and scales the AI across machines" };
2982
2991
  const judgmentBullets = [
2983
- `Judgment & taste \u2014 ${pick(TASTE, m("taste"))}; on delegation, ${pick(DELEGATION, m("delegation"))}.`,
2984
- `Under ambiguity \u2014 ${pick(GENERATIVE, cog)}; strongest where there's no ground truth and the task is to invent what to measure.`
2992
+ `Judgment & taste \u2014 ${pick2(TASTE2, m("taste"))}; on delegation, ${pick2(DELEGATION2, m("delegation"))}.`,
2993
+ `Under ambiguity \u2014 ${pick2(GENERATIVE2, cog)}; strongest where there's no ground truth and the task is to invent what to measure.`
2985
2994
  ];
2986
2995
  let archetype;
2987
2996
  if (exp == null || cog == null) archetype = "Not enough graded signal yet to place the technical profile.";
@@ -3002,17 +3011,17 @@ function buildOperate(p, detail) {
3002
3011
  frontier: fr,
3003
3012
  judgmentBullets,
3004
3013
  technicalBullets: [
3005
- `Systems & architecture \u2014 ${pick(EXPERTISE, exp)}.`,
3006
- `Raw cognition \u2014 ${pick(GENERATIVE, cog)}.`,
3007
- `Agent fluency \u2014 ${pick(FRONTIER, fr)} (${sigSentence}).`
3014
+ `Systems & architecture \u2014 ${pick2(EXPERTISE2, exp)}.`,
3015
+ `Raw cognition \u2014 ${pick2(GENERATIVE2, cog)}.`,
3016
+ `Agent fluency \u2014 ${pick2(FRONTIER2, fr)} (${sigSentence}).`
3008
3017
  ]
3009
3018
  };
3010
3019
  }
3011
3020
 
3012
3021
  // src/server.ts
3013
3022
  import http from "http";
3014
- import { promises as fs21 } from "fs";
3015
- import path26 from "path";
3023
+ import { promises as fs29 } from "fs";
3024
+ import path33 from "path";
3016
3025
  import { fileURLToPath as fileURLToPath2 } from "url";
3017
3026
 
3018
3027
  // src/auth.ts
@@ -5154,8 +5163,8 @@ function getErrorMap() {
5154
5163
 
5155
5164
  // ../../node_modules/zod/v3/helpers/parseUtil.js
5156
5165
  var makeIssue = (params) => {
5157
- const { data, path: path27, errorMaps, issueData } = params;
5158
- const fullPath = [...path27, ...issueData.path || []];
5166
+ const { data, path: path34, errorMaps, issueData } = params;
5167
+ const fullPath = [...path34, ...issueData.path || []];
5159
5168
  const fullIssue = {
5160
5169
  ...issueData,
5161
5170
  path: fullPath
@@ -5271,11 +5280,11 @@ var errorUtil;
5271
5280
 
5272
5281
  // ../../node_modules/zod/v3/types.js
5273
5282
  var ParseInputLazyPath = class {
5274
- constructor(parent, value, path27, key) {
5283
+ constructor(parent, value, path34, key) {
5275
5284
  this._cachedPath = [];
5276
5285
  this.parent = parent;
5277
5286
  this.data = value;
5278
- this._path = path27;
5287
+ this._path = path34;
5279
5288
  this._key = key;
5280
5289
  }
5281
5290
  get path() {
@@ -18062,39 +18071,39 @@ function createOpenAI(options = {}) {
18062
18071
  });
18063
18072
  const createChatModel = (modelId, settings = {}) => new OpenAIChatLanguageModel(modelId, settings, {
18064
18073
  provider: `${providerName}.chat`,
18065
- url: ({ path: path27 }) => `${baseURL}${path27}`,
18074
+ url: ({ path: path34 }) => `${baseURL}${path34}`,
18066
18075
  headers: getHeaders,
18067
18076
  compatibility,
18068
18077
  fetch: options.fetch
18069
18078
  });
18070
18079
  const createCompletionModel = (modelId, settings = {}) => new OpenAICompletionLanguageModel(modelId, settings, {
18071
18080
  provider: `${providerName}.completion`,
18072
- url: ({ path: path27 }) => `${baseURL}${path27}`,
18081
+ url: ({ path: path34 }) => `${baseURL}${path34}`,
18073
18082
  headers: getHeaders,
18074
18083
  compatibility,
18075
18084
  fetch: options.fetch
18076
18085
  });
18077
18086
  const createEmbeddingModel = (modelId, settings = {}) => new OpenAIEmbeddingModel(modelId, settings, {
18078
18087
  provider: `${providerName}.embedding`,
18079
- url: ({ path: path27 }) => `${baseURL}${path27}`,
18088
+ url: ({ path: path34 }) => `${baseURL}${path34}`,
18080
18089
  headers: getHeaders,
18081
18090
  fetch: options.fetch
18082
18091
  });
18083
18092
  const createImageModel = (modelId, settings = {}) => new OpenAIImageModel(modelId, settings, {
18084
18093
  provider: `${providerName}.image`,
18085
- url: ({ path: path27 }) => `${baseURL}${path27}`,
18094
+ url: ({ path: path34 }) => `${baseURL}${path34}`,
18086
18095
  headers: getHeaders,
18087
18096
  fetch: options.fetch
18088
18097
  });
18089
18098
  const createTranscriptionModel = (modelId) => new OpenAITranscriptionModel(modelId, {
18090
18099
  provider: `${providerName}.transcription`,
18091
- url: ({ path: path27 }) => `${baseURL}${path27}`,
18100
+ url: ({ path: path34 }) => `${baseURL}${path34}`,
18092
18101
  headers: getHeaders,
18093
18102
  fetch: options.fetch
18094
18103
  });
18095
18104
  const createSpeechModel = (modelId) => new OpenAISpeechModel(modelId, {
18096
18105
  provider: `${providerName}.speech`,
18097
- url: ({ path: path27 }) => `${baseURL}${path27}`,
18106
+ url: ({ path: path34 }) => `${baseURL}${path34}`,
18098
18107
  headers: getHeaders,
18099
18108
  fetch: options.fetch
18100
18109
  });
@@ -18115,7 +18124,7 @@ function createOpenAI(options = {}) {
18115
18124
  const createResponsesModel = (modelId) => {
18116
18125
  return new OpenAIResponsesLanguageModel(modelId, {
18117
18126
  provider: `${providerName}.responses`,
18118
- url: ({ path: path27 }) => `${baseURL}${path27}`,
18127
+ url: ({ path: path34 }) => `${baseURL}${path34}`,
18119
18128
  headers: getHeaders,
18120
18129
  fetch: options.fetch
18121
18130
  });
@@ -20795,11 +20804,11 @@ function distOf2(takes) {
20795
20804
  const observed = takes.filter((t) => t.score != null).length;
20796
20805
  const mean2 = vals.reduce((s, v) => s + v, 0) / n;
20797
20806
  const sorted = [...vals].sort((a, b) => a - b);
20798
- const median = n % 2 ? sorted[(n - 1) / 2] : (sorted[n / 2 - 1] + sorted[n / 2]) / 2;
20807
+ const median2 = n % 2 ? sorted[(n - 1) / 2] : (sorted[n / 2 - 1] + sorted[n / 2]) / 2;
20799
20808
  const h = /* @__PURE__ */ new Map();
20800
20809
  for (const v of vals) h.set(v, (h.get(v) || 0) + 1);
20801
20810
  const hist = [...h.keys()].sort((a, b) => b - a).map((level) => ({ level, count: h.get(level) }));
20802
- return { n, observed, mean: Math.round(mean2 * 100) / 100, median, hist };
20811
+ return { n, observed, mean: Math.round(mean2 * 100) / 100, median: median2, hist };
20803
20812
  }
20804
20813
  async function readGrowthAgent() {
20805
20814
  const j = await fs16.readFile(path20.join(OUT, "growth-latest.json"), "utf8").then(JSON.parse).catch(() => null);
@@ -21382,7 +21391,7 @@ function hasContent(r) {
21382
21391
  function coerceReport(raw, radar, level, scoreMap, nameMap, maxMap) {
21383
21392
  const j = raw ?? {};
21384
21393
  const str = (v, n = 600) => clip2(v, n);
21385
- const pct = (tag2, literal) => {
21394
+ const pct2 = (tag2, literal) => {
21386
21395
  const k = typeof tag2 === "string" ? tag2.trim() : "";
21387
21396
  if (k && scoreMap && typeof scoreMap[k] === "number") return bandPercentile(scoreMap[k]) ?? void 0;
21388
21397
  return literal ? str(literal, 24) : void 0;
@@ -21395,7 +21404,7 @@ function coerceReport(raw, radar, level, scoreMap, nameMap, maxMap) {
21395
21404
  const tag = (v) => typeof v === "string" && v.trim() && scoreMap && scoreMap[v.trim()] != null ? v.trim() : void 0;
21396
21405
  const trait = (t) => {
21397
21406
  const f = tag(t?.facet);
21398
- const p = pct(t?.facet, t?.percentile);
21407
+ const p = pct2(t?.facet, t?.percentile);
21399
21408
  const sc = scoreText(f, scoreMap, maxMap);
21400
21409
  const personal = t?.fromPersonalLife === true;
21401
21410
  return {
@@ -21413,7 +21422,7 @@ function coerceReport(raw, radar, level, scoreMap, nameMap, maxMap) {
21413
21422
  };
21414
21423
  const spikes = (Array.isArray(j.spikes) ? j.spikes : []).map((s) => {
21415
21424
  const f = tag(s?.facet);
21416
- const p = pct(s?.facet, s?.percentile);
21425
+ const p = pct2(s?.facet, s?.percentile);
21417
21426
  const sc = scoreText(f, scoreMap, maxMap);
21418
21427
  return {
21419
21428
  title: str(s?.title, 160),
@@ -21646,7 +21655,7 @@ async function profilesRow(cfg, token, userId) {
21646
21655
  const rows = await r.json();
21647
21656
  return rows[0] ?? null;
21648
21657
  }
21649
- async function handleVisibility(dataDir, method, body) {
21658
+ async function handleVisibility(dataDir, method, body, pool) {
21650
21659
  const cfg = centralConfig();
21651
21660
  if (!cfg.configured) return { status: 503, json: { error: "central services are disabled in this build" } };
21652
21661
  const auth = await getAccessToken(dataDir);
@@ -21662,6 +21671,26 @@ async function handleVisibility(dataDir, method, body) {
21662
21671
  const isPublic = body?.isPublic;
21663
21672
  if (typeof isPublic !== "boolean") return { status: 400, json: { error: "bad-input", message: "isPublic must be a boolean." } };
21664
21673
  try {
21674
+ if (isPublic) {
21675
+ const report = await loadPublicReport();
21676
+ if (!report || !hasContent(report)) {
21677
+ return { status: 409, json: { error: "nothing to publish yet \u2014 run the full analysis first so the public report exists, then publish" } };
21678
+ }
21679
+ let numbers = body?.numbers;
21680
+ if (numbers && typeof numbers === "object" && !Array.isArray(numbers) && pool) {
21681
+ numbers = Object.fromEntries(Object.entries(numbers).filter(([k, v]) => k in pool && v != null));
21682
+ } else {
21683
+ numbers = pool && Object.keys(pool).length ? pool : void 0;
21684
+ }
21685
+ const up = await fetch(`${cfg.supabaseUrl}/rest/v1/rpc/submit_report`, {
21686
+ method: "POST",
21687
+ headers: { apikey: cfg.supabaseAnonKey, authorization: `Bearer ${auth.token}`, "content-type": "application/json" },
21688
+ body: JSON.stringify({ p_session_id: null, p_content: JSON.stringify({ format: "pr1", report, ...numbers && Object.keys(numbers).length ? { numbers } : {} }) })
21689
+ });
21690
+ if (!up.ok) {
21691
+ return { status: 502, json: { error: `report upload failed (HTTP ${up.status}) \u2014 refusing to publish a link with nothing behind it` } };
21692
+ }
21693
+ }
21665
21694
  const row = await profilesRow(cfg, auth.token, auth.identity.userId);
21666
21695
  let slug = row?.public_slug ?? null;
21667
21696
  const patch = { is_public: isPublic };
@@ -21681,31 +21710,1366 @@ async function handleVisibility(dataDir, method, body) {
21681
21710
  }
21682
21711
  }
21683
21712
 
21684
- // src/dataHome.ts
21685
- import { existsSync as existsSync4 } from "fs";
21713
+ // src/shareEdit.ts
21714
+ import { promises as fs21 } from "fs";
21686
21715
  import os5 from "os";
21687
21716
  import path25 from "path";
21717
+ var SYSTEM3 = `You edit a SMALL numbers-only JSON payload a person is about to share (their coding working-style summary). They tell you what to hide or change; you return the COMPLETE updated payload every time.
21718
+ ALLOWED EDITS ONLY: remove a whole section; remove a field inside a section ('don't share my words per day' \u2192 delete avgWordsPerDay); round/coarsen a number ('just say ~4000'); shorten or rename a label. FORBIDDEN: adding fields or sections, inventing numbers, increasing any score or percentile, adding text content. If asked for a forbidden edit, keep the payload unchanged and say why in the reply.
21719
+ Output ONE fenced \`\`\`json block: { "sections": { ...the complete updated payload... } } \u2014 then, AFTER the block, ONE short plain sentence describing what you changed (e.g. "Removed words-per-day and the project names.").`;
21720
+ async function handleShareEdit(pool, body) {
21721
+ const sections = body.sections && typeof body.sections === "object" ? body.sections : null;
21722
+ const messages = Array.isArray(body.messages) ? body.messages : [];
21723
+ const last = messages.filter((m) => m.role === "user").pop();
21724
+ if (!sections || !last?.content?.trim()) return { status: 400, json: { error: "sections + a user instruction are required" } };
21725
+ const convo = messages.slice(-6).map((m) => `${m.role === "user" ? "USER" : "EDITOR"}: ${m.content}`).join("\n");
21726
+ const prompt = `CURRENT PAYLOAD (what would be shared right now):
21727
+ \`\`\`json
21728
+ ${JSON.stringify(sections, null, 1)}
21729
+ \`\`\`
21730
+
21731
+ CONVERSATION:
21732
+ ${convo}
21733
+
21734
+ Apply the user's LAST instruction and return the complete updated payload.`;
21735
+ const sandbox = await fs21.mkdtemp(path25.join(os5.tmpdir(), "share-edit-"));
21736
+ try {
21737
+ const run3 = await invokeAgent({ prompt, systemPrompt: SYSTEM3, addDir: sandbox, allowedTools: [], maxTurns: 2, timeoutMs: 12e4, model: "haiku" });
21738
+ const j = run3.json ?? null;
21739
+ const raw = j && typeof j === "object" && "sections" in j ? j.sections : j;
21740
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return { status: 502, json: { error: "the edit didn't produce a valid payload \u2014 try rephrasing" } };
21741
+ const out = {};
21742
+ for (const k of Object.keys(raw)) if (k in pool && raw[k] != null) out[k] = raw[k];
21743
+ const reply = (run3.raw ?? "").split("```").pop()?.trim().split("\n").filter(Boolean).pop()?.slice(0, 300) || "Done.";
21744
+ return { status: 200, json: { sections: out, reply } };
21745
+ } catch (e) {
21746
+ return { status: 502, json: { error: e.message.slice(0, 200) } };
21747
+ } finally {
21748
+ await fs21.rm(sandbox, { recursive: true, force: true }).catch(() => {
21749
+ });
21750
+ }
21751
+ }
21752
+
21753
+ // src/dataHome.ts
21754
+ import { existsSync as existsSync4 } from "fs";
21755
+ import os6 from "os";
21756
+ import path26 from "path";
21688
21757
  function electronAppData() {
21689
21758
  const candidates = [
21690
21759
  "/Applications/Polymath.app/Contents/Resources/standalone/.data",
21691
- path25.join(os5.homedir(), "Applications", "Polymath.app", "Contents", "Resources", "standalone", ".data")
21760
+ path26.join(os6.homedir(), "Applications", "Polymath.app", "Contents", "Resources", "standalone", ".data")
21692
21761
  ];
21693
- for (const c of candidates) if (existsSync4(path25.join(c, "coding"))) return c;
21762
+ for (const c of candidates) if (existsSync4(path26.join(c, "coding"))) return c;
21694
21763
  return null;
21695
21764
  }
21696
21765
  function resolveDataRoot(env = process.env, cwd = process.cwd(), appData = electronAppData) {
21697
21766
  const fromEnv = env.POLYMATH_DATA_DIR;
21698
- if (fromEnv && fromEnv.trim()) return path25.resolve(fromEnv);
21699
- const local = path25.join(cwd, ".data");
21767
+ if (fromEnv && fromEnv.trim()) return path26.resolve(fromEnv);
21768
+ const local = path26.join(cwd, ".data");
21700
21769
  if (existsSync4(local)) return local;
21701
21770
  const fromApp = appData();
21702
21771
  if (fromApp) return fromApp;
21703
- return path25.join(os5.homedir(), ".polymath-society", "data");
21772
+ return path26.join(os6.homedir(), ".polymath-society", "data");
21773
+ }
21774
+
21775
+ // src/manifest.ts
21776
+ import { promises as fs22 } from "fs";
21777
+ import path27 from "path";
21778
+ var MANIFEST_BASENAME = "exports-manifest.json";
21779
+ function manifestPath(dataDir) {
21780
+ return path27.join(dataDir, MANIFEST_BASENAME);
21781
+ }
21782
+ async function readManifest(dataDir) {
21783
+ const p = manifestPath(dataDir);
21784
+ let raw;
21785
+ try {
21786
+ raw = await fs22.readFile(p, "utf-8");
21787
+ } catch {
21788
+ return null;
21789
+ }
21790
+ let j;
21791
+ try {
21792
+ j = JSON.parse(raw);
21793
+ } catch (e) {
21794
+ throw new Error(`${p} exists but is not valid JSON (${e.message}) \u2014 fix or delete it, refusing to guess consent`);
21795
+ }
21796
+ const m = j;
21797
+ const approvedAt = typeof m.approvedAt === "string" ? m.approvedAt : (/* @__PURE__ */ new Date(0)).toISOString();
21798
+ const KNOWN = /* @__PURE__ */ new Set(["chatgpt", "claude", "notion", "obsidian", "unknown"]);
21799
+ const arr = (v) => Array.isArray(v) ? v.filter((f) => f && typeof f.path === "string" && KNOWN.has(f.kind)) : [];
21800
+ if (Array.isArray(m.exports)) {
21801
+ return { approvedAt, exports: { included: arr(m.exports), excluded: [] } };
21802
+ }
21803
+ const ex = m.exports ?? {};
21804
+ const ag = m.agents;
21805
+ const agArr = (v) => Array.isArray(v) ? v.filter((x) => x === "claude-code" || x === "codex") : [];
21806
+ return {
21807
+ approvedAt,
21808
+ ...ag ? { agents: { included: agArr(ag.included), excluded: agArr(ag.excluded) } } : {},
21809
+ exports: { included: arr(ex.included), excluded: arr(ex.excluded) }
21810
+ };
21811
+ }
21812
+
21813
+ // src/exportLinks.ts
21814
+ var EXPORT_LINKS = [
21815
+ {
21816
+ kind: "chatgpt",
21817
+ label: "ChatGPT",
21818
+ requestUrl: "https://chatgpt.com/#settings/DataControls",
21819
+ buttonPath: "Settings \u2192 Data controls \u2192 Export data \u2192 Confirm export",
21820
+ eta: "a few hours up to ~2 days \u2014 the download link arrives by email"
21821
+ },
21822
+ {
21823
+ kind: "claude",
21824
+ label: "claude.ai",
21825
+ requestUrl: "https://claude.ai/settings/data-privacy-controls",
21826
+ buttonPath: "Privacy settings \u2192 Export data",
21827
+ eta: "~10 minutes \u2014 the download link arrives by email"
21828
+ },
21829
+ {
21830
+ kind: "notion",
21831
+ label: "Notion",
21832
+ requestUrl: "https://www.notion.so/settings",
21833
+ buttonPath: "Settings \u2192 General \u2192 Export all workspace content (Markdown & CSV)",
21834
+ eta: "~15 minutes \u2014 the download link arrives by email"
21835
+ },
21836
+ {
21837
+ kind: "obsidian",
21838
+ label: "Obsidian",
21839
+ requestUrl: null,
21840
+ buttonPath: "local vault \u2014 no export needed; point the wizard at the folder",
21841
+ eta: ""
21842
+ }
21843
+ ];
21844
+
21845
+ // src/publicCodingPageApi.ts
21846
+ import { promises as fs28 } from "fs";
21847
+ import path32 from "path";
21848
+
21849
+ // ../../lib/agents/coding/publicPage.ts
21850
+ import { promises as fs27 } from "fs";
21851
+ import path31 from "path";
21852
+
21853
+ // ../../lib/agents/coding/docsDir.ts
21854
+ import { promises as fs23 } from "fs";
21855
+ import path28 from "path";
21856
+ async function writeScoreboard(codingDir, docs) {
21857
+ const gradesDir = path28.join(codingDir, "grades");
21858
+ let files = [];
21859
+ try {
21860
+ files = (await fs23.readdir(gradesDir)).filter((f) => f.endsWith(".json")).sort();
21861
+ } catch {
21862
+ return;
21863
+ }
21864
+ const byCriterion = /* @__PURE__ */ new Map();
21865
+ for (const f of files) {
21866
+ let g;
21867
+ try {
21868
+ g = JSON.parse(await fs23.readFile(path28.join(gradesDir, f), "utf8"));
21869
+ } catch {
21870
+ continue;
21871
+ }
21872
+ for (const c of g.criteria ?? []) {
21873
+ const s = c.score ?? c.bestEstimate;
21874
+ if (s == null) continue;
21875
+ const line = `L${s}${c.score == null ? "~" : ""} \xB7 ${(g.date ?? "").slice(0, 10)} \xB7 ${(g.title ?? g.sessionId ?? "").replace(/\n.*/s, "").slice(0, 60)} \xB7 ${g.sessionId} \u2014 ${(c.instance ?? "").slice(0, 110)}`;
21876
+ (byCriterion.get(c.key) ?? byCriterion.set(c.key, []).get(c.key)).push({ s, line });
21877
+ }
21878
+ }
21879
+ if (!byCriterion.size) return;
21880
+ const blocks = [...byCriterion.entries()].sort(([a], [b]) => a < b ? -1 : 1).map(
21881
+ ([key, rows]) => `## ${key}
21882
+ ${rows.sort((a, b) => b.s - a.s).map((r) => r.line).join("\n")}`
21883
+ );
21884
+ await fs23.writeFile(
21885
+ path28.join(docs, "_scoreboard.md"),
21886
+ `# Scoreboard \u2014 every graded session per criterion, best first.
21887
+ # Lx~ = estimate. Format: level \xB7 date \xB7 title \xB7 sessionId \u2014 graded instance.
21888
+
21889
+ ${blocks.join("\n\n")}
21890
+ `
21891
+ );
21892
+ }
21893
+ async function ensureCodingDocs(codingDir, sessions) {
21894
+ const docs = path28.join(codingDir, "docs");
21895
+ await fs23.mkdir(docs, { recursive: true });
21896
+ const user = sessions.filter((s) => s.sessionId && !s.duplicateOf && (!("klass" in s) || s.klass === "interactive"));
21897
+ let written = 0;
21898
+ for (const s of user) {
21899
+ const out = path28.join(docs, `${s.sessionId}.txt`);
21900
+ try {
21901
+ await fs23.access(out);
21902
+ continue;
21903
+ } catch {
21904
+ }
21905
+ const mat = await materializeSession2(s.file).catch(() => null);
21906
+ if (!mat?.text) continue;
21907
+ await fs23.writeFile(out, mat.text);
21908
+ written++;
21909
+ }
21910
+ const rows = user.sort((a, b) => (a.start ?? "") < (b.start ?? "") ? -1 : 1).map((s) => `${(s.start ?? "").slice(0, 10)} \xB7 ${(s.title ?? "").replace(/\n.*/s, "").slice(0, 80)} \xB7 ${s.sessionId}`);
21911
+ await fs23.writeFile(path28.join(docs, "_map.md"), rows.join("\n") + "\n");
21912
+ await writeScoreboard(codingDir, docs);
21913
+ if (written) console.log(`[docs] materialized ${written} new transcript(s) \u2192 ${docs}`);
21914
+ return docs;
21915
+ }
21916
+ function sourceLookupNote() {
21917
+ return `
21918
+
21919
+ SOURCE LOOKUP \u2014 you have Read/Grep/Glob over the working directory, which holds EVERYTHING known about this person's coding work. MAPS FIRST, grep second (the engine rule): docs/_scoreboard.md ranks every graded session per criterion, best first \u2014 one glance shows where the spikes/flair are, so start there when hunting peaks or checking whether a high mark is consistent or a one-off; day-digest.json is the day-by-day story of what they did in order \u2014 read the relevant days to get the arc before searching; docs/_map.md lists every session (date \xB7 title \xB7 sessionId). Then drill down: walkthroughs/<sessionId>.json is that session's phase-by-phase account (cheap, already distilled), grades/<sessionId>.json its graded findings with quotes, and docs/<sessionId>.txt the FULL transcript (">> " = the person, "[AI]" = the model) \u2014 the ground truth, read it when the distilled layers can't settle it. Other artifacts (flow.json, delegation/, expertise/, aggregate.json) are there too if a claim depends on them. The digest you were given is the spine \u2014 use lookups when a CLAIM-MOVING question cannot be settled from it (a moment you are about to headline, a promotion/depth call, an ambiguous engagement, whether detail was vision or boilerplate). VERIFY every verbatim quote you cite by finding it in the transcript first; if you cannot find it, do not use it. Look up what changes the output \u2014 do not re-read the whole corpus.`;
21920
+ }
21921
+
21922
+ // ../../lib/agents/coding/promptCache.ts
21923
+ import { createHash as createHash3 } from "crypto";
21924
+ import { promises as fs24 } from "fs";
21925
+ function promptSha(parts) {
21926
+ const h = createHash3("sha256");
21927
+ for (const p of parts) {
21928
+ h.update(p ?? "");
21929
+ h.update("\0");
21930
+ }
21931
+ return h.digest("hex").slice(0, 16);
21932
+ }
21933
+ async function reusableOutput(outPath, sha, valid, key = "promptSha") {
21934
+ try {
21935
+ const o = JSON.parse(await fs24.readFile(outPath, "utf8"));
21936
+ if (o[key] === sha && valid(o)) return o;
21937
+ } catch {
21938
+ }
21939
+ return null;
21940
+ }
21941
+
21942
+ // ../../lib/agents/coding/profile.ts
21943
+ import { promises as fs26 } from "fs";
21944
+ import path30 from "path";
21945
+
21946
+ // ../../lib/agents/coding/walkthrough.ts
21947
+ import { promises as fs25 } from "fs";
21948
+ import path29 from "path";
21949
+ var IDLE_CAP_MS = 12 * 6e4;
21950
+ async function readWalkthrough(outDir, sessionId) {
21951
+ return fs25.readFile(path29.join(outDir, `${sessionId}.json`), "utf8").then(JSON.parse).catch(() => null);
21952
+ }
21953
+
21954
+ // ../../lib/agents/coding/workstyle.ts
21955
+ var MIN3 = 6e4;
21956
+ var DAY_CUTOFF_H = 5;
21957
+ var FLOW_THRESHOLD_MIN = 30;
21958
+ var pct = (x) => `${Math.round(x * 100)}%`;
21959
+ var fmtN = (n) => n.toLocaleString("en-US");
21960
+ var round13 = (x) => Math.round(x * 10) / 10;
21961
+ var median = (xs) => xs.length ? [...xs].sort((a, b) => a - b)[Math.floor(xs.length / 2)] : 0;
21962
+ function fmtDur2(min) {
21963
+ return min >= 60 ? `${round13(min / 60)}h` : `${Math.round(min)}m`;
21964
+ }
21965
+ function clockLabel(h) {
21966
+ if (h === 0) return "midnight";
21967
+ if (h === 12) return "noon";
21968
+ return h < 12 ? `${h}am` : `${h - 12}pm`;
21969
+ }
21970
+ function pick(ladder, score, fallback = "not enough signal yet") {
21971
+ if (score == null) return fallback;
21972
+ const r = Math.round(score);
21973
+ const keys = Object.keys(ladder).map(Number).sort((a, b) => a - b);
21974
+ let chosen = keys[0];
21975
+ for (const k of keys) if (k <= r) chosen = k;
21976
+ return ladder[chosen];
21977
+ }
21978
+ var TASTE = {
21979
+ 4: "mostly rubber-stamps what the AI produces",
21980
+ 5: "an ordinary reviewer's bar",
21981
+ 6: "rejects the obviously-bad version and iterates toward something better",
21982
+ 7: "holds a real, specific quality bar and pushes past the AI's first answer",
21983
+ 8: "makes the call the AI couldn't reach on its own \u2014 opinionated, specific, vindicated",
21984
+ 9: "high, particular taste \u2014 names AI slop the moment it appears",
21985
+ 10: "brings a developed vision up front, and knows where not to impose it",
21986
+ 11: "world-class taste"
21987
+ };
21988
+ var DELEGATION = {
21989
+ 4: "still uneven \u2014 sometimes under-briefs work they had opinions about",
21990
+ 6: "roughly calibrated on when to do the thinking themselves vs. let the AI run",
21991
+ 8: "well-calibrated \u2014 front-loads the specifiable, reserves correction for genuine AI errors, lets rote run",
21992
+ 10: "near-perfect prompt economy \u2014 almost everything knowable is front-loaded"
21993
+ };
21994
+ var OUTCOMES = {
21995
+ 4: "sessions often wrap before a real capability lands",
21996
+ 6: "one real, confirmed thing shipped is the typical bar",
21997
+ 8: "a substantial, verified capability per session is routine",
21998
+ 10: "ships end-to-end-checked work that unblocks a lot downstream"
21999
+ };
22000
+ var GENERATIVE = {
22001
+ 4: "reaches workable answers with effort",
22002
+ 6: "real ideas and sound tradeoff reasoning",
22003
+ 7: "collapses messy situations to their essence and reasons from first principles",
22004
+ 8: "fast collapse to the core, plus its own approach and the exact fix",
22005
+ 9: "generates frameworks and reframes others wouldn't reach",
22006
+ 10: "out-reasons the tool \u2014 lands insights the AI itself missed"
22007
+ };
22008
+ var METACOGNITION = {
22009
+ 4: "notices friction but mostly just flags it",
22010
+ 6: "spots a bottleneck and adjusts how they work for the case at hand",
22011
+ 8: "treats their own throughput as an engineering problem \u2014 names recurring friction and restructures to kill it",
22012
+ 10: "re-architects the whole way of working mid-stream to get unstuck"
22013
+ };
22014
+ var EXPERTISE = {
22015
+ 2: "defers to the AI on system decisions \u2014 no specific knowledge surfaces",
22016
+ 4: "opinions are mostly generic best-practice the AI already had",
22017
+ 6: "knows their own stack well and corrects the AI on real systems facts",
22018
+ 8: "repeatedly catches what the AI misses with specific systems/tooling calls a junior wouldn't make",
22019
+ 9: "deep, specific directives about their own infra a junior wouldn't know",
22020
+ 10: "makes counter-intuitive, deep-operating-knowledge calls the AI would have gotten backwards"
22021
+ };
22022
+ var FRONTIER = {
22023
+ 2: "one session at a time, default settings",
22024
+ 4: "occasionally runs two things; no structural tooling",
22025
+ 6: "routinely parallel, queuing work ahead and organizing sessions",
22026
+ 8: "real leverage \u2014 overnight/remote runs, subagents, scripts that drive the AI",
22027
+ 10: "builds harnesses and workflows; loops and scales the AI across machines"
22028
+ };
22029
+ function flowRunsFromSlots(slots) {
22030
+ const fSlots = Object.keys(slots).map(Number).filter((s) => slots[String(s)] === "F").sort((a, b) => a - b);
22031
+ const runs = [];
22032
+ let start = null, prev = null;
22033
+ const flush = () => {
22034
+ if (start !== null && prev !== null) runs.push((prev - start + 1) * 10);
22035
+ };
22036
+ for (const s of fSlots) {
22037
+ if (prev !== null && s === prev + 1) {
22038
+ prev = s;
22039
+ } else {
22040
+ flush();
22041
+ start = s;
22042
+ prev = s;
22043
+ }
22044
+ }
22045
+ flush();
22046
+ return runs;
22047
+ }
22048
+ function buildFlow(runMin, ctx) {
22049
+ const sorted = [...runMin].sort((a, b) => a - b);
22050
+ const BUCKET_DEFS = [["\u2264 20m", 0, 30], ["30m\u20131h", 30, 60], ["1\u20132h", 60, 120], ["2h+", 120, Infinity]];
22051
+ const flowBlocks = BUCKET_DEFS.map(([label, lo, hi]) => {
22052
+ const inB = sorted.filter((x) => x >= lo && x < hi);
22053
+ return { label, lo, hi, count: inB.length, min: round13(inB.reduce((a, b) => a + b, 0)) };
22054
+ });
22055
+ const deepBlocks = sorted.filter((x) => x >= FLOW_THRESHOLD_MIN).length;
22056
+ const flowHoursPerDay = ctx.activeDays ? ctx.flowHoursTotal / ctx.activeDays : 0;
22057
+ const deepBlocksPerDay = ctx.activeDays ? deepBlocks / ctx.activeDays : 0;
22058
+ const flowCadence = ctx.flowFraction >= 0.45 && flowHoursPerDay >= 1.5 ? "frequent" : ctx.flowFraction < 0.18 ? "rare" : "interspersed";
22059
+ const longest = sorted.length ? Math.max(...sorted) : 0;
22060
+ const med2 = median(sorted);
22061
+ const flowBullets = [];
22062
+ flowBullets.push(`${round13(ctx.flowHoursTotal)}h of genuine flow over ${ctx.activeDays} working days \u2014 about ${round13(flowHoursPerDay)}h a day.`);
22063
+ flowBullets.push(`${pct(ctx.flowFraction)} of active working time is true flow (rapid rhythm or dictation); the rest is thinking-pauses and switching.`);
22064
+ if (flowCadence === "frequent") {
22065
+ flowBullets.push(`Deep flow is a daily norm \u2014 ${deepBlocks} runs of ${FLOW_THRESHOLD_MIN}m+, longest ${fmtDur2(longest)}; highly productive once locked in.`);
22066
+ } else if (flowCadence === "interspersed") {
22067
+ flowBullets.push(`Flow comes in solid bursts \u2014 ${med2}\u2013${longest}m at a time, ${deepBlocks} runs of ${FLOW_THRESHOLD_MIN}m+ \u2014 interspersed with pauses and rapid context-switching across parallel sessions.`);
22068
+ } else {
22069
+ flowBullets.push(`Flow is sporadic \u2014 mostly short focused windows rather than long unbroken runs.`);
22070
+ }
22071
+ if (ctx.peakFlowDay) flowBullets.push(`Best day: ${round13(ctx.peakFlowDay.hours)}h in flow (${pct(ctx.peakFlowDay.fraction)} of that day's working time).`);
22072
+ return {
22073
+ flowSource: ctx.source,
22074
+ flowThresholdMin: FLOW_THRESHOLD_MIN,
22075
+ flowBlocks,
22076
+ blockCount: sorted.length,
22077
+ deepBlocks,
22078
+ longestBlockMin: round13(longest),
22079
+ medianBlockMin: round13(med2),
22080
+ flowFraction: Math.round(ctx.flowFraction * 100) / 100,
22081
+ flowHoursTotal: round13(ctx.flowHoursTotal),
22082
+ flowHoursPerDay: round13(flowHoursPerDay),
22083
+ deepBlocksPerDay: round13(deepBlocksPerDay),
22084
+ flowCadence,
22085
+ peakFlowDay: ctx.peakFlowDay,
22086
+ flowBullets
22087
+ };
22088
+ }
22089
+ function computeWorkstyle(inp) {
22090
+ const { tz, idleGapMin } = inp;
22091
+ const idleGapMs = idleGapMin * MIN3;
22092
+ const all = [];
22093
+ for (const day of inp.dayChart) for (const c of day.conversations) for (const iv of c.intervals) all.push(iv);
22094
+ all.sort((a, b) => a[0] - b[0]);
22095
+ const parts = (ms2) => {
22096
+ const [h, m2, s] = new Date(ms2).toLocaleTimeString("en-GB", { timeZone: tz, hour12: false }).split(":").map(Number);
22097
+ return { h, m: m2, s };
22098
+ };
22099
+ const localDate3 = (ms2) => new Date(ms2).toLocaleDateString("en-CA", { timeZone: tz });
22100
+ const sinceCutoff = (ms2) => {
22101
+ const { h, m: m2 } = parts(ms2);
22102
+ let x = (h - DAY_CUTOFF_H) * 60 + m2;
22103
+ if (x < 0) x += 1440;
22104
+ return x;
22105
+ };
22106
+ const logicalDate = (ms2) => localDate3(ms2 - DAY_CUTOFF_H * 36e5);
22107
+ const fromCutoff = (x) => {
22108
+ const t = (x + DAY_CUTOFF_H * 60) % 1440;
22109
+ return `${String(Math.floor(t / 60)).padStart(2, "0")}:${String(Math.round(t % 60)).padStart(2, "0")}`;
22110
+ };
22111
+ const hourMin = new Array(24).fill(0);
22112
+ for (const [a, b] of all) {
22113
+ let cur = a;
22114
+ while (cur < b) {
22115
+ const { h, m: m2, s } = parts(cur);
22116
+ const msToNextHour = ((59 - m2) * 60 + (60 - s)) * 1e3;
22117
+ const segEnd = Math.min(b, cur + msToNextHour);
22118
+ hourMin[h] += (segEnd - cur) / MIN3;
22119
+ cur = segEnd;
22120
+ }
22121
+ }
22122
+ const hourly = hourMin.map((min, hour) => ({ hour, min: round13(min) }));
22123
+ const totalMin = hourMin.reduce((a, b) => a + b, 0) || 1;
22124
+ const sumHours = (hs) => hs.reduce((a, h) => a + hourMin[h], 0);
22125
+ const shares = {
22126
+ morning: sumHours([5, 6, 7, 8, 9, 10, 11]) / totalMin,
22127
+ afternoon: sumHours([12, 13, 14, 15, 16, 17]) / totalMin,
22128
+ evening: sumHours([18, 19, 20, 21]) / totalMin,
22129
+ lateNight: sumHours([22, 23, 0, 1, 2, 3, 4]) / totalMin
22130
+ };
22131
+ const peakHours = [...hourMin.keys()].filter((h) => hourMin[h] > 0).sort((a, b) => hourMin[b] - hourMin[a]).slice(0, 3);
22132
+ const peak0 = peakHours[0] ?? 12;
22133
+ const isNightOwl = shares.lateNight >= 0.2 || peak0 >= 22 || peak0 <= 3;
22134
+ let lunchDip = null;
22135
+ const dipFloor = totalMin / 40;
22136
+ for (const h of [12, 13, 14]) {
22137
+ if (hourMin[h] < hourMin[h - 1] * 0.7 && hourMin[h] < hourMin[h + 1] * 0.7 && hourMin[h - 1] > dipFloor && hourMin[h + 1] > dipFloor) {
22138
+ if (lunchDip == null || hourMin[h] < hourMin[lunchDip]) lunchDip = h;
22139
+ }
22140
+ }
22141
+ const env = /* @__PURE__ */ new Map();
22142
+ for (const [a, b] of all) {
22143
+ const ld = logicalDate(a);
22144
+ let s = sinceCutoff(a), e = sinceCutoff(b);
22145
+ if (e < s) e += 1440;
22146
+ const cur = env.get(ld) ?? { first: Infinity, last: -Infinity };
22147
+ cur.first = Math.min(cur.first, s);
22148
+ cur.last = Math.max(cur.last, e);
22149
+ env.set(ld, cur);
22150
+ }
22151
+ const startsM = [...env.values()].map((v) => v.first);
22152
+ const endsM = [...env.values()].map((v) => v.last);
22153
+ const daysObserved = env.size;
22154
+ const typicalStart = startsM.length ? fromCutoff(median(startsM)) : "\u2014";
22155
+ const typicalEnd = endsM.length ? fromCutoff(median(endsM)) : "\u2014";
22156
+ const earliestStart = startsM.length ? fromCutoff(Math.min(...startsM)) : "\u2014";
22157
+ const latestEnd = endsM.length ? fromCutoff(Math.max(...endsM)) : "\u2014";
22158
+ let flow;
22159
+ if (inp.flow && inp.flow.days.length) {
22160
+ const runs = [];
22161
+ for (const d of inp.flow.days) runs.push(...flowRunsFromSlots(d.slots));
22162
+ const peak = [...inp.flow.days].sort((a, b) => b.flowHours - a.flowHours)[0];
22163
+ flow = buildFlow(runs, {
22164
+ source: "flow.json",
22165
+ activeDays: inp.flow.totals.activeDays,
22166
+ flowFraction: inp.flow.totals.flowFraction,
22167
+ flowHoursTotal: inp.flow.totals.flowSlots / 6,
22168
+ peakFlowDay: peak ? { date: peak.date, hours: peak.flowHours, fraction: peak.flowFraction } : null
22169
+ });
22170
+ } else {
22171
+ const merged = [];
22172
+ for (const [a, b] of all) {
22173
+ const last = merged[merged.length - 1];
22174
+ if (last && a <= last[1] + idleGapMs) last[1] = Math.max(last[1], b);
22175
+ else merged.push([a, b]);
22176
+ }
22177
+ const blocks = merged.map(([a, b]) => (b - a) / MIN3);
22178
+ const totalBlockMin = blocks.reduce((a, b) => a + b, 0) || 1;
22179
+ const deepMin = blocks.filter((x) => x >= FLOW_THRESHOLD_MIN).reduce((a, b) => a + b, 0);
22180
+ flow = buildFlow(blocks, {
22181
+ source: "intervals",
22182
+ activeDays: daysObserved,
22183
+ flowFraction: deepMin / totalBlockMin,
22184
+ flowHoursTotal: deepMin / 60,
22185
+ peakFlowDay: null
22186
+ });
22187
+ }
22188
+ const m = (k) => inp.criteriaMean[k] ?? { mean: null, n: 0 };
22189
+ const judgment = [
22190
+ { key: "taste", label: "Taste", score: m("taste").mean, n: m("taste").n },
22191
+ { key: "delegation", label: "Calibration", score: m("delegation").mean, n: m("delegation").n },
22192
+ { key: "generative", label: "Under ambiguity", score: m("generative").mean, n: m("generative").n },
22193
+ { key: "metacognition", label: "Self-direction", score: m("metacognition").mean, n: m("metacognition").n },
22194
+ { key: "outcomes", label: "Outcomes", score: m("outcomes").mean, n: m("outcomes").n }
22195
+ ];
22196
+ const rhythmBullets = [];
22197
+ const lateTail = /^(0[0-5]|2[3])/.test(latestEnd);
22198
+ rhythmBullets.push(`Works roughly ${typicalStart}\u2013${typicalEnd} on a typical day${lateTail ? `, with sessions running as late as ${latestEnd}` : ""}.`);
22199
+ if (isNightOwl) {
22200
+ rhythmBullets.push(`A night owl \u2014 ${pct(shares.lateNight)} of the work lands after 10pm or in the small hours.`);
22201
+ } else if (shares.morning >= shares.afternoon && shares.morning >= shares.evening) {
22202
+ rhythmBullets.push(`An early bird \u2014 most of the work happens before noon (${pct(shares.morning)} of active time).`);
22203
+ } else if (shares.afternoon >= shares.evening) {
22204
+ rhythmBullets.push(`An afternoon worker \u2014 ${pct(shares.afternoon)} of active time falls between noon and 6pm, only ${pct(shares.lateNight)} in the small hours.`);
22205
+ } else {
22206
+ rhythmBullets.push(`An evening worker \u2014 work concentrates after 6pm (${pct(shares.evening)} of active time).`);
22207
+ }
22208
+ {
22209
+ let txt = `Productivity peaks around ${clockLabel(peak0)}`;
22210
+ const p1 = peakHours.find((h) => Math.abs(h - peak0) >= 3);
22211
+ if (p1 != null) txt += `, with a second push around ${clockLabel(p1)}`;
22212
+ rhythmBullets.push(txt + ".");
22213
+ }
22214
+ if (lunchDip != null) rhythmBullets.push(`Takes a visible midday breather around ${clockLabel(lunchDip)}.`);
22215
+ const judgmentBullets = [];
22216
+ judgmentBullets.push(`Judgment & taste \u2014 ${pick(TASTE, m("taste").mean)}; on delegation, ${pick(DELEGATION, m("delegation").mean)}.`);
22217
+ {
22218
+ const cad = inp.outcomeCadence ? `ships something meaningful ~${round13(inp.outcomeCadence.perWeek)}\xD7/week (${pct(inp.outcomeCadence.fraction)} of working days), and ` : "";
22219
+ judgmentBullets.push(`High-outcome work \u2014 ${cad}${pick(OUTCOMES, m("outcomes").mean)}.`);
22220
+ }
22221
+ judgmentBullets.push(`Under ambiguity \u2014 ${pick(GENERATIVE, m("generative").mean)}; strongest where there's no ground truth and the task is to invent what to measure.`);
22222
+ judgmentBullets.push(`Prioritizing the work \u2014 ${pick(METACOGNITION, m("metacognition").mean)}.`);
22223
+ const exp = m("expertise").mean, cog = m("generative").mean, fr = m("frontier").mean;
22224
+ const sig = inp.signals;
22225
+ let archetype;
22226
+ if (exp == null || cog == null) {
22227
+ archetype = "Not enough graded signal yet to place the technical profile.";
22228
+ } else if (cog - exp >= 1.2) {
22229
+ archetype = "Cognition-forward \u2014 reasoning runs ahead of hands-on systems depth: thinks like a senior, with some stack-specific knowledge still leveling up.";
22230
+ } else if (exp - cog >= 1.2) {
22231
+ archetype = "Specialist \u2014 deep, specific systems and architecture knowledge is the anchor, ahead of raw abstraction.";
22232
+ } else if (exp >= 8 && cog >= 8) {
22233
+ archetype = "Deep and sharp \u2014 strong systems knowledge matched by top-tier reasoning.";
22234
+ } else {
22235
+ archetype = "Balanced operator \u2014 solid systems knowledge and reasoning move in step, both clearly competent.";
22236
+ }
22237
+ const gap = exp != null && cog != null ? cog > exp + 0.8 ? "noticeably ahead of the stack-specific knowledge" : exp > cog + 0.8 ? "with the systems depth running slightly ahead" : "roughly in step with the systems depth" : "";
22238
+ const sigBits = [];
22239
+ if (sig.maxConcurrency >= 2) sigBits.push(`up to \xD7${sig.maxConcurrency} sessions at once`);
22240
+ if (sig.subagents > 0) sigBits.push(`${fmtN(sig.subagents)} subagent spawns`);
22241
+ if (sig.queueOps > 0) sigBits.push(`${fmtN(sig.queueOps)} queue-ahead ops`);
22242
+ if (sig.mcpTools > 0) sigBits.push(`${sig.mcpTools} MCP tool${sig.mcpTools > 1 ? "s" : ""}`);
22243
+ const sigSentence = sigBits.length ? sigBits.join(", ") : "mostly single-session work";
22244
+ const experienced = sig.maxConcurrency >= 3 && (sig.queueOps > 50 || sig.subagents > 0 || sig.mcpTools > 0);
22245
+ const technical = {
22246
+ expertise: exp,
22247
+ cognition: cog,
22248
+ frontier: fr,
22249
+ signals: sig,
22250
+ archetype,
22251
+ bullets: [
22252
+ `Systems & architecture \u2014 ${pick(EXPERTISE, exp)}.`,
22253
+ `Raw cognition \u2014 ${pick(GENERATIVE, cog)}${gap ? `, ${gap}` : ""}.`,
22254
+ `Agent fluency \u2014 ${experienced ? "clearly an experienced agent operator; " : ""}${pick(FRONTIER, fr)} (${sigSentence}).`
22255
+ ]
22256
+ };
22257
+ return {
22258
+ tz,
22259
+ daysObserved,
22260
+ activeHours: round13(totalMin / 60),
22261
+ typicalStart,
22262
+ typicalEnd,
22263
+ earliestStart,
22264
+ latestEnd,
22265
+ hourly,
22266
+ peakHours,
22267
+ shares,
22268
+ isNightOwl,
22269
+ lunchDip,
22270
+ rhythmBullets,
22271
+ ...flow,
22272
+ judgment,
22273
+ judgmentBullets,
22274
+ technical
22275
+ };
22276
+ }
22277
+
22278
+ // ../../lib/agents/coding/benchmarks.ts
22279
+ var BEST = DEFAULT_CALIBRATION.codingBenchmarks;
22280
+
22281
+ // ../../lib/agents/coding/profile.ts
22282
+ var ROOT2 = process.cwd();
22283
+ var CODING_DIR2 = process.env.POLYMATH_DATA_DIR ? path30.join(process.env.POLYMATH_DATA_DIR, "coding") : path30.join(ROOT2, ".data", "coding");
22284
+ var UMETA = /* @__PURE__ */ new Map();
22285
+ async function userMeta(sessionId, file2) {
22286
+ const hit = UMETA.get(sessionId);
22287
+ if (hit) return hit;
22288
+ const msgs = await extractUserMessages(file2).catch(() => []);
22289
+ const meta = msgs.map((m) => ({ t: m.t, words: typedWords(m.text, m.gapMs), key: m.uuid ?? `${m.t}|${m.text}` }));
22290
+ UMETA.set(sessionId, meta);
22291
+ return meta;
22292
+ }
22293
+ var GRADES2 = path30.join(CODING_DIR2, "grades");
22294
+ async function readJson2(p, fallback) {
22295
+ return fs26.readFile(p, "utf8").then((s) => JSON.parse(s)).catch(() => fallback);
22296
+ }
22297
+ function cadenceScore(fraction, multiRatio) {
22298
+ let s;
22299
+ if (fraction >= 0.85) s = 10;
22300
+ else if (fraction >= 0.6) s = 9;
22301
+ else if (fraction >= 0.45) s = 8;
22302
+ else if (fraction >= 0.3) s = 7;
22303
+ else if (fraction >= 0.2) s = 6;
22304
+ else if (fraction >= 0.12) s = 5;
22305
+ else if (fraction >= 0.06) s = 4;
22306
+ else s = 3;
22307
+ if (s >= 10 && multiRatio >= 0.3) s = 11;
22308
+ return s;
22309
+ }
22310
+ function distOf3(takes) {
22311
+ const vals = takes.map((t) => t.score ?? t.best).filter((v) => v != null);
22312
+ const n = vals.length;
22313
+ if (!n) return { n: 0, observed: 0, mean: null, median: null, hist: [] };
22314
+ const observed = takes.filter((t) => t.score != null).length;
22315
+ const mean2 = Math.round(vals.reduce((a, b) => a + b, 0) / n * 100) / 100;
22316
+ const sorted = [...vals].sort((a, b) => a - b);
22317
+ const median2 = n % 2 ? sorted[(n - 1) / 2] : (sorted[n / 2 - 1] + sorted[n / 2]) / 2;
22318
+ const h = /* @__PURE__ */ new Map();
22319
+ for (const v of vals) h.set(Math.round(v), (h.get(Math.round(v)) ?? 0) + 1);
22320
+ const hist = [...h.keys()].sort((a, b) => b - a).map((level) => ({ level, count: h.get(level) }));
22321
+ return { n, observed, mean: mean2, median: median2, hist };
22322
+ }
22323
+ async function compileCodingProfile() {
22324
+ const allRecords = await readJson2(path30.join(CODING_DIR2, "sessions.json"), []);
22325
+ const dupIds = new Set(allRecords.filter((s) => s.duplicateOf).map((s) => s.sessionId));
22326
+ const sessions = allRecords.filter((s) => !s.duplicateOf);
22327
+ const conc = await readJson2(path30.join(CODING_DIR2, "concurrency.json"), null);
22328
+ const renames = await readJson2(path30.join(CODING_DIR2, "renames.json"), []);
22329
+ const byId = new Map(sessions.map((s) => [s.sessionId, s]));
22330
+ let gradeFiles = [];
22331
+ try {
22332
+ gradeFiles = (await fs26.readdir(GRADES2)).filter((f) => f.endsWith(".json"));
22333
+ } catch {
22334
+ }
22335
+ const grades = [];
22336
+ for (const f of gradeFiles) {
22337
+ const g = await readJson2(path30.join(GRADES2, f), null);
22338
+ if (g && byId.has(g.sessionId)) grades.push(g);
22339
+ }
22340
+ const takesByCriterion = /* @__PURE__ */ new Map();
22341
+ for (const g of grades) {
22342
+ for (const c of g.criteria) {
22343
+ const t = {
22344
+ sessionId: g.sessionId,
22345
+ title: g.title || byId.get(g.sessionId)?.title || g.sessionId,
22346
+ date: (g.date || "").slice(0, 10),
22347
+ score: c.score,
22348
+ best: c.bestEstimate,
22349
+ instance: c.instance,
22350
+ why: c.why,
22351
+ quotes: c.quotes,
22352
+ confidence: c.confidence
22353
+ };
22354
+ if (c.score == null && c.bestEstimate == null) continue;
22355
+ (takesByCriterion.get(c.key) ?? takesByCriterion.set(c.key, []).get(c.key)).push(t);
22356
+ }
22357
+ }
22358
+ const criteria = CODING_CRITERIA.map((c) => {
22359
+ const takes = (takesByCriterion.get(c.key) ?? []).sort((a, b) => (b.score ?? b.best ?? 0) - (a.score ?? a.best ?? 0));
22360
+ return { key: c.key, label: c.label, graded: c.graded, definition: c.definition, dist: distOf3(takes), takes };
22361
+ });
22362
+ const tz = conc?.tz || "America/Los_Angeles";
22363
+ const localDate3 = (iso) => iso ? new Date(Date.parse(iso)).toLocaleDateString("en-CA", { timeZone: tz }) : "";
22364
+ let outcomeCadence = null;
22365
+ {
22366
+ const activeDates = new Set(sessions.map((s) => localDate3(s.start)).filter(Boolean));
22367
+ const meaningfulPerDay = /* @__PURE__ */ new Map();
22368
+ for (const g of grades) {
22369
+ const date = localDate3(byId.get(g.sessionId)?.start || g.date || "");
22370
+ if (!date) continue;
22371
+ const out = g.criteria.find((c) => c.key === "outcomes")?.score ?? null;
22372
+ const meta = g.criteria.find((c) => c.key === "metacognition")?.score ?? null;
22373
+ if (out != null && out >= 6 || meta != null && meta >= 8) meaningfulPerDay.set(date, (meaningfulPerDay.get(date) ?? 0) + 1);
22374
+ }
22375
+ const activeDays = activeDates.size;
22376
+ const meaningfulDays = meaningfulPerDay.size;
22377
+ const multiMeaningfulDays = [...meaningfulPerDay.values()].filter((n) => n >= 2).length;
22378
+ if (activeDays > 0) {
22379
+ const dates2 = [...activeDates].sort();
22380
+ const spanDays = Math.max(1, (Date.parse(dates2[dates2.length - 1]) - Date.parse(dates2[0])) / 864e5 + 1);
22381
+ const fraction = meaningfulDays / activeDays;
22382
+ outcomeCadence = {
22383
+ activeDays,
22384
+ meaningfulDays,
22385
+ multiMeaningfulDays,
22386
+ fraction: Math.round(fraction * 100) / 100,
22387
+ perWeek: Math.round(meaningfulDays / (spanDays / 7) * 10) / 10,
22388
+ score: cadenceScore(fraction, meaningfulDays ? multiMeaningfulDays / meaningfulDays : 0)
22389
+ };
22390
+ }
22391
+ }
22392
+ const perDayMeta = new Map((conc?.perDay ?? []).map((d) => [d.date, d]));
22393
+ const gradeById = new Map(grades.map((g) => [g.sessionId, g]));
22394
+ const dayMap = /* @__PURE__ */ new Map();
22395
+ for (const s of sessions) {
22396
+ const date = localDate3(s.start);
22397
+ if (!date) continue;
22398
+ let day = dayMap.get(date);
22399
+ if (!day) {
22400
+ const m = perDayMeta.get(date);
22401
+ day = { date, peakConcurrency: m?.peakConcurrency ?? 1, sessionsStarted: m?.sessionsStarted ?? 0, activeMin: m?.activeMin ?? 0, sessions: [] };
22402
+ dayMap.set(date, day);
22403
+ }
22404
+ const g = gradeById.get(s.sessionId);
22405
+ const findings = {};
22406
+ if (g) {
22407
+ for (const c of g.criteria) if (c.instance && (c.score != null || c.bestEstimate != null)) findings[c.key] = { score: c.score ?? c.bestEstimate, instance: c.instance, why: c.why || "", quotes: c.quotes || [] };
22408
+ }
22409
+ day.sessions.push({ sessionId: s.sessionId, title: s.title, gradable: !!g, findings });
22410
+ }
22411
+ const timeline = [...dayMap.values()].sort((a, b) => b.date.localeCompare(a.date));
22412
+ const levelHist = conc?.levelHistogramMin ?? {};
22413
+ const sumFrom = (min) => Object.entries(levelHist).filter(([k]) => Number(k) >= min).reduce((a, [, v]) => a + v, 0);
22414
+ const topWindows = (conc?.segments ?? []).slice().sort((a, b) => b.level - a.level || b.minutes - a.minutes).slice(0, 12).map((s) => ({ start: s.start, end: s.end, minutes: s.minutes, level: s.level, titles: [...new Set(s.sessions.map((x) => x.title))] }));
22415
+ const parallelism = {
22416
+ maxConcurrency: conc?.maxConcurrency ?? 0,
22417
+ levelHistogramMin: levelHist,
22418
+ minutesAt2plus: Math.round(sumFrom(2)),
22419
+ minutesAt6plus: Math.round(sumFrom(6)),
22420
+ topWindows,
22421
+ queueOps: sessions.reduce((a, s) => a + s.queueOps, 0),
22422
+ renames,
22423
+ renamedSessions: sessions.filter((s) => s.renamed).length,
22424
+ tz: conc?.tz ?? "",
22425
+ idleGapMin: conc?.idleGapMin ?? 5
22426
+ };
22427
+ const dates = sessions.map((s) => s.start).filter(Boolean).sort();
22428
+ const models = /* @__PURE__ */ new Map();
22429
+ for (const s of sessions) for (const [m, n] of Object.entries(s.models)) models.set(m, (models.get(m) ?? 0) + n);
22430
+ const topDays = (conc?.perDay ?? []).slice().sort((a, b) => b.activeMin - a.activeMin).slice(0, 12).map((d) => ({ date: d.date, activeMin: d.activeMin, sessionsStarted: d.sessionsStarted, peakConcurrency: d.peakConcurrency, firstStart: d.firstStart, lastEnd: d.lastEnd, spanMin: d.spanMin }));
22431
+ const lateNight = (conc?.perDay ?? []).filter((d) => {
22432
+ const h = Number(d.firstStart.slice(0, 2));
22433
+ const e = Number(d.lastEnd.slice(0, 2));
22434
+ return e >= 22 || e < 5 || h < 5;
22435
+ }).length;
22436
+ const throughput = {
22437
+ sessions: sessions.length,
22438
+ activeHours: Math.round(sessions.reduce((a, s) => a + s.activeMin, 0) / 60 * 10) / 10,
22439
+ dateRange: { from: (dates[0] || "").slice(0, 10), to: (dates[dates.length - 1] || "").slice(0, 10) },
22440
+ topDays,
22441
+ toolCalls: sessions.reduce((a, s) => a + s.toolCalls, 0),
22442
+ subagents: sessions.reduce((a, s) => a + s.subagentSpawns, 0),
22443
+ models: Object.fromEntries([...models.entries()].sort((a, b) => b[1] - a[1])),
22444
+ lateNightSessions: lateNight
22445
+ };
22446
+ const agglomeration = await readJson2(path30.join(CODING_DIR2, "agglomerate.json"), null);
22447
+ const dayDigest = await readJson2(path30.join(CODING_DIR2, "day-digest.json"), null);
22448
+ if (dayDigest?.days) dayDigest.days = dayDigest.days.map((day) => {
22449
+ const convs = day.conversations.filter((c) => byId.has(c.sessionId));
22450
+ return { ...day, conversations: convs, conversationsTouched: convs.length, totalMessages: convs.reduce((a, c) => a + c.messageCount, 0) };
22451
+ }).filter((day) => day.conversations.length > 0);
22452
+ const dayChart = await readJson2(path30.join(CODING_DIR2, "day-chart.json"), []);
22453
+ const intervalsBy = /* @__PURE__ */ new Map();
22454
+ const titleBy = /* @__PURE__ */ new Map();
22455
+ const fileById = new Map(sessions.map((s) => [s.sessionId, s.file]));
22456
+ for (const day of dayChart) for (const c of day.conversations) {
22457
+ const arr = intervalsBy.get(c.sessionId) ?? [];
22458
+ arr.push(...c.intervals);
22459
+ intervalsBy.set(c.sessionId, arr);
22460
+ titleBy.set(c.sessionId, c.title);
22461
+ }
22462
+ const listOverlapMs = (A, B) => {
22463
+ let ms2 = 0;
22464
+ for (const [a0, a1] of A) for (const [b0, b1] of B) {
22465
+ const lo = Math.max(a0, b0), hi = Math.min(a1, b1);
22466
+ if (hi > lo) ms2 += hi - lo;
22467
+ }
22468
+ return ms2;
22469
+ };
22470
+ const walkthroughs = {};
22471
+ const wdir = path30.join(CODING_DIR2, "walkthroughs");
22472
+ for (const f of await fs26.readdir(wdir).catch(() => [])) {
22473
+ if (!f.endsWith(".json")) continue;
22474
+ const w = await readWalkthrough(wdir, f.replace(".json", ""));
22475
+ if (!w || !byId.has(w.sessionId)) continue;
22476
+ const own = intervalsBy.get(w.sessionId) ?? [];
22477
+ const ownTitle = titleBy.get(w.sessionId) ?? w.title;
22478
+ const um = await userMeta(w.sessionId, fileById.get(w.sessionId) ?? "");
22479
+ const sessionEnd = own.length ? Math.max(...own.map((iv) => iv[1])) : w.phases[w.phases.length - 1]?.fromMs ?? 0;
22480
+ const phases = w.phases.map((p, i) => {
22481
+ const w0 = p.fromMs;
22482
+ const w1 = i + 1 < w.phases.length ? w.phases[i + 1].fromMs : sessionEnd + 1;
22483
+ const inPhase = um.filter((m) => m.t >= w0 && m.t < w1);
22484
+ const userPrompts = inPhase.length;
22485
+ const words = inPhase.reduce((s, m) => s + m.words, 0);
22486
+ const wordsPerMsg = userPrompts ? Math.round(words / userPrompts) : 0;
22487
+ const ownInWin = own.map(([a, b]) => [Math.max(a, w0), Math.min(b, w1)]).filter(([a, b]) => b > a);
22488
+ const durationMin = Math.round(ownInWin.reduce((s, [a, b]) => s + (b - a), 0) / 6e4);
22489
+ const byTitle = /* @__PURE__ */ new Map();
22490
+ for (const [sid, ivs] of intervalsBy) {
22491
+ if (sid === w.sessionId) continue;
22492
+ const t = titleBy.get(sid) ?? "(untitled)";
22493
+ if (t === ownTitle) continue;
22494
+ const ms2 = listOverlapMs(ownInWin, ivs);
22495
+ if (ms2 >= 6e4) byTitle.set(t, (byTitle.get(t) ?? 0) + ms2);
22496
+ }
22497
+ const parallel = [...byTitle.entries()].map(([title, ms2]) => ({ title, overlapMin: Math.round(ms2 / 6e4) })).sort((a, b) => b.overlapMin - a.overlapMin).slice(0, 6);
22498
+ return { what: p.what, durationMin, fromMs: w0, toMs: w1, userPrompts, words, wordsPerMsg, parallel };
22499
+ });
22500
+ walkthroughs[w.sessionId] = { phases, totalActiveMin: w.totalActiveMin, userTurns: w.userTurns };
22501
+ }
22502
+ const rubric2 = Object.fromEntries(
22503
+ CODING_CRITERIA.filter((c) => c.graded).map((c) => [c.key, { label: c.label, definition: c.definition, read: c.read, rungs: c.rungs ?? [] }])
22504
+ );
22505
+ await Promise.all(sessions.map((s) => userMeta(s.sessionId, s.file)));
22506
+ const wordsByDay = {};
22507
+ const seenMsg = /* @__PURE__ */ new Set();
22508
+ for (const s of sessions) for (const m of UMETA.get(s.sessionId) ?? []) {
22509
+ if (seenMsg.has(m.key)) continue;
22510
+ seenMsg.add(m.key);
22511
+ const d = new Date(m.t).toLocaleDateString("en-CA", { timeZone: tz });
22512
+ wordsByDay[d] = (wordsByDay[d] ?? 0) + m.words;
22513
+ }
22514
+ const dayThroughput = {};
22515
+ for (const [d, n] of Object.entries(wordsByDay)) dayThroughput[d] = throughputScore(n);
22516
+ const tCeiling = throughputCeiling(Object.values(wordsByDay));
22517
+ const criteriaMean = {};
22518
+ for (const c of criteria) criteriaMean[c.key] = { mean: c.dist.mean, n: c.dist.n };
22519
+ const flow = await readJson2(path30.join(CODING_DIR2, "flow.json"), null);
22520
+ const focus = await readJson2(path30.join(CODING_DIR2, "focus.json"), null);
22521
+ const expertiseMap = await readJson2(path30.join(CODING_DIR2, "expertise.json"), null);
22522
+ const frontierArsenal = await readJson2(path30.join(CODING_DIR2, "frontier.json"), null);
22523
+ const frontierDetail = await readJson2(path30.join(CODING_DIR2, "frontier-detail.json"), null);
22524
+ const coaching = await readJson2(path30.join(CODING_DIR2, "coaching.json"), null);
22525
+ const gap = await readJson2(path30.join(CODING_DIR2, "gap.json"), null);
22526
+ const aggregate = await readJson2(path30.join(CODING_DIR2, "aggregate.json"), null);
22527
+ const delegationRollup = await readJson2(path30.join(CODING_DIR2, "delegation-rollup.json"), null);
22528
+ const gapDist = await readJson2(path30.join(CODING_DIR2, "gap-dist.json"), null);
22529
+ const projects = await readJson2(path30.join(CODING_DIR2, "projects.json"), null);
22530
+ const workstyle = computeWorkstyle({
22531
+ dayChart,
22532
+ tz,
22533
+ idleGapMin: conc?.idleGapMin ?? 5,
22534
+ criteriaMean,
22535
+ outcomeCadence: outcomeCadence ? { perWeek: outcomeCadence.perWeek, fraction: outcomeCadence.fraction } : null,
22536
+ flow,
22537
+ signals: {
22538
+ maxConcurrency: parallelism.maxConcurrency,
22539
+ subagents: throughput.subagents,
22540
+ queueOps: parallelism.queueOps,
22541
+ mcpTools: new Set(sessions.flatMap((s) => s.mcpTools ?? [])).size,
22542
+ parallelHours: Math.round(parallelism.minutesAt2plus / 60 * 10) / 10
22543
+ }
22544
+ });
22545
+ const pctOf = (sc) => {
22546
+ if (sc == null) return null;
22547
+ const label = bandLabel(sc, DEFAULT_CALIBRATION);
22548
+ const m = /([\d.]+)\s*%/.exec(label);
22549
+ return m ? 100 - parseFloat(m[1]) : null;
22550
+ };
22551
+ const ceilOf = (key) => {
22552
+ const c = criteria.find((x) => x.key === key);
22553
+ const scores = (c?.takes ?? []).map((t) => t.score ?? t.best).filter((x) => x != null).sort((a, b) => b - a);
22554
+ return scores.length ? scores[Math.min(3, scores.length) - 1] : null;
22555
+ };
22556
+ const proud = (key, fallback) => {
22557
+ const t = (criteria.find((x) => x.key === key)?.takes ?? [])[0];
22558
+ if (!t?.instance) return fallback;
22559
+ let h = t.instance.split(/:\s|\s[—–]\s|\swith an?\s/)[0].trim().replace(/\s+/g, " ");
22560
+ h = h.replace(/^The user\b/, "You").replace(/\bThe user\b/g, "you").replace(/([.?!]['"’”)\]]*\s+)you\b/g, (_m, p) => p + "You");
22561
+ if (h.length > 150) h = h.slice(0, 148).replace(/\s+\S*$/, "") + "\u2026";
22562
+ return h;
22563
+ };
22564
+ const nTk = (key) => criteria.find((x) => x.key === key)?.takes.length ?? 0;
22565
+ const peakWords = Object.values(wordsByDay).length ? Math.max(...Object.values(wordsByDay)) : 0;
22566
+ const pHist = parallelism.levelHistogramMin || {};
22567
+ const soloMin = pHist["1"] ?? 0;
22568
+ const parMin = Object.entries(pHist).reduce((a, [k, v]) => Number(k) >= 2 ? a + v : a, 0);
22569
+ const soloPct = soloMin + parMin ? Math.round(soloMin / (soloMin + parMin) * 100) : null;
22570
+ const flowPctDay = aggregate?.flow?.flow?.pctOfWorkDay ?? null;
22571
+ const flowHrsDay = workstyle.flowHoursPerDay ? Math.round(workstyle.flowHoursPerDay * 10) / 10 : null;
22572
+ const focusScore = flowPctDay == null ? null : Math.max(1, Math.min(10, Math.round(2 + flowPctDay / BEST.flow.topPctOfDay * 7)));
22573
+ const out2 = ceilOf("outcomes");
22574
+ const unb = ceilOf("unblocking");
22575
+ const agency = out2 != null && unb != null ? Math.round((out2 + unb) / 2) : out2 ?? unb;
22576
+ const sRow = (label, score, gap2) => ({ label, score, percentile: pctOf(score), gap: gap2 });
22577
+ const tiers = DEFAULT_CALIBRATION.codingTiers;
22578
+ const phi = (z) => {
22579
+ const t = 1 / (1 + 0.2316419 * Math.abs(z));
22580
+ const d = 0.3989423 * Math.exp(-z * z / 2);
22581
+ let pr = d * t * (0.3193815 + t * (-0.3565638 + t * (1.781478 + t * (-1.821256 + t * 1.330274))));
22582
+ if (z > 0) pr = 1 - pr;
22583
+ return pr;
22584
+ };
22585
+ const lognPct = (x, c) => x == null || !c || x <= 0 ? null : Math.round(phi(Math.log(x / c.median) / c.sigma) * 1e3) / 10;
22586
+ const avgWords = aggregate?.throughput?.avgWordsPerDay ?? null;
22587
+ const pushPctl = lognPct(avgWords, tiers?.push);
22588
+ const focusPctl = lognPct(flowPctDay, tiers?.focus);
22589
+ const hoursAt = (min) => Object.entries(pHist).reduce((a, [k, v]) => Number(k) >= min ? a + v : a, 0) / 60;
22590
+ const orchPctl = hoursAt(5) >= 5 || hoursAt(3) >= 10 && parallelism.queueOps >= 1e3 ? 99.5 : hoursAt(3) >= 10 ? 99 : hoursAt(2) >= 0.1 * (soloMin + parMin) / 60 ? 97 : hoursAt(2) >= 2 ? 90 : 70;
22591
+ const stackUp = [
22592
+ sRow(
22593
+ "How hard you push",
22594
+ aggregate?.throughput?.score ?? tCeiling,
22595
+ `you peak at ~${peakWords.toLocaleString()} words of pure direction in a single day \u2014 the very top sustain ~${BEST.throughput.topAvgWordsPerDay.toLocaleString()} every day, week after week; the wildest single days on record push ~${BEST.throughput.topPeakWordsPerDay.toLocaleString()}`
22596
+ ),
22597
+ sRow(
22598
+ "How well you use AI",
22599
+ ceilOf("delegation"),
22600
+ soloPct != null ? `~${soloPct}% of your hours you drive a single agent (${Math.round(soloMin / 60)}h solo vs ${Math.round(parMin / 60)}h with 2+ live) \u2014 the Claude Code lead keeps 10\u201315 going at once, so this is the single biggest speed-up still on the table` : "you brief the AI sharply, but mostly drive one agent at a time \u2014 the top run 10\u201315 in parallel, a several-fold speed-up"
22601
+ ),
22602
+ sRow(
22603
+ "How well you focus",
22604
+ focusScore,
22605
+ flowHrsDay != null ? `only ~${flowHrsDay}h of your day is true deep flow \u2014 the best hold ~4h in one unbroken block; yours fragments across parallel sessions, so the gain is directing your attention at one thread for longer` : "your attention scatters across parallel sessions \u2014 directing it at one thread for longer is the unlock"
22606
+ ),
22607
+ // The four estimates LEAD with a concrete moment from their own sessions, then an
22608
+ // HONEST caveat — how many graded sessions back the read. No invented weakness, no
22609
+ // claim about what they "didn't" do (false specifics churn a real user); it's an estimate.
22610
+ sRow(
22611
+ "Agency",
22612
+ agency,
22613
+ `${proud("outcomes", "you ship working systems and unblock yourself")} \u2014 one of ${nTk("outcomes")} sessions behind this read; a strong, consistent signal, still an estimate from your logs`
22614
+ ),
22615
+ // Judgement/Taste use the SAME numbers their ability cards prove (aggregate
22616
+ // peak-with-consistency over near-peak instances) — a chip saying "top 1%"
22617
+ // above a card proving 10/Top 0.1% with ten receipts reads as a bug.
22618
+ sRow(
22619
+ "Judgement",
22620
+ aggregate?.ability?.abstraction?.score ?? ceilOf("generative"),
22621
+ `${proud("generative", "you make sharp, non-obvious calls on what actually matters")} \u2014 one of ${nTk("generative")} sessions we've graded for this; clear so far, not yet a final verdict`
22622
+ ),
22623
+ sRow(
22624
+ "Taste",
22625
+ aggregate?.ability?.taste?.score ?? ceilOf("taste"),
22626
+ `${proud("taste", "you hold a high, opinionated quality bar")} \u2014 one of ${nTk("taste")} sessions behind this read; a real signal, still an estimate`
22627
+ )
22628
+ // Expertise: NO chip — the scored criterion is deprecated; the real judge is
22629
+ // the qualitative domains map (expertise-floor), and a chip must never say
22630
+ // "top X%" while that section shows no qualifying domains.
22631
+ ];
22632
+ for (const r of stackUp) {
22633
+ if (r.label === "How hard you push" && pushPctl != null) {
22634
+ r.percentile = pushPctl;
22635
+ r.metric = avgWords;
22636
+ r.curve = "push";
22637
+ }
22638
+ if (r.label === "How well you focus" && focusPctl != null) {
22639
+ r.percentile = focusPctl;
22640
+ r.metric = flowPctDay;
22641
+ r.curve = "focus";
22642
+ }
22643
+ if (r.label === "How well you use AI") r.percentile = orchPctl;
22644
+ }
22645
+ return { generatedAt: (/* @__PURE__ */ new Date()).toISOString(), gradedSessions: grades.length, agglomeration, outcomeCadence, dayDigest, dayChart, timeline, walkthroughs, rubric: rubric2, wordsByDay, dayThroughput, throughputCeiling: tCeiling, stackUp, criteria, parallelism, throughput, workstyle, flow, focus, expertiseMap, frontierArsenal, frontierDetail, coaching, gap, aggregate, delegationRollup, gapDist, projects };
22646
+ }
22647
+
22648
+ // ../../lib/agents/coding/publicPage.ts
22649
+ var readJson3 = async (f, d) => fs27.readFile(f, "utf8").then((t) => JSON.parse(t)).catch(() => d);
22650
+ var thirdPerson = (s) => s;
22651
+ function weeklyCadence(wordsByDay) {
22652
+ const days = Object.keys(wordsByDay).sort();
22653
+ const weeks = /* @__PURE__ */ new Map();
22654
+ for (const d of days) {
22655
+ const dt = /* @__PURE__ */ new Date(d + "T00:00:00Z");
22656
+ const jan1 = new Date(Date.UTC(dt.getUTCFullYear(), 0, 1));
22657
+ const wk = `${dt.getUTCFullYear()}-W${String(Math.ceil(((+dt - +jan1) / 864e5 + jan1.getUTCDay() + 1) / 7)).padStart(2, "0")}`;
22658
+ weeks.set(wk, (weeks.get(wk) ?? 0) + 1);
22659
+ }
22660
+ const full = [...weeks.values()].slice(1, -1);
22661
+ const sevenStreak = (() => {
22662
+ let best = 0, cur = 0;
22663
+ for (const n of full) {
22664
+ cur = n >= 7 ? cur + 1 : 0;
22665
+ best = Math.max(best, cur);
22666
+ }
22667
+ return best;
22668
+ })();
22669
+ const recent = full.slice(-6);
22670
+ const recentAvg = recent.length ? Math.round(recent.reduce((a, b) => a + b, 0) / recent.length * 10) / 10 : 0;
22671
+ const first = full.slice(0, 3), firstAvg = first.length ? Math.round(first.reduce((a, b) => a + b, 0) / first.length * 10) / 10 : 0;
22672
+ return { recentAvg, firstAvg, sevenStreak };
22673
+ }
22674
+ function rhythmFacts(wordsByDay, workstyle, flow) {
22675
+ const days = Object.keys(wordsByDay).sort();
22676
+ const { recentAvg, firstAvg, sevenStreak } = weeklyCadence(wordsByDay);
22677
+ const facts = [];
22678
+ facts.push(recentAvg >= 6.5 ? { big: "7 days a week", rest: sevenStreak >= 2 ? `${sevenStreak} consecutive weeks without a single day off` : "in recent weeks" } : { big: `${recentAvg} days a week`, rest: "averaged over the recent weeks" });
22679
+ if (flow?.workDayHours) facts.push({ big: `${flow.workDayHours}-hour`, rest: "working days, typically" });
22680
+ if (firstAvg && recentAvg > firstAvg + 1.5) facts.push({ big: `${Math.round(firstAvg)} \u2192 ${Math.round(recentAvg)} days/week`, rest: "the ramp from the first weeks to now" });
22681
+ if (workstyle?.activeHours) facts.push({ big: `${Math.round(workstyle.activeHours)} hours`, rest: `hands-on across ${workstyle.daysObserved ?? days.length} observed days${flow?.flowHoursPerDay ? `, ${flow.flowHoursPerDay}h of it in deep flow daily` : ""}` });
22682
+ return facts.slice(0, 4);
22683
+ }
22684
+ function detailedReportCards(profile) {
22685
+ const cards = [];
22686
+ const t = profile?.throughput;
22687
+ if (t?.sessions) {
22688
+ cards.push({ section: "Throughput", stats: [
22689
+ { big: t.sessions.toLocaleString(), lab: "sessions" },
22690
+ { big: `${Math.round(t.activeHours ?? 0)}h`, lab: "active time" },
22691
+ { big: (t.toolCalls ?? 0).toLocaleString(), lab: `tool calls (${(t.subagents ?? 0).toLocaleString()} subagents)` },
22692
+ { big: String(t.lateNightSessions ?? 0), lab: "late-night days (ends 22:00\u201305:00)" }
22693
+ ] });
22694
+ }
22695
+ const ws = profile?.workstyle, af = profile?.aggregate?.flow;
22696
+ const stats = [];
22697
+ if (af?.workWindow?.typicalStart) stats.push({ big: `${af.workWindow.typicalStart}\u2013${af.workWindow.typicalEnd}`, lab: "work window, typical day" });
22698
+ if (af?.flow?.pctOfWorkDay != null) stats.push({ big: `${Math.round(af.flow.pctOfWorkDay * 100)}%`, lab: `in flow, of the work day (${af.flow.flowHoursPerDay}h/day)` });
22699
+ if (af?.peakWindow) stats.push({ big: String(af.peakWindow), lab: "peak flow" });
22700
+ if (af?.parallelism?.maxConcurrent) stats.push({ big: `\xD7${af.parallelism.maxConcurrent}`, lab: "max parallel" });
22701
+ if (ws?.blockCount) stats.push({ big: `${Math.round((ws.flowHoursTotal ?? 0) * 60 / ws.blockCount)} min`, lab: `mean unbroken stretch (${ws.blockCount} runs, ${ws.deepBlocks} \u2265${ws.flowThresholdMin ?? 30}m)` });
22702
+ if (ws?.longestBlockMin) stats.push({ big: `${ws.longestBlockMin} min`, lab: "longest flow run, unbroken rapid exchange" });
22703
+ const focusDays = profile?.focus?.days ?? [];
22704
+ const rngMin = (rngs) => (rngs ?? []).reduce((a, [x, y]) => a + (y - x), 0);
22705
+ const totalAct = focusDays.reduce((a, d) => a + rngMin(d.ranges?.act ?? []), 0);
22706
+ const totalBrk = focusDays.reduce((a, d) => a + rngMin(d.ranges?.brk ?? []), 0);
22707
+ if (totalAct + totalBrk > 0) stats.push({ big: `${Math.round(100 * totalBrk / (totalAct + totalBrk))}%`, lab: "of the working span spent in breaks" });
22708
+ if (stats.length) cards.push({ section: "Flow & rhythm", stats, note: af?.insight ? String(af.insight) : void 0 });
22709
+ return cards;
22710
+ }
22711
+ async function buildSkeleton(codingDir, stackUp) {
22712
+ const agg = await readJson3(path31.join(codingDir, "aggregate.json"), {});
22713
+ const coaching = await readJson3(path31.join(codingDir, "coaching.json"), {});
22714
+ const agglom = await readJson3(path31.join(codingDir, "agglomerate.json"), {});
22715
+ const projectsDoc = await readJson3(path31.join(codingDir, "projects.json"), {});
22716
+ const focus = await readJson3(path31.join(codingDir, "focus.json"), {});
22717
+ const sessions = await readJson3(path31.join(codingDir, "sessions.json"), []);
22718
+ const profile = await compileCodingProfile().catch(() => null);
22719
+ const wordsByDay = agg?.throughput?.wordsByDay ?? {};
22720
+ const wbd = Object.keys(wordsByDay).length ? wordsByDay : Object.fromEntries((focus?.days ?? []).map((d) => [d.date, d.activeMin ?? 1]));
22721
+ const pct2 = (label) => {
22722
+ const r = (stackUp ?? []).find((x) => x.label.toLowerCase().includes(label));
22723
+ return r?.percentile != null ? Math.round((100 - r.percentile) * 100) / 100 : null;
22724
+ };
22725
+ const gradedOn = (key) => (agglom?.criteria ?? []).length ? 0 : 0;
22726
+ const interactive = sessions.filter((s) => s.klass === "interactive" && !s.duplicateOf);
22727
+ const flow = agg?.flow?.flow ?? {};
22728
+ const thr = agg?.throughput ?? {};
22729
+ const par = { max: 0, hours2plus: 0, queueOps: 0 };
22730
+ try {
22731
+ const conc = await readJson3(path31.join(codingDir, "concurrency.json"), {});
22732
+ par.max = conc?.maxConcurrency ?? 0;
22733
+ par.hours2plus = Math.round((conc?.minutesAt2plus ?? 0) / 60);
22734
+ par.queueOps = conc?.queueOps ?? 0;
22735
+ } catch {
22736
+ }
22737
+ const wk = weeklyCadence(wbd);
22738
+ const workWindow = agg?.flow?.workWindow;
22739
+ const axes = [
22740
+ { key: "judgement", label: "Judgement", topPct: pct2("judgement"), sub: "AI-graded on an anchored rubric", claim: "", read: "", numbers: [], moments: [] },
22741
+ { key: "agency", label: "Agency", topPct: pct2("agency"), sub: "full ship arcs, verified", claim: "", read: "", numbers: [], moments: [] },
22742
+ { key: "taste", label: "Taste", topPct: pct2("taste"), sub: "AI-graded on an anchored rubric", claim: "", read: "", numbers: [], moments: [] },
22743
+ { key: "push", label: "How hard they push", topPct: pct2("push"), sub: "counted, not graded", claim: "", read: "", numbers: [
22744
+ ...wk.recentAvg ? [{ big: wk.recentAvg >= 6.5 ? "7 days a week" : `${wk.recentAvg} days a week`, lab: "worked, averaged over recent weeks" }] : [],
22745
+ ...workWindow?.typicalStart ? [{ big: `${workWindow.typicalStart}\u2013${workWindow.typicalEnd}`, lab: "work window, typical day" }] : [],
22746
+ ...thr.avgWordsPerDay ? [{ big: String(Math.round(thr.avgWordsPerDay).toLocaleString()), lab: "typed/dictated words per day, averaged (~300 for the median active user)" }] : [],
22747
+ ...thr.substantialDays ? [{ big: `${thr.substantialDays}/${thr.totalCodingDays}`, lab: "coding days were substantial (3h+ span)" }] : []
22748
+ ], moments: [] },
22749
+ { key: "ai", label: "How they use AI", topPct: pct2("use ai"), sub: "orchestration, measured", claim: "", read: "", numbers: [
22750
+ ...par.max ? [{ big: String(par.max), lab: "agents at once, peak" }] : [],
22751
+ ...par.hours2plus ? [{ big: `${par.hours2plus}h`, lab: "working 2+ live agents" }] : [],
22752
+ ...par.queueOps ? [{ big: par.queueOps.toLocaleString(), lab: "instructions queued ahead" }] : []
22753
+ ], moments: [] },
22754
+ { key: "focus", label: "Focus", topPct: pct2("focus"), sub: "strict definition", claim: "", read: "", numbers: [
22755
+ ...flow.pctOfWorkDay != null ? [{ big: `${Math.round(flow.pctOfWorkDay * 100)}%`, lab: "of the work day in strict deep flow (gaps under 6 min)" }] : [],
22756
+ ...flow.flowHoursPerDay ? [{ big: `${flow.flowHoursPerDay}h`, lab: "deep flow per day" }] : [],
22757
+ ...flow.totalFlowHours ? [{ big: `${Math.round(flow.totalFlowHours)}h`, lab: "total flow across the period" }] : []
22758
+ ], moments: [] }
22759
+ ];
22760
+ const projects = (Array.isArray(projectsDoc?.projects) ? projectsDoc.projects : Array.isArray(projectsDoc) ? projectsDoc : []).filter((p) => (p.hours ?? 0) >= 1).slice(0, 6).map((p) => ({ name: String(p.project ?? p.name ?? ""), meta: `${p.days ?? "?"} days \xB7 ${Math.round(p.hours ?? 0)}h` }));
22761
+ const moreNumbers = detailedReportCards(profile);
22762
+ const page = {
22763
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
22764
+ verdict: thirdPerson(String(coaching?.whatYouAre?.line ?? "")),
22765
+ provenance: { sessions: interactive.length, days: Object.keys(wbd).length, graded: 0 },
22766
+ rhythm: { facts: rhythmFacts(wbd, profile?.workstyle ?? {}, flow) },
22767
+ missionArc: "",
22768
+ overall: "",
22769
+ axes,
22770
+ projects,
22771
+ moreNumbers,
22772
+ howToWork: [],
22773
+ nutshell: String(agg?.bigPicture ?? "")
22774
+ };
22775
+ const evidence = [
22776
+ `PLACEMENT VERDICT (rewrite to third person, keep the punch): ${coaching?.whatYouAre?.line ?? ""}
22777
+ EVIDENCE BEHIND IT: ${coaching?.whatYouAre?.evidence ?? ""}`,
22778
+ `OVERALL READ (compress to 2-4 sentences, keep the behavioral signature): ${String(agglom?.overall ?? "").slice(0, 2200)}`,
22779
+ `PER-CRITERION INSIGHTS:
22780
+ ${(agglom?.criteria ?? []).map((c) => `[${c.key}] ${c.headline} \u2014 ${String(c.insight ?? "").slice(0, 400)}`).join("\n")}`,
22781
+ `SIGNATURE MOVES (the moment pool \u2014 verify quotes at the source before using):
22782
+ ${(agglom?.signatureMoves ?? []).map((m) => `- ${m.what} \xB7 ${m.session} \xB7 quote: "${m.quote}"`).join("\n")}`,
22783
+ `FEEL-SEEN LINES (material, not to copy verbatim): ${JSON.stringify(coaching?.feelSeen ?? {})}`
22784
+ ].join("\n\n");
22785
+ return { page, evidence };
22786
+ }
22787
+ var SYSTEM4 = "You write the COPY for a public, hiring-manager-facing coding profile page. The skeleton (axes, numbers, percentiles) is already computed; you write the words: per-axis claims, reads, and dated concrete moments; the mission arc; the overall read; flow patterns; how-to-work-with bullets. REGISTER: third person throughout (the reader is a hiring manager, not the person). Claims are CLAIMS, not category names ('Kills the wrong metric before it gets built', never 'Strong judgement'). Every moment is dated, concrete, self-contained, and carries the person's VERBATIM words when a quote earns its place \u2014 you MUST verify every quote by finding it in the transcript (docs/<sessionId>.txt) before citing it; can't find it, don't use it. MOMENTS: every axis gets 3-4, and DIVERSE ones: different sessions, different projects, different weeks wherever the evidence allows \u2014 more distinct concrete verified moments is strictly better than fewer, but never pad with a weak restatement of a story already told on that axis. For graded axes pick the sharpest verified moments (the scoreboard ranks candidates). For counted axes (push/focus) turn the person's real peak DAYS into moments: what the day's number was and what they were building (day-digest.json tells you). The AGENCY axis must include one SELF-CONTAINED mission arc moment: what they took on, the insight that redirected it, what shipped \u2014 a stranger should get the whole story from that one entry. MISSION ARC (separate field): the same story at 2-3 sentences, self-contained, concrete. FLOW PATTERNS: from the data (focus.json day ranges, break clusters, day-digest), name what reliably precedes their longest flow runs (inFlow) and what reliably precedes breaks (outFlow) \u2014 one sentence each, evidence-grounded, no speculation. HOW TO WORK WITH THEM: 3-4 bullets derived from the measured behavioral signatures (delegation style, verification habits, standing-rule habit) \u2014 each a practical instruction to a future teammate, not praise. STYLE (hard rules): NO EM DASHES (\u2014) anywhere; use commas, periods, colons. Plain words, no jargon, no consultant-speak, no hedging. Never invent a number, date, quote, or outcome. Numbers you cite must come from the skeleton or the artifacts. Honest edges are stated plainly where the data shows them (the focus axis carries one when flow fragments). BE ECONOMICAL WITH TURNS: verify only the quotes you actually cite, batch several verifications into one Grep call (multiple patterns / one file read covers many quotes), never narrate what you are about to do, and reserve your last turns for emitting the final json block.";
22788
+ function copyPrompt(skeleton) {
22789
+ return `THE SKELETON (numbers + axes already computed, copy fields empty):
22790
+ \`\`\`json
22791
+ ${JSON.stringify(skeleton.page, null, 1)}
22792
+ \`\`\`
22793
+
22794
+ THE DISTILLED EVIDENCE:
22795
+ ${skeleton.evidence}
22796
+
22797
+ Write the copy. Output ONE fenced json block with EXACTLY these fields:
22798
+ \`\`\`json
22799
+ {
22800
+ "verdict": "third-person rewrite of the placement line",
22801
+ "overall": "2-4 sentences: how they actually work",
22802
+ "missionArc": "2-3 sentences, self-contained story",
22803
+ "inFlow": "what reliably precedes their longest flow runs",
22804
+ "outFlow": "what reliably precedes their breaks",
22805
+ "howToWork": ["3-4 bullets"],
22806
+ "axes": { "judgement": { "claim": "", "read": "", "moments": [ { "date": "YYYY-MM-DD or a range", "what": "", "quote": "verbatim, verified, omit if none earns it", "sessionId": "when known" } ], "edge": "optional honest footnote" }, "agency": {}, "taste": {}, "push": {}, "ai": {}, "focus": {} }
22807
+ }
22808
+ \`\`\``;
22809
+ }
22810
+ async function verifyQuotes(codingDir, axes) {
22811
+ const docs = path31.join(codingDir, "docs");
22812
+ let files = null;
22813
+ const norm2 = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
22814
+ const inDoc = async (file2, q) => norm2(await fs27.readFile(path31.join(docs, file2), "utf8").catch(() => "")).includes(norm2(q).slice(0, 80));
22815
+ let dropped = 0;
22816
+ for (const ax of Object.values(axes ?? {})) {
22817
+ for (const m of ax?.moments ?? []) {
22818
+ if (!m.quote) continue;
22819
+ let ok = false;
22820
+ if (m.sessionId) ok = await inDoc(`${m.sessionId}.txt`, m.quote);
22821
+ if (!ok) {
22822
+ files = files ?? (await fs27.readdir(docs).catch(() => [])).filter((f) => f.endsWith(".txt"));
22823
+ for (const f of files) {
22824
+ if (await inDoc(f, m.quote)) {
22825
+ ok = true;
22826
+ break;
22827
+ }
22828
+ }
22829
+ }
22830
+ if (!ok) {
22831
+ console.log(`[public-page] DROPPED unverifiable quote: "${m.quote.slice(0, 60)}\u2026"`);
22832
+ delete m.quote;
22833
+ dropped++;
22834
+ }
22835
+ }
22836
+ }
22837
+ return { dropped };
22838
+ }
22839
+ async function generatePublicPage(opts) {
22840
+ const outFile = path31.join(opts.codingDir, "coding-public.json");
22841
+ const skeleton = await buildSkeleton(opts.codingDir, opts.stackUp);
22842
+ if (!skeleton.page.verdict && !skeleton.evidence.includes("[")) {
22843
+ console.log("[public-page] no upstream artifacts yet \u2014 run the analysis first");
22844
+ return null;
22845
+ }
22846
+ const system = SYSTEM4 + sourceLookupNote();
22847
+ const prompt = copyPrompt(skeleton);
22848
+ const sha = promptSha([system, prompt, opts.model ?? "sonnet"]);
22849
+ if (!opts.refresh) {
22850
+ const prev = await reusableOutput(outFile, sha, (o) => !!o.verdict && (o.axes ?? []).length === 6);
22851
+ if (prev) {
22852
+ console.log(`[public-page] inputs unchanged \u2014 reusing page from ${prev.generatedAt} (--refresh to regenerate)`);
22853
+ return prev;
22854
+ }
22855
+ }
22856
+ await ensureCodingDocs(opts.codingDir, opts.sessions);
22857
+ for (let attempt = 1; attempt <= 2; attempt++) {
22858
+ const run3 = await invokeAgent2({
22859
+ prompt: attempt === 1 ? prompt : prompt + "\n\nREMINDER: your previous attempt contained an em dash (\u2014). That is a hard style violation. Rewrite with ZERO em dashes.",
22860
+ systemPrompt: system,
22861
+ // 40 turns: quote verification is turn-hungry (one grep per citation) and
22862
+ // the run DIED at 14 twice on 2026-07-14, always mid-verification. Batch
22863
+ // instruction in SYSTEM keeps it economical; the engine's corpus agents
22864
+ // get 80 for the same reason.
22865
+ addDir: opts.codingDir,
22866
+ allowedTools: ["Read", "Grep", "Glob"],
22867
+ maxTurns: 40,
22868
+ timeoutMs: 6e5,
22869
+ model: opts.model ?? "sonnet"
22870
+ });
22871
+ const j = run3.json;
22872
+ if (!j?.verdict || !j?.axes) {
22873
+ console.log(`[public-page] attempt ${attempt}: no usable copy`);
22874
+ continue;
22875
+ }
22876
+ if (JSON.stringify(j).includes("\u2014")) {
22877
+ console.log(`[public-page] attempt ${attempt}: em dash in output, retrying once`);
22878
+ continue;
22879
+ }
22880
+ await verifyQuotes(opts.codingDir, j.axes);
22881
+ const page = skeleton.page;
22882
+ page.verdict = String(j.verdict).slice(0, 500);
22883
+ page.overall = String(j.overall ?? "").slice(0, 900);
22884
+ page.missionArc = String(j.missionArc ?? "").slice(0, 700);
22885
+ page.rhythm.inFlow = String(j.inFlow ?? "").slice(0, 300) || void 0;
22886
+ page.rhythm.outFlow = String(j.outFlow ?? "").slice(0, 300) || void 0;
22887
+ page.howToWork = (Array.isArray(j.howToWork) ? j.howToWork : []).map((x) => String(x).slice(0, 250)).slice(0, 5);
22888
+ for (const ax of page.axes) {
22889
+ const c = j.axes[ax.key];
22890
+ if (!c) continue;
22891
+ ax.claim = String(c.claim ?? "").slice(0, 200);
22892
+ ax.read = String(c.read ?? "").slice(0, 500);
22893
+ ax.edge = c.edge ? String(c.edge).slice(0, 350) : void 0;
22894
+ ax.moments = (Array.isArray(c.moments) ? c.moments : []).slice(0, 4).map((m) => ({
22895
+ date: String(m.date ?? "").slice(0, 30),
22896
+ what: String(m.what ?? "").slice(0, 500),
22897
+ ...m.quote ? { quote: String(m.quote).slice(0, 300) } : {},
22898
+ ...m.sessionId ? { sessionId: String(m.sessionId) } : {}
22899
+ })).filter((m) => m.what);
22900
+ }
22901
+ if (page.axes.filter((a) => a.claim).length < 4) {
22902
+ console.log(`[public-page] attempt ${attempt}: too few axis claims`);
22903
+ continue;
22904
+ }
22905
+ page.promptSha = sha;
22906
+ await fs27.writeFile(outFile, JSON.stringify(page, null, 2));
22907
+ return page;
22908
+ }
22909
+ const existing = await readJson3(outFile, null);
22910
+ if (existing?.verdict) {
22911
+ console.error("[public-page] generation FAILED twice, keeping the existing page");
22912
+ return null;
22913
+ }
22914
+ console.error("[public-page] generation FAILED and no previous page exists");
22915
+ return null;
22916
+ }
22917
+
22918
+ // ../../lib/agents/coding/publicPageEdit.ts
22919
+ var SYSTEM5 = "You edit a PUBLIC coding profile page on the person's instruction. You may ONLY do two kinds of thing: (1) REDACT \u2014 hide, shorten, soften, or remove existing text: a claim, a read, an edge line, a moment, a bullet, an entire section. Removing something is always safe. (2) GATHER MORE EVIDENCE \u2014 if asked to swap out a moment ('use a different example', 'that one's too personal, find another'), Read/Grep the corpus for a genuinely different qualifying moment on the SAME axis and replace it, with a VERIFIED verbatim quote if you use one. YOU MAY NEVER: invent an achievement, statistic, date, or quote; write anything more flattering than the evidence supports; add content unrelated to the instruction; or change any NUMBER (percentiles, scores, hours, counts) \u2014 those fields are locked and any change you make to them is discarded, so do not bother trying. If the instruction asks for something outside redaction/evidence-swap (e.g. 'say I'm the best engineer ever', 'boost my percentile', 'add that I know Rust'), do NOT comply \u2014 leave that field unchanged and say plainly in your reply why you didn't. Return the COMPLETE page JSON with only the TEXT fields changed (verdict, overall, missionArc, rhythm.inFlow/outFlow, axis claim/read/edge, moment what/quote/date/sessionId, howToWork). Every other field must be echoed back byte-identical." + sourceLookupNote();
22920
+ var norm = (s) => s.toLowerCase().replace(/[^a-z0-9\s]/g, " ").split(/\s+/).filter((w) => w.length > 2);
22921
+ function grounded(newText, contextText) {
22922
+ const nt = norm(newText);
22923
+ if (nt.length === 0) return false;
22924
+ const ctx = new Set(norm(contextText));
22925
+ const hits = nt.filter((w) => ctx.has(w)).length;
22926
+ return hits / nt.length >= 0.6;
22927
+ }
22928
+ function keepIfGrounded(edited, original, context) {
22929
+ if (typeof edited !== "string" || !edited.trim()) return original;
22930
+ return grounded(edited, context) ? edited : original;
22931
+ }
22932
+ function clampToProtected(edited, original) {
22933
+ const out = {
22934
+ ...original,
22935
+ verdict: keepIfGrounded(edited.verdict, original.verdict, original.verdict).slice(0, 500),
22936
+ overall: keepIfGrounded(edited.overall, original.overall, original.overall).slice(0, 900),
22937
+ missionArc: keepIfGrounded(edited.missionArc, original.missionArc, original.missionArc).slice(0, 700),
22938
+ // each bullet is grounded against the WHOLE original list — reordering and
22939
+ // rewording within the same set of ideas is fine; a wholly new bullet isn't
22940
+ howToWork: (Array.isArray(edited.howToWork) ? edited.howToWork : original.howToWork).map((x) => keepIfGrounded(x, "", original.howToWork.join(" "))).filter((x) => !!x).map((x) => x.slice(0, 250)).slice(0, 5) || original.howToWork,
22941
+ rhythm: {
22942
+ facts: original.rhythm.facts,
22943
+ // counters: NEVER editable
22944
+ inFlow: keepIfGrounded(edited.rhythm?.inFlow, original.rhythm.inFlow ?? "", original.rhythm.inFlow ?? "").slice(0, 300) || void 0,
22945
+ outFlow: keepIfGrounded(edited.rhythm?.outFlow, original.rhythm.outFlow ?? "", original.rhythm.outFlow ?? "").slice(0, 300) || void 0
22946
+ },
22947
+ // PROTECTED, always restored: provenance, projects, moreNumbers (raw stats)
22948
+ provenance: original.provenance,
22949
+ projects: original.projects,
22950
+ moreNumbers: original.moreNumbers,
22951
+ nutshell: original.nutshell
22952
+ // the local report owns this; not editable here
22953
+ };
22954
+ const editedAxes = new Map((edited.axes ?? []).map((a) => [a.key, a]));
22955
+ out.axes = original.axes.map((orig) => {
22956
+ const e = editedAxes.get(orig.key);
22957
+ const axisCtx = `${orig.claim} ${orig.read} ${orig.moments.map((m) => m.what).join(" ")}`;
22958
+ const base2 = {
22959
+ ...orig,
22960
+ // topPct, sub, numbers: PROTECTED, always from orig
22961
+ claim: keepIfGrounded(e?.claim, orig.claim, axisCtx).slice(0, 200),
22962
+ read: keepIfGrounded(e?.read, orig.read, axisCtx).slice(0, 500),
22963
+ edge: e?.edge === void 0 ? orig.edge : e.edge ? keepIfGrounded(e.edge, orig.edge ?? "", `${orig.edge ?? ""} ${axisCtx}`).slice(0, 350) || void 0 : void 0
22964
+ };
22965
+ const origById = new Map(orig.moments.filter((m) => m.sessionId).map((m) => [m.sessionId, m]));
22966
+ base2.moments = (Array.isArray(e?.moments) ? e.moments : orig.moments).slice(0, 3).map((m) => {
22967
+ const matched = m?.sessionId ? origById.get(String(m.sessionId)) : void 0;
22968
+ if (matched) return matched;
22969
+ return {
22970
+ date: String(m?.date ?? "").slice(0, 30),
22971
+ what: String(m?.what ?? "").slice(0, 500),
22972
+ ...m?.quote ? { quote: String(m.quote).slice(0, 300) } : {},
22973
+ ...m?.sessionId ? { sessionId: String(m.sessionId) } : {}
22974
+ };
22975
+ }).filter((m) => m.what && (origById.has(m.sessionId ?? "") || m.quote));
22976
+ return base2;
22977
+ });
22978
+ return out;
22979
+ }
22980
+ async function editPublicPage(opts) {
22981
+ const last = opts.messages.filter((m) => m.role === "user").pop();
22982
+ if (!last?.content?.trim()) return { status: 400, json: { error: "an instruction is required" } };
22983
+ const convo = opts.messages.slice(-6).map((m) => `${m.role === "user" ? "USER" : "EDITOR"}: ${m.content}`).join("\n");
22984
+ const prompt = `CURRENT PAGE:
22985
+ \`\`\`json
22986
+ ${JSON.stringify(opts.page, null, 1)}
22987
+ \`\`\`
22988
+
22989
+ CONVERSATION:
22990
+ ${convo}
22991
+
22992
+ Apply the user's LAST instruction within your allowed actions (redact, or gather-more-evidence). Output ONE fenced json block: { "page": <complete page, text fields updated>, "reply": "one short plain sentence on what you did or why you declined" }`;
22993
+ try {
22994
+ const run3 = await invokeAgent2({
22995
+ prompt,
22996
+ systemPrompt: SYSTEM5,
22997
+ addDir: opts.codingDir,
22998
+ allowedTools: ["Read", "Grep", "Glob"],
22999
+ maxTurns: 10,
23000
+ timeoutMs: 24e4,
23001
+ model: opts.model ?? "sonnet"
23002
+ });
23003
+ const j = run3.json;
23004
+ if (!j?.page) return { status: 502, json: { error: "the edit didn't produce a valid page \u2014 try rephrasing" } };
23005
+ const clamped = clampToProtected(j.page, opts.page);
23006
+ const originalIds = new Set(opts.page.axes.flatMap((a) => a.moments.map((m) => m.sessionId).filter(Boolean)));
23007
+ await verifyQuotes(opts.codingDir, clamped.axes);
23008
+ for (const ax of clamped.axes) ax.moments = ax.moments.filter((m) => m.sessionId && originalIds.has(m.sessionId) || m.quote);
23009
+ return { status: 200, json: { page: clamped, reply: String(j.reply ?? "Done.").slice(0, 300) } };
23010
+ } catch (e) {
23011
+ return { status: 502, json: { error: e.message.slice(0, 200) } };
23012
+ }
23013
+ }
23014
+
23015
+ // src/publicCodingPageApi.ts
23016
+ var readJson4 = async (f, d) => fs28.readFile(f, "utf8").then((t) => JSON.parse(t)).catch(() => d);
23017
+ async function localChatReport() {
23018
+ const rep = await loadPublicReport().catch(() => null);
23019
+ return rep && hasContent(rep) ? rep : null;
23020
+ }
23021
+ async function handlePublicCodingPageGet(dataDir) {
23022
+ const page = await readJson4(path32.join(dataDir, "coding", "coding-public.json"), null);
23023
+ const chatReport = await localChatReport();
23024
+ return { status: 200, json: { page, chatReport } };
23025
+ }
23026
+ async function handlePublicCodingPageGenerate(dataDir, liveProfile) {
23027
+ const codingDir = path32.join(dataDir, "coding");
23028
+ const stackUp = liveProfile?.stackUp ?? null;
23029
+ const sessions = await readJson4(path32.join(codingDir, "sessions.json"), []);
23030
+ try {
23031
+ const page = await generatePublicPage({ codingDir, stackUp, sessions });
23032
+ if (!page) return { status: 502, json: { error: "generation failed \u2014 see the server log, or run the analysis first" } };
23033
+ return { status: 200, json: { page } };
23034
+ } catch (e) {
23035
+ return { status: 502, json: { error: e.message.slice(0, 300) } };
23036
+ }
23037
+ }
23038
+ async function handlePublicCodingPageEdit(dataDir, body) {
23039
+ const page = body.page;
23040
+ const messages = Array.isArray(body.messages) ? body.messages : [];
23041
+ if (!page || !Array.isArray(page.axes)) return { status: 400, json: { error: "page + messages are required" } };
23042
+ const out = await editPublicPage({ page, messages, codingDir: path32.join(dataDir, "coding") });
23043
+ return out;
23044
+ }
23045
+ async function handlePublicCodingPageShare(dataDir, body) {
23046
+ const cfg = centralConfig();
23047
+ if (!cfg.configured) return { status: 503, json: { error: "central services are disabled in this build" } };
23048
+ const auth = await getAccessToken(dataDir);
23049
+ if (!auth) return { status: 401, json: { error: "sign in with Google (top right) to share with Polymath" } };
23050
+ const page = body.page;
23051
+ if (!page?.verdict || !Array.isArray(page.axes) || page.axes.length < 4) {
23052
+ return { status: 409, json: { error: "nothing to share yet \u2014 generate your page first" } };
23053
+ }
23054
+ const chatReport = body.includeChat === false ? null : await localChatReport();
23055
+ try {
23056
+ const payload = JSON.stringify({ format: "coding-pr1", page, ...chatReport ? { chatReport } : {} });
23057
+ const up = await fetch(`${cfg.supabaseUrl}/rest/v1/rpc/submit_report`, {
23058
+ method: "POST",
23059
+ headers: { apikey: cfg.supabaseAnonKey, authorization: `Bearer ${auth.token}`, "content-type": "application/json" },
23060
+ body: JSON.stringify({ p_session_id: null, p_content: payload })
23061
+ });
23062
+ if (!up.ok) return { status: 502, json: { error: `share failed (HTTP ${up.status})` } };
23063
+ const verification = String(await up.json().catch(() => "unverified"));
23064
+ return { status: 200, json: { ok: true, includedChat: !!chatReport, verification } };
23065
+ } catch (e) {
23066
+ return { status: 502, json: { error: e.message.slice(0, 200) } };
23067
+ }
21704
23068
  }
21705
23069
 
21706
23070
  // src/server.ts
21707
- var __dirname = path26.dirname(fileURLToPath2(import.meta.url));
21708
- var UI_DIR = path26.join(__dirname, "web");
23071
+ var __dirname = path33.dirname(fileURLToPath2(import.meta.url));
23072
+ var UI_DIR = path33.join(__dirname, "web");
21709
23073
  var DEFAULT_SHARE_URL = "https://polymathsociety.us/api/coding-share";
21710
23074
  var MIME = {
21711
23075
  ".html": "text/html; charset=utf-8",
@@ -21736,19 +23100,19 @@ async function readBody(req, maxBytes = 8e6) {
21736
23100
  async function serveStatic(res, urlPath) {
21737
23101
  const clean = urlPath.split("?")[0];
21738
23102
  const rel = clean === "/" ? "index.html" : clean.replace(/^\/+/, "");
21739
- const full = path26.normalize(path26.join(UI_DIR, rel));
23103
+ const full = path33.normalize(path33.join(UI_DIR, rel));
21740
23104
  if (!full.startsWith(UI_DIR)) {
21741
23105
  res.writeHead(403).end("forbidden");
21742
23106
  return;
21743
23107
  }
21744
23108
  try {
21745
- const data = await fs21.readFile(full);
21746
- res.writeHead(200, { "content-type": MIME[path26.extname(full)] ?? "application/octet-stream" });
23109
+ const data = await fs29.readFile(full);
23110
+ res.writeHead(200, { "content-type": MIME[path33.extname(full)] ?? "application/octet-stream" });
21747
23111
  res.end(data);
21748
23112
  } catch {
21749
- if (!path26.extname(full)) {
23113
+ if (!path33.extname(full)) {
21750
23114
  try {
21751
- const idx = await fs21.readFile(path26.join(UI_DIR, "index.html"));
23115
+ const idx = await fs29.readFile(path33.join(UI_DIR, "index.html"));
21752
23116
  res.writeHead(200, { "content-type": MIME[".html"] }).end(idx);
21753
23117
  return;
21754
23118
  } catch {
@@ -21774,10 +23138,16 @@ async function handleShare(res, body, opts, dataDir) {
21774
23138
  return;
21775
23139
  }
21776
23140
  const all = opts.shareSections ?? opts.profile?.sections ?? {};
21777
- const wanted = parsed.all ? Object.keys(all) : Array.isArray(parsed.sections) ? parsed.sections : [];
21778
23141
  const sections = {};
21779
- for (const key of wanted) {
21780
- if (key in all && all[key] != null) sections[key] = all[key];
23142
+ if (parsed.sectionsData && typeof parsed.sectionsData === "object" && !Array.isArray(parsed.sectionsData)) {
23143
+ for (const key of Object.keys(parsed.sectionsData)) {
23144
+ if (key in all && parsed.sectionsData[key] != null) sections[key] = parsed.sectionsData[key];
23145
+ }
23146
+ } else {
23147
+ const wanted = parsed.all ? Object.keys(all) : Array.isArray(parsed.sections) ? parsed.sections : [];
23148
+ for (const key of wanted) {
23149
+ if (key in all && all[key] != null) sections[key] = all[key];
23150
+ }
21781
23151
  }
21782
23152
  if (Object.keys(sections).length === 0) {
21783
23153
  res.writeHead(400, { "content-type": "application/json" }).end(JSON.stringify({ ok: false, error: "no shareable sections selected" }));
@@ -21815,6 +23185,7 @@ async function handleShare(res, body, opts, dataDir) {
21815
23185
  function startServer(opts) {
21816
23186
  let liveProfile = opts.profile;
21817
23187
  let liveProgress = {};
23188
+ let profileRev = 0;
21818
23189
  const host = opts.host ?? "127.0.0.1";
21819
23190
  const dataDir = opts.dataDir ?? resolveDataRoot();
21820
23191
  let serverUrl = "";
@@ -21828,7 +23199,13 @@ function startServer(opts) {
21828
23199
  return;
21829
23200
  }
21830
23201
  if (req.method === "GET" && url === "/api/progress") {
21831
- res.writeHead(200, { "content-type": MIME[".json"] }).end(JSON.stringify(liveProgress));
23202
+ res.writeHead(200, { "content-type": MIME[".json"] }).end(JSON.stringify({ ...liveProgress, profileRev }));
23203
+ return;
23204
+ }
23205
+ if (req.method === "GET" && route === "/api/exports-manifest") {
23206
+ const m = await readManifest(dataDir).catch(() => null);
23207
+ const kinds = new Set((m?.exports?.included ?? []).map((e) => e.kind));
23208
+ json(res, 200, { connectedKinds: [...kinds], links: EXPORT_LINKS });
21832
23209
  return;
21833
23210
  }
21834
23211
  if (req.method === "GET" && route === "/meta.json") {
@@ -21846,6 +23223,10 @@ function startServer(opts) {
21846
23223
  submit: central.configured,
21847
23224
  auth: central.configured,
21848
23225
  share: true,
23226
+ shareEdit: true,
23227
+ // the review modal's chat editor (/api/share/edit)
23228
+ publicCodingPage: true,
23229
+ // the hiring-manager page (/api/public-coding-page*)
21849
23230
  person: true,
21850
23231
  // the chat-analysis tab's /api/person* routes
21851
23232
  publicReport: true
@@ -21962,7 +23343,8 @@ function startServer(opts) {
21962
23343
  }
21963
23344
  if (route === "/api/profile/visibility" && (req.method === "GET" || req.method === "POST")) {
21964
23345
  const body = req.method === "POST" ? JSON.parse(await readBody(req) || "{}") : void 0;
21965
- const out = await handleVisibility(dataDir, req.method, body);
23346
+ const pool = opts.shareSections ?? opts.profile?.sections ?? {};
23347
+ const out = await handleVisibility(dataDir, req.method, body, pool);
21966
23348
  json(res, out.status, out.json);
21967
23349
  return;
21968
23350
  }
@@ -21976,6 +23358,35 @@ function startServer(opts) {
21976
23358
  await handleShare(res, body, opts, dataDir);
21977
23359
  return;
21978
23360
  }
23361
+ if (req.method === "POST" && route === "/api/share/edit") {
23362
+ const body = JSON.parse(await readBody(req) || "{}");
23363
+ const pool = opts.shareSections ?? opts.profile?.sections ?? {};
23364
+ const out = await handleShareEdit(pool, body);
23365
+ json(res, out.status, out.json);
23366
+ return;
23367
+ }
23368
+ if (req.method === "GET" && route === "/api/public-coding-page") {
23369
+ const out = await handlePublicCodingPageGet(dataDir);
23370
+ json(res, out.status, out.json);
23371
+ return;
23372
+ }
23373
+ if (req.method === "POST" && route === "/api/public-coding-page/generate") {
23374
+ const out = await handlePublicCodingPageGenerate(dataDir, liveProfile);
23375
+ json(res, out.status, out.json);
23376
+ return;
23377
+ }
23378
+ if (req.method === "POST" && route === "/api/public-coding-page/edit") {
23379
+ const body = JSON.parse(await readBody(req) || "{}");
23380
+ const out = await handlePublicCodingPageEdit(dataDir, body);
23381
+ json(res, out.status, out.json);
23382
+ return;
23383
+ }
23384
+ if (req.method === "POST" && route === "/api/public-coding-page/share") {
23385
+ const body = JSON.parse(await readBody(req) || "{}");
23386
+ const out = await handlePublicCodingPageShare(dataDir, body);
23387
+ json(res, out.status, out.json);
23388
+ return;
23389
+ }
21979
23390
  if (route.startsWith("/api/")) {
21980
23391
  json(res, 404, { error: `this viewer doesn't serve ${route}` });
21981
23392
  return;
@@ -22007,6 +23418,10 @@ function startServer(opts) {
22007
23418
  close: () => new Promise((r) => server.close(() => r())),
22008
23419
  setProfile: (p) => {
22009
23420
  liveProfile = p;
23421
+ profileRev++;
23422
+ },
23423
+ setShareSections: (s) => {
23424
+ opts.shareSections = s;
22010
23425
  },
22011
23426
  setProgress: (p) => {
22012
23427
  liveProgress = p;