chofex-cli 0.1.184 → 0.1.187

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +10 -8
  2. package/dist/index.js +419 -221
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -19,11 +19,11 @@ chofex register
19
19
  chofex status
20
20
  chofex requirements
21
21
  chofex challenge list
22
- chofex challenge query
23
- chofex challenge notebook
24
- chofex challenge test --source ./shipping.js
25
- chofex challenge evaluate --source ./shipping.js
26
- chofex challenge ranking
22
+ chofex challenge query --challenge black-box
23
+ chofex challenge notebook --challenge black-box
24
+ chofex challenge test --challenge black-box --source ./shipping.js
25
+ chofex challenge evaluate --challenge black-box --source ./shipping.js
26
+ chofex challenge ranking --challenge black-box
27
27
  chofex challenge init --challenge broken-agent
28
28
  cd broken-agent && npm test
29
29
  chofex challenge test --challenge broken-agent --source ./scheduler.js
@@ -46,9 +46,11 @@ actual. Usa `CHOFEX_AUTO_UPDATE=0` para desactivar esta comprobación.
46
46
  optional phone number, bio, portfolio URL, shipped project, LinkedIn and GitHub
47
47
  URLs, and Terms and Conditions. Use `--input` to submit a completed JSON
48
48
  application.
49
- After attendance confirmation, `chofex badge regenerate` updates the public
50
- badge name, one-line description, picture, and QR destination without changing
51
- the submitted application.
49
+ Acceptance creates a default badge from the Clerk profile visible to reviewers.
50
+ `chofex confirm` updates the public name, one-line description, and picture
51
+ while separately collecting the legal full name and venue details required for attendance. After confirmation,
52
+ `chofex badge regenerate` can also update those public fields and the QR
53
+ destination without changing the submitted application.
52
54
  The technical challenges do not block submission, but every challenge winner must submit
53
55
  an application before being accepted. Top results receive a direct pass; other
54
56
  scores give organizers another review metric.
package/dist/index.js CHANGED
@@ -58580,11 +58580,13 @@ var challengeCatalog = [
58580
58580
  code: "02",
58581
58581
  theme: "Broken Agent",
58582
58582
  title: "The Scheduler",
58583
- summary: "Take a plausible AI-generated job scheduler whose public tests already pass and harden it against production reality.",
58584
- coreSkill: "Production correctness & reliability",
58583
+ summary: "Audita un job scheduler generado por AI cuyos tests públicos ya pasan y hazlo confiable bajo condiciones reales de producción.",
58584
+ coreSkill: "Correctitud y confiabilidad en producción",
58585
58585
  format: "accuracy",
58586
- formatLabel: "Production readiness score",
58586
+ formatLabel: "Puntaje de preparación para producción",
58587
58587
  opensAt: "2026-09-24T19:25:00.000Z",
58588
+ closesAt: "2026-10-02T05:00:00.000Z",
58589
+ rankingVisibleAt: "2026-10-01T20:00:00.000Z",
58588
58590
  queryLimit: 0,
58589
58591
  evaluationLimit: 5,
58590
58592
  hiddenSampleSize: 100,
@@ -58664,18 +58666,18 @@ var isChallengeRankingVisibleAt = (challenge, now3) => {
58664
58666
  };
58665
58667
  var challengeTimeZoneOffsetMs = 5 * 60 * 60 * 1000;
58666
58668
  var monthNames = [
58667
- "January",
58668
- "February",
58669
- "March",
58670
- "April",
58671
- "May",
58672
- "June",
58673
- "July",
58674
- "August",
58675
- "September",
58676
- "October",
58677
- "November",
58678
- "December"
58669
+ "enero",
58670
+ "febrero",
58671
+ "marzo",
58672
+ "abril",
58673
+ "mayo",
58674
+ "junio",
58675
+ "julio",
58676
+ "agosto",
58677
+ "septiembre",
58678
+ "octubre",
58679
+ "noviembre",
58680
+ "diciembre"
58679
58681
  ];
58680
58682
  var formatChallengeOpeningInPeru = (opensAt) => {
58681
58683
  const localTime = new Date(Date.parse(opensAt) - challengeTimeZoneOffsetMs);
@@ -58687,9 +58689,9 @@ var formatChallengeOpeningInPeru = (opensAt) => {
58687
58689
  const year = localTime.getUTCFullYear();
58688
58690
  const hour = String(localTime.getUTCHours()).padStart(2, "0");
58689
58691
  const minute = String(localTime.getUTCMinutes()).padStart(2, "0");
58690
- return `${month} ${day}, ${year} at ${hour}:${minute} (UTC-5)`;
58692
+ return `${day} de ${month} de ${year} a las ${hour}:${minute} (UTC-5)`;
58691
58693
  };
58692
- var challengeOpeningNotice = (title, opensAt) => `${title} opens ${formatChallengeOpeningInPeru(opensAt)}. Queries and evaluations are disabled until then; no attempts will be consumed.`;
58694
+ var challengeOpeningNotice = (title, opensAt) => `${title} abre el ${formatChallengeOpeningInPeru(opensAt)}. Las consultas y evaluaciones están deshabilitadas hasta entonces; no se consumirá ningún intento.`;
58693
58695
  var ChallengeSlugSchema = exports_Schema.Literals([
58694
58696
  "black-box",
58695
58697
  "broken-agent",
@@ -58721,6 +58723,7 @@ var ChallengeScoreSchema = exports_Schema.Struct({
58721
58723
  meanError: exports_Schema.Number,
58722
58724
  queriesUsed: exports_Schema.Number,
58723
58725
  runtimeMs: exports_Schema.Number,
58726
+ executionCost: exports_Schema.optional(exports_Schema.Number),
58724
58727
  evaluationsUsed: exports_Schema.optional(exports_Schema.Number),
58725
58728
  breakdown: exports_Schema.optional(exports_Schema.Struct({
58726
58729
  coreBehavior: exports_Schema.Struct({
@@ -58815,6 +58818,7 @@ var ChallengeEvaluationSummarySchema = exports_Schema.Struct({
58815
58818
  meanError: exports_Schema.Number,
58816
58819
  queriesUsed: exports_Schema.Number,
58817
58820
  runtimeMs: exports_Schema.Number,
58821
+ executionCost: exports_Schema.optional(exports_Schema.Number),
58818
58822
  shareCode: exports_Schema.String,
58819
58823
  rank: exports_Schema.optional(exports_Schema.Number),
58820
58824
  percentile: exports_Schema.optional(exports_Schema.Number),
@@ -58854,6 +58858,7 @@ var ChallengeEvaluationResultSchema = exports_Schema.Struct({
58854
58858
  meanError: exports_Schema.Number,
58855
58859
  queriesUsed: exports_Schema.Number,
58856
58860
  runtimeMs: exports_Schema.Number,
58861
+ executionCost: exports_Schema.optional(exports_Schema.Number),
58857
58862
  shareCode: exports_Schema.String,
58858
58863
  rank: exports_Schema.optional(exports_Schema.Number),
58859
58864
  competitorCount: exports_Schema.optional(exports_Schema.Number),
@@ -58877,6 +58882,7 @@ var ChallengeRankingEntrySchema = exports_Schema.Struct({
58877
58882
  meanError: exports_Schema.Number,
58878
58883
  queriesUsed: exports_Schema.Number,
58879
58884
  runtimeMs: exports_Schema.Number,
58885
+ executionCost: exports_Schema.optional(exports_Schema.Number),
58880
58886
  evaluatedAt: exports_Schema.String
58881
58887
  });
58882
58888
  var ChallengeRankingSchema = exports_Schema.Struct({
@@ -59045,7 +59051,9 @@ var ApplicationPartId = exports_Schema.Literals([
59045
59051
  ]);
59046
59052
  var acceptedDetailsInputFields = {
59047
59053
  fullName: nonBlank(200),
59048
- phone,
59054
+ name: exports_Schema.optional(nonBlank(200)),
59055
+ oneLiner: exports_Schema.optional(nonBlank(30)),
59056
+ phone: exports_Schema.optional(phone),
59049
59057
  dateOfBirth: exports_Schema.String.pipe(exports_Schema.check(exports_Schema.isPattern(/^\d{4}-\d{2}-\d{2}$/, {
59050
59058
  message: "Must use YYYY-MM-DD format"
59051
59059
  }))),
@@ -59763,7 +59771,7 @@ var getChallengeRanking = (options, slug) => publicRequest(options, `/api/v1/cha
59763
59771
 
59764
59772
  // src/challenge-input.ts
59765
59773
  import { readFile as readFile3 } from "node:fs/promises";
59766
- var defaultChallengeSlug = "black-box";
59774
+ var defaultChallengeSlug = "broken-agent";
59767
59775
  var readStdin = async () => {
59768
59776
  const chunks = [];
59769
59777
  for await (const chunk of process.stdin) {
@@ -59892,28 +59900,28 @@ var challengeShowText = (attempt) => {
59892
59900
  const queriesRemaining = progress.queriesLimit - progress.queriesUsed;
59893
59901
  const evaluationsRemaining = progress.evaluationsLimit - progress.evaluationsUsed;
59894
59902
  if (challenge.slug === "broken-agent") {
59895
- let caseStatus2 = "READY TO AUDIT";
59903
+ let caseStatus2 = "LISTO PARA AUDITAR";
59896
59904
  if (attempt.latestEvaluation)
59897
- caseStatus2 = "EVALUATED";
59905
+ caseStatus2 = "EVALUADO";
59898
59906
  const lines3 = [
59899
- `${challenge.title.toUpperCase()} — CASE #${challenge.code}`,
59907
+ `${challenge.title.toUpperCase()} — CASO #${challenge.code}`,
59900
59908
  challenge.summary,
59901
59909
  "",
59902
- `CASE STATUS: ${caseStatus2}`,
59910
+ `ESTADO DEL CASO: ${caseStatus2}`,
59903
59911
  "",
59904
- "The public tests are green. Your job is to make the scheduler trustworthy under production conditions.",
59905
- "AI tools are allowed.",
59912
+ "Los tests públicos están verdes. Tu trabajo es hacer que el scheduler sea confiable bajo condiciones de producción.",
59913
+ "Las herramientas de AI están permitidas.",
59906
59914
  "",
59907
- `Evaluations ${evaluationsRemaining} / ${progress.evaluationsLimit} remaining`
59915
+ `Evaluaciones ${evaluationsRemaining} / ${progress.evaluationsLimit} restantes`
59908
59916
  ];
59909
59917
  if (progress.bestAccuracy !== undefined) {
59910
- lines3.push("", `Best score ${percent(progress.bestAccuracy)}`);
59918
+ lines3.push("", `Mejor puntaje ${percent(progress.bestAccuracy)}`);
59911
59919
  if (progress.rank !== undefined)
59912
- lines3.push(`Rank #${progress.rank}`);
59920
+ lines3.push(`Puesto #${progress.rank}`);
59913
59921
  if (progress.shareCode)
59914
- lines3.push(`Share code #${progress.shareCode}`);
59922
+ lines3.push(`Código #${progress.shareCode}`);
59915
59923
  }
59916
- lines3.push("", "YOUR NEXT MOVE", "Read the contract, audit scheduler.js, and keep the visible suite green.", " cd broken-agent && npm test", " chofex challenge test --challenge broken-agent --source ./broken-agent/scheduler.js");
59924
+ lines3.push("", "SIGUIENTE PASO", "Lee el contrato, audita scheduler.js y conserva verdes los tests visibles.", " cd broken-agent && npm test", " chofex challenge test --challenge broken-agent --source ./scheduler.js");
59917
59925
  return lines3.join(`
59918
59926
  `);
59919
59927
  }
@@ -60081,21 +60089,21 @@ var notebookCsvText = (observations) => {
60081
60089
  var challengeTestText = (result3) => {
60082
60090
  if (result3.kind === "broken_agent") {
60083
60091
  const lines3 = [
60084
- "PUBLIC SUITE — BROKEN AGENT",
60085
- `${result3.matchedObservations} / ${result3.observationCount} visible behaviors passing`
60092
+ "TESTS PÚBLICOS — BROKEN AGENT",
60093
+ `${result3.matchedObservations} / ${result3.observationCount} comportamientos visibles pasan`
60086
60094
  ];
60087
60095
  if (result3.accuracy === 1) {
60088
- lines3.push("", "Everything passes.", "Unfortunately, that does not mean the scheduler is production-correct.", "Reason about concurrency, restarts, failures, and idempotency before evaluating.", "", "Next: spend one official evaluation when you would ship it", " chofex challenge evaluate --challenge broken-agent --source ./broken-agent/scheduler.js");
60096
+ lines3.push("", "Todo pasa.", "Eso todavía no significa que el scheduler sea correcto en producción.", "Razona sobre concurrencia, reinicios, fallas e idempotencia antes de evaluar.", "", "Siguiente: usa una evaluación oficial cuando lo enviarías a producción", " chofex challenge evaluate --challenge broken-agent --source ./scheduler.js");
60089
60097
  return lines3.join(`
60090
60098
  `);
60091
60099
  }
60092
60100
  if (result3.mismatches.length > 0) {
60093
- lines3.push("", "Visible failures:");
60101
+ lines3.push("", "Fallas visibles:");
60094
60102
  for (const mismatch of result3.mismatches) {
60095
60103
  lines3.push(` ${String(mismatch.actual)}`);
60096
60104
  }
60097
60105
  }
60098
- lines3.push("", "Fix the public regressions before using an official evaluation.");
60106
+ lines3.push("", "Corrige las regresiones públicas antes de usar una evaluación oficial.");
60099
60107
  return lines3.join(`
60100
60108
  `);
60101
60109
  }
@@ -60126,29 +60134,30 @@ var challengeTestText = (result3) => {
60126
60134
  };
60127
60135
  var challengeEvaluateText = (result3) => {
60128
60136
  if (result3.breakdown) {
60137
+ const executionCost = result3.executionCost ?? result3.runtimeMs;
60129
60138
  const capabilityLines = [
60130
- ["Core behavior", result3.breakdown.coreBehavior],
60131
- ["Persistence", result3.breakdown.persistence],
60132
- ["Concurrency", result3.breakdown.concurrency],
60133
- ["Failure recovery", result3.breakdown.failureRecovery],
60134
- ["Idempotency", result3.breakdown.idempotency],
60135
- ["Regression safety", result3.breakdown.regressionSafety],
60136
- ["Performance", result3.breakdown.performance]
60139
+ ["Comportamiento base", result3.breakdown.coreBehavior],
60140
+ ["Persistencia", result3.breakdown.persistence],
60141
+ ["Concurrencia", result3.breakdown.concurrency],
60142
+ ["Recuperación", result3.breakdown.failureRecovery],
60143
+ ["Idempotencia", result3.breakdown.idempotency],
60144
+ ["Sin regresiones", result3.breakdown.regressionSafety],
60145
+ ["Rendimiento", result3.breakdown.performance]
60137
60146
  ];
60138
60147
  const lines3 = [
60139
- "OFFICIAL VERDICT — BROKEN AGENT — PRODUCTION READINESS",
60140
- `Score ${(result3.accuracy * 100).toFixed(2)} / ${result3.sampleSize}`,
60141
- `Runtime ${result3.runtimeMs} ms`,
60148
+ "VEREDICTO OFICIAL — BROKEN AGENT — PREPARACIÓN PARA PRODUCCIÓN",
60149
+ `Puntaje ${(result3.accuracy * 100).toFixed(2)} / ${result3.sampleSize}`,
60150
+ `Costo determinístico ${executionCost} ops`,
60142
60151
  "",
60143
- "Capability scores",
60152
+ "Puntajes por capacidad",
60144
60153
  ...capabilityLines.map(([label, score]) => `${label.padEnd(20)}${score.earned.toFixed(2)} / ${score.available}`)
60145
60154
  ];
60146
60155
  if (result3.rank !== undefined) {
60147
- lines3.push(`Rank #${result3.rank}`);
60156
+ lines3.push(`Puesto #${result3.rank}`);
60148
60157
  }
60149
- lines3.push(`Official evaluations remaining ${result3.evaluationsRemaining} / ${result3.evaluationsLimit}`, "", result3.shareText, "", "Next: inspect the leaderboard", " chofex challenge ranking --challenge broken-agent");
60158
+ lines3.push(`Evaluaciones oficiales restantes ${result3.evaluationsRemaining} / ${result3.evaluationsLimit}`, "", result3.shareText, "", "Siguiente: consulta el ranking", " chofex challenge ranking --challenge broken-agent");
60150
60159
  if (result3.evaluationsRemaining > 0) {
60151
- lines3.push("", "The evaluator reports capabilities, not individual hidden cases. Reason before submitting again.", " chofex challenge test --challenge broken-agent --source ./broken-agent/scheduler.js");
60160
+ lines3.push("", "El evaluador reporta capacidades, no casos ocultos individuales. Razona antes de volver a enviar.", " chofex challenge test --challenge broken-agent --source ./scheduler.js");
60152
60161
  }
60153
60162
  return lines3.join(`
60154
60163
  `);
@@ -60173,23 +60182,24 @@ var challengeRankingText = (ranking, now3 = new Date) => {
60173
60182
  const lines2 = [`${ranking.challenge.theme} — ${ranking.challenge.title}`];
60174
60183
  const { rankingVisibleAt } = ranking.challenge;
60175
60184
  if (rankingVisibleAt && !isChallengeRankingVisibleAt(ranking.challenge, now3)) {
60176
- lines2.push("", `Ranking available ${formatChallengeOpeningInPeru(rankingVisibleAt)}.`);
60185
+ lines2.push("", `Ranking disponible ${formatChallengeOpeningInPeru(rankingVisibleAt)}.`);
60177
60186
  return lines2.join(`
60178
60187
  `);
60179
60188
  }
60180
60189
  lines2.push("");
60181
60190
  if (ranking.entries.length === 0) {
60182
- lines2.push("No official evaluations yet.");
60191
+ lines2.push("Todavía no hay evaluaciones oficiales.");
60183
60192
  return lines2.join(`
60184
60193
  `);
60185
60194
  }
60186
60195
  if (ranking.challenge.slug === "broken-agent") {
60187
- lines2.push("Rank Score Points Runtime Name");
60196
+ lines2.push("Psto Puntaje Puntos Costo Nombre");
60188
60197
  for (const entry of ranking.entries.slice(0, 20)) {
60189
60198
  const rank = String(entry.rank).padStart(4, " ");
60190
60199
  const accuracy = percent(entry.accuracy).padStart(8, " ");
60191
60200
  const points = `${entry.exactCount}/${entry.sampleSize}`.padStart(11, " ");
60192
- const runtime = `${entry.runtimeMs}ms`.padStart(8, " ");
60201
+ const executionCost = entry.executionCost ?? entry.runtimeMs;
60202
+ const runtime = `${executionCost}ops`.padStart(8, " ");
60193
60203
  const profileLinks = [entry.githubUrl, entry.linkedInUrl].filter(Boolean);
60194
60204
  const participant = [entry.displayName, ...profileLinks].join(" ");
60195
60205
  lines2.push(`${rank} ${accuracy} ${points} ${runtime} ${participant}`);
@@ -60248,16 +60258,83 @@ var brokenAgentPackageJson = `${JSON.stringify({
60248
60258
  var brokenAgentStarterSource = `/**
60249
60259
  * Broken Agent — The Scheduler
60250
60260
  *
60251
- * A coding agent implemented this scheduler and declared the feature complete.
60252
- * Every public test passes. The implementation is not production-correct.
60253
- * Keep the createScheduler interface intact and harden the implementation.
60261
+ * Un agente de código implementó este scheduler y declaró la tarea terminada.
60262
+ * Todos los tests públicos pasan, pero todavía hay fallas sutiles de producción.
60263
+ * Mantén intacta la interfaz createScheduler y endurece la implementación.
60254
60264
  */
60255
- function createScheduler({ store, clock, execute }) {
60265
+ function createScheduler({ store, clock, execute, workerId }) {
60266
+ const clone = (value) => JSON.parse(JSON.stringify(value));
60267
+ const canonical = (value) => {
60268
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
60269
+ if (Array.isArray(value)) return "[" + value.map(canonical).join(",") + "]";
60270
+ const entries = Object.keys(value).sort().map((key) =>
60271
+ JSON.stringify(key) + ":" + canonical(value[key]),
60272
+ );
60273
+ return "{" + entries.join(",") + "}";
60274
+ };
60275
+ const isJsonValue = (value) => {
60276
+ if (value === null) return true;
60277
+ if (["string", "boolean"].includes(typeof value)) return true;
60278
+ if (typeof value === "number") return Number.isFinite(value);
60279
+ if (Array.isArray(value)) return value.every(isJsonValue);
60280
+ if (!value || typeof value !== "object") return false;
60281
+ const prototype = Object.getPrototypeOf(value);
60282
+ if (prototype !== Object.prototype && prototype !== null) return false;
60283
+ return Object.values(value).every(isJsonValue);
60284
+ };
60285
+ const isIsoTimestamp = (value) => {
60286
+ if (typeof value !== "string") return false;
60287
+ const match = value.match(
60288
+ /^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})(?:\\.\\d{1,3})?(?:Z|([+-])(\\d{2}):(\\d{2}))$/,
60289
+ );
60290
+ if (!match) return false;
60291
+ const [, year, month, day, hour, minute, second, , offsetHour, offsetMinute] = match;
60292
+ const values = [year, month, day, hour, minute, second, offsetHour || "0", offsetMinute || "0"].map(Number);
60293
+ const [yearValue, monthValue, dayValue, hourValue, minuteValue, secondValue, offsetHourValue, offsetMinuteValue] = values;
60294
+ const maximumDay = new Date(Date.UTC(yearValue, monthValue, 0)).getUTCDate();
60295
+ return monthValue >= 1 && monthValue <= 12 &&
60296
+ dayValue >= 1 && dayValue <= maximumDay &&
60297
+ hourValue <= 23 && minuteValue <= 59 && secondValue <= 59 &&
60298
+ offsetHourValue <= 23 && offsetMinuteValue <= 59 &&
60299
+ Number.isFinite(Date.parse(value));
60300
+ };
60301
+ const ordered = (jobs) => jobs.sort((left, right) =>
60302
+ Date.parse(left.runAt) - Date.parse(right.runAt) ||
60303
+ left.id.localeCompare(right.id),
60304
+ );
60305
+ const normalize = (input) => {
60306
+ if (!input || typeof input !== "object") throw new Error("Job is required");
60307
+ if (typeof input.id !== "string" || !/^[A-Za-z0-9_-]{1,64}$/.test(input.id)) {
60308
+ throw new Error("Job id is invalid");
60309
+ }
60310
+ if (
60311
+ !isIsoTimestamp(input.runAt)
60312
+ ) throw new Error("runAt must include an explicit timezone");
60313
+ if (!isJsonValue(input.payload)) throw new Error("payload must be JSON");
60314
+ return {
60315
+ id: input.id,
60316
+ runAt: input.runAt,
60317
+ payload: clone(input.payload),
60318
+ status: "pending",
60319
+ attempts: 0,
60320
+ };
60321
+ };
60322
+
60256
60323
  return {
60257
60324
  async schedule(input) {
60258
- const job = { ...input, status: "pending" };
60259
- await store.transaction((tx) => tx.put(job));
60260
- return job;
60325
+ const requested = normalize(input);
60326
+ return store.transaction((tx) => {
60327
+ const existing = tx.get(requested.id);
60328
+ if (!existing) {
60329
+ tx.put(requested);
60330
+ return clone(requested);
60331
+ }
60332
+ const identical =
60333
+ existing.runAt === requested.runAt &&
60334
+ canonical(existing.payload) === canonical(requested.payload);
60335
+ if (!identical) throw new Error("A different job already uses this id");
60336
+ return clone(existing);
60337
+ });
60261
60338
  },
60262
60339
 
60263
60340
  async cancel(id) {
@@ -60270,18 +60347,59 @@ function createScheduler({ store, clock, execute }) {
60270
60347
  },
60271
60348
 
60272
60349
  async list() {
60273
- return store.transaction((tx) => tx.list());
60350
+ const jobs = await store.transaction((tx) => tx.list());
60351
+ return ordered(jobs).map(clone);
60274
60352
  },
60275
60353
 
60276
60354
  async runDue() {
60277
- const now = clock.now().getTime();
60278
- const jobs = await store.transaction((tx) => tx.list());
60279
- for (const job of jobs) {
60280
- if (job.status !== "pending" || Date.parse(job.runAt) > now) continue;
60281
- await execute(job);
60282
- await store.transaction((tx) =>
60283
- tx.put({ ...job, status: "completed" }),
60284
- );
60355
+ const visited = new Set();
60356
+ while (true) {
60357
+ const now = clock.now().getTime();
60358
+ const claimed = await store.transaction((tx) => {
60359
+ for (const job of tx.list()) {
60360
+ if (
60361
+ job.status === "running" &&
60362
+ typeof job.leaseUntil === "number" &&
60363
+ job.leaseUntil < now
60364
+ ) {
60365
+ tx.put({ ...job, status: "pending" });
60366
+ }
60367
+ }
60368
+ const due = ordered(tx.list()).find((job) =>
60369
+ job.status === "pending" &&
60370
+ !visited.has(job.id) &&
60371
+ Date.parse(job.runAt) <= now,
60372
+ );
60373
+ if (!due) return undefined;
60374
+ const running = {
60375
+ ...due,
60376
+ status: "running",
60377
+ attempts: due.attempts + 1,
60378
+ owner: workerId,
60379
+ leaseUntil: now + 30_000,
60380
+ };
60381
+ tx.put(running);
60382
+ return running;
60383
+ });
60384
+ if (!claimed) return;
60385
+ visited.add(claimed.id);
60386
+
60387
+ try {
60388
+ await execute(clone(claimed));
60389
+ await store.transaction((tx) => {
60390
+ const current = tx.get(claimed.id);
60391
+ if (current?.status === "running" && current.owner === workerId) {
60392
+ tx.put({ ...current, status: "completed" });
60393
+ }
60394
+ });
60395
+ } catch {
60396
+ await store.transaction((tx) => {
60397
+ const current = tx.get(claimed.id);
60398
+ if (current?.status !== "running" || current.owner !== workerId) return;
60399
+ const status = current.attempts > 3 ? "failed" : "pending";
60400
+ tx.put({ ...current, status });
60401
+ });
60402
+ }
60285
60403
  }
60286
60404
  },
60287
60405
  };
@@ -60291,78 +60409,98 @@ module.exports = { createScheduler };
60291
60409
  `;
60292
60410
  var brokenAgentReadme = `# Broken Agent — The Scheduler
60293
60411
 
60294
- > A coding agent was asked to implement a feature. It says it is done. It is
60295
- > not. Your job is to ship it.
60412
+ > Un agente de código dice que terminó. Todos los tests públicos pasan. Tu
60413
+ > trabajo es decidir si realmente lo enviarías a producción y repararlo.
60296
60414
 
60297
- The repository starts with all public tests passing. Repair \`scheduler.js\`
60298
- without changing its exported \`createScheduler(dependencies)\` interface.
60415
+ Repara \`scheduler.js\` sin cambiar la interfaz exportada
60416
+ \`createScheduler(dependencies)\`.
60299
60417
 
60300
- ## Commands
60418
+ ## Comandos
60301
60419
 
60302
60420
  \`\`\`sh
60303
60421
  npm test
60304
- chofex challenge test --challenge broken-agent --source ./broken-agent/scheduler.js
60305
- chofex challenge evaluate --challenge broken-agent --source ./broken-agent/scheduler.js
60422
+ chofex challenge test --challenge broken-agent --source ./scheduler.js
60423
+ chofex challenge evaluate --challenge broken-agent --source ./scheduler.js
60306
60424
  \`\`\`
60307
60425
 
60308
- Local and public tests are unlimited. You have **5 official evaluations**
60309
- against hidden production scenarios. AI tools are explicitly allowed.
60426
+ Los tests locales y públicos son ilimitados. Tienes **5 evaluaciones oficiales**
60427
+ contra escenarios ocultos. Las herramientas de AI están permitidas.
60310
60428
 
60311
- ## Public contract
60429
+ ## Contrato normativo
60312
60430
 
60313
- \`createScheduler({ store, clock, execute, workerId })\` returns:
60431
+ \`createScheduler({ store, clock, execute, workerId })\` devuelve:
60314
60432
 
60315
60433
  - \`schedule({ id, runAt, payload })\`
60316
60434
  - \`cancel(id)\`
60317
60435
  - \`list()\`
60318
60436
  - \`runDue()\`
60319
60437
 
60320
- ### Jobs
60321
-
60322
- - \`id\` matches \`^[A-Za-z0-9_-]{1,64}$\`.
60323
- - \`runAt\` is an ISO-8601 timestamp with an explicit timezone.
60324
- - \`payload\` is JSON-serializable.
60325
- - Scheduling an identical job ID is idempotent. Reusing an ID for different
60326
- work must reject without changing the original job.
60327
- - Past timestamps are valid and become due immediately.
60328
- - \`list()\` returns jobs ordered by \`runAt\`, then \`id\`.
60329
-
60330
- ### Durability and concurrency
60331
-
60332
- - The supplied store survives scheduler instances and process restarts.
60333
- - Several workers may call \`runDue()\` concurrently against the same store.
60334
- - A worker claim lasts 30 seconds. Another worker may reclaim the job when
60335
- \`clock.now()\` reaches that lease deadline.
60336
- - A pending job may be cancelled. A running or terminal job may not.
60337
- - Failed executions retry, without preventing other due jobs from running.
60338
- - A job becomes \`failed\` after 3 unsuccessful attempts.
60339
- - The execution adapter applies effects idempotently by job ID. A worker may
60340
- stop after the effect is applied but before completion is recorded; retrying
60341
- must eventually complete the job without duplicating the effect.
60342
-
60343
- The store exposes only \`transaction(action)\`. Inside an action, \`tx\`
60344
- provides synchronous \`get(id)\`, \`put(job)\`, \`delete(id)\`, and \`list()\`
60345
- operations. Transactions are serialized. Do not keep a transaction open while
60346
- executing a job.
60347
-
60348
- \`clock.now()\` returns a \`Date\`. \`execute(job)\` returns a promise.
60349
-
60350
- ## Evaluation
60351
-
60352
- The hidden evaluator checks the published contract under deterministic
60353
- concurrency, restart, failure, idempotency, compatibility, and load scenarios.
60354
- It reports capability-level scores, not individual hidden-test failures.
60355
-
60356
- - Core behavior: 10 points
60357
- - Persistence: 15 points
60358
- - Concurrency: 20 points
60359
- - Failure recovery: 20 points
60360
- - Idempotency: 15 points
60361
- - Regression safety: 15 points
60362
- - Performance: 5 points, available only when the load jobs complete correctly
60363
-
60364
- Ranking order is total score, fewer official evaluations used, runtime, then
60365
- official submission time. Submission time is only the final tie-breaker.
60438
+ ### Jobs y valores de retorno
60439
+
60440
+ - \`id\` cumple \`^[A-Za-z0-9_-]{1,64}$\`.
60441
+ - \`runAt\` usa el perfil ISO-8601
60442
+ \`YYYY-MM-DDTHH:mm:ss[.SSS](Z|±HH:mm)\`: los segundos y la zona son
60443
+ obligatorios, y la fracción opcional admite entre uno y tres dígitos.
60444
+ - \`payload\` es un valor JSON estricto: null, boolean, string, número finito,
60445
+ arreglo u objeto compuesto únicamente por otros valores JSON.
60446
+ - Los timestamps pasados son válidos y quedan vencidos inmediatamente.
60447
+ - \`schedule()\` persiste y devuelve el job. Programar el mismo ID, timestamp y
60448
+ payload JSON estructuralmente equivalente es idempotente. Reutilizar un ID
60449
+ para otro trabajo debe rechazar sin cambiar el job original.
60450
+ - \`cancel()\` devuelve true solo cuando cambia un job pendiente a cancelado.
60451
+ Un job running, completed, failed o inexistente no puede cancelarse.
60452
+ - \`list()\` devuelve todos los jobs ordenados cronológicamente por \`runAt\` y,
60453
+ ante empate, por \`id\`. Como mínimo expone \`id\`, \`runAt\`, \`payload\`,
60454
+ \`status\` y \`attempts\`.
60455
+
60456
+ ### Estado, concurrencia y recuperación
60457
+
60458
+ - Los estados son \`pending\`, \`running\`, \`completed\`, \`failed\` y
60459
+ \`cancelled\`. Los jobs nuevos empiezan pending con cero intentos.
60460
+ - El store sobrevive nuevas instancias del scheduler y reinicios del proceso.
60461
+ - Varios workers, incluso instancias que reutilizan un \`workerId\` después de
60462
+ reiniciar, pueden llamar \`runDue()\` contra el mismo store.
60463
+ - \`runDue()\` intenta una vez cada job vencido al inicio de esa llamada. Un job
60464
+ que falla queda pendiente para una llamada posterior y no bloquea otros jobs.
60465
+ - Cada ejecución debe reclamarse atómicamente antes de llamar al executor. El
60466
+ claim dura 30 segundos y necesita una identidad única distinta de
60467
+ \`workerId\`. Cuando \`clock.now()\` alcanza el deadline, otro worker puede
60468
+ reclamarlo. Un worker obsoleto nunca puede completar ni reabrir un claim más
60469
+ nuevo.
60470
+ - Un intento se cuenta al reclamar el job. Después de 3 ejecuciones fallidas el
60471
+ job queda failed.
60472
+ - \`execute(job)\` puede fallar antes o después de aplicar el efecto. El adapter
60473
+ aplica efectos idempotentemente por job ID, por lo que reintentar debe llevar
60474
+ el job a completed sin duplicar el efecto. No se promete exactly-once para
60475
+ efectos arbitrarios.
60476
+ - \`runDue()\` resuelve cuando ya procesó todos los jobs que podía reclamar en
60477
+ esa llamada. No mantiene una transacción abierta mientras ejecuta un job.
60478
+
60479
+ El store expone solo \`transaction(action)\`. Dentro de una acción, \`tx\`
60480
+ ofrece operaciones síncronas \`get(id)\`, \`put(job)\`, \`delete(id)\` y
60481
+ \`list()\`. Las transacciones están serializadas. \`clock.now()\` devuelve un
60482
+ \`Date\`; \`execute(job)\` devuelve una promesa.
60483
+
60484
+ ## Evaluación
60485
+
60486
+ El evaluador usa variantes determinísticas por participante para probar el
60487
+ contrato bajo concurrencia, reinicios, fallas, idempotencia, compatibilidad y
60488
+ carga. Reporta puntajes por capacidad, no casos ocultos individuales.
60489
+
60490
+ - Comportamiento base: 10 puntos
60491
+ - Persistencia: 15 puntos
60492
+ - Concurrencia: 20 puntos
60493
+ - Recuperación ante fallas: 20 puntos
60494
+ - Idempotencia: 15 puntos
60495
+ - Seguridad contra regresiones: 15 puntos
60496
+ - Rendimiento: 5 puntos, solo si la carga termina correctamente y sin perder
60497
+ seguridad de concurrencia
60498
+
60499
+ El ranking exige una postulación enviada o revisada. Ordena por puntaje total,
60500
+ menos evaluaciones oficiales, costo determinístico de ejecución y hora del mejor
60501
+ envío. El costo es \`transacciones + ceil(jobs recorridos por tx.list / 10)\` en
60502
+ una carga equivalente para todos; no usa tiempo de servidor. Todos los puntajes
60503
+ válidos aparecen en el ranking.
60366
60504
  `;
60367
60505
  var brokenAgentPublicTestSource = `const test = require("node:test");
60368
60506
  const assert = require("node:assert/strict");
@@ -60394,52 +60532,99 @@ class MemoryStore {
60394
60532
  }
60395
60533
  }
60396
60534
 
60397
- const setup = () => {
60398
- const store = new MemoryStore();
60399
- const now = new Date("2026-10-17T15:00:00.000Z");
60535
+ const setup = (options = {}) => {
60536
+ const store = options.store || new MemoryStore();
60537
+ const now = options.now || new Date("2026-10-17T15:00:00.000Z");
60400
60538
  const executed = [];
60539
+ const execute = options.execute || (async (job) => executed.push(job.id));
60401
60540
  const scheduler = createScheduler({
60402
60541
  store,
60403
60542
  clock: { now: () => new Date(now) },
60404
- execute: async (job) => executed.push(job.id),
60405
- workerId: "public-worker",
60543
+ execute,
60544
+ workerId: options.workerId || "public-worker",
60406
60545
  });
60407
60546
  return { store, scheduler, executed };
60408
60547
  };
60409
60548
 
60410
- const job = (id, runAt = "2026-10-17T15:00:00.000Z") => ({
60549
+ const job = (id, runAt = "2026-10-17T15:00:00.000Z", payload = { message: id }) => ({
60411
60550
  id,
60412
60551
  runAt,
60413
- payload: { message: id },
60552
+ payload,
60414
60553
  });
60415
60554
 
60416
- test("schedules and lists a job", async () => {
60555
+ test("programa un job y conserva el estado público", async () => {
60417
60556
  const { scheduler } = setup();
60418
- await scheduler.schedule(job("job-1"));
60557
+ const scheduled = await scheduler.schedule(job("job-1"));
60558
+ assert.equal(scheduled.status, "pending");
60559
+ assert.equal(scheduled.attempts, 0);
60419
60560
  assert.equal((await scheduler.list()).length, 1);
60420
60561
  });
60421
60562
 
60422
- test("executes a due job", async () => {
60423
- const { scheduler, executed } = setup();
60424
- await scheduler.schedule(job("job-1"));
60425
- await scheduler.runDue();
60426
- assert.deepEqual(executed, ["job-1"]);
60427
- });
60428
-
60429
- test("does not execute a future job", async () => {
60563
+ test("ejecuta vencidos y deja futuros pendientes", async () => {
60430
60564
  const { scheduler, executed } = setup();
60565
+ await scheduler.schedule(job("due"));
60431
60566
  await scheduler.schedule(job("future", "2026-10-17T16:00:00.000Z"));
60432
60567
  await scheduler.runDue();
60433
- assert.deepEqual(executed, []);
60568
+ assert.deepEqual(executed, ["due"]);
60569
+ const jobs = await scheduler.list();
60570
+ assert.equal(jobs.find((entry) => entry.id === "due").status, "completed");
60571
+ assert.equal(jobs.find((entry) => entry.id === "future").status, "pending");
60434
60572
  });
60435
60573
 
60436
- test("cancels a pending job", async () => {
60574
+ test("cancela únicamente jobs pendientes", async () => {
60437
60575
  const { scheduler, executed } = setup();
60438
60576
  await scheduler.schedule(job("cancel-me"));
60439
60577
  assert.equal(await scheduler.cancel("cancel-me"), true);
60578
+ assert.equal(await scheduler.cancel("cancel-me"), false);
60440
60579
  await scheduler.runDue();
60441
60580
  assert.deepEqual(executed, []);
60442
60581
  });
60582
+
60583
+ test("programar el mismo trabajo es idempotente y los conflictos rechazan", async () => {
60584
+ const { scheduler } = setup();
60585
+ await scheduler.schedule(job("same", undefined, { a: 1, b: 2 }));
60586
+ await scheduler.schedule(job("same", undefined, { b: 2, a: 1 }));
60587
+ await assert.rejects(() => scheduler.schedule(job("same", undefined, { a: 2 })));
60588
+ assert.equal((await scheduler.list()).length, 1);
60589
+ });
60590
+
60591
+ test("valida IDs, timestamps con zona y payloads JSON", async () => {
60592
+ const { scheduler } = setup();
60593
+ await assert.rejects(() => scheduler.schedule(job("contains spaces")));
60594
+ await assert.rejects(() => scheduler.schedule(job("bad-time", "tomorrow-ish")));
60595
+ await assert.rejects(() => scheduler.schedule(job("missing-zone", "2026-10-17T15:00:00")));
60596
+ await assert.rejects(() => scheduler.schedule(job("missing-seconds", "2026-10-17T15:00Z")));
60597
+ await assert.rejects(() => scheduler.schedule(job("long-fraction", "2026-10-17T15:00:00.1234Z")));
60598
+ await assert.rejects(() => scheduler.schedule(job("bad-payload", undefined, { value: undefined })));
60599
+ });
60600
+
60601
+ test("ordena cronológicamente y usa el ID para desempatar", async () => {
60602
+ const { scheduler } = setup();
60603
+ await scheduler.schedule(job("later", "2026-10-17T16:30:00.000Z"));
60604
+ await scheduler.schedule(job("first", "2026-10-17T10:00:00-05:00"));
60605
+ await scheduler.schedule(job("also-first", "2026-10-17T15:00:00.000Z"));
60606
+ assert.deepEqual((await scheduler.list()).map((entry) => entry.id), ["also-first", "first", "later"]);
60607
+ });
60608
+
60609
+ test("una falla no bloquea otros jobs y reintenta en la llamada siguiente", async () => {
60610
+ let failures = 1;
60611
+ const executed = [];
60612
+ const { scheduler } = setup({
60613
+ execute: async (entry) => {
60614
+ if (entry.id === "flaky" && failures > 0) {
60615
+ failures -= 1;
60616
+ throw new Error("transient");
60617
+ }
60618
+ executed.push(entry.id);
60619
+ },
60620
+ });
60621
+ await scheduler.schedule(job("flaky"));
60622
+ await scheduler.schedule(job("healthy"));
60623
+ await scheduler.runDue();
60624
+ assert.deepEqual(executed, ["healthy"]);
60625
+ await scheduler.runDue();
60626
+ assert.deepEqual(executed, ["healthy", "flaky"]);
60627
+ });
60443
60628
  `;
60444
60629
 
60445
60630
  // src/challenge-scaffold.ts
@@ -60653,69 +60838,69 @@ var badgeText = (result3) => {
60653
60838
 
60654
60839
  // src/challenge-commands.ts
60655
60840
  var challengeQuickstart = {
60656
- title: "THE SHIPPING MACHINE",
60841
+ title: "BROKEN AGENT — THE SCHEDULER",
60657
60842
  story: [
60658
- "A delivery company is about to retire the service that prices every shipment.",
60659
- "There is no documentation and no source code—only five controls and the price the machine returns."
60843
+ "Un agente de código implementó un job scheduler y declaró la tarea terminada.",
60844
+ "Los siete tests públicos pasan, pero eso no demuestra que sea seguro bajo concurrencia, reinicios y fallas."
60660
60845
  ],
60661
- mission: "Learn the hidden pricing rules, then replace the machine with your own calculateShipping(input) function.",
60846
+ mission: "Audita scheduler.js, conserva createScheduler(dependencies) y haz que el sistema cumpla el contrato de producción.",
60662
60847
  rules: [
60663
- "You have 25 oracle queries to gather evidence.",
60664
- "You have 3 official evaluations against hidden shipments.",
60665
- "Local notebook tests are free. Accuracy wins, then exact matches, then fewer oracle queries.",
60666
- "AI tools are welcome, but the hidden rules are personalized to you."
60848
+ "Tienes 5 evaluaciones oficiales contra variantes ocultas y determinísticas por participante.",
60849
+ "Los tests locales y públicos son ilimitados.",
60850
+ "Gana el puntaje total; los empates usan menos evaluaciones, costo determinístico y hora del mejor envío.",
60851
+ "Las herramientas de AI están permitidas."
60667
60852
  ],
60668
60853
  workflow: [
60669
60854
  {
60670
60855
  step: 1,
60671
- action: "Sign in",
60856
+ action: "Inicia sesión",
60672
60857
  command: "chofex login",
60673
- note: "Opens Clerk authentication in your browser."
60858
+ note: "Abre la autenticación de Clerk en tu navegador."
60674
60859
  },
60675
60860
  {
60676
60861
  step: 2,
60677
- action: "Prepare your field kit",
60678
- command: "chofex challenge init",
60679
- note: "Creates a documented shipping.js starter without overwriting existing work."
60862
+ action: "Crea el repositorio del challenge",
60863
+ command: "chofex challenge init --challenge broken-agent",
60864
+ note: "Crea broken-agent/ con el contrato, el scheduler y siete tests públicos sin sobrescribir trabajo existente."
60680
60865
  },
60681
60866
  {
60682
60867
  step: 3,
60683
- action: "Check your budget",
60684
- command: "chofex challenge show",
60685
- note: "Shows remaining oracle queries and official evaluations."
60868
+ action: "Confirma el punto de partida",
60869
+ command: "cd broken-agent && npm test",
60870
+ note: "Todo pasa. Ahora debes encontrar los riesgos que los happy paths no cubren."
60686
60871
  },
60687
60872
  {
60688
60873
  step: 4,
60689
- action: "Probe the Black Box",
60690
- command: "chofex challenge query --distance 10 --weight 3 --hour 14 --fragile false --express false",
60691
- note: "A successful query reveals one shipping price and consumes one query."
60874
+ action: "Revisa tu presupuesto",
60875
+ command: "chofex challenge show --challenge broken-agent",
60876
+ note: "Muestra las evaluaciones oficiales restantes y tu mejor puntaje."
60692
60877
  },
60693
60878
  {
60694
60879
  step: 5,
60695
- action: "Study your observations",
60696
- command: "chofex challenge notebook",
60697
- note: "Compare inputs and outputs to infer the pricing rules."
60880
+ action: "Audita y repara",
60881
+ command: "$EDITOR scheduler.js",
60882
+ note: "Razona sobre claims únicos, leases, reinicios, carreras, cancelación, reintentos e idempotencia."
60698
60883
  },
60699
60884
  {
60700
60885
  step: 6,
60701
- action: "Edit and test your replacement",
60702
- command: "chofex challenge test --source ./shipping.js",
60703
- note: "Checks your solution against your notebook without consuming an official evaluation."
60886
+ action: "Ejecuta los tests públicos",
60887
+ command: "chofex challenge test --challenge broken-agent --source ./scheduler.js",
60888
+ note: "Es seguro repetirlos y no consumen evaluaciones oficiales."
60704
60889
  },
60705
60890
  {
60706
60891
  step: 7,
60707
- action: "Submit to the hidden test set",
60708
- command: "chofex challenge evaluate --source ./shipping.js",
60709
- note: "Official evaluations are limited. Use test as often as needed, then evaluate when your solution is ready."
60892
+ action: "Solicita un veredicto oculto",
60893
+ command: "chofex challenge evaluate --challenge broken-agent --source ./scheduler.js",
60894
+ note: "Úsalo solo cuando enviarías la implementación a producción."
60710
60895
  },
60711
60896
  {
60712
60897
  step: 8,
60713
- action: "Check the leaderboard",
60714
- command: "chofex challenge ranking",
60715
- note: "Shows the public ranking without consuming budget."
60898
+ action: "Consulta el ranking",
60899
+ command: "chofex challenge ranking --challenge broken-agent",
60900
+ note: "Se revela el 1 de octubre a las 15:00, hora de Perú."
60716
60901
  }
60717
60902
  ],
60718
- helpCommand: "chofex challenge query --help"
60903
+ helpCommand: "chofex challenge test --help"
60719
60904
  };
60720
60905
  var launchNoticeFor = (slug) => {
60721
60906
  const challenge = challengeBySlug(slug);
@@ -60759,7 +60944,7 @@ var challengeQuickstartText = (participationState, launchNotice) => {
60759
60944
  `);
60760
60945
  };
60761
60946
  var optionalString = (name, description) => exports_Flag.string(name).pipe(exports_Flag.optional, exports_Flag.withDescription(description));
60762
- var challengeFlag = exports_Flag.string("challenge").pipe(exports_Flag.withDefault(defaultChallengeSlug), exports_Flag.withDescription("Challenge slug (default: black-box)"));
60947
+ var challengeFlag = exports_Flag.string("challenge").pipe(exports_Flag.withDefault(defaultChallengeSlug), exports_Flag.withDescription("Challenge slug (default: broken-agent)"));
60763
60948
  var sourceFlag = optionalString("source", "JavaScript challenge solution file");
60764
60949
  var inputFlag = optionalString("input", "JSON file, or - for stdin");
60765
60950
  var formatFlag = exports_Flag.choice("format", ["table", "json", "csv"]).pipe(exports_Flag.withDefault("table"), exports_Flag.withDescription("Notebook format"));
@@ -60780,24 +60965,24 @@ var challengeInitText = (result3) => {
60780
60965
  if (result3.challenge === "broken-agent") {
60781
60966
  if (result3.status === "exists") {
60782
60967
  return [
60783
- `${result3.path} already exists. Left it unchanged.`,
60968
+ `${result3.path} ya existe. No se modificó.`,
60784
60969
  "",
60785
- "Next: keep hardening your scheduler, then run the public tests",
60970
+ "Siguiente: continúa endureciendo el scheduler y ejecuta los tests públicos",
60786
60971
  ` cd ${result3.path} && npm test`,
60787
- ` chofex challenge test --challenge broken-agent --source ./${result3.path}/scheduler.js`
60972
+ " chofex challenge test --challenge broken-agent --source ./scheduler.js"
60788
60973
  ].join(`
60789
60974
  `);
60790
60975
  }
60791
60976
  return [
60792
- `Created ${result3.path}`,
60793
- "The previous agent says the scheduler is done. Everything passes.",
60794
- "Unfortunately, the agent was wrong.",
60977
+ `Se creó ${result3.path}`,
60978
+ "El agente anterior dice que el scheduler está terminado. Todo pasa.",
60979
+ "Pero todavía no es seguro para producción.",
60795
60980
  "",
60796
- "Start with the public contract and tests:",
60981
+ "Empieza con el contrato normativo y los tests públicos:",
60797
60982
  ` cd ${result3.path} && npm test`,
60798
60983
  "",
60799
- "Then repair scheduler.js and test it safely:",
60800
- ` chofex challenge test --challenge broken-agent --source ./${result3.path}/scheduler.js`
60984
+ "Luego repara scheduler.js y pruébalo sin consumir evaluaciones:",
60985
+ " chofex challenge test --challenge broken-agent --source ./scheduler.js"
60801
60986
  ].join(`
60802
60987
  `);
60803
60988
  }
@@ -60850,24 +61035,24 @@ var initCommand = exports_Command.make("init", { challenge: challengeFlag }, exp
60850
61035
  data
60851
61036
  })));
60852
61037
  yield* execute(options.output, operation, challengeInitText);
60853
- })).pipe(exports_Command.withDescription("Create a challenge starter without overwriting existing work."), exports_Command.withExamples([
61038
+ })).pipe(exports_Command.withDescription("Crea el starter de un challenge sin sobrescribir trabajo existente."), exports_Command.withExamples([
60854
61039
  {
60855
- command: "chofex challenge init",
60856
- description: "Prepare a safe JavaScript solution file"
61040
+ command: "chofex challenge init --challenge broken-agent",
61041
+ description: "Prepara el repositorio de Broken Agent"
60857
61042
  },
60858
61043
  {
60859
61044
  command: "chofex challenge init --challenge broken-agent",
60860
- description: "Prepare the Broken Agent starter repository"
61045
+ description: "Usa explícitamente el slug del challenge"
60861
61046
  }
60862
61047
  ]));
60863
61048
  var showCommand = exports_Command.make("show", { challenge: challengeFlag }, exports_Effect.fn("challengeShowCommand")(function* ({ challenge }) {
60864
61049
  const options = yield* root;
60865
61050
  const token = exports_Option.getOrUndefined(options.token);
60866
61051
  yield* execute(options.output, getChallengeAttempt({ apiUrl: options.apiUrl, token }, challenge), challengeShowText);
60867
- })).pipe(exports_Command.withDescription("Check your Black Box query budget, evaluation budget, score, and saved observations. Requires sign-in."), exports_Command.withExamples([
61052
+ })).pipe(exports_Command.withDescription("Consulta presupuesto, puntaje y progreso del challenge. Requiere iniciar sesión."), exports_Command.withExamples([
60868
61053
  {
60869
61054
  command: "chofex challenge show",
60870
- description: "Check your progress before spending limited attempts"
61055
+ description: "Revisa tu progreso antes de consumir intentos limitados"
60871
61056
  }
60872
61057
  ]));
60873
61058
  var queryCommand = exports_Command.make("query", {
@@ -60902,11 +61087,11 @@ var queryCommand = exports_Command.make("query", {
60902
61087
  yield* execute(options.output, operation, challengeQueryText);
60903
61088
  })).pipe(exports_Command.withDescription("Send one shipment to the undocumented oracle. A successful query consumes one limited request. Requires sign-in."), exports_Command.withExamples([
60904
61089
  {
60905
- command: "chofex challenge query --distance 10 --weight 3 --hour 14 --fragile false --express false",
61090
+ command: "chofex challenge query --challenge black-box --distance 10 --weight 3 --hour 14 --fragile false --express false",
60906
61091
  description: "Spend one oracle query"
60907
61092
  },
60908
61093
  {
60909
- command: "chofex challenge query --input shipment.json",
61094
+ command: "chofex challenge query --challenge black-box --input shipment.json",
60910
61095
  description: "Read the shipment fields from a JSON file"
60911
61096
  }
60912
61097
  ]));
@@ -60924,11 +61109,11 @@ var notebookCommand = exports_Command.make("notebook", { challenge: challengeFla
60924
61109
  });
60925
61110
  })).pipe(exports_Command.withDescription("Review every input and price you observed. Export table, JSON, or CSV for analysis. Requires sign-in."), exports_Command.withExamples([
60926
61111
  {
60927
- command: "chofex challenge notebook",
61112
+ command: "chofex challenge notebook --challenge black-box",
60928
61113
  description: "Read observations in a terminal table"
60929
61114
  },
60930
61115
  {
60931
- command: "chofex challenge notebook --format csv > observations.csv",
61116
+ command: "chofex challenge notebook --challenge black-box --format csv > observations.csv",
60932
61117
  description: "Save observations for a spreadsheet"
60933
61118
  }
60934
61119
  ]));
@@ -60942,8 +61127,8 @@ var testCommand = exports_Command.make("test", { challenge: challengeFlag, sourc
60942
61127
  yield* execute(options.output, operation, challengeTestText);
60943
61128
  })).pipe(exports_Command.withDescription("Run the challenge's public tests. Safe to repeat; official evaluations are not consumed. Requires sign-in."), exports_Command.withExamples([
60944
61129
  {
60945
- command: "chofex challenge test --source ./shipping.js",
60946
- description: "Test a JavaScript replacement against your notebook"
61130
+ command: "chofex challenge test --challenge broken-agent --source ./scheduler.js",
61131
+ description: "Ejecuta los tests públicos de Broken Agent"
60947
61132
  }
60948
61133
  ]));
60949
61134
  var evaluateCommand = exports_Command.make("evaluate", { challenge: challengeFlag, source: sourceFlag }, exports_Effect.fn("challengeEvaluateCommand")(function* ({ challenge, source }) {
@@ -60956,8 +61141,8 @@ var evaluateCommand = exports_Command.make("evaluate", { challenge: challengeFla
60956
61141
  yield* execute(options.output, operation, challengeEvaluateText);
60957
61142
  })).pipe(exports_Command.withDescription("Score a solution on hidden cases. This consumes one limited official evaluation. Requires sign-in."), exports_Command.withExamples([
60958
61143
  {
60959
- command: "chofex challenge evaluate --source ./shipping.js",
60960
- description: "Spend one official evaluation when your solution is ready"
61144
+ command: "chofex challenge evaluate --challenge broken-agent --source ./scheduler.js",
61145
+ description: "Consume una evaluación oficial cuando tu solución esté lista"
60961
61146
  }
60962
61147
  ]));
60963
61148
  var rankingCommand = exports_Command.make("ranking", { challenge: challengeFlag }, exports_Effect.fn("challengeRankingCommand")(function* ({ challenge }) {
@@ -60965,8 +61150,8 @@ var rankingCommand = exports_Command.make("ranking", { challenge: challengeFlag
60965
61150
  yield* execute(options.output, getChallengeRanking({ apiUrl: options.apiUrl }, challenge), challengeRankingText);
60966
61151
  })).pipe(exports_Command.withDescription("View the public leaderboard. No login required and no challenge budget consumed."), exports_Command.withExamples([
60967
61152
  {
60968
- command: "chofex challenge ranking",
60969
- description: "Compare official hidden-set scores"
61153
+ command: "chofex challenge ranking --challenge broken-agent",
61154
+ description: "Compara puntajes oficiales"
60970
61155
  }
60971
61156
  ]));
60972
61157
  var challengeCommand = exports_Command.make("challenge", {}, exports_Effect.fn("challengeQuickstartCommand")(function* () {
@@ -60985,7 +61170,7 @@ var challengeCommand = exports_Command.make("challenge", {}, exports_Effect.fn("
60985
61170
  workflow = rankingStep ? [rankingStep] : [];
60986
61171
  helpCommand = "chofex challenge ranking --help";
60987
61172
  story = [];
60988
- mission = "El Challenge 1 terminó. El ranking final sigue disponible.";
61173
+ mission = "El Challenge 2 terminó. El ranking final sigue disponible.";
60989
61174
  rules = [];
60990
61175
  } else if (participationState === "scheduled") {
60991
61176
  workflow = [];
@@ -61015,18 +61200,18 @@ var challengeCommand = exports_Command.make("challenge", {}, exports_Effect.fn("
61015
61200
  return;
61016
61201
  }
61017
61202
  yield* exports_Console.log(challengeQuickstartText(participationState, launchNotice));
61018
- })).pipe(exports_Command.withDescription("Play mini technical challenges. Start here: learn the Black Box workflow from first query to leaderboard. Run without a subcommand for the guided quickstart."), exports_Command.withExamples([
61203
+ })).pipe(exports_Command.withDescription("Compite en challenges técnicos. Ejecuta el comando sin subcomandos para abrir la guía del challenge actual."), exports_Command.withExamples([
61019
61204
  {
61020
61205
  command: "chofex challenge",
61021
- description: "Open the guided Black Box quickstart"
61206
+ description: "Abre la guía de Broken Agent"
61022
61207
  },
61023
61208
  {
61024
- command: "chofex challenge query --distance 10 --weight 3 --hour 14 --fragile false --express false",
61025
- description: "Make your first oracle query"
61209
+ command: "chofex challenge init --challenge broken-agent",
61210
+ description: "Crea el repositorio de Broken Agent"
61026
61211
  },
61027
61212
  {
61028
- command: "chofex challenge query --help",
61029
- description: "See flags and examples for the query step"
61213
+ command: "chofex challenge test --help",
61214
+ description: "Revisa cómo ejecutar los tests públicos"
61030
61215
  }
61031
61216
  ]), exports_Command.withSubcommands([
61032
61217
  listCommand,
@@ -61325,8 +61510,8 @@ var shirtSizePrompt = (participationMode) => {
61325
61510
  choices: ["xs", "s", "m", "l", "xl", "2xl", "3xl", "prefer_not_to_say"].map((value3) => ({ title: value3.toUpperCase(), value: value3 }))
61326
61511
  });
61327
61512
  };
61328
- var interactiveAcceptedDetails = (participationMode, pictures) => exports_Effect.gen(function* () {
61329
- let fullName = pictures.currentFullName;
61513
+ var interactiveAcceptedDetails = (participationMode, defaults2) => exports_Effect.gen(function* () {
61514
+ let fullName = defaults2.currentFullName;
61330
61515
  if (fullName) {
61331
61516
  const nameChoice = yield* exports_Prompt.run(exports_Prompt.select({
61332
61517
  message: `Is "${fullName}" your full name exactly as it appears on your ID document?`,
@@ -61341,8 +61526,10 @@ var interactiveAcceptedDetails = (participationMode, pictures) => exports_Effect
61341
61526
  if (!fullName) {
61342
61527
  fullName = yield* exports_Prompt.run(requiredText("Full name exactly as it appears on your ID document", acceptedDetailsInputFields.fullName));
61343
61528
  }
61529
+ const name = yield* exports_Prompt.run(requiredText("Nombre impreso en tu carnet", acceptedDetailsInputFields.fullName, defaults2.currentName ?? fullName));
61530
+ const oneLiner = yield* exports_Prompt.run(requiredText("Presentación de una línea para tu carnet", badgeRegenerationInputFields.oneLiner, defaults2.currentOneLiner));
61344
61531
  const details = yield* exports_Prompt.run(exports_Prompt.all({
61345
- phone: requiredText("Phone number", acceptedDetailsInputFields.phone, pictures.currentPhone),
61532
+ phone: optionalText2("Teléfono para WhatsApp (opcional)", acceptedDetailsInputFields.phone, defaults2.currentPhone),
61346
61533
  dateOfBirth: dateOfBirthPrompt(),
61347
61534
  nationalIdNumber: requiredText("National ID or passport number", acceptedDetailsInputFields.nationalIdNumber),
61348
61535
  shirtSize: shirtSizePrompt(participationMode),
@@ -61355,27 +61542,33 @@ var interactiveAcceptedDetails = (participationMode, pictures) => exports_Effect
61355
61542
  })
61356
61543
  }));
61357
61544
  const pictureChoices = [];
61358
- if (pictures.clerkPictureUrl) {
61545
+ if (defaults2.clerkPictureUrl) {
61359
61546
  pictureChoices.push({
61360
- title: `Use my Clerk picture (${pictures.clerkPictureUrl})`,
61547
+ title: `Usar mi foto de Clerk (${defaults2.clerkPictureUrl})`,
61361
61548
  value: "clerk"
61362
61549
  });
61363
61550
  }
61364
- if (pictures.githubUrl) {
61551
+ if (defaults2.githubUrl) {
61365
61552
  pictureChoices.push({
61366
- title: `Use my GitHub picture (${pictures.githubUrl})`,
61553
+ title: `Usar mi foto de GitHub (${defaults2.githubUrl})`,
61367
61554
  value: "github"
61368
61555
  });
61369
61556
  }
61370
61557
  pictureChoices.push({
61371
- title: "Upload a different picture",
61558
+ title: "Subir una foto diferente",
61372
61559
  value: "upload"
61373
61560
  });
61374
61561
  const pictureSource = yield* exports_Prompt.run(exports_Prompt.select({
61375
61562
  message: "Confirm the profile picture reviewers should use",
61376
61563
  choices: pictureChoices
61377
61564
  }));
61378
- return withoutEmptyStrings({ fullName, ...details, pictureSource });
61565
+ return withoutEmptyStrings({
61566
+ fullName,
61567
+ name,
61568
+ oneLiner,
61569
+ ...details,
61570
+ pictureSource
61571
+ });
61379
61572
  });
61380
61573
  var inputOrInteractive = (path4, interactive) => {
61381
61574
  if (path4)
@@ -61390,7 +61583,7 @@ var inputOrInteractive = (path4, interactive) => {
61390
61583
  }));
61391
61584
  };
61392
61585
  var applicationInput = (path4, publicBaseUrl, defaults2 = {}) => inputOrInteractive(path4, collectApplicationFields(publicBaseUrl, defaults2).pipe(exports_Effect.map((input) => withoutEmptyStrings(input)))).pipe(exports_Effect.flatMap((input) => decode2(ApplicationInput, input, applicationInputFieldNames)), exports_Effect.flatMap((input) => validateSemantics(input, applicationSemanticRequirements(input))));
61393
- var acceptedDetailsInput = (path4, participationMode, pictures = {}) => inputOrInteractive(path4, interactiveAcceptedDetails(participationMode, pictures)).pipe(exports_Effect.flatMap((input) => decode2(AcceptedDetailsInput, input, acceptedDetailsInputFieldNames)), exports_Effect.flatMap((input) => validateSemantics(input, acceptedDetailsSemanticRequirements(input, participationMode))));
61586
+ var acceptedDetailsInput = (path4, participationMode, defaults2 = {}) => inputOrInteractive(path4, interactiveAcceptedDetails(participationMode, defaults2)).pipe(exports_Effect.flatMap((input) => decode2(AcceptedDetailsInput, input, acceptedDetailsInputFieldNames)), exports_Effect.flatMap((input) => validateSemantics(input, acceptedDetailsSemanticRequirements(input, participationMode))));
61394
61587
  var picturePathInput = (suppliedPath) => {
61395
61588
  if (suppliedPath)
61396
61589
  return exports_Effect.succeed(suppliedPath);
@@ -63479,11 +63672,14 @@ var confirmCommand = exports_Command.make("confirm", {
63479
63672
  return yield* exports_Effect.fail(cliError("INVALID_APPLICATION_STATE", "Acceptance details can only be submitted after acceptance"));
63480
63673
  }
63481
63674
  const currentUser = yield* getCurrentUser(client);
63675
+ const currentBadge = yield* getBadge(client);
63482
63676
  const body = yield* acceptedDetailsInput(exports_Option.getOrUndefined(input), current.data.registration.participationMode, {
63483
63677
  clerkPictureUrl: currentUser.data.clerkPictureUrl,
63484
63678
  githubUrl: current.data.registration.githubUrl,
63485
63679
  currentPhone: current.data.registration.phone,
63486
- currentFullName: joinFullName(current.data.registration.firstName, current.data.registration.lastName) || current.data.registration.fullName
63680
+ currentName: currentBadge.data.profile?.fullName,
63681
+ currentOneLiner: currentBadge.data.profile?.oneLiner ?? current.data.registration.role,
63682
+ currentFullName: current.data.registration.fullName || joinFullName(current.data.registration.firstName, current.data.registration.lastName)
63487
63683
  });
63488
63684
  const picturePath = exports_Option.getOrUndefined(picture);
63489
63685
  if (body.pictureSource === "upload") {
@@ -63620,6 +63816,8 @@ var applicationTemplate = {
63620
63816
  };
63621
63817
  var acceptanceTemplate = {
63622
63818
  fullName: "Ada Lovelace",
63819
+ name: "Ada L.",
63820
+ oneLiner: "Computing pioneer",
63623
63821
  phone: "+44 20 0000 0000",
63624
63822
  dateOfBirth: "1990-01-01",
63625
63823
  nationalIdNumber: "passport-or-national-id",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chofex-cli",
3
- "version": "0.1.184",
3
+ "version": "0.1.187",
4
4
  "description": "Command-line client for Hack the Andes",
5
5
  "type": "module",
6
6
  "bin": {