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.
- package/README.md +10 -8
- package/dist/index.js +419 -221
- 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
|
-
|
|
50
|
-
|
|
51
|
-
the
|
|
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: "
|
|
58584
|
-
coreSkill: "
|
|
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: "
|
|
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
|
-
"
|
|
58668
|
-
"
|
|
58669
|
-
"
|
|
58670
|
-
"
|
|
58671
|
-
"
|
|
58672
|
-
"
|
|
58673
|
-
"
|
|
58674
|
-
"
|
|
58675
|
-
"
|
|
58676
|
-
"
|
|
58677
|
-
"
|
|
58678
|
-
"
|
|
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 `${
|
|
58692
|
+
return `${day} de ${month} de ${year} a las ${hour}:${minute} (UTC-5)`;
|
|
58691
58693
|
};
|
|
58692
|
-
var challengeOpeningNotice = (title, opensAt) => `${title}
|
|
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
|
-
|
|
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 = "
|
|
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 = "
|
|
59903
|
+
let caseStatus2 = "LISTO PARA AUDITAR";
|
|
59896
59904
|
if (attempt.latestEvaluation)
|
|
59897
|
-
caseStatus2 = "
|
|
59905
|
+
caseStatus2 = "EVALUADO";
|
|
59898
59906
|
const lines3 = [
|
|
59899
|
-
`${challenge.title.toUpperCase()} —
|
|
59907
|
+
`${challenge.title.toUpperCase()} — CASO #${challenge.code}`,
|
|
59900
59908
|
challenge.summary,
|
|
59901
59909
|
"",
|
|
59902
|
-
`
|
|
59910
|
+
`ESTADO DEL CASO: ${caseStatus2}`,
|
|
59903
59911
|
"",
|
|
59904
|
-
"
|
|
59905
|
-
"AI
|
|
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
|
-
`
|
|
59915
|
+
`Evaluaciones ${evaluationsRemaining} / ${progress.evaluationsLimit} restantes`
|
|
59908
59916
|
];
|
|
59909
59917
|
if (progress.bestAccuracy !== undefined) {
|
|
59910
|
-
lines3.push("", `
|
|
59918
|
+
lines3.push("", `Mejor puntaje ${percent(progress.bestAccuracy)}`);
|
|
59911
59919
|
if (progress.rank !== undefined)
|
|
59912
|
-
lines3.push(`
|
|
59920
|
+
lines3.push(`Puesto #${progress.rank}`);
|
|
59913
59921
|
if (progress.shareCode)
|
|
59914
|
-
lines3.push(`
|
|
59922
|
+
lines3.push(`Código #${progress.shareCode}`);
|
|
59915
59923
|
}
|
|
59916
|
-
lines3.push("", "
|
|
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
|
-
"
|
|
60085
|
-
`${result3.matchedObservations} / ${result3.observationCount}
|
|
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("", "
|
|
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("", "
|
|
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("", "
|
|
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
|
-
["
|
|
60131
|
-
["
|
|
60132
|
-
["
|
|
60133
|
-
["
|
|
60134
|
-
["
|
|
60135
|
-
["
|
|
60136
|
-
["
|
|
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
|
-
"
|
|
60140
|
-
`
|
|
60141
|
-
`
|
|
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
|
-
"
|
|
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(`
|
|
60156
|
+
lines3.push(`Puesto #${result3.rank}`);
|
|
60148
60157
|
}
|
|
60149
|
-
lines3.push(`
|
|
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("", "
|
|
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
|
|
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("
|
|
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("
|
|
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
|
|
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
|
-
*
|
|
60252
|
-
*
|
|
60253
|
-
*
|
|
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
|
|
60259
|
-
|
|
60260
|
-
|
|
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
|
-
|
|
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
|
|
60278
|
-
|
|
60279
|
-
|
|
60280
|
-
|
|
60281
|
-
|
|
60282
|
-
|
|
60283
|
-
|
|
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
|
-
>
|
|
60295
|
-
>
|
|
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
|
-
|
|
60298
|
-
|
|
60415
|
+
Repara \`scheduler.js\` sin cambiar la interfaz exportada
|
|
60416
|
+
\`createScheduler(dependencies)\`.
|
|
60299
60417
|
|
|
60300
|
-
##
|
|
60418
|
+
## Comandos
|
|
60301
60419
|
|
|
60302
60420
|
\`\`\`sh
|
|
60303
60421
|
npm test
|
|
60304
|
-
chofex challenge test --challenge broken-agent --source ./
|
|
60305
|
-
chofex challenge evaluate --challenge broken-agent --source ./
|
|
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
|
-
|
|
60309
|
-
|
|
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
|
-
##
|
|
60429
|
+
## Contrato normativo
|
|
60312
60430
|
|
|
60313
|
-
\`createScheduler({ store, clock, execute, workerId })\`
|
|
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\`
|
|
60323
|
-
- \`runAt\`
|
|
60324
|
-
-
|
|
60325
|
-
|
|
60326
|
-
|
|
60327
|
-
|
|
60328
|
-
-
|
|
60329
|
-
|
|
60330
|
-
|
|
60331
|
-
|
|
60332
|
-
-
|
|
60333
|
-
|
|
60334
|
-
-
|
|
60335
|
-
\`
|
|
60336
|
-
|
|
60337
|
-
|
|
60338
|
-
|
|
60339
|
-
|
|
60340
|
-
|
|
60341
|
-
|
|
60342
|
-
|
|
60343
|
-
|
|
60344
|
-
|
|
60345
|
-
|
|
60346
|
-
|
|
60347
|
-
|
|
60348
|
-
|
|
60349
|
-
|
|
60350
|
-
|
|
60351
|
-
|
|
60352
|
-
|
|
60353
|
-
|
|
60354
|
-
|
|
60355
|
-
|
|
60356
|
-
|
|
60357
|
-
|
|
60358
|
-
-
|
|
60359
|
-
|
|
60360
|
-
|
|
60361
|
-
|
|
60362
|
-
|
|
60363
|
-
|
|
60364
|
-
|
|
60365
|
-
|
|
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
|
|
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
|
|
60552
|
+
payload,
|
|
60414
60553
|
});
|
|
60415
60554
|
|
|
60416
|
-
test("
|
|
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("
|
|
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("
|
|
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
|
|
60841
|
+
title: "BROKEN AGENT — THE SCHEDULER",
|
|
60657
60842
|
story: [
|
|
60658
|
-
"
|
|
60659
|
-
"
|
|
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: "
|
|
60846
|
+
mission: "Audita scheduler.js, conserva createScheduler(dependencies) y haz que el sistema cumpla el contrato de producción.",
|
|
60662
60847
|
rules: [
|
|
60663
|
-
"
|
|
60664
|
-
"
|
|
60665
|
-
"
|
|
60666
|
-
"
|
|
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: "
|
|
60856
|
+
action: "Inicia sesión",
|
|
60672
60857
|
command: "chofex login",
|
|
60673
|
-
note: "
|
|
60858
|
+
note: "Abre la autenticación de Clerk en tu navegador."
|
|
60674
60859
|
},
|
|
60675
60860
|
{
|
|
60676
60861
|
step: 2,
|
|
60677
|
-
action: "
|
|
60678
|
-
command: "chofex challenge init",
|
|
60679
|
-
note: "
|
|
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: "
|
|
60684
|
-
command: "
|
|
60685
|
-
note: "
|
|
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: "
|
|
60690
|
-
command: "chofex challenge
|
|
60691
|
-
note: "
|
|
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: "
|
|
60696
|
-
command: "
|
|
60697
|
-
note: "
|
|
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: "
|
|
60702
|
-
command: "chofex challenge test --source ./
|
|
60703
|
-
note: "
|
|
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: "
|
|
60708
|
-
command: "chofex challenge evaluate --source ./
|
|
60709
|
-
note: "
|
|
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: "
|
|
60714
|
-
command: "chofex challenge ranking",
|
|
60715
|
-
note: "
|
|
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
|
|
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:
|
|
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}
|
|
60968
|
+
`${result3.path} ya existe. No se modificó.`,
|
|
60784
60969
|
"",
|
|
60785
|
-
"
|
|
60970
|
+
"Siguiente: continúa endureciendo el scheduler y ejecuta los tests públicos",
|
|
60786
60971
|
` cd ${result3.path} && npm test`,
|
|
60787
|
-
|
|
60972
|
+
" chofex challenge test --challenge broken-agent --source ./scheduler.js"
|
|
60788
60973
|
].join(`
|
|
60789
60974
|
`);
|
|
60790
60975
|
}
|
|
60791
60976
|
return [
|
|
60792
|
-
`
|
|
60793
|
-
"
|
|
60794
|
-
"
|
|
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
|
-
"
|
|
60981
|
+
"Empieza con el contrato normativo y los tests públicos:",
|
|
60797
60982
|
` cd ${result3.path} && npm test`,
|
|
60798
60983
|
"",
|
|
60799
|
-
"
|
|
60800
|
-
|
|
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("
|
|
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: "
|
|
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: "
|
|
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("
|
|
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: "
|
|
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 ./
|
|
60946
|
-
description: "
|
|
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 ./
|
|
60960
|
-
description: "
|
|
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: "
|
|
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
|
|
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("
|
|
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: "
|
|
61206
|
+
description: "Abre la guía de Broken Agent"
|
|
61022
61207
|
},
|
|
61023
61208
|
{
|
|
61024
|
-
command: "chofex challenge
|
|
61025
|
-
description: "
|
|
61209
|
+
command: "chofex challenge init --challenge broken-agent",
|
|
61210
|
+
description: "Crea el repositorio de Broken Agent"
|
|
61026
61211
|
},
|
|
61027
61212
|
{
|
|
61028
|
-
command: "chofex challenge
|
|
61029
|
-
description: "
|
|
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,
|
|
61329
|
-
let fullName =
|
|
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:
|
|
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 (
|
|
61545
|
+
if (defaults2.clerkPictureUrl) {
|
|
61359
61546
|
pictureChoices.push({
|
|
61360
|
-
title: `
|
|
61547
|
+
title: `Usar mi foto de Clerk (${defaults2.clerkPictureUrl})`,
|
|
61361
61548
|
value: "clerk"
|
|
61362
61549
|
});
|
|
61363
61550
|
}
|
|
61364
|
-
if (
|
|
61551
|
+
if (defaults2.githubUrl) {
|
|
61365
61552
|
pictureChoices.push({
|
|
61366
|
-
title: `
|
|
61553
|
+
title: `Usar mi foto de GitHub (${defaults2.githubUrl})`,
|
|
61367
61554
|
value: "github"
|
|
61368
61555
|
});
|
|
61369
61556
|
}
|
|
61370
61557
|
pictureChoices.push({
|
|
61371
|
-
title: "
|
|
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({
|
|
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,
|
|
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
|
-
|
|
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",
|