chofex-cli 0.1.186 → 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 +8 -8
- package/dist/index.js +407 -224
- 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,9 @@ 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
|
-
Acceptance creates a default badge from the profile visible to reviewers.
|
|
50
|
-
`chofex confirm` updates
|
|
51
|
-
while collecting the venue details required for attendance. After confirmation,
|
|
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
52
|
`chofex badge regenerate` can also update those public fields and the QR
|
|
53
53
|
destination without changing the submitted application.
|
|
54
54
|
The technical challenges do not block submission, but every challenge winner must submit
|
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,7 @@ var ApplicationPartId = exports_Schema.Literals([
|
|
|
59045
59051
|
]);
|
|
59046
59052
|
var acceptedDetailsInputFields = {
|
|
59047
59053
|
fullName: nonBlank(200),
|
|
59048
|
-
|
|
59054
|
+
name: exports_Schema.optional(nonBlank(200)),
|
|
59049
59055
|
oneLiner: exports_Schema.optional(nonBlank(30)),
|
|
59050
59056
|
phone: exports_Schema.optional(phone),
|
|
59051
59057
|
dateOfBirth: exports_Schema.String.pipe(exports_Schema.check(exports_Schema.isPattern(/^\d{4}-\d{2}-\d{2}$/, {
|
|
@@ -59765,7 +59771,7 @@ var getChallengeRanking = (options, slug) => publicRequest(options, `/api/v1/cha
|
|
|
59765
59771
|
|
|
59766
59772
|
// src/challenge-input.ts
|
|
59767
59773
|
import { readFile as readFile3 } from "node:fs/promises";
|
|
59768
|
-
var defaultChallengeSlug = "
|
|
59774
|
+
var defaultChallengeSlug = "broken-agent";
|
|
59769
59775
|
var readStdin = async () => {
|
|
59770
59776
|
const chunks = [];
|
|
59771
59777
|
for await (const chunk of process.stdin) {
|
|
@@ -59894,28 +59900,28 @@ var challengeShowText = (attempt) => {
|
|
|
59894
59900
|
const queriesRemaining = progress.queriesLimit - progress.queriesUsed;
|
|
59895
59901
|
const evaluationsRemaining = progress.evaluationsLimit - progress.evaluationsUsed;
|
|
59896
59902
|
if (challenge.slug === "broken-agent") {
|
|
59897
|
-
let caseStatus2 = "
|
|
59903
|
+
let caseStatus2 = "LISTO PARA AUDITAR";
|
|
59898
59904
|
if (attempt.latestEvaluation)
|
|
59899
|
-
caseStatus2 = "
|
|
59905
|
+
caseStatus2 = "EVALUADO";
|
|
59900
59906
|
const lines3 = [
|
|
59901
|
-
`${challenge.title.toUpperCase()} —
|
|
59907
|
+
`${challenge.title.toUpperCase()} — CASO #${challenge.code}`,
|
|
59902
59908
|
challenge.summary,
|
|
59903
59909
|
"",
|
|
59904
|
-
`
|
|
59910
|
+
`ESTADO DEL CASO: ${caseStatus2}`,
|
|
59905
59911
|
"",
|
|
59906
|
-
"
|
|
59907
|
-
"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.",
|
|
59908
59914
|
"",
|
|
59909
|
-
`
|
|
59915
|
+
`Evaluaciones ${evaluationsRemaining} / ${progress.evaluationsLimit} restantes`
|
|
59910
59916
|
];
|
|
59911
59917
|
if (progress.bestAccuracy !== undefined) {
|
|
59912
|
-
lines3.push("", `
|
|
59918
|
+
lines3.push("", `Mejor puntaje ${percent(progress.bestAccuracy)}`);
|
|
59913
59919
|
if (progress.rank !== undefined)
|
|
59914
|
-
lines3.push(`
|
|
59920
|
+
lines3.push(`Puesto #${progress.rank}`);
|
|
59915
59921
|
if (progress.shareCode)
|
|
59916
|
-
lines3.push(`
|
|
59922
|
+
lines3.push(`Código #${progress.shareCode}`);
|
|
59917
59923
|
}
|
|
59918
|
-
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");
|
|
59919
59925
|
return lines3.join(`
|
|
59920
59926
|
`);
|
|
59921
59927
|
}
|
|
@@ -60083,21 +60089,21 @@ var notebookCsvText = (observations) => {
|
|
|
60083
60089
|
var challengeTestText = (result3) => {
|
|
60084
60090
|
if (result3.kind === "broken_agent") {
|
|
60085
60091
|
const lines3 = [
|
|
60086
|
-
"
|
|
60087
|
-
`${result3.matchedObservations} / ${result3.observationCount}
|
|
60092
|
+
"TESTS PÚBLICOS — BROKEN AGENT",
|
|
60093
|
+
`${result3.matchedObservations} / ${result3.observationCount} comportamientos visibles pasan`
|
|
60088
60094
|
];
|
|
60089
60095
|
if (result3.accuracy === 1) {
|
|
60090
|
-
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");
|
|
60091
60097
|
return lines3.join(`
|
|
60092
60098
|
`);
|
|
60093
60099
|
}
|
|
60094
60100
|
if (result3.mismatches.length > 0) {
|
|
60095
|
-
lines3.push("", "
|
|
60101
|
+
lines3.push("", "Fallas visibles:");
|
|
60096
60102
|
for (const mismatch of result3.mismatches) {
|
|
60097
60103
|
lines3.push(` ${String(mismatch.actual)}`);
|
|
60098
60104
|
}
|
|
60099
60105
|
}
|
|
60100
|
-
lines3.push("", "
|
|
60106
|
+
lines3.push("", "Corrige las regresiones públicas antes de usar una evaluación oficial.");
|
|
60101
60107
|
return lines3.join(`
|
|
60102
60108
|
`);
|
|
60103
60109
|
}
|
|
@@ -60128,29 +60134,30 @@ var challengeTestText = (result3) => {
|
|
|
60128
60134
|
};
|
|
60129
60135
|
var challengeEvaluateText = (result3) => {
|
|
60130
60136
|
if (result3.breakdown) {
|
|
60137
|
+
const executionCost = result3.executionCost ?? result3.runtimeMs;
|
|
60131
60138
|
const capabilityLines = [
|
|
60132
|
-
["
|
|
60133
|
-
["
|
|
60134
|
-
["
|
|
60135
|
-
["
|
|
60136
|
-
["
|
|
60137
|
-
["
|
|
60138
|
-
["
|
|
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]
|
|
60139
60146
|
];
|
|
60140
60147
|
const lines3 = [
|
|
60141
|
-
"
|
|
60142
|
-
`
|
|
60143
|
-
`
|
|
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`,
|
|
60144
60151
|
"",
|
|
60145
|
-
"
|
|
60152
|
+
"Puntajes por capacidad",
|
|
60146
60153
|
...capabilityLines.map(([label, score]) => `${label.padEnd(20)}${score.earned.toFixed(2)} / ${score.available}`)
|
|
60147
60154
|
];
|
|
60148
60155
|
if (result3.rank !== undefined) {
|
|
60149
|
-
lines3.push(`
|
|
60156
|
+
lines3.push(`Puesto #${result3.rank}`);
|
|
60150
60157
|
}
|
|
60151
|
-
lines3.push(`
|
|
60158
|
+
lines3.push(`Evaluaciones oficiales restantes ${result3.evaluationsRemaining} / ${result3.evaluationsLimit}`, "", result3.shareText, "", "Siguiente: consulta el ranking", " chofex challenge ranking --challenge broken-agent");
|
|
60152
60159
|
if (result3.evaluationsRemaining > 0) {
|
|
60153
|
-
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");
|
|
60154
60161
|
}
|
|
60155
60162
|
return lines3.join(`
|
|
60156
60163
|
`);
|
|
@@ -60175,23 +60182,24 @@ var challengeRankingText = (ranking, now3 = new Date) => {
|
|
|
60175
60182
|
const lines2 = [`${ranking.challenge.theme} — ${ranking.challenge.title}`];
|
|
60176
60183
|
const { rankingVisibleAt } = ranking.challenge;
|
|
60177
60184
|
if (rankingVisibleAt && !isChallengeRankingVisibleAt(ranking.challenge, now3)) {
|
|
60178
|
-
lines2.push("", `Ranking
|
|
60185
|
+
lines2.push("", `Ranking disponible ${formatChallengeOpeningInPeru(rankingVisibleAt)}.`);
|
|
60179
60186
|
return lines2.join(`
|
|
60180
60187
|
`);
|
|
60181
60188
|
}
|
|
60182
60189
|
lines2.push("");
|
|
60183
60190
|
if (ranking.entries.length === 0) {
|
|
60184
|
-
lines2.push("
|
|
60191
|
+
lines2.push("Todavía no hay evaluaciones oficiales.");
|
|
60185
60192
|
return lines2.join(`
|
|
60186
60193
|
`);
|
|
60187
60194
|
}
|
|
60188
60195
|
if (ranking.challenge.slug === "broken-agent") {
|
|
60189
|
-
lines2.push("
|
|
60196
|
+
lines2.push("Psto Puntaje Puntos Costo Nombre");
|
|
60190
60197
|
for (const entry of ranking.entries.slice(0, 20)) {
|
|
60191
60198
|
const rank = String(entry.rank).padStart(4, " ");
|
|
60192
60199
|
const accuracy = percent(entry.accuracy).padStart(8, " ");
|
|
60193
60200
|
const points = `${entry.exactCount}/${entry.sampleSize}`.padStart(11, " ");
|
|
60194
|
-
const
|
|
60201
|
+
const executionCost = entry.executionCost ?? entry.runtimeMs;
|
|
60202
|
+
const runtime = `${executionCost}ops`.padStart(8, " ");
|
|
60195
60203
|
const profileLinks = [entry.githubUrl, entry.linkedInUrl].filter(Boolean);
|
|
60196
60204
|
const participant = [entry.displayName, ...profileLinks].join(" ");
|
|
60197
60205
|
lines2.push(`${rank} ${accuracy} ${points} ${runtime} ${participant}`);
|
|
@@ -60250,16 +60258,83 @@ var brokenAgentPackageJson = `${JSON.stringify({
|
|
|
60250
60258
|
var brokenAgentStarterSource = `/**
|
|
60251
60259
|
* Broken Agent — The Scheduler
|
|
60252
60260
|
*
|
|
60253
|
-
*
|
|
60254
|
-
*
|
|
60255
|
-
*
|
|
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.
|
|
60256
60264
|
*/
|
|
60257
|
-
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
|
+
|
|
60258
60323
|
return {
|
|
60259
60324
|
async schedule(input) {
|
|
60260
|
-
const
|
|
60261
|
-
|
|
60262
|
-
|
|
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
|
+
});
|
|
60263
60338
|
},
|
|
60264
60339
|
|
|
60265
60340
|
async cancel(id) {
|
|
@@ -60272,18 +60347,59 @@ function createScheduler({ store, clock, execute }) {
|
|
|
60272
60347
|
},
|
|
60273
60348
|
|
|
60274
60349
|
async list() {
|
|
60275
|
-
|
|
60350
|
+
const jobs = await store.transaction((tx) => tx.list());
|
|
60351
|
+
return ordered(jobs).map(clone);
|
|
60276
60352
|
},
|
|
60277
60353
|
|
|
60278
60354
|
async runDue() {
|
|
60279
|
-
const
|
|
60280
|
-
|
|
60281
|
-
|
|
60282
|
-
|
|
60283
|
-
|
|
60284
|
-
|
|
60285
|
-
|
|
60286
|
-
|
|
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
|
+
}
|
|
60287
60403
|
}
|
|
60288
60404
|
},
|
|
60289
60405
|
};
|
|
@@ -60293,78 +60409,98 @@ module.exports = { createScheduler };
|
|
|
60293
60409
|
`;
|
|
60294
60410
|
var brokenAgentReadme = `# Broken Agent — The Scheduler
|
|
60295
60411
|
|
|
60296
|
-
>
|
|
60297
|
-
>
|
|
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.
|
|
60298
60414
|
|
|
60299
|
-
|
|
60300
|
-
|
|
60415
|
+
Repara \`scheduler.js\` sin cambiar la interfaz exportada
|
|
60416
|
+
\`createScheduler(dependencies)\`.
|
|
60301
60417
|
|
|
60302
|
-
##
|
|
60418
|
+
## Comandos
|
|
60303
60419
|
|
|
60304
60420
|
\`\`\`sh
|
|
60305
60421
|
npm test
|
|
60306
|
-
chofex challenge test --challenge broken-agent --source ./
|
|
60307
|
-
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
|
|
60308
60424
|
\`\`\`
|
|
60309
60425
|
|
|
60310
|
-
|
|
60311
|
-
|
|
60426
|
+
Los tests locales y públicos son ilimitados. Tienes **5 evaluaciones oficiales**
|
|
60427
|
+
contra escenarios ocultos. Las herramientas de AI están permitidas.
|
|
60312
60428
|
|
|
60313
|
-
##
|
|
60429
|
+
## Contrato normativo
|
|
60314
60430
|
|
|
60315
|
-
\`createScheduler({ store, clock, execute, workerId })\`
|
|
60431
|
+
\`createScheduler({ store, clock, execute, workerId })\` devuelve:
|
|
60316
60432
|
|
|
60317
60433
|
- \`schedule({ id, runAt, payload })\`
|
|
60318
60434
|
- \`cancel(id)\`
|
|
60319
60435
|
- \`list()\`
|
|
60320
60436
|
- \`runDue()\`
|
|
60321
60437
|
|
|
60322
|
-
### Jobs
|
|
60323
|
-
|
|
60324
|
-
- \`id\`
|
|
60325
|
-
- \`runAt\`
|
|
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
|
-
|
|
60366
|
-
|
|
60367
|
-
|
|
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.
|
|
60368
60504
|
`;
|
|
60369
60505
|
var brokenAgentPublicTestSource = `const test = require("node:test");
|
|
60370
60506
|
const assert = require("node:assert/strict");
|
|
@@ -60396,52 +60532,99 @@ class MemoryStore {
|
|
|
60396
60532
|
}
|
|
60397
60533
|
}
|
|
60398
60534
|
|
|
60399
|
-
const setup = () => {
|
|
60400
|
-
const store = new MemoryStore();
|
|
60401
|
-
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");
|
|
60402
60538
|
const executed = [];
|
|
60539
|
+
const execute = options.execute || (async (job) => executed.push(job.id));
|
|
60403
60540
|
const scheduler = createScheduler({
|
|
60404
60541
|
store,
|
|
60405
60542
|
clock: { now: () => new Date(now) },
|
|
60406
|
-
execute
|
|
60407
|
-
workerId: "public-worker",
|
|
60543
|
+
execute,
|
|
60544
|
+
workerId: options.workerId || "public-worker",
|
|
60408
60545
|
});
|
|
60409
60546
|
return { store, scheduler, executed };
|
|
60410
60547
|
};
|
|
60411
60548
|
|
|
60412
|
-
const job = (id, runAt = "2026-10-17T15:00:00.000Z") => ({
|
|
60549
|
+
const job = (id, runAt = "2026-10-17T15:00:00.000Z", payload = { message: id }) => ({
|
|
60413
60550
|
id,
|
|
60414
60551
|
runAt,
|
|
60415
|
-
payload
|
|
60552
|
+
payload,
|
|
60416
60553
|
});
|
|
60417
60554
|
|
|
60418
|
-
test("
|
|
60555
|
+
test("programa un job y conserva el estado público", async () => {
|
|
60419
60556
|
const { scheduler } = setup();
|
|
60420
|
-
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);
|
|
60421
60560
|
assert.equal((await scheduler.list()).length, 1);
|
|
60422
60561
|
});
|
|
60423
60562
|
|
|
60424
|
-
test("
|
|
60425
|
-
const { scheduler, executed } = setup();
|
|
60426
|
-
await scheduler.schedule(job("job-1"));
|
|
60427
|
-
await scheduler.runDue();
|
|
60428
|
-
assert.deepEqual(executed, ["job-1"]);
|
|
60429
|
-
});
|
|
60430
|
-
|
|
60431
|
-
test("does not execute a future job", async () => {
|
|
60563
|
+
test("ejecuta vencidos y deja futuros pendientes", async () => {
|
|
60432
60564
|
const { scheduler, executed } = setup();
|
|
60565
|
+
await scheduler.schedule(job("due"));
|
|
60433
60566
|
await scheduler.schedule(job("future", "2026-10-17T16:00:00.000Z"));
|
|
60434
60567
|
await scheduler.runDue();
|
|
60435
|
-
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");
|
|
60436
60572
|
});
|
|
60437
60573
|
|
|
60438
|
-
test("
|
|
60574
|
+
test("cancela únicamente jobs pendientes", async () => {
|
|
60439
60575
|
const { scheduler, executed } = setup();
|
|
60440
60576
|
await scheduler.schedule(job("cancel-me"));
|
|
60441
60577
|
assert.equal(await scheduler.cancel("cancel-me"), true);
|
|
60578
|
+
assert.equal(await scheduler.cancel("cancel-me"), false);
|
|
60442
60579
|
await scheduler.runDue();
|
|
60443
60580
|
assert.deepEqual(executed, []);
|
|
60444
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
|
+
});
|
|
60445
60628
|
`;
|
|
60446
60629
|
|
|
60447
60630
|
// src/challenge-scaffold.ts
|
|
@@ -60655,69 +60838,69 @@ var badgeText = (result3) => {
|
|
|
60655
60838
|
|
|
60656
60839
|
// src/challenge-commands.ts
|
|
60657
60840
|
var challengeQuickstart = {
|
|
60658
|
-
title: "THE
|
|
60841
|
+
title: "BROKEN AGENT — THE SCHEDULER",
|
|
60659
60842
|
story: [
|
|
60660
|
-
"
|
|
60661
|
-
"
|
|
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."
|
|
60662
60845
|
],
|
|
60663
|
-
mission: "
|
|
60846
|
+
mission: "Audita scheduler.js, conserva createScheduler(dependencies) y haz que el sistema cumpla el contrato de producción.",
|
|
60664
60847
|
rules: [
|
|
60665
|
-
"
|
|
60666
|
-
"
|
|
60667
|
-
"
|
|
60668
|
-
"
|
|
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."
|
|
60669
60852
|
],
|
|
60670
60853
|
workflow: [
|
|
60671
60854
|
{
|
|
60672
60855
|
step: 1,
|
|
60673
|
-
action: "
|
|
60856
|
+
action: "Inicia sesión",
|
|
60674
60857
|
command: "chofex login",
|
|
60675
|
-
note: "
|
|
60858
|
+
note: "Abre la autenticación de Clerk en tu navegador."
|
|
60676
60859
|
},
|
|
60677
60860
|
{
|
|
60678
60861
|
step: 2,
|
|
60679
|
-
action: "
|
|
60680
|
-
command: "chofex challenge init",
|
|
60681
|
-
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."
|
|
60682
60865
|
},
|
|
60683
60866
|
{
|
|
60684
60867
|
step: 3,
|
|
60685
|
-
action: "
|
|
60686
|
-
command: "
|
|
60687
|
-
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."
|
|
60688
60871
|
},
|
|
60689
60872
|
{
|
|
60690
60873
|
step: 4,
|
|
60691
|
-
action: "
|
|
60692
|
-
command: "chofex challenge
|
|
60693
|
-
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."
|
|
60694
60877
|
},
|
|
60695
60878
|
{
|
|
60696
60879
|
step: 5,
|
|
60697
|
-
action: "
|
|
60698
|
-
command: "
|
|
60699
|
-
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."
|
|
60700
60883
|
},
|
|
60701
60884
|
{
|
|
60702
60885
|
step: 6,
|
|
60703
|
-
action: "
|
|
60704
|
-
command: "chofex challenge test --source ./
|
|
60705
|
-
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."
|
|
60706
60889
|
},
|
|
60707
60890
|
{
|
|
60708
60891
|
step: 7,
|
|
60709
|
-
action: "
|
|
60710
|
-
command: "chofex challenge evaluate --source ./
|
|
60711
|
-
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."
|
|
60712
60895
|
},
|
|
60713
60896
|
{
|
|
60714
60897
|
step: 8,
|
|
60715
|
-
action: "
|
|
60716
|
-
command: "chofex challenge ranking",
|
|
60717
|
-
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ú."
|
|
60718
60901
|
}
|
|
60719
60902
|
],
|
|
60720
|
-
helpCommand: "chofex challenge
|
|
60903
|
+
helpCommand: "chofex challenge test --help"
|
|
60721
60904
|
};
|
|
60722
60905
|
var launchNoticeFor = (slug) => {
|
|
60723
60906
|
const challenge = challengeBySlug(slug);
|
|
@@ -60761,7 +60944,7 @@ var challengeQuickstartText = (participationState, launchNotice) => {
|
|
|
60761
60944
|
`);
|
|
60762
60945
|
};
|
|
60763
60946
|
var optionalString = (name, description) => exports_Flag.string(name).pipe(exports_Flag.optional, exports_Flag.withDescription(description));
|
|
60764
|
-
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)"));
|
|
60765
60948
|
var sourceFlag = optionalString("source", "JavaScript challenge solution file");
|
|
60766
60949
|
var inputFlag = optionalString("input", "JSON file, or - for stdin");
|
|
60767
60950
|
var formatFlag = exports_Flag.choice("format", ["table", "json", "csv"]).pipe(exports_Flag.withDefault("table"), exports_Flag.withDescription("Notebook format"));
|
|
@@ -60782,24 +60965,24 @@ var challengeInitText = (result3) => {
|
|
|
60782
60965
|
if (result3.challenge === "broken-agent") {
|
|
60783
60966
|
if (result3.status === "exists") {
|
|
60784
60967
|
return [
|
|
60785
|
-
`${result3.path}
|
|
60968
|
+
`${result3.path} ya existe. No se modificó.`,
|
|
60786
60969
|
"",
|
|
60787
|
-
"
|
|
60970
|
+
"Siguiente: continúa endureciendo el scheduler y ejecuta los tests públicos",
|
|
60788
60971
|
` cd ${result3.path} && npm test`,
|
|
60789
|
-
|
|
60972
|
+
" chofex challenge test --challenge broken-agent --source ./scheduler.js"
|
|
60790
60973
|
].join(`
|
|
60791
60974
|
`);
|
|
60792
60975
|
}
|
|
60793
60976
|
return [
|
|
60794
|
-
`
|
|
60795
|
-
"
|
|
60796
|
-
"
|
|
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.",
|
|
60797
60980
|
"",
|
|
60798
|
-
"
|
|
60981
|
+
"Empieza con el contrato normativo y los tests públicos:",
|
|
60799
60982
|
` cd ${result3.path} && npm test`,
|
|
60800
60983
|
"",
|
|
60801
|
-
"
|
|
60802
|
-
|
|
60984
|
+
"Luego repara scheduler.js y pruébalo sin consumir evaluaciones:",
|
|
60985
|
+
" chofex challenge test --challenge broken-agent --source ./scheduler.js"
|
|
60803
60986
|
].join(`
|
|
60804
60987
|
`);
|
|
60805
60988
|
}
|
|
@@ -60852,24 +61035,24 @@ var initCommand = exports_Command.make("init", { challenge: challengeFlag }, exp
|
|
|
60852
61035
|
data
|
|
60853
61036
|
})));
|
|
60854
61037
|
yield* execute(options.output, operation, challengeInitText);
|
|
60855
|
-
})).pipe(exports_Command.withDescription("
|
|
61038
|
+
})).pipe(exports_Command.withDescription("Crea el starter de un challenge sin sobrescribir trabajo existente."), exports_Command.withExamples([
|
|
60856
61039
|
{
|
|
60857
|
-
command: "chofex challenge init",
|
|
60858
|
-
description: "
|
|
61040
|
+
command: "chofex challenge init --challenge broken-agent",
|
|
61041
|
+
description: "Prepara el repositorio de Broken Agent"
|
|
60859
61042
|
},
|
|
60860
61043
|
{
|
|
60861
61044
|
command: "chofex challenge init --challenge broken-agent",
|
|
60862
|
-
description: "
|
|
61045
|
+
description: "Usa explícitamente el slug del challenge"
|
|
60863
61046
|
}
|
|
60864
61047
|
]));
|
|
60865
61048
|
var showCommand = exports_Command.make("show", { challenge: challengeFlag }, exports_Effect.fn("challengeShowCommand")(function* ({ challenge }) {
|
|
60866
61049
|
const options = yield* root;
|
|
60867
61050
|
const token = exports_Option.getOrUndefined(options.token);
|
|
60868
61051
|
yield* execute(options.output, getChallengeAttempt({ apiUrl: options.apiUrl, token }, challenge), challengeShowText);
|
|
60869
|
-
})).pipe(exports_Command.withDescription("
|
|
61052
|
+
})).pipe(exports_Command.withDescription("Consulta presupuesto, puntaje y progreso del challenge. Requiere iniciar sesión."), exports_Command.withExamples([
|
|
60870
61053
|
{
|
|
60871
61054
|
command: "chofex challenge show",
|
|
60872
|
-
description: "
|
|
61055
|
+
description: "Revisa tu progreso antes de consumir intentos limitados"
|
|
60873
61056
|
}
|
|
60874
61057
|
]));
|
|
60875
61058
|
var queryCommand = exports_Command.make("query", {
|
|
@@ -60904,11 +61087,11 @@ var queryCommand = exports_Command.make("query", {
|
|
|
60904
61087
|
yield* execute(options.output, operation, challengeQueryText);
|
|
60905
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([
|
|
60906
61089
|
{
|
|
60907
|
-
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",
|
|
60908
61091
|
description: "Spend one oracle query"
|
|
60909
61092
|
},
|
|
60910
61093
|
{
|
|
60911
|
-
command: "chofex challenge query --input shipment.json",
|
|
61094
|
+
command: "chofex challenge query --challenge black-box --input shipment.json",
|
|
60912
61095
|
description: "Read the shipment fields from a JSON file"
|
|
60913
61096
|
}
|
|
60914
61097
|
]));
|
|
@@ -60926,11 +61109,11 @@ var notebookCommand = exports_Command.make("notebook", { challenge: challengeFla
|
|
|
60926
61109
|
});
|
|
60927
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([
|
|
60928
61111
|
{
|
|
60929
|
-
command: "chofex challenge notebook",
|
|
61112
|
+
command: "chofex challenge notebook --challenge black-box",
|
|
60930
61113
|
description: "Read observations in a terminal table"
|
|
60931
61114
|
},
|
|
60932
61115
|
{
|
|
60933
|
-
command: "chofex challenge notebook --format csv > observations.csv",
|
|
61116
|
+
command: "chofex challenge notebook --challenge black-box --format csv > observations.csv",
|
|
60934
61117
|
description: "Save observations for a spreadsheet"
|
|
60935
61118
|
}
|
|
60936
61119
|
]));
|
|
@@ -60944,8 +61127,8 @@ var testCommand = exports_Command.make("test", { challenge: challengeFlag, sourc
|
|
|
60944
61127
|
yield* execute(options.output, operation, challengeTestText);
|
|
60945
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([
|
|
60946
61129
|
{
|
|
60947
|
-
command: "chofex challenge test --source ./
|
|
60948
|
-
description: "
|
|
61130
|
+
command: "chofex challenge test --challenge broken-agent --source ./scheduler.js",
|
|
61131
|
+
description: "Ejecuta los tests públicos de Broken Agent"
|
|
60949
61132
|
}
|
|
60950
61133
|
]));
|
|
60951
61134
|
var evaluateCommand = exports_Command.make("evaluate", { challenge: challengeFlag, source: sourceFlag }, exports_Effect.fn("challengeEvaluateCommand")(function* ({ challenge, source }) {
|
|
@@ -60958,8 +61141,8 @@ var evaluateCommand = exports_Command.make("evaluate", { challenge: challengeFla
|
|
|
60958
61141
|
yield* execute(options.output, operation, challengeEvaluateText);
|
|
60959
61142
|
})).pipe(exports_Command.withDescription("Score a solution on hidden cases. This consumes one limited official evaluation. Requires sign-in."), exports_Command.withExamples([
|
|
60960
61143
|
{
|
|
60961
|
-
command: "chofex challenge evaluate --source ./
|
|
60962
|
-
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"
|
|
60963
61146
|
}
|
|
60964
61147
|
]));
|
|
60965
61148
|
var rankingCommand = exports_Command.make("ranking", { challenge: challengeFlag }, exports_Effect.fn("challengeRankingCommand")(function* ({ challenge }) {
|
|
@@ -60967,8 +61150,8 @@ var rankingCommand = exports_Command.make("ranking", { challenge: challengeFlag
|
|
|
60967
61150
|
yield* execute(options.output, getChallengeRanking({ apiUrl: options.apiUrl }, challenge), challengeRankingText);
|
|
60968
61151
|
})).pipe(exports_Command.withDescription("View the public leaderboard. No login required and no challenge budget consumed."), exports_Command.withExamples([
|
|
60969
61152
|
{
|
|
60970
|
-
command: "chofex challenge ranking",
|
|
60971
|
-
description: "
|
|
61153
|
+
command: "chofex challenge ranking --challenge broken-agent",
|
|
61154
|
+
description: "Compara puntajes oficiales"
|
|
60972
61155
|
}
|
|
60973
61156
|
]));
|
|
60974
61157
|
var challengeCommand = exports_Command.make("challenge", {}, exports_Effect.fn("challengeQuickstartCommand")(function* () {
|
|
@@ -60987,7 +61170,7 @@ var challengeCommand = exports_Command.make("challenge", {}, exports_Effect.fn("
|
|
|
60987
61170
|
workflow = rankingStep ? [rankingStep] : [];
|
|
60988
61171
|
helpCommand = "chofex challenge ranking --help";
|
|
60989
61172
|
story = [];
|
|
60990
|
-
mission = "El Challenge
|
|
61173
|
+
mission = "El Challenge 2 terminó. El ranking final sigue disponible.";
|
|
60991
61174
|
rules = [];
|
|
60992
61175
|
} else if (participationState === "scheduled") {
|
|
60993
61176
|
workflow = [];
|
|
@@ -61017,18 +61200,18 @@ var challengeCommand = exports_Command.make("challenge", {}, exports_Effect.fn("
|
|
|
61017
61200
|
return;
|
|
61018
61201
|
}
|
|
61019
61202
|
yield* exports_Console.log(challengeQuickstartText(participationState, launchNotice));
|
|
61020
|
-
})).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([
|
|
61021
61204
|
{
|
|
61022
61205
|
command: "chofex challenge",
|
|
61023
|
-
description: "
|
|
61206
|
+
description: "Abre la guía de Broken Agent"
|
|
61024
61207
|
},
|
|
61025
61208
|
{
|
|
61026
|
-
command: "chofex challenge
|
|
61027
|
-
description: "
|
|
61209
|
+
command: "chofex challenge init --challenge broken-agent",
|
|
61210
|
+
description: "Crea el repositorio de Broken Agent"
|
|
61028
61211
|
},
|
|
61029
61212
|
{
|
|
61030
|
-
command: "chofex challenge
|
|
61031
|
-
description: "
|
|
61213
|
+
command: "chofex challenge test --help",
|
|
61214
|
+
description: "Revisa cómo ejecutar los tests públicos"
|
|
61032
61215
|
}
|
|
61033
61216
|
]), exports_Command.withSubcommands([
|
|
61034
61217
|
listCommand,
|
|
@@ -61327,8 +61510,8 @@ var shirtSizePrompt = (participationMode) => {
|
|
|
61327
61510
|
choices: ["xs", "s", "m", "l", "xl", "2xl", "3xl", "prefer_not_to_say"].map((value3) => ({ title: value3.toUpperCase(), value: value3 }))
|
|
61328
61511
|
});
|
|
61329
61512
|
};
|
|
61330
|
-
var interactiveAcceptedDetails = (participationMode,
|
|
61331
|
-
let fullName =
|
|
61513
|
+
var interactiveAcceptedDetails = (participationMode, defaults2) => exports_Effect.gen(function* () {
|
|
61514
|
+
let fullName = defaults2.currentFullName;
|
|
61332
61515
|
if (fullName) {
|
|
61333
61516
|
const nameChoice = yield* exports_Prompt.run(exports_Prompt.select({
|
|
61334
61517
|
message: `Is "${fullName}" your full name exactly as it appears on your ID document?`,
|
|
@@ -61343,10 +61526,10 @@ var interactiveAcceptedDetails = (participationMode, pictures) => exports_Effect
|
|
|
61343
61526
|
if (!fullName) {
|
|
61344
61527
|
fullName = yield* exports_Prompt.run(requiredText("Full name exactly as it appears on your ID document", acceptedDetailsInputFields.fullName));
|
|
61345
61528
|
}
|
|
61346
|
-
const
|
|
61347
|
-
const oneLiner = yield* exports_Prompt.run(requiredText("Presentación de una línea para tu carnet", badgeRegenerationInputFields.oneLiner,
|
|
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));
|
|
61348
61531
|
const details = yield* exports_Prompt.run(exports_Prompt.all({
|
|
61349
|
-
phone: optionalText2("Teléfono para WhatsApp (opcional)", acceptedDetailsInputFields.phone,
|
|
61532
|
+
phone: optionalText2("Teléfono para WhatsApp (opcional)", acceptedDetailsInputFields.phone, defaults2.currentPhone),
|
|
61350
61533
|
dateOfBirth: dateOfBirthPrompt(),
|
|
61351
61534
|
nationalIdNumber: requiredText("National ID or passport number", acceptedDetailsInputFields.nationalIdNumber),
|
|
61352
61535
|
shirtSize: shirtSizePrompt(participationMode),
|
|
@@ -61359,20 +61542,20 @@ var interactiveAcceptedDetails = (participationMode, pictures) => exports_Effect
|
|
|
61359
61542
|
})
|
|
61360
61543
|
}));
|
|
61361
61544
|
const pictureChoices = [];
|
|
61362
|
-
if (
|
|
61545
|
+
if (defaults2.clerkPictureUrl) {
|
|
61363
61546
|
pictureChoices.push({
|
|
61364
|
-
title: `
|
|
61547
|
+
title: `Usar mi foto de Clerk (${defaults2.clerkPictureUrl})`,
|
|
61365
61548
|
value: "clerk"
|
|
61366
61549
|
});
|
|
61367
61550
|
}
|
|
61368
|
-
if (
|
|
61551
|
+
if (defaults2.githubUrl) {
|
|
61369
61552
|
pictureChoices.push({
|
|
61370
|
-
title: `
|
|
61553
|
+
title: `Usar mi foto de GitHub (${defaults2.githubUrl})`,
|
|
61371
61554
|
value: "github"
|
|
61372
61555
|
});
|
|
61373
61556
|
}
|
|
61374
61557
|
pictureChoices.push({
|
|
61375
|
-
title: "
|
|
61558
|
+
title: "Subir una foto diferente",
|
|
61376
61559
|
value: "upload"
|
|
61377
61560
|
});
|
|
61378
61561
|
const pictureSource = yield* exports_Prompt.run(exports_Prompt.select({
|
|
@@ -61381,7 +61564,7 @@ var interactiveAcceptedDetails = (participationMode, pictures) => exports_Effect
|
|
|
61381
61564
|
}));
|
|
61382
61565
|
return withoutEmptyStrings({
|
|
61383
61566
|
fullName,
|
|
61384
|
-
|
|
61567
|
+
name,
|
|
61385
61568
|
oneLiner,
|
|
61386
61569
|
...details,
|
|
61387
61570
|
pictureSource
|
|
@@ -61400,7 +61583,7 @@ var inputOrInteractive = (path4, interactive) => {
|
|
|
61400
61583
|
}));
|
|
61401
61584
|
};
|
|
61402
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))));
|
|
61403
|
-
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))));
|
|
61404
61587
|
var picturePathInput = (suppliedPath) => {
|
|
61405
61588
|
if (suppliedPath)
|
|
61406
61589
|
return exports_Effect.succeed(suppliedPath);
|
|
@@ -63494,7 +63677,7 @@ var confirmCommand = exports_Command.make("confirm", {
|
|
|
63494
63677
|
clerkPictureUrl: currentUser.data.clerkPictureUrl,
|
|
63495
63678
|
githubUrl: current.data.registration.githubUrl,
|
|
63496
63679
|
currentPhone: current.data.registration.phone,
|
|
63497
|
-
|
|
63680
|
+
currentName: currentBadge.data.profile?.fullName,
|
|
63498
63681
|
currentOneLiner: currentBadge.data.profile?.oneLiner ?? current.data.registration.role,
|
|
63499
63682
|
currentFullName: current.data.registration.fullName || joinFullName(current.data.registration.firstName, current.data.registration.lastName)
|
|
63500
63683
|
});
|
|
@@ -63633,7 +63816,7 @@ var applicationTemplate = {
|
|
|
63633
63816
|
};
|
|
63634
63817
|
var acceptanceTemplate = {
|
|
63635
63818
|
fullName: "Ada Lovelace",
|
|
63636
|
-
|
|
63819
|
+
name: "Ada L.",
|
|
63637
63820
|
oneLiner: "Computing pioneer",
|
|
63638
63821
|
phone: "+44 20 0000 0000",
|
|
63639
63822
|
dateOfBirth: "1990-01-01",
|