chofex-cli 0.1.189 → 0.1.191

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +7 -4
  2. package/dist/index.js +248 -24
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -27,7 +27,7 @@ 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
30
- chofex challenge evaluate --challenge broken-agent --source ./scheduler.js
30
+ chofex challenge evaluate --challenge broken-agent --source ./scheduler.js --review ./review.json
31
31
  chofex challenge ranking --challenge broken-agent
32
32
  chofex confirm
33
33
  chofex badge
@@ -51,9 +51,12 @@ Acceptance creates a default badge from the Clerk profile visible to reviewers.
51
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
- The technical challenges do not block submission, but every challenge winner must submit
55
- an application before being accepted. Top results receive a direct pass; other
56
- scores give organizers another review metric.
54
+ The technical challenges do not block application submission, but they are
55
+ mandatory for admission. An application does not reserve a seat; organizers
56
+ select the strongest engineers from challenge rankings. Broken Agent requires a
57
+ participant-only browser handoff with passkey user verification before each
58
+ official evaluation. Every challenge slug resolves to the current server
59
+ version; legacy attempts do not count toward admission.
57
60
 
58
61
  The public ranking is read-only at `https://hacktheandes.com/challenges`.
59
62
 
package/dist/index.js CHANGED
@@ -58554,6 +58554,7 @@ var runWith2 = (command, config) => {
58554
58554
  // ../../packages/challenges-contract/src/index.ts
58555
58555
  var blackBoxChallengeSlug = "black-box";
58556
58556
  var brokenAgentChallengeSlug = "broken-agent";
58557
+ var challengeAdmissionNotice = "Los challenges técnicos son obligatorios para competir por un cupo. Enviar la postulación solo crea tu candidatura: no reserva una plaza. Siempre compites en la versión vigente; los intentos legacy no cuentan. Los mejores resultados de los rankings serán seleccionados para el evento.";
58557
58558
  var challengeCatalog = [
58558
58559
  {
58559
58560
  slug: blackBoxChallengeSlug,
@@ -58710,6 +58711,33 @@ var JavascriptSourceSolutionSchema = exports_Schema.Struct({
58710
58711
  kind: exports_Schema.Literal("javascript_source"),
58711
58712
  source: exports_Schema.String.pipe(exports_Schema.check(exports_Schema.isMinLength(1), exports_Schema.isMaxLength(32768)))
58712
58713
  });
58714
+ var HumanReviewAnswerSchema = exports_Schema.Trim.pipe(exports_Schema.check(exports_Schema.isMinLength(20), exports_Schema.isMaxLength(1000)));
58715
+ var BrokenAgentHumanReviewSchema = exports_Schema.Struct({
58716
+ sourceDigest: exports_Schema.String.pipe(exports_Schema.check(exports_Schema.isPattern(/^[a-f0-9]{64}$/, {
58717
+ identifier: "SHA-256 source digest"
58718
+ }))),
58719
+ focus: exports_Schema.Literals([
58720
+ "concurrency",
58721
+ "persistence",
58722
+ "lease_recovery",
58723
+ "retry_idempotency",
58724
+ "regression_safety",
58725
+ "performance"
58726
+ ]),
58727
+ failureScenario: HumanReviewAnswerSchema,
58728
+ evidence: HumanReviewAnswerSchema,
58729
+ decision: exports_Schema.Literals(["ship", "block"]),
58730
+ confidence: exports_Schema.Int.pipe(exports_Schema.check(exports_Schema.isBetween({ minimum: 0, maximum: 100 }))),
58731
+ remainingRisk: HumanReviewAnswerSchema
58732
+ });
58733
+ var BrokenAgentEvaluationSolutionSchema = exports_Schema.Struct({
58734
+ ...JavascriptSourceSolutionSchema.fields,
58735
+ review: BrokenAgentHumanReviewSchema
58736
+ });
58737
+ var ChallengeSolutionSchema = exports_Schema.Union([
58738
+ JavascriptSourceSolutionSchema,
58739
+ BrokenAgentEvaluationSolutionSchema
58740
+ ]);
58713
58741
  var ChallengeObservationSchema = exports_Schema.Struct({
58714
58742
  sequence: exports_Schema.Number,
58715
58743
  input: exports_Schema.Unknown,
@@ -58809,6 +58837,7 @@ var ChallengeCatalogItemSchema = exports_Schema.Struct({
58809
58837
  playable: exports_Schema.Boolean,
58810
58838
  open: exports_Schema.Boolean,
58811
58839
  closed: exports_Schema.optional(exports_Schema.Boolean),
58840
+ challengeVersion: exports_Schema.optional(exports_Schema.String),
58812
58841
  rankingPath: exports_Schema.String
58813
58842
  });
58814
58843
  var ChallengeEvaluationSummarySchema = exports_Schema.Struct({
@@ -58826,6 +58855,11 @@ var ChallengeEvaluationSummarySchema = exports_Schema.Struct({
58826
58855
  breakdown: exports_Schema.optional(ChallengeScoreSchema.fields.breakdown)
58827
58856
  });
58828
58857
  var ChallengeAttemptViewSchema = exports_Schema.Struct({
58858
+ admission: exports_Schema.optional(exports_Schema.Struct({
58859
+ challengesMandatory: exports_Schema.Literal(true),
58860
+ selectionBasis: exports_Schema.Literal("challenge_rankings"),
58861
+ notice: exports_Schema.String
58862
+ })),
58829
58863
  challenge: ChallengeCatalogItemSchema,
58830
58864
  progress: ParticipantChallengeProgressSchema,
58831
58865
  observations: exports_Schema.Array(ChallengeObservationSchema),
@@ -58891,6 +58925,11 @@ var ChallengeRankingSchema = exports_Schema.Struct({
58891
58925
  competitorCount: exports_Schema.Number
58892
58926
  });
58893
58927
  var ChallengeCatalogSchema = exports_Schema.Struct({
58928
+ admission: exports_Schema.optional(exports_Schema.Struct({
58929
+ challengesMandatory: exports_Schema.Literal(true),
58930
+ selectionBasis: exports_Schema.Literal("challenge_rankings"),
58931
+ notice: exports_Schema.String
58932
+ })),
58894
58933
  challenges: exports_Schema.Array(ChallengeCatalogItemSchema)
58895
58934
  });
58896
58935
 
@@ -59240,7 +59279,12 @@ var PictureUploadCompletionSchema = exports_Schema.Struct({
59240
59279
  });
59241
59280
  var RegistrationResultSchema = exports_Schema.Struct({
59242
59281
  registration: RegistrationViewSchema,
59243
- requirements: RegistrationRequirementsSchema
59282
+ requirements: RegistrationRequirementsSchema,
59283
+ admission: exports_Schema.optional(exports_Schema.Struct({
59284
+ challengesMandatory: exports_Schema.Literal(true),
59285
+ selectionBasis: exports_Schema.Literal("challenge_rankings"),
59286
+ notice: exports_Schema.String
59287
+ }))
59244
59288
  });
59245
59289
  var CreatedRegistrationSchema = RegistrationResultSchema;
59246
59290
  var ApiSuccessSchema = (data) => exports_Schema.Struct({
@@ -59770,6 +59814,7 @@ var evaluateChallenge = (options, slug, input) => request3(options, `/api/v1/cha
59770
59814
  var getChallengeRanking = (options, slug) => publicRequest(options, `/api/v1/challenges/${slug}/ranking`, { method: "GET" }, decodeChallengeRanking);
59771
59815
 
59772
59816
  // src/challenge-input.ts
59817
+ import { createHash as createHash3 } from "node:crypto";
59773
59818
  import { readFile as readFile3 } from "node:fs/promises";
59774
59819
  var defaultChallengeSlug = "broken-agent";
59775
59820
  var readStdin = async () => {
@@ -59796,6 +59841,50 @@ var javascriptSourceFromPath = (path4, challenge = defaultChallengeSlug) => {
59796
59841
  }
59797
59842
  return readTextFile(path4).pipe(exports_Effect.map((source) => ({ kind: "javascript_source", source })));
59798
59843
  };
59844
+ var humanReviewRequiredDetails = {
59845
+ requiredFields: [
59846
+ "sourceDigest",
59847
+ "focus",
59848
+ "failureScenario",
59849
+ "evidence",
59850
+ "decision",
59851
+ "confidence",
59852
+ "remainingRisk"
59853
+ ],
59854
+ next: "Ask the participant to reason about the patch and provide these answers in review.json, then pass --review review.json."
59855
+ };
59856
+ var brokenAgentReviewFromPath = (path4) => {
59857
+ if (!path4) {
59858
+ return exports_Effect.fail(cliError("HUMAN_REVIEW_REQUIRED", "Broken Agent requires the participant's engineering review before an official evaluation", false, humanReviewRequiredDetails));
59859
+ }
59860
+ return readTextFile(path4).pipe(exports_Effect.flatMap((contents) => exports_Effect.try({
59861
+ try: () => JSON.parse(contents),
59862
+ catch: (error2) => cliError("INVALID_REVIEW_FILE", `Could not parse JSON review: ${String(error2)}`)
59863
+ })), exports_Effect.flatMap((input) => exports_Schema.decodeUnknownEffect(BrokenAgentHumanReviewSchema, {
59864
+ onExcessProperty: "error"
59865
+ })(input).pipe(exports_Effect.mapError((error2) => cliError("INVALID_HUMAN_REVIEW", error2.message, false, {
59866
+ ...humanReviewRequiredDetails,
59867
+ issues: String(error2)
59868
+ })))));
59869
+ };
59870
+ var challengeEvaluationInput = (sourcePath, reviewPath, challenge) => exports_Effect.gen(function* () {
59871
+ const solution = yield* javascriptSourceFromPath(sourcePath, challenge);
59872
+ if (challenge !== "broken-agent")
59873
+ return solution;
59874
+ const sourceDigest = createHash3("sha256").update(solution.source).digest("hex");
59875
+ if (!reviewPath) {
59876
+ return yield* exports_Effect.fail(cliError("HUMAN_REVIEW_REQUIRED", "Broken Agent requires the participant's engineering review before an official evaluation", false, { ...humanReviewRequiredDetails, sourceDigest }));
59877
+ }
59878
+ const review = yield* brokenAgentReviewFromPath(reviewPath);
59879
+ if (review.sourceDigest !== sourceDigest) {
59880
+ return yield* exports_Effect.fail(cliError("STALE_HUMAN_REVIEW", "review.json does not match the current scheduler.js", false, {
59881
+ expectedSourceDigest: sourceDigest,
59882
+ reviewSourceDigest: review.sourceDigest,
59883
+ next: "Show the participant the updated evidence, obtain a fresh decision, and replace review.json."
59884
+ }));
59885
+ }
59886
+ return { ...solution, review };
59887
+ });
59799
59888
  var requiredInteger = (message) => exports_Prompt.text({
59800
59889
  message,
59801
59890
  validate: (value3) => {
@@ -59870,7 +59959,12 @@ var challengeParticipationNotice = (title, opensAt, closesAt, now3 = new Date) =
59870
59959
  return challengeLaunchNotice(title, opensAt, now3);
59871
59960
  };
59872
59961
  var challengeListText = (catalog) => {
59873
- const lines2 = [`${eventName} challenges`, ""];
59962
+ const lines2 = [
59963
+ `${eventName} challenges`,
59964
+ "",
59965
+ catalog.admission?.notice ?? challengeAdmissionNotice,
59966
+ ""
59967
+ ];
59874
59968
  for (const challenge of catalog.challenges) {
59875
59969
  let state = `abre ${formatChallengeOpeningInPeru(challenge.opensAt)}`;
59876
59970
  if (challenge.closed) {
@@ -59883,6 +59977,9 @@ var challengeListText = (catalog) => {
59883
59977
  const playable = challenge.playable ? "" : " (próximamente)";
59884
59978
  lines2.push(`${challenge.code} ${challenge.theme} — ${challenge.title}${playable}`);
59885
59979
  lines2.push(` ${challenge.coreSkill} · ${challenge.formatLabel} · ${state}`);
59980
+ if (challenge.challengeVersion) {
59981
+ lines2.push(` Versión vigente: ${challenge.challengeVersion}`);
59982
+ }
59886
59983
  lines2.push(` Ranking: ${challenge.rankingPath}`);
59887
59984
  lines2.push("");
59888
59985
  }
@@ -59907,10 +60004,14 @@ var challengeShowText = (attempt) => {
59907
60004
  `${challenge.title.toUpperCase()} — CASO #${challenge.code}`,
59908
60005
  challenge.summary,
59909
60006
  "",
60007
+ attempt.admission?.notice ?? challengeAdmissionNotice,
60008
+ "",
60009
+ `VERSIÓN VIGENTE: ${challenge.challengeVersion ?? "administrada por el servidor"}`,
60010
+ "",
59910
60011
  `ESTADO DEL CASO: ${caseStatus2}`,
59911
60012
  "",
59912
60013
  "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.",
60014
+ "Las herramientas de AI están permitidas: el agente implementa; tú eliges el riesgo, revisas la evidencia y decides ship o block.",
59914
60015
  "",
59915
60016
  `Evaluaciones ${evaluationsRemaining} / ${progress.evaluationsLimit} restantes`
59916
60017
  ];
@@ -59921,7 +60022,7 @@ var challengeShowText = (attempt) => {
59921
60022
  if (progress.shareCode)
59922
60023
  lines3.push(`Código #${progress.shareCode}`);
59923
60024
  }
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");
60025
+ lines3.push("", "SIGUIENTE PASO", "Lee el contrato. Antes de editar, discute tres trazas de falla y elige cuál investigar primero.", " cd broken-agent && npm test", " chofex challenge test --challenge broken-agent --source ./scheduler.js");
59925
60026
  return lines3.join(`
59926
60027
  `);
59927
60028
  }
@@ -59939,6 +60040,10 @@ var challengeShowText = (attempt) => {
59939
60040
  `${challenge.title.toUpperCase()} — CASE #${challenge.code}`,
59940
60041
  challenge.summary,
59941
60042
  "",
60043
+ attempt.admission?.notice ?? challengeAdmissionNotice,
60044
+ "",
60045
+ `CURRENT VERSION: ${challenge.challengeVersion ?? "server-managed"}`,
60046
+ "",
59942
60047
  `CASE STATUS: ${caseStatus}`,
59943
60048
  "",
59944
60049
  "Your job is to replace the machine, not merely guess its next answer.",
@@ -60093,7 +60198,7 @@ var challengeTestText = (result3) => {
60093
60198
  `${result3.matchedObservations} / ${result3.observationCount} comportamientos visibles pasan`
60094
60199
  ];
60095
60200
  if (result3.accuracy === 1) {
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");
60201
+ lines3.push("", "Todo pasa.", "Eso todavía no significa que el scheduler sea correcto en producción.", "Antes de evaluar, el participante debe revisar la evidencia y tomar la decisión de release.", "", "Siguiente: discutan una traza de falla concreta y creen review.json con las palabras del participante.", "Luego usa una evaluación oficial solo si su decisión lo permite", " chofex challenge evaluate --challenge broken-agent --source ./scheduler.js --review ./review.json");
60097
60202
  return lines3.join(`
60098
60203
  `);
60099
60204
  }
@@ -60420,11 +60525,65 @@ Repara \`scheduler.js\` sin cambiar la interfaz exportada
60420
60525
  \`\`\`sh
60421
60526
  npm test
60422
60527
  chofex challenge test --challenge broken-agent --source ./scheduler.js
60423
- chofex challenge evaluate --challenge broken-agent --source ./scheduler.js
60528
+ chofex challenge evaluate --challenge broken-agent --source ./scheduler.js --review ./review.json
60424
60529
  \`\`\`
60425
60530
 
60426
60531
  Los tests locales y públicos son ilimitados. Tienes **5 evaluaciones oficiales**
60427
- contra escenarios ocultos. Las herramientas de AI están permitidas.
60532
+ contra escenarios ocultos. Las herramientas de AI están permitidas, pero este es
60533
+ un challenge de colaboración: el agente implementa y el participante toma las
60534
+ decisiones de ingeniería.
60535
+
60536
+ Los challenges técnicos son obligatorios para competir por un cupo. Enviar la
60537
+ postulación solo crea tu candidatura: no reserva una plaza. Los mejores
60538
+ resultados de los rankings serán seleccionados para el evento.
60539
+
60540
+ ## Protocolo humano–agente
60541
+
60542
+ No le pidas al agente que resuelva todo en silencio. Antes de modificar el
60543
+ scheduler, el agente debe presentarte al menos tres trazas de falla concretas del
60544
+ starter. Tú eliges cuál investigar primero y explicas qué resultado nunca debería
60545
+ ocurrir. El agente convierte esa decisión en una prueba y luego implementa.
60546
+
60547
+ Cuando los tests estén verdes, el agente debe enseñarte el cambio, la evidencia y
60548
+ los supuestos que todavía no verificó. La evaluación oficial requiere
60549
+ \`review.json\` con tus propias palabras:
60550
+
60551
+ - \`focus\`: \`concurrency\`, \`persistence\`, \`lease_recovery\`,
60552
+ \`retry_idempotency\`, \`regression_safety\` o \`performance\`;
60553
+ - \`sourceDigest\`: SHA-256 de \`scheduler.js\` para vincular el juicio al cambio
60554
+ exacto (el agente puede calcular este valor mecánico);
60555
+ - \`failureScenario\`: una secuencia concreta de eventos y su resultado
60556
+ incorrecto;
60557
+ - \`evidence\`: la prueba o inspección que revisaste y qué demostró;
60558
+ - \`decision\`: \`ship\` o \`block\`;
60559
+ - \`confidence\`: un entero de 0 a 100;
60560
+ - \`remainingRisk\`: el riesgo que aceptas o que todavía bloquea el release.
60561
+
60562
+ Forma del archivo (reemplaza cada valor entre <...>):
60563
+
60564
+ \`\`\`json
60565
+ {
60566
+ "sourceDigest": "<sha256 de scheduler.js>",
60567
+ "focus": "<área elegida>",
60568
+ "failureScenario": "<tu traza concreta>",
60569
+ "evidence": "<la evidencia que revisaste>",
60570
+ "decision": "<ship o block>",
60571
+ "confidence": 0,
60572
+ "remainingRisk": "<el riesgo que queda>"
60573
+ }
60574
+ \`\`\`
60575
+
60576
+ Cada respuesta de texto debe tener entre 20 y 1,000 caracteres. El agente puede
60577
+ explicar, debatir y guardar tus respuestas, pero no puede elegir el foco, inventar
60578
+ tu razonamiento ni tomar la decisión de release por ti. Si cambias
60579
+ \`scheduler.js\`, vuelve a revisar la evidencia antes de reemplazar el review.
60580
+
60581
+ La primera ejecución de \`challenge evaluate\` crea un enlace corto de
60582
+ aprobación y **no consume** una evaluación. Abre ese enlace personalmente,
60583
+ revisa el review vinculado al SHA-256 exacto y confirma con Face ID, Touch ID,
60584
+ Windows Hello, PIN del dispositivo o una llave de seguridad. Después repite el
60585
+ mismo comando para ejecutar la evaluación oficial. Cada evaluación requiere una
60586
+ aprobación nueva; la sesión OAuth del CLI no puede aprobarla.
60428
60587
 
60429
60588
  ## Contrato normativo
60430
60589
 
@@ -60706,7 +60865,7 @@ var root = exports_Command.make("chofex").pipe(exports_Command.withSharedFlags({
60706
60865
  apiUrl: exports_Flag.string(rootValueFlagNames.apiUrl).pipe(exports_Flag.withDefault(config.apiUrl), exports_Flag.withDescription("Chofex API base URL")),
60707
60866
  output: exports_Flag.choice(rootValueFlagNames.output, ["human", "json"]).pipe(exports_Flag.withDefault("human"), exports_Flag.withDescription("Output format")),
60708
60867
  token: exports_Flag.string(rootValueFlagNames.token).pipe(exports_Flag.optional, exports_Flag.withDescription("Clerk OAuth token (prefer CHOFEX_TOKEN to avoid shell history)"))
60709
- }), exports_Command.withDescription(`Register for and manage your ${eventName} application`));
60868
+ }), exports_Command.withDescription(`Apply to ${eventName} and compete for a seat through the mandatory technical challenges`));
60710
60869
 
60711
60870
  // src/output.ts
60712
60871
  var printJson = (value3) => exports_Effect.sync(() => {
@@ -60785,8 +60944,14 @@ var requirementsText = (result3) => {
60785
60944
  if (requirements.stage === "draft") {
60786
60945
  return `Application draft in progress.${registrationPartsText(result3)}`;
60787
60946
  }
60788
- if (requirements.stage === "review")
60789
- return "No action needed while your application is reviewed.";
60947
+ if (requirements.stage === "review") {
60948
+ return [
60949
+ "Application received. A seat has not been assigned.",
60950
+ result3.admission?.notice ?? challengeAdmissionNotice,
60951
+ "Next command: chofex challenge"
60952
+ ].join(`
60953
+ `);
60954
+ }
60790
60955
  let feedback = "";
60791
60956
  const rejectionReason = requirements.rejectionReason ?? registration.rejectionReason;
60792
60957
  if (rejectionReason) {
@@ -60845,10 +61010,12 @@ var challengeQuickstart = {
60845
61010
  ],
60846
61011
  mission: "Audita scheduler.js, conserva createScheduler(dependencies) y haz que el sistema cumpla el contrato de producción.",
60847
61012
  rules: [
61013
+ "Los challenges son obligatorios: enviar la postulación no reserva un cupo.",
61014
+ "Los mejores resultados de los rankings serán seleccionados para el evento.",
60848
61015
  "Tienes 5 evaluaciones oficiales contra variantes ocultas y determinísticas por participante.",
60849
61016
  "Los tests locales y públicos son ilimitados.",
60850
61017
  "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."
61018
+ "Las herramientas de AI están permitidas, pero el participante toma las decisiones de ingeniería."
60852
61019
  ],
60853
61020
  workflow: [
60854
61021
  {
@@ -60877,24 +61044,48 @@ var challengeQuickstart = {
60877
61044
  },
60878
61045
  {
60879
61046
  step: 5,
61047
+ action: "Elige el riesgo con el participante",
61048
+ command: "Discute tres trazas de falla concretas",
61049
+ note: "El participante elige cuál investigar primero y explica qué resultado nunca debería ocurrir. El agente no puede decidirlo por su cuenta."
61050
+ },
61051
+ {
61052
+ step: 6,
60880
61053
  action: "Audita y repara",
60881
61054
  command: "$EDITOR scheduler.js",
60882
- note: "Razona sobre claims únicos, leases, reinicios, carreras, cancelación, reintentos e idempotencia."
61055
+ note: "Convierte la traza elegida en evidencia reproducible y luego endurece claims, leases, reinicios, carreras, cancelación, reintentos e idempotencia."
60883
61056
  },
60884
61057
  {
60885
- step: 6,
61058
+ step: 7,
60886
61059
  action: "Ejecuta los tests públicos",
60887
61060
  command: "chofex challenge test --challenge broken-agent --source ./scheduler.js",
60888
61061
  note: "Es seguro repetirlos y no consumen evaluaciones oficiales."
60889
61062
  },
60890
61063
  {
60891
- step: 7,
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."
61064
+ step: 8,
61065
+ action: "Obtén el juicio del participante",
61066
+ command: "Crea review.json con sus propias palabras",
61067
+ note: "Debe describir una traza de falla, la evidencia revisada, ship o block, confianza y riesgo restante. Cambiar el código exige revisar de nuevo."
60895
61068
  },
60896
61069
  {
60897
- step: 8,
61070
+ step: 9,
61071
+ action: "Crea el handoff de evaluación",
61072
+ command: "chofex challenge evaluate --challenge broken-agent --source ./scheduler.js --review ./review.json",
61073
+ note: "Devuelve un enlace de aprobación sin consumir una evaluación oficial."
61074
+ },
61075
+ {
61076
+ step: 10,
61077
+ action: "El participante aprueba en el navegador",
61078
+ command: "Abre approvalUrl personalmente",
61079
+ note: "Revisa el razonamiento y confirma con Face ID, Touch ID, Windows Hello, PIN o llave de seguridad. El agente no puede completar este paso."
61080
+ },
61081
+ {
61082
+ step: 11,
61083
+ action: "Solicita el veredicto oculto",
61084
+ command: "chofex challenge evaluate --challenge broken-agent --source ./scheduler.js --review ./review.json",
61085
+ note: "Repite el mismo comando antes de que venza la aprobación; recién entonces consume una evaluación."
61086
+ },
61087
+ {
61088
+ step: 12,
60898
61089
  action: "Consulta el ranking",
60899
61090
  command: "chofex challenge ranking --challenge broken-agent",
60900
61091
  note: "Se revela el 1 de octubre a las 15:00, hora de Perú."
@@ -60946,6 +61137,35 @@ var challengeQuickstartText = (participationState, launchNotice) => {
60946
61137
  var optionalString = (name, description) => exports_Flag.string(name).pipe(exports_Flag.optional, exports_Flag.withDescription(description));
60947
61138
  var challengeFlag = exports_Flag.string("challenge").pipe(exports_Flag.withDefault(defaultChallengeSlug), exports_Flag.withDescription("Challenge slug (default: broken-agent)"));
60948
61139
  var sourceFlag = optionalString("source", "JavaScript challenge solution file");
61140
+ var reviewFlag = optionalString("review", "Participant-authored Broken Agent engineering review JSON");
61141
+ var evaluationErrorText = (error2) => {
61142
+ if (error2.code !== "HUMAN_APPROVAL_REQUIRED")
61143
+ return;
61144
+ if (!error2.details || typeof error2.details !== "object")
61145
+ return;
61146
+ const details = error2.details;
61147
+ if (typeof details.approvalUrl !== "string")
61148
+ return;
61149
+ let retryCommand = "chofex challenge evaluate --challenge broken-agent --source ./scheduler.js --review ./review.json";
61150
+ if (typeof details.retryCommand === "string") {
61151
+ retryCommand = details.retryCommand;
61152
+ }
61153
+ const lines2 = [
61154
+ "INTERVENCIÓN DEL PARTICIPANTE REQUERIDA",
61155
+ "La evaluación todavía no consumió un intento.",
61156
+ "",
61157
+ "El participante debe abrir personalmente este enlace, revisar sus respuestas y aprobar con Face ID, Touch ID, Windows Hello, PIN o llave de seguridad:",
61158
+ ` ${details.approvalUrl}`,
61159
+ "",
61160
+ "Después de aprobar, repite:",
61161
+ ` ${retryCommand}`
61162
+ ];
61163
+ if (typeof details.expiresAt === "string") {
61164
+ lines2.push("", `La aprobación vence: ${details.expiresAt}`);
61165
+ }
61166
+ return lines2.join(`
61167
+ `);
61168
+ };
60949
61169
  var inputFlag = optionalString("input", "JSON file, or - for stdin");
60950
61170
  var formatFlag = exports_Flag.choice("format", ["table", "json", "csv"]).pipe(exports_Flag.withDefault("table"), exports_Flag.withDescription("Notebook format"));
60951
61171
  var numberFromOption = (value3) => {
@@ -61131,17 +61351,21 @@ var testCommand = exports_Command.make("test", { challenge: challengeFlag, sourc
61131
61351
  description: "Ejecuta los tests públicos de Broken Agent"
61132
61352
  }
61133
61353
  ]));
61134
- var evaluateCommand = exports_Command.make("evaluate", { challenge: challengeFlag, source: sourceFlag }, exports_Effect.fn("challengeEvaluateCommand")(function* ({ challenge, source }) {
61354
+ var evaluateCommand = exports_Command.make("evaluate", { challenge: challengeFlag, source: sourceFlag, review: reviewFlag }, exports_Effect.fn("challengeEvaluateCommand")(function* ({
61355
+ challenge,
61356
+ source,
61357
+ review
61358
+ }) {
61135
61359
  const options = yield* root;
61136
61360
  const token = exports_Option.getOrUndefined(options.token);
61137
61361
  const operation = exports_Effect.gen(function* () {
61138
- const solution = yield* javascriptSourceFromPath(exports_Option.getOrUndefined(source), challenge);
61362
+ const solution = yield* challengeEvaluationInput(exports_Option.getOrUndefined(source), exports_Option.getOrUndefined(review), challenge);
61139
61363
  return yield* evaluateChallenge({ apiUrl: options.apiUrl, token }, challenge, solution);
61140
61364
  });
61141
- yield* execute(options.output, operation, challengeEvaluateText);
61142
- })).pipe(exports_Command.withDescription("Score a solution on hidden cases. This consumes one limited official evaluation. Requires sign-in."), exports_Command.withExamples([
61365
+ yield* execute(options.output, operation, challengeEvaluateText, evaluationErrorText);
61366
+ })).pipe(exports_Command.withDescription("Score a solution on hidden cases. Broken Agent requires participant browser approval before a limited evaluation is consumed."), exports_Command.withExamples([
61143
61367
  {
61144
- command: "chofex challenge evaluate --challenge broken-agent --source ./scheduler.js",
61368
+ command: "chofex challenge evaluate --challenge broken-agent --source ./scheduler.js --review ./review.json",
61145
61369
  description: "Consume una evaluación oficial cuando tu solución esté lista"
61146
61370
  }
61147
61371
  ]));
@@ -61200,7 +61424,7 @@ var challengeCommand = exports_Command.make("challenge", {}, exports_Effect.fn("
61200
61424
  return;
61201
61425
  }
61202
61426
  yield* exports_Console.log(challengeQuickstartText(participationState, launchNotice));
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([
61427
+ })).pipe(exports_Command.withDescription("Compite en challenges técnicos obligatorios por un cupo. Ejecuta el comando sin subcomandos para abrir la guía del challenge actual."), exports_Command.withExamples([
61204
61428
  {
61205
61429
  command: "chofex challenge",
61206
61430
  description: "Abre la guía de Broken Agent"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chofex-cli",
3
- "version": "0.1.189",
3
+ "version": "0.1.191",
4
4
  "description": "Command-line client for Hack the Andes",
5
5
  "type": "module",
6
6
  "bin": {