nexgenk 1.4.1 → 1.5.0

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 (2) hide show
  1. package/dist/main.mjs +934 -488
  2. package/package.json +1 -1
package/dist/main.mjs CHANGED
@@ -111222,7 +111222,7 @@ function dump$1(input, options) {
111222
111222
  if (writeNode(state, 0, value, true, true)) return state.dump + "\n";
111223
111223
  return "";
111224
111224
  }
111225
- var common, exception, snippet, TYPE_CONSTRUCTOR_OPTIONS, YAML_NODE_KINDS, type$2, schema, str$3, seq, map, failsafe, _null, bool$1, int, YAML_FLOAT_PATTERN, SCIENTIFIC_WITHOUT_DOT, float, json, core, YAML_DATE_REGEXP, YAML_TIMESTAMP_REGEXP, timestamp$1, merge, BASE64_MAP, binary, _hasOwnProperty$3, _toString$2, omap, _toString$1, pairs, _hasOwnProperty$2, set$1, _default, _hasOwnProperty$1, CONTEXT_FLOW_IN, CONTEXT_FLOW_OUT, CONTEXT_BLOCK_IN, CONTEXT_BLOCK_OUT, CHOMPING_CLIP, CHOMPING_STRIP, CHOMPING_KEEP, PATTERN_NON_PRINTABLE, PATTERN_NON_ASCII_LINE_BREAKS, PATTERN_FLOW_INDICATORS, PATTERN_TAG_HANDLE, PATTERN_TAG_URI, simpleEscapeCheck, simpleEscapeMap, i$1, directiveHandlers, loader, _toString, _hasOwnProperty, CHAR_BOM, CHAR_TAB, CHAR_LINE_FEED, CHAR_CARRIAGE_RETURN, CHAR_SPACE, CHAR_EXCLAMATION, CHAR_DOUBLE_QUOTE, CHAR_SHARP, CHAR_PERCENT, CHAR_AMPERSAND, CHAR_SINGLE_QUOTE, CHAR_ASTERISK, CHAR_COMMA, CHAR_MINUS, CHAR_COLON, CHAR_EQUALS, CHAR_GREATER_THAN, CHAR_QUESTION, CHAR_COMMERCIAL_AT, CHAR_LEFT_SQUARE_BRACKET, CHAR_RIGHT_SQUARE_BRACKET, CHAR_GRAVE_ACCENT, CHAR_LEFT_CURLY_BRACKET, CHAR_VERTICAL_LINE, CHAR_RIGHT_CURLY_BRACKET, ESCAPE_SEQUENCES, DEPRECATED_BOOLEANS_SYNTAX, DEPRECATED_BASE60_SYNTAX, QUOTING_TYPE_SINGLE, QUOTING_TYPE_DOUBLE, STYLE_PLAIN, STYLE_SINGLE, STYLE_LITERAL, STYLE_FOLDED, STYLE_DOUBLE, dumper, load;
111225
+ var common, exception, snippet, TYPE_CONSTRUCTOR_OPTIONS, YAML_NODE_KINDS, type$2, schema, str$3, seq, map, failsafe, _null, bool, int, YAML_FLOAT_PATTERN, SCIENTIFIC_WITHOUT_DOT, float, json, core, YAML_DATE_REGEXP, YAML_TIMESTAMP_REGEXP, timestamp$1, merge, BASE64_MAP, binary, _hasOwnProperty$3, _toString$2, omap, _toString$1, pairs, _hasOwnProperty$2, set$1, _default, _hasOwnProperty$1, CONTEXT_FLOW_IN, CONTEXT_FLOW_OUT, CONTEXT_BLOCK_IN, CONTEXT_BLOCK_OUT, CHOMPING_CLIP, CHOMPING_STRIP, CHOMPING_KEEP, PATTERN_NON_PRINTABLE, PATTERN_NON_ASCII_LINE_BREAKS, PATTERN_FLOW_INDICATORS, PATTERN_TAG_HANDLE, PATTERN_TAG_URI, simpleEscapeCheck, simpleEscapeMap, i$1, directiveHandlers, loader, _toString, _hasOwnProperty, CHAR_BOM, CHAR_TAB, CHAR_LINE_FEED, CHAR_CARRIAGE_RETURN, CHAR_SPACE, CHAR_EXCLAMATION, CHAR_DOUBLE_QUOTE, CHAR_SHARP, CHAR_PERCENT, CHAR_AMPERSAND, CHAR_SINGLE_QUOTE, CHAR_ASTERISK, CHAR_COMMA, CHAR_MINUS, CHAR_COLON, CHAR_EQUALS, CHAR_GREATER_THAN, CHAR_QUESTION, CHAR_COMMERCIAL_AT, CHAR_LEFT_SQUARE_BRACKET, CHAR_RIGHT_SQUARE_BRACKET, CHAR_GRAVE_ACCENT, CHAR_LEFT_CURLY_BRACKET, CHAR_VERTICAL_LINE, CHAR_RIGHT_CURLY_BRACKET, ESCAPE_SEQUENCES, DEPRECATED_BOOLEANS_SYNTAX, DEPRECATED_BASE60_SYNTAX, QUOTING_TYPE_SINGLE, QUOTING_TYPE_DOUBLE, STYLE_PLAIN, STYLE_SINGLE, STYLE_LITERAL, STYLE_FOLDED, STYLE_DOUBLE, dumper, load;
111226
111226
  var init_js_yaml = __esmMin((() => {
111227
111227
  common = {
111228
111228
  isNothing,
@@ -111330,7 +111330,7 @@ var init_js_yaml = __esmMin((() => {
111330
111330
  },
111331
111331
  defaultStyle: "lowercase"
111332
111332
  });
111333
- bool$1 = new type$2("tag:yaml.org,2002:bool", {
111333
+ bool = new type$2("tag:yaml.org,2002:bool", {
111334
111334
  kind: "scalar",
111335
111335
  resolve: resolveYamlBoolean,
111336
111336
  construct: constructYamlBoolean,
@@ -111387,7 +111387,7 @@ var init_js_yaml = __esmMin((() => {
111387
111387
  });
111388
111388
  json = failsafe.extend({ implicit: [
111389
111389
  _null,
111390
- bool$1,
111390
+ bool,
111391
111391
  int,
111392
111392
  float
111393
111393
  ] });
@@ -125575,9 +125575,9 @@ var init_update_config = __esmMin((() => {
125575
125575
  };
125576
125576
  }));
125577
125577
  // ../../packages/agent-core-v2/src/features/wave/wave.ts
125578
- var WAVE_FLAG_ID$1, WAVE_FLAG_ENV, WAVE_SEAT_PROFILE, WAVE_TOOL_DOMAIN;
125578
+ var WAVE_FLAG_ID, WAVE_FLAG_ENV, WAVE_SEAT_PROFILE, WAVE_TOOL_DOMAIN;
125579
125579
  var init_wave$3 = __esmMin((() => {
125580
- WAVE_FLAG_ID$1 = "wave";
125580
+ WAVE_FLAG_ID = "wave";
125581
125581
  WAVE_FLAG_ENV = "NEXGEN_EXPERIMENTAL_WAVE";
125582
125582
  WAVE_SEAT_PROFILE = "wave-seat";
125583
125583
  WAVE_TOOL_DOMAIN = "wave";
@@ -125604,7 +125604,7 @@ var init_wave$1 = __esmMin((() => {
125604
125604
  ...parsed$1,
125605
125605
  path: PSEUDO_PATH$1,
125606
125606
  dir: PSEUDO_PATH$1,
125607
- experimentalFlag: WAVE_FLAG_ID$1,
125607
+ experimentalFlag: WAVE_FLAG_ID,
125608
125608
  metadata: {
125609
125609
  ...parsed$1.metadata,
125610
125610
  type: parsed$1.metadata.type ?? "inline"
@@ -219297,6 +219297,26 @@ var init_decide$1 = __esmMin((() => {
219297
219297
  IWaveDecideTool = createDecorator("waveDecideTool");
219298
219298
  }));
219299
219299
  // ../../packages/agent-core-v2/src/features/wave/core/config.ts
219300
+ function seats(prefix, count) {
219301
+ return Array.from({ length: Math.max(0, count) }, (_, index) => `${prefix}${index + 1}`);
219302
+ }
219303
+ function waveRoundSeats(roles, round) {
219304
+ if (round === "propose") return seats("P", roles.proponents);
219305
+ if (round === "defend") return seats("D", roles.defenders);
219306
+ if (round === "attack") return seats("A", roles.adversaries);
219307
+ if (round === "judge") return seats("J", roles.judges);
219308
+ const refiners = seats("R", roles.refiners);
219309
+ return round === "refine" ? refiners.slice(0, 1) : refiners.slice(1);
219310
+ }
219311
+ function waveConfigState(config) {
219312
+ const state = config;
219313
+ return {
219314
+ ...config,
219315
+ version: state.version ?? 1,
219316
+ wavesOpen: state.wavesOpen ?? [],
219317
+ raw: state.raw ?? {}
219318
+ };
219319
+ }
219300
219320
  function record$2(value) {
219301
219321
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
219302
219322
  }
@@ -219306,9 +219326,6 @@ function num(value, fallback) {
219306
219326
  function text(value, fallback) {
219307
219327
  return typeof value === "string" ? value : fallback;
219308
219328
  }
219309
- function bool(value, fallback) {
219310
- return typeof value === "boolean" ? value : fallback;
219311
- }
219312
219329
  function strArray$1(value, fallback) {
219313
219330
  return Array.isArray(value) && value.every((item) => typeof item === "string") ? value : fallback;
219314
219331
  }
@@ -219320,6 +219337,9 @@ function makeWaveConfig(raw, loadedFrom) {
219320
219337
  const roles = record$2(base["roles"]);
219321
219338
  const quorum = record$2(base["quorum"]);
219322
219339
  return {
219340
+ version: num(base["version"], 1),
219341
+ wavesOpen: strArray$1(base["wavesOpen"], []),
219342
+ raw: base,
219323
219343
  layout: {
219324
219344
  waves: text(layout["waves"], DEFAULT_LAYOUT.waves),
219325
219345
  backlog: text(layout["backlog"], DEFAULT_LAYOUT.backlog),
@@ -219338,8 +219358,7 @@ function makeWaveConfig(raw, loadedFrom) {
219338
219358
  minGoalChars: num(thresholds["minGoalChars"], DEFAULT_THRESHOLDS.minGoalChars),
219339
219359
  minScopeChars: num(thresholds["minScopeChars"], DEFAULT_THRESHOLDS.minScopeChars),
219340
219360
  minFieldChars: num(thresholds["minFieldChars"], DEFAULT_THRESHOLDS.minFieldChars),
219341
- minMetricChars: num(thresholds["minMetricChars"], DEFAULT_THRESHOLDS.minMetricChars),
219342
- allowVerifyTargetMissing: bool(thresholds["allowVerifyTargetMissing"], DEFAULT_THRESHOLDS.allowVerifyTargetMissing)
219361
+ minMetricChars: num(thresholds["minMetricChars"], DEFAULT_THRESHOLDS.minMetricChars)
219343
219362
  },
219344
219363
  vocabulary: {
219345
219364
  areas: strArray$1(vocabulary["areas"], DEFAULT_VOCABULARY.areas),
@@ -219363,8 +219382,7 @@ function makeWaveConfig(raw, loadedFrom) {
219363
219382
  minAxes: num(quorum["minAxes"], DEFAULT_QUORUM.minAxes),
219364
219383
  axes: strArray$1(quorum["axes"], DEFAULT_QUORUM.axes)
219365
219384
  },
219366
- loadedFrom,
219367
- missingKeys: CONFIG_REQUIRED_KEYS.filter((key) => base[key] === void 0)
219385
+ loadedFrom
219368
219386
  };
219369
219387
  }
219370
219388
  function readWaveConfig(fs, root) {
@@ -219383,8 +219401,24 @@ function containedPath(root, candidate) {
219383
219401
  if (resolved !== resolvedRoot && !resolved.startsWith(`${resolvedRoot}${sep}`)) throw new Error(`ruta fuera del workspace: ${candidate}`);
219384
219402
  return resolved;
219385
219403
  }
219386
- var DEFAULT_LAYOUT, DEFAULT_THRESHOLDS, DEFAULT_VOCABULARY, DEFAULT_ROLES, DEFAULT_QUORUM, CONFIG_REQUIRED_KEYS;
219404
+ var WAVE_ROUNDS, WAVE_ROUND_DIRS, DEFAULT_LAYOUT, DEFAULT_THRESHOLDS, DEFAULT_VOCABULARY, DEFAULT_ROLES, DEFAULT_QUORUM;
219387
219405
  var init_config$6 = __esmMin((() => {
219406
+ WAVE_ROUNDS = [
219407
+ "propose",
219408
+ "defend",
219409
+ "attack",
219410
+ "judge",
219411
+ "refine",
219412
+ "audit"
219413
+ ];
219414
+ WAVE_ROUND_DIRS = {
219415
+ propose: "01-proposals",
219416
+ defend: "02-defenses",
219417
+ attack: "03-attacks",
219418
+ judge: "04-verdicts",
219419
+ refine: "05-refined",
219420
+ audit: "05-refined"
219421
+ };
219388
219422
  DEFAULT_LAYOUT = {
219389
219423
  waves: "design/waves",
219390
219424
  backlog: "backlog",
@@ -219402,8 +219436,7 @@ var init_config$6 = __esmMin((() => {
219402
219436
  minGoalChars: 40,
219403
219437
  minScopeChars: 40,
219404
219438
  minFieldChars: 10,
219405
- minMetricChars: 4,
219406
- allowVerifyTargetMissing: false
219439
+ minMetricChars: 4
219407
219440
  };
219408
219441
  DEFAULT_VOCABULARY = {
219409
219442
  areas: [
@@ -219482,13 +219515,6 @@ var init_config$6 = __esmMin((() => {
219482
219515
  "encaje"
219483
219516
  ]
219484
219517
  };
219485
- CONFIG_REQUIRED_KEYS = [
219486
- "version",
219487
- "layout",
219488
- "waves",
219489
- "thresholds",
219490
- "vocabulary"
219491
- ];
219492
219518
  }));
219493
219519
  // ../../packages/agent-core-v2/src/features/wave/core/nodeFs.ts
219494
219520
  var nodeWaveFs;
@@ -219574,17 +219600,13 @@ function evaluateQuorum(input) {
219574
219600
  if (winner === void 0) throw new Error("no hay propuestas que comparar");
219575
219601
  if (second === void 0) return {
219576
219602
  verdict: "refine",
219577
- winner: void 0,
219603
+ winner,
219578
219604
  second: void 0,
219579
219605
  medians,
219580
219606
  medianMargin: 0,
219581
219607
  judgesHoldingMargin: 0,
219582
- perJudge: input.verdicts.map((verdict) => ({
219583
- judge: verdict.judge,
219584
- margin: 0,
219585
- holds: false
219586
- })),
219587
- reason: `solo hay una propuesta (${winner}): sin comparacion no hay quorum`
219608
+ perJudge: [],
219609
+ reason: `sin comparacion no hay quorum: solo hay una propuesta (${winner})`
219588
219610
  };
219589
219611
  const winnerMedian = medians[winner] ?? 0;
219590
219612
  const secondMedian = medians[second] ?? 0;
@@ -219609,8 +219631,7 @@ function evaluateQuorum(input) {
219609
219631
  });
219610
219632
  const judgesHoldingMargin = perJudge.filter((entry) => entry.holds).length;
219611
219633
  const marginOk = medianMargin >= minPoints;
219612
- const requiredJudges = Math.min(minAxes, perJudge.length);
219613
- const majorityOk = judgesHoldingMargin >= requiredJudges;
219634
+ const majorityOk = judgesHoldingMargin >= minAxes;
219614
219635
  if (marginOk && majorityOk) return {
219615
219636
  verdict: "adopt",
219616
219637
  winner,
@@ -219619,9 +219640,9 @@ function evaluateQuorum(input) {
219619
219640
  medianMargin,
219620
219641
  judgesHoldingMargin,
219621
219642
  perJudge,
219622
- reason: `mediana ${winner} ${winnerMedian} vs ${second} ${secondMedian}: margen ${medianMargin} en ${judgesHoldingMargin}/${perJudge.length} jueces (minimo ${requiredJudges})`
219643
+ reason: `mediana ${winner} ${winnerMedian} vs ${second} ${secondMedian}: margen ${medianMargin} en ${judgesHoldingMargin}/${perJudge.length} jueces`
219623
219644
  };
219624
- const reason = [marginOk ? void 0 : `margen ${medianMargin} < ${minPoints}`, majorityOk ? void 0 : `solo ${judgesHoldingMargin}/${perJudge.length} jueces superan el margen (minimo ${requiredJudges})`].filter((part) => part !== void 0).join(" y ");
219645
+ const reason = [marginOk ? void 0 : `margen ${medianMargin} < ${minPoints}`, majorityOk ? void 0 : `solo ${judgesHoldingMargin}/${perJudge.length} jueces superan el margen (minimo ${minAxes})`].filter((part) => part !== void 0).join(" y ");
219625
219646
  return {
219626
219647
  verdict: round >= 2 ? "deadlock" : "refine",
219627
219648
  winner,
@@ -219656,6 +219677,7 @@ function runWaveDecideForRoot(input) {
219656
219677
  isError: true
219657
219678
  };
219658
219679
  }
219680
+ const holding = result.perJudge.length === 0 ? "sin comparacion" : `${result.judgesHoldingMargin}/${result.perJudge.length}`;
219659
219681
  const lines = [
219660
219682
  `QUORUM — ${result.verdict}`,
219661
219683
  "",
@@ -219663,8 +219685,9 @@ function runWaveDecideForRoot(input) {
219663
219685
  "",
219664
219686
  ` ganador: ${result.winner ?? "(empate)"}`,
219665
219687
  ` margen: ${result.medianMargin} (minimo ${input.minPoints ?? config.quorum.minPoints})`,
219666
- ` jueces con margen: ${result.judgesHoldingMargin}/${result.perJudge.length} (minimo ${input.minAxes ?? config.quorum.minAxes})`,
219688
+ ` jueces con margen: ${holding} (minimo ${input.minAxes ?? config.quorum.minAxes})`,
219667
219689
  ...result.perJudge.map((entry) => ` ${entry.judge}: ${entry.margin}${entry.holds ? " ok" : " NO"}`),
219690
+ ...result.perJudge.filter((entry) => entry.margin < 0).map((entry) => ` disidencia: ${entry.judge} ${entry.margin}`),
219668
219691
  "",
219669
219692
  ` motivo: ${result.reason}`
219670
219693
  ];
@@ -219682,7 +219705,7 @@ var init_decideRunner = __esmMin((() => {
219682
219705
  // ../../packages/agent-core-v2/src/features/wave/tools/decide/decide.md?raw
219683
219706
  var decide_default;
219684
219707
  var init_decide = __esmMin((() => {
219685
- decide_default = "Compute the wave quorum from the judges' totals and say what may happen next.\n\nPass one entry per judge with its totals per proposal (`{ judge: \"J1\", scores: { P1: 17, P2: 15, P3: 14 } }`). The tool takes the **median** per proposal, then checks the winner beats the second by `minPoints` in at least `minAxes` judges. Thresholds default to `workflow.config.json`.\n\nAnswers `adopt`, `refine` (no quorum yet, compose R1 and audit with R2) or `deadlock` (two rounds without quorum: adopt R1 with written dissent). A tie in the median never crowns a winner.\n";
219708
+ decide_default = "Compute the wave quorum from the judges' totals and say what may happen next.\n\nPass one entry per judge with its totals per proposal (`{ judge: \"J1\", scores: { P1: 17, P2: 15, P3: 14 } }`). The tool takes the **median** per proposal and applies both conditions of `core/quorum.ts`: the winner's median must beat the second's by `minPoints` (**margen de medianas**), and at least `minAxes` judges must give the winner that margin or more over the second (**mayoría de jueces**). Thresholds default to `workflow.config.json`.\n\nAnswers `adopt`, `refine` (no quorum yet, compose R1 and audit with R2) or `deadlock` (two rounds without quorum: adopt R1 with written dissent). A tie in the median, or a single proposal with nothing to compare, never crowns a winner and never counts as quorum.\n";
219686
219709
  }));
219687
219710
  // ../../packages/agent-core-v2/src/features/wave/tools/decide/decideTool.ts
219688
219711
  var WaveDecideTool;
@@ -219778,6 +219801,18 @@ function walk$1(fs, dir, accept, acc = []) {
219778
219801
  }
219779
219802
  var init_fsJson = __esmMin((() => {}));
219780
219803
  // ../../packages/agent-core-v2/src/features/wave/tools/open/openRunner.ts
219804
+ function briefTemplate(root) {
219805
+ const fs = nodeWaveFs;
219806
+ const local = join(root, BRIEF_TEMPLATE$1);
219807
+ if (fs.exists(local)) return { file: local };
219808
+ if (fs.isDirectory(join(root, "workflow"))) return { missing: BRIEF_TEMPLATE$1 };
219809
+ return fs.exists(BUNDLED_BRIEF_TEMPLATE) ? { file: BUNDLED_BRIEF_TEMPLATE } : { missing: BRIEF_TEMPLATE$1 };
219810
+ }
219811
+ function nextEpic(backlogDir) {
219812
+ const numbers = asArray$2(asRecord$2(readJson$2(nodeWaveFs, join(backlogDir, "epics.json")) ?? {})["epics"]).map((epic) => /^E(\d{2,})$/.exec(String(asRecord$2(epic)["id"]))?.[1]).filter((value) => value !== void 0).map(Number);
219813
+ const next = numbers.length > 0 ? Math.max(...numbers) + 1 : 1;
219814
+ return `E${String(next).padStart(2, "0")}`;
219815
+ }
219781
219816
  function slugify(name) {
219782
219817
  return name.normalize("NFD").replaceAll(/[\u0300-\u036F]/g, "").toLowerCase().replaceAll(/[^a-z0-9-]+/g, "-").replaceAll(/^-+|-+$/g, "");
219783
219818
  }
@@ -219791,15 +219826,29 @@ function runWaveOpenForRoot(input) {
219791
219826
  };
219792
219827
  const layout = asRecord$2(asRecord$2(rawConfig)["layout"]);
219793
219828
  const wavesRel = typeof layout["waves"] === "string" ? layout["waves"] : "design/waves";
219829
+ const backlogRel = typeof layout["backlog"] === "string" ? layout["backlog"] : "backlog";
219830
+ const minimum = makeWaveConfig(rawConfig).thresholds.minBriefSections;
219794
219831
  let wavesDir;
219832
+ let backlogDir;
219795
219833
  try {
219796
219834
  wavesDir = containedPath(input.root, wavesRel);
219835
+ backlogDir = containedPath(input.root, backlogRel);
219797
219836
  } catch (error) {
219798
219837
  return {
219799
219838
  output: error instanceof Error ? error.message : String(error),
219800
219839
  isError: true
219801
219840
  };
219802
219841
  }
219842
+ const template = briefTemplate(input.root);
219843
+ if (template.file === void 0) return {
219844
+ output: `falta ${template.missing ?? BRIEF_TEMPLATE$1}: no se genera un brief sin la plantilla unica`,
219845
+ isError: true
219846
+ };
219847
+ const source = fs.read(template.file);
219848
+ if (source === void 0) return {
219849
+ output: `no se pudo leer ${template.file}`,
219850
+ isError: true
219851
+ };
219803
219852
  const existing = fs.list(wavesDir).filter((name) => fs.isDirectory(join(wavesDir, name))).map((name) => Number(/^W(\d+)/.exec(name)?.[1] ?? NaN)).filter((value) => !Number.isNaN(value));
219804
219853
  const next = input.id ?? `W${existing.length > 0 ? Math.max(...existing) + 1 : 0}`;
219805
219854
  if (!/^W\d{1,2}$/.test(next)) return {
@@ -219816,8 +219865,9 @@ function runWaveOpenForRoot(input) {
219816
219865
  isError: true
219817
219866
  };
219818
219867
  const dir = join(wavesDir, `${next}-${slug}`);
219868
+ const brief = source.replaceAll("{{WAVE}}", next).replaceAll("{{NAME}}", slug).replaceAll("{{NEXT_EPIC}}", nextEpic(backlogDir));
219819
219869
  for (const sub of SUBDIRS) fs.mkdir(join(dir, sub));
219820
- fs.write(join(dir, "00-brief.md"), BRIEF_SKELETON.replaceAll("{{WAVE}}", next).replaceAll("{{NAME}}", slug));
219870
+ fs.write(join(dir, "00-brief.md"), brief);
219821
219871
  const declared = asArray$2(asRecord$2(rawConfig)["waves"]).map(String);
219822
219872
  if (!declared.includes(next)) writeJson(fs, configFile, {
219823
219873
  ...asRecord$2(rawConfig),
@@ -219825,11 +219875,11 @@ function runWaveOpenForRoot(input) {
219825
219875
  });
219826
219876
  return { output: [
219827
219877
  `oleada creada: ${next}-${slug}`,
219828
- ` brief: ${wavesRel}/${next}-${slug}/00-brief.md (rellena las 7 secciones antes de lanzar asientos)`,
219878
+ ` brief: ${wavesRel}/${next}-${slug}/00-brief.md (rellena las ${minimum} secciones antes de lanzar asientos)`,
219829
219879
  ` config: ${next} declarada en workflow.config.json (el gate la exige desde ahora)`
219830
219880
  ].join("\n") };
219831
219881
  }
219832
- var SUBDIRS, BRIEF_SKELETON;
219882
+ var SUBDIRS, BRIEF_TEMPLATE$1, BUNDLED_BRIEF_TEMPLATE;
219833
219883
  var init_openRunner = __esmMin((() => {
219834
219884
  init_config$6();
219835
219885
  init_fsJson();
@@ -219841,24 +219891,8 @@ var init_openRunner = __esmMin((() => {
219841
219891
  "04-verdicts",
219842
219892
  "05-refined"
219843
219893
  ];
219844
- BRIEF_SKELETON = [
219845
- "# {{WAVE}} — Brief: {{NAME}}",
219846
- "",
219847
- "## 1. Contexto vinculante",
219848
- "",
219849
- "## 2. Alcance",
219850
- "",
219851
- "## 3. Evidencia disponible",
219852
- "",
219853
- "## 4. Preguntas que esta oleada resuelve",
219854
- "",
219855
- "## 5. Formato de salida",
219856
- "",
219857
- "## 6. Reglas duras",
219858
- "",
219859
- "## 7. Rúbrica (0-5)",
219860
- ""
219861
- ].join("\n");
219894
+ BRIEF_TEMPLATE$1 = "workflow/templates/brief.md";
219895
+ BUNDLED_BRIEF_TEMPLATE = resolve(import.meta.dirname, "../../../../../../../workflow/templates/brief.md");
219862
219896
  }));
219863
219897
  // ../../packages/agent-core-v2/src/features/wave/tools/open/open.md?raw
219864
219898
  var open_default;
@@ -219916,6 +219950,85 @@ var init_round$1 = __esmMin((() => {
219916
219950
  }).strict();
219917
219951
  IWaveRoundTool = createDecorator("waveRoundTool");
219918
219952
  }));
219953
+ // ../../packages/agent-core-v2/src/features/wave/tools/submit/submitRunner.ts
219954
+ function seatTicketHash(ticket) {
219955
+ return createHash("sha256").update(ticket).digest("hex");
219956
+ }
219957
+ function findWaveDir(root, wavesRel, wave) {
219958
+ const fs = nodeWaveFs;
219959
+ const wavesDir = containedPath(root, wavesRel);
219960
+ const name = fs.list(wavesDir).filter((entry) => fs.isDirectory(join(wavesDir, entry))).find((entry) => entry === wave || entry.startsWith(`${wave}-`));
219961
+ return name === void 0 ? void 0 : join(wavesDir, name);
219962
+ }
219963
+ function runWaveSubmitForRoot(input) {
219964
+ const fs = nodeWaveFs;
219965
+ const rawConfig = readJson$2(fs, join(input.root, "workflow.config.json"));
219966
+ if (rawConfig === void 0) return {
219967
+ output: `no hay workflow.config.json en ${input.root}`,
219968
+ isError: true
219969
+ };
219970
+ const config = makeWaveConfig(rawConfig);
219971
+ const wavesRel = config.layout.waves;
219972
+ const seats = waveRoundSeats(config.roles, input.round);
219973
+ if (!seats.includes(input.role)) return {
219974
+ output: `seats.unknown_seat: el rol "${input.role}" no pertenece a la ronda ${input.round} (esperado ${seats.join(", ")})`,
219975
+ isError: true
219976
+ };
219977
+ let base;
219978
+ try {
219979
+ base = findWaveDir(input.root, wavesRel, input.wave);
219980
+ } catch (error) {
219981
+ return {
219982
+ output: error instanceof Error ? error.message : String(error),
219983
+ isError: true
219984
+ };
219985
+ }
219986
+ if (base === void 0) return {
219987
+ output: `no existe la oleada ${input.wave} en ${wavesRel}`,
219988
+ isError: true
219989
+ };
219990
+ const dirName = WAVE_ROUND_DIRS[input.round];
219991
+ const dir = join(base, dirName);
219992
+ if (!fs.isDirectory(dir)) return {
219993
+ output: `falta el directorio ${dirName} en la oleada ${input.wave}`,
219994
+ isError: true
219995
+ };
219996
+ const seatsDir = join(base, WAVE_SEATS_DIR);
219997
+ if (fs.isDirectory(seatsDir) && fs.list(seatsDir).length > 0) {
219998
+ const presented = (input.ticket ?? "").trim();
219999
+ if (presented === "") return {
220000
+ output: `seats.foreign_replace: el asiento ${input.role} no presento el ticket que emitio la ronda`,
220001
+ isError: true
220002
+ };
220003
+ const stored = (fs.read(join(seatsDir, input.role)) ?? "").trim();
220004
+ if (stored === "" || stored !== seatTicketHash(presented)) return {
220005
+ output: `seats.foreign_replace: el ticket presentado no es el del asiento ${input.role}`,
220006
+ isError: true
220007
+ };
220008
+ if (input.replace === true && (input.reason ?? "").trim() === "") return {
220009
+ output: `seats.replace_without_reason: reescribir el artefacto del asiento ${input.role} exige un motivo no vacio`,
220010
+ isError: true
220011
+ };
220012
+ }
220013
+ const file = join(dir, `${input.role}.md`);
220014
+ if (fs.exists(file) && input.replace !== true) return {
220015
+ output: `el asiento ${input.role} ya entrego su artefacto; un asiento escribe una vez (usa replace solo para relanzar un fallo)`,
220016
+ isError: true
220017
+ };
220018
+ if (input.artifact.trim() === "") return {
220019
+ output: "el artefacto esta vacio: un asiento que no escribe, fallo",
220020
+ isError: true
220021
+ };
220022
+ fs.write(file, input.artifact.endsWith("\n") ? input.artifact : `${input.artifact}\n`);
220023
+ return { output: `escrito ${wavesRel}/${base.split("/").pop()}/${dirName}/${input.role}.md` };
220024
+ }
220025
+ var WAVE_SEATS_DIR;
220026
+ var init_submitRunner = __esmMin((() => {
220027
+ init_config$6();
220028
+ init_fsJson();
220029
+ init_nodeFs();
220030
+ WAVE_SEATS_DIR = ".seats";
220031
+ }));
219919
220032
  // ../../packages/agent-core-v2/src/features/wave/tools/round/prompts.ts
219920
220033
  var SEAT_PRELUDE, ROLE_PROMPTS, JUDGE_BIASES;
219921
220034
  var init_prompts$2 = __esmMin((() => {
@@ -219943,16 +220056,6 @@ var init_prompts$2 = __esmMin((() => {
219943
220056
  ];
219944
220057
  }));
219945
220058
  // ../../packages/agent-core-v2/src/features/wave/tools/round/roundRunner.ts
219946
- function range(prefix, count) {
219947
- return Array.from({ length: Math.max(1, count) }, (_, index) => `${prefix}${index + 1}`);
219948
- }
219949
- function roleOf(cfg, round) {
219950
- if (round === "propose") return cfg.proponents;
219951
- if (round === "defend") return cfg.defenders;
219952
- if (round === "attack") return cfg.adversaries;
219953
- if (round === "judge") return cfg.judges;
219954
- return 1;
219955
- }
219956
220059
  function runWaveRoundForRoot(input) {
219957
220060
  const fs = nodeWaveFs;
219958
220061
  const rawConfig = readJson$2(fs, join(input.root, "workflow.config.json"));
@@ -219961,9 +220064,8 @@ function runWaveRoundForRoot(input) {
219961
220064
  seats: [],
219962
220065
  isError: true
219963
220066
  };
219964
- const config = asRecord$2(rawConfig);
219965
- const layout = asRecord$2(config["layout"]);
219966
- const wavesRel = typeof layout["waves"] === "string" ? layout["waves"] : "design/waves";
220067
+ const config = makeWaveConfig(rawConfig);
220068
+ const wavesRel = config.layout.waves;
219967
220069
  let wavesDir;
219968
220070
  try {
219969
220071
  wavesDir = containedPath(input.root, wavesRel);
@@ -219988,35 +220090,35 @@ function runWaveRoundForRoot(input) {
219988
220090
  isError: true
219989
220091
  };
219990
220092
  const sections = brief.match(/^##\s+\S/gm)?.length ?? 0;
219991
- if (sections < 7) return {
219992
- output: `el brief tiene ${sections} secciones < 7: rellena las siete antes de lanzar la ronda`,
220093
+ const minimum = config.thresholds.minBriefSections;
220094
+ if (sections < minimum) return {
220095
+ output: `el brief tiene ${sections} secciones < ${minimum}: rellena las ${minimum} antes de lanzar la ronda`,
219993
220096
  seats: [],
219994
220097
  isError: true
219995
220098
  };
219996
- const roles = asRecord$2(config["roles"]);
219997
- const counts = {
219998
- proponents: typeof roles["proponents"] === "number" ? roles["proponents"] : 3,
219999
- defenders: typeof roles["defenders"] === "number" ? roles["defenders"] : 3,
220000
- adversaries: typeof roles["adversaries"] === "number" ? roles["adversaries"] : 3,
220001
- judges: typeof roles["judges"] === "number" ? roles["judges"] : 3
220002
- };
220003
- const shape = ROUNDS[input.round];
220004
- const seats = shape.roles(roleOf(counts, input.round)).map((role) => {
220099
+ const dir = WAVE_ROUND_DIRS[input.round];
220100
+ const seatsDir = join(wavesDir, waveDir, WAVE_SEATS_DIR);
220101
+ const seats = waveRoundSeats(config.roles, input.round).map((role) => {
220005
220102
  const bias = input.round === "judge" ? JUDGE_BIASES[(Number(role.slice(1)) - 1) % JUDGE_BIASES.length] : void 0;
220006
- const file = `${wavesRel}/${waveDir}/${shape.dir}/${role}.md`;
220103
+ const file = `${wavesRel}/${waveDir}/${dir}/${role}.md`;
220104
+ const ticket = randomBytes(16).toString("hex");
220105
+ fs.write(join(seatsDir, role), `${seatTicketHash(ticket)}\n`);
220007
220106
  const lines = [
220008
220107
  SEAT_PRELUDE,
220009
220108
  "",
220010
220109
  ROLE_PROMPTS[input.round] ?? "",
220011
220110
  "",
220111
+ `Perfil: ${WAVE_SEAT_PROFILE}`,
220012
220112
  `Oleada: ${input.wave}. Brief: ${briefRel}.`,
220013
220113
  `Escribe: ${file}.`,
220014
- `Entrega con WaveSubmit({ wave: "${input.wave}", round: "${input.round}", role: "${role}", artifact: "..." }).`
220114
+ `Ticket de asiento: ${ticket}. Es capacidad, no identidad: la ronda lo emitio para este asiento y WaveSubmit lo exige.`,
220115
+ `Entrega con WaveSubmit({ wave: "${input.wave}", round: "${input.round}", role: "${role}", ticket: "${ticket}", artifact: "..." }).`
220015
220116
  ];
220016
220117
  if (bias !== void 0) lines.push(`Tu sesgo declarado es ${bias}: puntua CON el, no contra el.`);
220017
220118
  return {
220018
220119
  role,
220019
220120
  file,
220121
+ ticket,
220020
220122
  prompt: lines.join("\n")
220021
220123
  };
220022
220124
  });
@@ -220025,38 +220127,13 @@ function runWaveRoundForRoot(input) {
220025
220127
  seats
220026
220128
  };
220027
220129
  }
220028
- var ROUNDS;
220029
220130
  var init_roundRunner = __esmMin((() => {
220030
220131
  init_config$6();
220031
220132
  init_fsJson();
220032
220133
  init_nodeFs();
220134
+ init_wave$3();
220135
+ init_submitRunner();
220033
220136
  init_prompts$2();
220034
- ROUNDS = {
220035
- propose: {
220036
- dir: "01-proposals",
220037
- roles: (count) => range("P", count)
220038
- },
220039
- defend: {
220040
- dir: "02-defenses",
220041
- roles: (count) => range("D", count)
220042
- },
220043
- attack: {
220044
- dir: "03-attacks",
220045
- roles: (count) => range("A", count)
220046
- },
220047
- judge: {
220048
- dir: "04-verdicts",
220049
- roles: (count) => range("J", count)
220050
- },
220051
- refine: {
220052
- dir: "05-refined",
220053
- roles: () => ["R1"]
220054
- },
220055
- audit: {
220056
- dir: "05-refined",
220057
- roles: () => ["R2"]
220058
- }
220059
- };
220060
220137
  }));
220061
220138
  // ../../packages/agent-core-v2/src/features/wave/tools/round/round.md?raw
220062
220139
  var round_default;
@@ -220112,111 +220189,17 @@ var init_submit$1 = __esmMin((() => {
220112
220189
  ]),
220113
220190
  role: string().min(2),
220114
220191
  artifact: string().min(1),
220192
+ ticket: string().optional(),
220193
+ reason: string().optional(),
220115
220194
  replace: boolean$1().optional(),
220116
220195
  root: string().optional()
220117
220196
  }).strict();
220118
220197
  IWaveSubmitTool = createDecorator("waveSubmitTool");
220119
220198
  }));
220120
- // ../../packages/agent-core-v2/src/features/wave/tools/submit/submitRunner.ts
220121
- function slotNames(slot, roles) {
220122
- const configured = roles[slot.role];
220123
- const count = typeof configured === "number" && Number.isFinite(configured) ? Math.max(0, Math.floor(configured)) : 0;
220124
- return Array.from({ length: count }, (_, index) => `${slot.prefix}${index + 1}`);
220125
- }
220126
- function findWaveDir(root, wavesRel, wave) {
220127
- const fs = nodeWaveFs;
220128
- const wavesDir = containedPath(root, wavesRel);
220129
- const name = fs.list(wavesDir).filter((entry) => fs.isDirectory(join(wavesDir, entry))).find((entry) => entry === wave || entry.startsWith(`${wave}-`));
220130
- return name === void 0 ? void 0 : join(wavesDir, name);
220131
- }
220132
- function runWaveSubmitForRoot(input) {
220133
- const fs = nodeWaveFs;
220134
- const rawConfig = readJson$2(fs, join(input.root, "workflow.config.json"));
220135
- if (rawConfig === void 0) return {
220136
- output: `no hay workflow.config.json en ${input.root}`,
220137
- isError: true
220138
- };
220139
- const layout = asRecord$2(asRecord$2(rawConfig)["layout"]);
220140
- const wavesRel = typeof layout["waves"] === "string" ? layout["waves"] : "design/waves";
220141
- const slot = ROUND_SLOTS[input.round];
220142
- const names = slotNames(slot, asRecord$2(asRecord$2(rawConfig)["roles"]));
220143
- const accepted = input.round === "audit" ? names.filter((name) => name !== "R1") : input.round === "refine" ? names.slice(0, 1) : names;
220144
- if (!accepted.includes(input.role)) return {
220145
- output: `el rol "${input.role}" no pertenece a la ronda ${input.round} (esperado ${accepted.join(", ") || "ninguno configurado"})`,
220146
- isError: true
220147
- };
220148
- let base;
220149
- try {
220150
- base = findWaveDir(input.root, wavesRel, input.wave);
220151
- } catch (error) {
220152
- return {
220153
- output: error instanceof Error ? error.message : String(error),
220154
- isError: true
220155
- };
220156
- }
220157
- if (base === void 0) return {
220158
- output: `no existe la oleada ${input.wave} en ${wavesRel}`,
220159
- isError: true
220160
- };
220161
- const dir = join(base, slot.dir);
220162
- if (!fs.isDirectory(dir)) return {
220163
- output: `falta el directorio ${slot.dir} en la oleada ${input.wave}`,
220164
- isError: true
220165
- };
220166
- const file = join(dir, `${input.role}.md`);
220167
- if (fs.exists(file) && input.replace !== true) return {
220168
- output: `el asiento ${input.role} ya entrego su artefacto; un asiento escribe una vez (usa replace solo para relanzar un fallo)`,
220169
- isError: true
220170
- };
220171
- if (input.artifact.trim() === "") return {
220172
- output: "el artefacto esta vacio: un asiento que no escribe, fallo",
220173
- isError: true
220174
- };
220175
- fs.write(file, input.artifact.endsWith("\n") ? input.artifact : `${input.artifact}\n`);
220176
- return { output: `escrito ${wavesRel}/${base.split("/").pop()}/${slot.dir}/${input.role}.md` };
220177
- }
220178
- var ROUND_SLOTS;
220179
- var init_submitRunner = __esmMin((() => {
220180
- init_config$6();
220181
- init_fsJson();
220182
- init_nodeFs();
220183
- ROUND_SLOTS = {
220184
- propose: {
220185
- dir: "01-proposals",
220186
- prefix: "P",
220187
- role: "proponents"
220188
- },
220189
- defend: {
220190
- dir: "02-defenses",
220191
- prefix: "D",
220192
- role: "defenders"
220193
- },
220194
- attack: {
220195
- dir: "03-attacks",
220196
- prefix: "A",
220197
- role: "adversaries"
220198
- },
220199
- judge: {
220200
- dir: "04-verdicts",
220201
- prefix: "J",
220202
- role: "judges"
220203
- },
220204
- refine: {
220205
- dir: "05-refined",
220206
- prefix: "R",
220207
- role: "refiners"
220208
- },
220209
- audit: {
220210
- dir: "05-refined",
220211
- prefix: "R",
220212
- role: "refiners"
220213
- }
220214
- };
220215
- }));
220216
220199
  // ../../packages/agent-core-v2/src/features/wave/tools/submit/submit.md?raw
220217
220200
  var submit_default;
220218
220201
  var init_submit = __esmMin((() => {
220219
- submit_default = "Deliver the artifact for your seat. This is the only way a seat writes: the tool binds the role to its slot and refuses anything else.\n\nRole and round must match — `propose` accepts P1..Pn, `defend` D1..Dn, `attack` A1..An, `judge` J1..Jn, `refine` R1, `audit` R2. A seat writes once; use `replace` only to relaunch a failed seat after its artifact was already written.\n\nPass the artifact as the text/JSON body. An empty artifact is rejected: a seat that did not write has failed, and the round must be relaunched rather than summarized by the orchestrator.\n";
220202
+ submit_default = "Deliver the artifact for your seat. This is the only way a seat writes: the tool binds the role to its slot and refuses anything else.\n\nRole and round must match — `propose` accepts P1..Pn, `defend` D1..Dn, `attack` A1..An, `judge` J1..Jn, `refine` R1, `audit` R2..Rn. A seat writes once; use `replace` only to relaunch a failed seat after its artifact was already written.\n\nPass the `ticket` the round gave you: the tool hashes it and compares it with the sha256 the round left in `design/waves/<W>/.seats/<role>`. A missing ticket, or the ticket of another seat, is refused as `seats.foreign_replace`. The ticket is capacity, not identity (capacidad, no identidad): it proves the orchestrator issued this seat, it does not say who wrote the artifact. `replace` also needs a non-empty `reason`, or the tool refuses it as `seats.replace_without_reason`: a silent overwrite is the substitution this tool exists to prevent.\n\nPass the artifact as the text/JSON body. An empty artifact is rejected: a seat that did not write has failed, and the round must be relaunched rather than summarized by the orchestrator.\n";
220220
220203
  }));
220221
220204
  // ../../packages/agent-core-v2/src/features/wave/tools/submit/submitTool.ts
220222
220205
  var WaveSubmitTool;
@@ -220246,6 +220229,8 @@ var init_submitTool = __esmMin((() => {
220246
220229
  round: args.round,
220247
220230
  role: args.role,
220248
220231
  artifact: args.artifact,
220232
+ ticket: args.ticket,
220233
+ reason: args.reason,
220249
220234
  replace: args.replace
220250
220235
  }))
220251
220236
  };
@@ -220253,7 +220238,196 @@ var init_submitTool = __esmMin((() => {
220253
220238
  };
220254
220239
  WaveSubmitTool = __decorate([__decorateParam(0, ISessionContext)], WaveSubmitTool);
220255
220240
  }));
220241
+ // ../../packages/agent-core-v2/src/features/wave/core/verify/runner.ts
220242
+ function splitCommand(command) {
220243
+ const tokens = [];
220244
+ let current = "";
220245
+ let quote;
220246
+ for (const char of command) {
220247
+ if (quote !== void 0) {
220248
+ if (char === quote) quote = void 0;
220249
+ else current += char;
220250
+ continue;
220251
+ }
220252
+ if (char === "\"" || char === "'") {
220253
+ quote = char;
220254
+ continue;
220255
+ }
220256
+ if (SHELL_METACHARACTERS.has(char)) throw new Error(`metacaracter de shell prohibido en: ${command}`);
220257
+ if (char === " " || char === " ") {
220258
+ if (current !== "") {
220259
+ tokens.push(current);
220260
+ current = "";
220261
+ }
220262
+ continue;
220263
+ }
220264
+ current += char;
220265
+ }
220266
+ if (quote !== void 0) throw new Error(`comillas sin cerrar en: ${command}`);
220267
+ if (current !== "") tokens.push(current);
220268
+ if (tokens.length === 0) throw new Error("comando vacio");
220269
+ return tokens;
220270
+ }
220271
+ function containPaths(argv, root) {
220272
+ for (const token of argv) {
220273
+ if (token.startsWith("-")) continue;
220274
+ if (!token.includes("/") && !/\.(mjs|cjs|js|ts|sh)$/.test(token)) continue;
220275
+ containedPath(root, token);
220276
+ }
220277
+ }
220278
+ function runCommand$2(command, options) {
220279
+ let argv;
220280
+ try {
220281
+ argv = splitCommand(command);
220282
+ const binary = (argv[0] ?? "").replace(/\.exe$/i, "");
220283
+ const allowed = options.allowBinaries.map((item) => item.replace(/\.exe$/i, ""));
220284
+ if (!allowed.includes(binary)) throw new Error(`binario no permitido: ${binary} (permitidos: ${allowed.join(", ")})`);
220285
+ containPaths(argv, options.root);
220286
+ } catch (error) {
220287
+ return {
220288
+ ok: false,
220289
+ output: `rechazado: ${error instanceof Error ? error.message : String(error)}`,
220290
+ rejected: true
220291
+ };
220292
+ }
220293
+ const result = spawnSync(argv[0] ?? "", argv.slice(1), {
220294
+ cwd: options.cwd ?? options.root,
220295
+ encoding: "utf8",
220296
+ timeout: options.timeoutMs ?? DEFAULT_TIMEOUT_MS$1,
220297
+ stdio: "pipe",
220298
+ shell: false
220299
+ });
220300
+ const output = `${result.stdout ?? ""}${result.stderr ?? ""}${result.error === void 0 ? "" : result.error.message}`.replaceAll(/\s+/g, " ").trim().slice(0, 300);
220301
+ if (result.error !== void 0) return {
220302
+ ok: false,
220303
+ output: `no se pudo ejecutar: ${output}`,
220304
+ rejected: false
220305
+ };
220306
+ return {
220307
+ ok: result.status === 0,
220308
+ output,
220309
+ rejected: false,
220310
+ status: result.status ?? void 0
220311
+ };
220312
+ }
220313
+ function exitDetail(outcome) {
220314
+ return outcome.status === void 0 ? outcome.output : `exit ${outcome.status}: ${outcome.output}`;
220315
+ }
220316
+ function evaluateMutant(ac, verifyCommand, options) {
220317
+ const script = asText$1(asRecord$2(ac["falsifier"])["script"]);
220318
+ if (script === "") return {
220319
+ state: "declarado",
220320
+ detail: "sin falsifier.script: el AC queda declarado y cuenta como FALLO"
220321
+ };
220322
+ const acId = asText$1(ac["id"]);
220323
+ const apply = runCommand$2(`${script} apply`, options);
220324
+ if (apply.rejected) return {
220325
+ state: "rechazado",
220326
+ detail: `apply rechazado (${script}, ${acId}): ${apply.output}`
220327
+ };
220328
+ let mutated;
220329
+ let restore;
220330
+ try {
220331
+ if (apply.ok) mutated = runCommand$2(verifyCommand, options);
220332
+ } finally {
220333
+ restore = runCommand$2(`${script} restore`, options);
220334
+ }
220335
+ const failures = [];
220336
+ if (!apply.ok) failures.push(`apply fallo (${script}, ${acId}) ${exitDetail(apply)}`);
220337
+ if (restore.rejected || !restore.ok) failures.push(`restore fallo (${script}, ${acId}), el arbol puede estar sucio: ${restore.output}`);
220338
+ if (failures.length > 0) return {
220339
+ state: "error",
220340
+ detail: failures.join(" · ")
220341
+ };
220342
+ if (mutated === void 0) return {
220343
+ state: "error",
220344
+ detail: `no se ejecuto el verify con la mutacion de ${acId}`
220345
+ };
220346
+ if (mutated.ok) return {
220347
+ state: "sobrevive",
220348
+ detail: "el verify pasa con la mutacion aplicada: el AC no muerde"
220349
+ };
220350
+ if (!runCommand$2(verifyCommand, options).ok) return {
220351
+ state: "error",
220352
+ detail: "el verify no vuelve a verde tras restaurar"
220353
+ };
220354
+ return {
220355
+ state: "muerto",
220356
+ detail: "rojo con la mutacion, verde tras restaurar"
220357
+ };
220358
+ }
220359
+ function selectTaskCriteria(fs, root, cfg, taskId, ac) {
220360
+ const tasksDir = containedPath(root, cfg.layout.backlog);
220361
+ const file = walk$1(fs, join(tasksDir, "tasks"), (path) => path.endsWith(`${taskId}.json`))[0];
220362
+ if (file === void 0) throw new Error(`no encuentro la task ${taskId} en ${cfg.layout.backlog}/tasks/`);
220363
+ const criteria = asArray$2(asRecord$2(readJson$2(fs, file))["ac"]).map(asRecord$2);
220364
+ if (ac === void 0) return {
220365
+ taskId,
220366
+ criteria
220367
+ };
220368
+ const selected = criteria.filter((entry) => asText$1(entry["id"]) === ac);
220369
+ if (selected.length === 0) throw new Error(`no existe el criterio ${ac} en la task ${taskId}`);
220370
+ return {
220371
+ taskId,
220372
+ criteria: selected
220373
+ };
220374
+ }
220375
+ function verifyTask(fs, root, cfg, taskId, options) {
220376
+ const { criteria } = selectTaskCriteria(fs, root, cfg, taskId, options?.ac);
220377
+ const runOptions = {
220378
+ root,
220379
+ allowBinaries: cfg.vocabulary.verifyCommands,
220380
+ cwd: options?.cwd,
220381
+ timeoutMs: options?.timeoutMs
220382
+ };
220383
+ const acs = [];
220384
+ let declared = 0;
220385
+ for (const ac of criteria) {
220386
+ const verifyCommand = asText$1(ac["verify"]);
220387
+ const verify = runCommand$2(verifyCommand, runOptions);
220388
+ const mutant = evaluateMutant(ac, verifyCommand, runOptions);
220389
+ if (mutant.state === "declarado") declared++;
220390
+ const declaredOk = options?.allowDeclared === true && mutant.state === "declarado";
220391
+ const ok = verify.ok && (mutant.state === "muerto" || declaredOk);
220392
+ acs.push({
220393
+ acId: asText$1(ac["id"]),
220394
+ verify,
220395
+ mutant,
220396
+ ok
220397
+ });
220398
+ }
220399
+ return {
220400
+ taskId,
220401
+ acs,
220402
+ declared,
220403
+ ok: acs.length > 0 && acs.every((ac) => ac.ok)
220404
+ };
220405
+ }
220406
+ var SHELL_METACHARACTERS, DEFAULT_TIMEOUT_MS$1;
220407
+ var init_runner = __esmMin((() => {
220408
+ init_config$6();
220409
+ init_fsJson();
220410
+ SHELL_METACHARACTERS = /* @__PURE__ */ new Set([
220411
+ "|",
220412
+ "&",
220413
+ ";",
220414
+ "<",
220415
+ ">",
220416
+ "`",
220417
+ "$",
220418
+ "\n",
220419
+ "\r"
220420
+ ]);
220421
+ DEFAULT_TIMEOUT_MS$1 = 6e5;
220422
+ }));
220256
220423
  // ../../packages/agent-core-v2/src/features/wave/core/gate/checks.ts
220424
+ function measurableThreshold(text, units) {
220425
+ const low = normalize$1(text);
220426
+ if (OPERATOR_WITH_NUMBER.test(low)) return true;
220427
+ if (EXACT_VALUE.test(low)) return true;
220428
+ if (units.some((unit) => new RegExp(`\\d\\s*${escapeRegExp$1(unit)}\\b`).test(low))) return true;
220429
+ return low.split(/\s*(?:,|;|\by\b)\s*/).filter(Boolean).length >= 3;
220430
+ }
220257
220431
  function record$1(value) {
220258
220432
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
220259
220433
  }
@@ -220281,38 +220455,350 @@ function walk(fs, dir, accept, acc = []) {
220281
220455
  }
220282
220456
  return acc;
220283
220457
  }
220284
- function escapeRegExp$1(value) {
220285
- return value.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&");
220458
+ function requiredArtifacts(cfg) {
220459
+ const required = ["00-brief.md"];
220460
+ for (const round of WAVE_ROUNDS) for (const seat of waveRoundSeats(cfg.roles, round)) required.push(`${WAVE_ROUND_DIRS[round]}/${seat}.md`);
220461
+ required.push(DECISION_FILE, "07-epics.json");
220462
+ return required;
220463
+ }
220464
+ function briefSections(text) {
220465
+ const sections = [];
220466
+ let current;
220467
+ for (const line of text.split("\n")) {
220468
+ const heading = /^##\s+(.+?)\s*$/.exec(line);
220469
+ if (heading !== null) {
220470
+ current = {
220471
+ title: heading[1] ?? "",
220472
+ body: []
220473
+ };
220474
+ sections.push(current);
220475
+ continue;
220476
+ }
220477
+ if (/^#\s+/.test(line)) {
220478
+ current = void 0;
220479
+ continue;
220480
+ }
220481
+ current?.body.push(line);
220482
+ }
220483
+ return sections.map((section) => ({
220484
+ title: section.title,
220485
+ body: section.body.join("\n").trim()
220486
+ }));
220286
220487
  }
220287
- function hasMeasure(value) {
220288
- if (/[<>=≤≥]/.test(value)) return true;
220289
- if (/\b(ms|s|seg|segundos?|minutos?|filas?|bytes?|p95|peticiones?|veces|puntos?|codigo|status|exact\w*)\b/i.test(value)) return true;
220290
- return /[a-z]/i.test(value) && !/^\d+(\s+\d+)+$/.test(value);
220488
+ function sectionKeyword(title) {
220489
+ return normalize$1(title).replace(/^\d+\.?\s*/, "").match(/[a-z0-9]+/)?.[0] ?? "";
220291
220490
  }
220292
- function isDestructive(mutation) {
220293
- return /(borrar|borra|eliminar|elimina|drop|truncate|wipe|rm -rf)[^.]{0,40}(esquema|schema|cluster|volumen|directorio|carpeta|repositorio|contenedor|base de datos|database|entorno)/i.test(mutation) || /(drop|truncate)\s+(table|database|schema)/i.test(mutation);
220491
+ function decisionStrip(value) {
220492
+ return (value ?? "").replaceAll("*", "").trim();
220294
220493
  }
220295
- function verifyTargetExists(waveDir, target) {
220296
- const resolved = resolve(waveDir, target);
220297
- return resolved.startsWith(waveDir) && existsSync(resolved);
220494
+ function decisionSplitRow(line) {
220495
+ return line.trim().replace(/^\|/, "").replace(/\|$/, "").split("|").map((cell) => cell.trim());
220298
220496
  }
220299
- function waveDirectories(fs, root, cfg) {
220300
- const dir = containedPath(root, cfg.layout.waves);
220301
- const map = /* @__PURE__ */ new Map();
220302
- for (const wave of cfg.waves) {
220303
- const match = fs.list(dir).find((name) => name === wave || name.startsWith(`${wave}-`));
220304
- if (match !== void 0) map.set(wave, join(dir, match));
220497
+ function isDecisionSeparator(row) {
220498
+ return row.length > 0 && row.every((cell) => DECISION_SEPARATOR.test(decisionStrip(cell)));
220499
+ }
220500
+ function decisionTables(text) {
220501
+ const blocks = [];
220502
+ let current = [];
220503
+ for (const line of text.split("\n")) {
220504
+ if (line.trim().startsWith("|")) {
220505
+ current.push(decisionSplitRow(line));
220506
+ continue;
220507
+ }
220508
+ if (current.length > 0) blocks.push(current);
220509
+ current = [];
220305
220510
  }
220306
- return map;
220511
+ if (current.length > 0) blocks.push(current);
220512
+ return blocks.filter((block) => block.length >= 2).map((block) => ({
220513
+ header: block[0] ?? [],
220514
+ body: block.slice(1).filter((row) => !isDecisionSeparator(row) && row.length === (block[0] ?? []).length)
220515
+ }));
220307
220516
  }
220308
- function requiredArtifacts(cfg) {
220309
- const required = ["00-brief.md"];
220310
- for (let i = 1; i <= cfg.roles.proponents; i++) required.push(`01-proposals/P${i}.md`);
220311
- for (let i = 1; i <= cfg.roles.defenders; i++) required.push(`02-defenses/D${i}.md`);
220312
- for (let i = 1; i <= cfg.roles.adversaries; i++) required.push(`03-attacks/A${i}.md`);
220313
- for (let i = 1; i <= cfg.roles.judges; i++) required.push(`04-verdicts/J${i}.md`);
220314
- required.push("05-refined/R1.md", "05-refined/R2.md", DECISION_FILE, "07-epics.json");
220315
- return required;
220517
+ function decisionNumber(cell) {
220518
+ const value = decisionStrip(cell);
220519
+ if (value === "") return void 0;
220520
+ const match = DECISION_LEADING_NUMBER.exec(value);
220521
+ return match === null ? void 0 : Number(match[0].replace(",", "."));
220522
+ }
220523
+ function decisionProposal(cell) {
220524
+ const value = decisionStrip(cell);
220525
+ if (value === "") return void 0;
220526
+ return DECISION_PROPOSAL.test(value.toLowerCase()) ? value.toUpperCase() : void 0;
220527
+ }
220528
+ function decisionAxis(row, axes) {
220529
+ const low = normalize$1(decisionStrip(row));
220530
+ if (low === "") return void 0;
220531
+ const exact = axes.find((axis) => normalize$1(axis) === low);
220532
+ if (exact !== void 0) return exact;
220533
+ return axes.find((axis) => low.includes(normalize$1(axis)));
220534
+ }
220535
+ function isDecisionTotal(cell) {
220536
+ return /\btotal\b/.test(normalize$1(decisionStrip(cell)));
220537
+ }
220538
+ function isDecisionMedian(cell) {
220539
+ return /\bmedian/.test(normalize$1(decisionStrip(cell)));
220540
+ }
220541
+ function decisionColumn(header, predicate) {
220542
+ return header.findIndex((cell) => predicate(cell));
220543
+ }
220544
+ function findDecisionMatrix(blocks, axes) {
220545
+ let best;
220546
+ for (const block of blocks) {
220547
+ const header = block.header.map(decisionStrip);
220548
+ const columnProposals = header.filter((cell) => decisionProposal(cell) !== void 0);
220549
+ const rowProposals = block.body.filter((row) => decisionProposal(row[0]) !== void 0);
220550
+ const axisColumns = header.filter((cell) => decisionAxis(cell, axes) !== void 0 && !isDecisionTotal(cell) && !isDecisionMedian(cell));
220551
+ const axisRows = block.body.filter((row) => decisionAxis(row[0], axes) !== void 0);
220552
+ const byColumn = columnProposals.length >= 2 && axisRows.length > 0;
220553
+ const byRow = rowProposals.length >= 2 && axisColumns.length > 0;
220554
+ if (!byColumn && !byRow) continue;
220555
+ const score = Math.max(axisRows.length, axisColumns.length) * 10 + Math.max(columnProposals.length, rowProposals.length);
220556
+ if (best === void 0 || score > best.score) best = {
220557
+ block,
220558
+ orientation: byColumn ? "columns" : "rows",
220559
+ score
220560
+ };
220561
+ }
220562
+ return best;
220563
+ }
220564
+ function readDecisionColumns(block, axes, proposals, found) {
220565
+ const indexes = /* @__PURE__ */ new Map();
220566
+ for (const [index, cell] of block.header.entries()) {
220567
+ const proposal = decisionProposal(cell);
220568
+ if (proposal !== void 0 && !indexes.has(proposal)) indexes.set(proposal, index);
220569
+ }
220570
+ for (const row of block.body) {
220571
+ const axis = decisionAxis(row[0], axes);
220572
+ if (axis === void 0 || found.has(axis)) continue;
220573
+ const cells = {};
220574
+ found.set(axis, {
220575
+ label: decisionStrip(row[0]),
220576
+ cells
220577
+ });
220578
+ for (const proposal of proposals) {
220579
+ const index = indexes.get(proposal);
220580
+ cells[proposal] = index === void 0 ? void 0 : decisionNumber(row[index]);
220581
+ }
220582
+ }
220583
+ return indexes;
220584
+ }
220585
+ function readDecisionRows(block, axes, found) {
220586
+ const columns = [];
220587
+ for (const [index, cell] of block.header.entries()) {
220588
+ const axis = decisionAxis(cell, axes);
220589
+ if (axis === void 0 || isDecisionTotal(cell) || isDecisionMedian(cell)) continue;
220590
+ columns.push({
220591
+ axis,
220592
+ index,
220593
+ label: decisionStrip(cell)
220594
+ });
220595
+ }
220596
+ const seen = [];
220597
+ for (const row of block.body) {
220598
+ const proposal = decisionProposal(row[0]);
220599
+ if (proposal === void 0) continue;
220600
+ if (!seen.includes(proposal)) seen.push(proposal);
220601
+ for (const column of columns) {
220602
+ const existing = found.get(column.axis);
220603
+ const entry = existing ?? {
220604
+ label: column.label,
220605
+ cells: {}
220606
+ };
220607
+ if (existing === void 0) found.set(column.axis, entry);
220608
+ if (entry.cells[proposal] !== void 0) continue;
220609
+ entry.cells[proposal] = decisionNumber(row[column.index]);
220610
+ }
220611
+ }
220612
+ return seen;
220613
+ }
220614
+ function decisionDeclarations(blocks, matrix, proposals) {
220615
+ const totals = {};
220616
+ const medians = {};
220617
+ const take = (target, proposal, value) => {
220618
+ if (proposal === void 0 || value === void 0 || target[proposal] !== void 0) return;
220619
+ target[proposal] = value;
220620
+ };
220621
+ if (matrix !== void 0) {
220622
+ const header = matrix.block.header.map(decisionStrip);
220623
+ const totalColumn = matrix.orientation === "rows" ? decisionColumn(header, isDecisionTotal) : -1;
220624
+ const medianColumn = matrix.orientation === "rows" ? decisionColumn(header, isDecisionMedian) : -1;
220625
+ for (const row of matrix.block.body) {
220626
+ const proposal = decisionProposal(row[0]);
220627
+ if (proposal !== void 0) {
220628
+ if (totalColumn !== -1) take(totals, proposal, decisionNumber(row[totalColumn]));
220629
+ if (medianColumn !== -1) take(medians, proposal, decisionNumber(row[medianColumn]));
220630
+ continue;
220631
+ }
220632
+ if (isDecisionTotal(row[0]) || isDecisionMedian(row[0])) {
220633
+ const target = isDecisionTotal(row[0]) ? totals : medians;
220634
+ for (const item of proposals) {
220635
+ const index = header.findIndex((cell) => decisionProposal(cell) === item);
220636
+ take(target, item, index === -1 ? void 0 : decisionNumber(row[index]));
220637
+ }
220638
+ }
220639
+ }
220640
+ }
220641
+ for (const block of blocks) {
220642
+ const header = block.header.map(decisionStrip);
220643
+ const totalColumn = decisionColumn(header, isDecisionTotal);
220644
+ const medianColumn = decisionColumn(header, isDecisionMedian);
220645
+ if (totalColumn === -1 && medianColumn === -1) continue;
220646
+ for (const row of block.body) {
220647
+ const proposal = decisionProposal(row[0]);
220648
+ if (proposal === void 0) continue;
220649
+ if (totalColumn !== -1) take(totals, proposal, decisionNumber(row[totalColumn]));
220650
+ if (medianColumn !== -1) take(medians, proposal, decisionNumber(row[medianColumn]));
220651
+ }
220652
+ }
220653
+ return {
220654
+ totals,
220655
+ medians
220656
+ };
220657
+ }
220658
+ function decisionMargins(blocks) {
220659
+ for (const block of blocks) {
220660
+ const header = block.header.map(decisionStrip);
220661
+ const judgeColumn = decisionColumn(header, (cell) => normalize$1(cell).includes("juez"));
220662
+ const marginColumn = decisionColumn(header, (cell) => normalize$1(cell).includes("margen"));
220663
+ if (judgeColumn === -1 || marginColumn === -1) continue;
220664
+ const preferColumn = decisionColumn(header, (cell) => normalize$1(cell).includes("prefer"));
220665
+ const rows = [];
220666
+ for (const row of block.body) {
220667
+ const judge = decisionStrip(row[judgeColumn]);
220668
+ if (!DECISION_JUDGE.test(judge)) continue;
220669
+ const margin = DECISION_SIGNED_MARGIN.exec(decisionStrip(row[marginColumn]));
220670
+ if (margin === null) continue;
220671
+ rows.push({
220672
+ judge,
220673
+ margin: Number((margin[1] ?? "0").replace(",", ".")),
220674
+ prefers: preferColumn === -1 ? void 0 : decisionProposal(row[preferColumn])
220675
+ });
220676
+ }
220677
+ if (rows.length > 0) return rows;
220678
+ }
220679
+ return [];
220680
+ }
220681
+ function decisionDissent(text) {
220682
+ const lines = text.split("\n");
220683
+ const start = lines.findIndex((line) => /^#{1,3}\s+\d*\.?\s*disidencias/i.test(line));
220684
+ if (start === -1) return void 0;
220685
+ const level = /^(#{1,3})/.exec(lines[start] ?? "")?.[1]?.length ?? 1;
220686
+ const collected = [];
220687
+ for (const line of lines.slice(start + 1)) {
220688
+ const heading = /^(#{1,3})\s+\S/.exec(line);
220689
+ if (heading !== null && (heading[1] ?? "").length <= level) break;
220690
+ collected.push(line);
220691
+ }
220692
+ return collected.join("\n").trim();
220693
+ }
220694
+ function parseDecisionMatrix(text, options) {
220695
+ const axes = options.axes.map(String);
220696
+ const blocks = decisionTables(text);
220697
+ const matrix = findDecisionMatrix(blocks, axes);
220698
+ const found = /* @__PURE__ */ new Map();
220699
+ let proposals = options.proposals.map(String);
220700
+ let present = [];
220701
+ if (matrix !== void 0) {
220702
+ if (matrix.orientation === "columns") {
220703
+ if (proposals.length === 0) proposals = matrix.block.header.map(decisionStrip).map(decisionProposal).filter((label) => label !== void 0);
220704
+ const indexes = readDecisionColumns(matrix.block, axes, proposals, found);
220705
+ present = proposals.filter((proposal) => indexes.has(proposal));
220706
+ } else {
220707
+ if (proposals.length === 0) proposals = matrix.block.body.map((row) => decisionProposal(row[0])).filter((label) => label !== void 0);
220708
+ const seen = readDecisionRows(matrix.block, axes, found);
220709
+ present = proposals.filter((proposal) => seen.includes(proposal));
220710
+ }
220711
+ }
220712
+ const axisRows = [];
220713
+ const cells = [];
220714
+ const missing = [];
220715
+ for (const axis of axes) {
220716
+ const row = found.get(axis);
220717
+ if (row === void 0) continue;
220718
+ axisRows.push({
220719
+ axis,
220720
+ label: row.label,
220721
+ cells: row.cells
220722
+ });
220723
+ for (const proposal of present) {
220724
+ const value = row.cells[proposal];
220725
+ if (typeof value === "number") cells.push({
220726
+ axis,
220727
+ proposal,
220728
+ value
220729
+ });
220730
+ else missing.push({
220731
+ axis,
220732
+ proposal
220733
+ });
220734
+ }
220735
+ }
220736
+ const declarations = decisionDeclarations(blocks, matrix, proposals);
220737
+ const axesFound = axisRows.map((row) => row.axis);
220738
+ return {
220739
+ present: matrix !== void 0,
220740
+ orientation: matrix?.orientation,
220741
+ header: matrix === void 0 ? [] : matrix.block.header.map(decisionStrip),
220742
+ proposals,
220743
+ presentProposals: present,
220744
+ axisRows,
220745
+ cells,
220746
+ missing,
220747
+ axesFound,
220748
+ axesMissing: axes.filter((axis) => !axesFound.includes(axis)),
220749
+ totals: declarations.totals,
220750
+ medians: declarations.medians,
220751
+ margins: decisionMargins(blocks),
220752
+ dissent: decisionDissent(text)
220753
+ };
220754
+ }
220755
+ function decisionProposalSum(matrix, proposal) {
220756
+ const values = matrix.axisRows.map((row) => row.cells[proposal]);
220757
+ if (values.length === 0) return void 0;
220758
+ let total = 0;
220759
+ for (const value of values) {
220760
+ if (typeof value !== "number") return void 0;
220761
+ total += value;
220762
+ }
220763
+ return total;
220764
+ }
220765
+ function decisionMedian(values) {
220766
+ if (values.length === 0) return void 0;
220767
+ const sorted = [...values].toSorted((left, right) => left - right);
220768
+ const middle = Math.floor(sorted.length / 2);
220769
+ const current = sorted[middle];
220770
+ if (current === void 0) return void 0;
220771
+ if (sorted.length % 2 === 1) return current;
220772
+ const previous = sorted[middle - 1];
220773
+ return previous === void 0 ? void 0 : (previous + current) / 2;
220774
+ }
220775
+ function decisionRecalculateMedians(verdicts, options) {
220776
+ const tables = [];
220777
+ const missing = [];
220778
+ for (const verdict of verdicts) {
220779
+ const matrix = parseDecisionMatrix(verdict.text, options);
220780
+ if (!(matrix.present && matrix.axesMissing.length === 0 && matrix.missing.length === 0)) {
220781
+ missing.push(verdict.file);
220782
+ continue;
220783
+ }
220784
+ const totals = {};
220785
+ for (const proposal of matrix.proposals) totals[proposal] = decisionProposalSum(matrix, proposal) ?? NaN;
220786
+ tables.push({
220787
+ file: verdict.file,
220788
+ totals
220789
+ });
220790
+ }
220791
+ const medians = {};
220792
+ if (missing.length === 0 && tables.length > 0) for (const proposal of options.proposals) {
220793
+ const values = tables.map((entry) => entry.totals[proposal]).filter((value) => typeof value === "number" && Number.isFinite(value));
220794
+ if (values.length !== tables.length) continue;
220795
+ const value = decisionMedian(values);
220796
+ if (value !== void 0) medians[proposal] = value;
220797
+ }
220798
+ return {
220799
+ medians,
220800
+ missing
220801
+ };
220316
220802
  }
220317
220803
  function runWaveGate(fs, root, cfg) {
220318
220804
  const findings = [];
@@ -220341,8 +220827,24 @@ function runWaveGate(fs, root, cfg) {
220341
220827
  let epics = [];
220342
220828
  let tasks = [];
220343
220829
  let totalAc = 0;
220830
+ const state = waveConfigState(cfg);
220344
220831
  block("config", () => {
220345
220832
  if (cfg.loadedFrom === void 0) add("config.missing", null, "no se encontro workflow.config.json");
220833
+ const raw = state.raw;
220834
+ const version = typeof raw["version"] === "number" ? raw["version"] : state.version;
220835
+ for (const key of [
220836
+ "version",
220837
+ "layout",
220838
+ "waves",
220839
+ "thresholds",
220840
+ "vocabulary"
220841
+ ]) if (raw[key] === void 0) add("config.missing_key", null, `falta la clave "${key}"`);
220842
+ if (version !== 1 && version !== 2) add("config.version_unknown", null, `version "${String(version)}" fuera de uno y dos: el gate no sabe leerla`);
220843
+ if (version === 2) {
220844
+ if (raw["wavesOpen"] === void 0) add("config.missing_key", null, "falta la clave \"wavesOpen\"");
220845
+ if (raw["vocabulary"] === void 0 || record$1(raw["vocabulary"])["units"] === void 0) add("config.missing_key", null, "falta la clave \"vocabulary.units\"");
220846
+ if (raw["falsifier"] === void 0) add("config.missing_key", null, "falta la clave \"falsifier\"");
220847
+ }
220346
220848
  if (cfg.waves.length === 0) add("config.bad_waves", null, "waves debe ser un array no vacio");
220347
220849
  if (vocabulary.verifyCommands.length === 0) add("config.bad_vocabulary", null, "vocabulary.verifyCommands vacio");
220348
220850
  if (vocabulary.verifyPrefixes.length === 0) add("config.bad_vocabulary", null, "vocabulary.verifyPrefixes vacio");
@@ -220378,19 +220880,89 @@ function runWaveGate(fs, root, cfg) {
220378
220880
  }
220379
220881
  }
220380
220882
  });
220883
+ block("brief", () => {
220884
+ const templateFile = join(root, BRIEF_TEMPLATE);
220885
+ if (!fs.exists(templateFile)) return;
220886
+ const template = briefSections(fs.read(templateFile) ?? "");
220887
+ if (wavesDir === void 0 || !fs.isDirectory(wavesDir)) return;
220888
+ for (const wave of cfg.waves) {
220889
+ const match = waveDirs.find((name) => name === wave || name.startsWith(`${wave}-`));
220890
+ if (match === void 0) continue;
220891
+ const file = join(wavesDir, match, "00-brief.md");
220892
+ const text = fs.read(file);
220893
+ if (text === void 0) continue;
220894
+ const at = relative$1(root, file);
220895
+ const sections = briefSections(text);
220896
+ if (sections.length !== template.length) add("waves.brief_template_drift", at, `${sections.length} secciones frente a ${template.length} de ${BRIEF_TEMPLATE}: el brief ya no sigue la plantilla`);
220897
+ for (const [index, expected] of template.entries()) {
220898
+ const section = sections[index];
220899
+ if (section === void 0) continue;
220900
+ if (sectionKeyword(section.title) !== sectionKeyword(expected.title)) add("waves.brief_template_drift", at, `la seccion ${index + 1} se titula "${section.title}" y ${BRIEF_TEMPLATE} exige "${expected.title}"`);
220901
+ }
220902
+ for (const section of sections) if (section.body === "") add("waves.brief_empty_section", at, `la seccion "${section.title}" no tiene contenido`);
220903
+ }
220904
+ });
220381
220905
  block("decision", () => {
220382
220906
  const dir = wavesDir;
220383
220907
  if (dir === void 0 || !fs.isDirectory(dir)) return;
220908
+ const axes = cfg.quorum.axes;
220909
+ const proposals = Array.from({ length: cfg.roles.proponents }, (_, index) => `P${index + 1}`);
220910
+ const judges = cfg.roles.judges;
220384
220911
  for (const wave of cfg.waves) {
220385
220912
  const match = waveDirs.find((name) => name === wave || name.startsWith(`${wave}-`));
220386
220913
  if (match === void 0) continue;
220387
- const file = join(dir, match, DECISION_FILE);
220914
+ const base = join(dir, match);
220915
+ const file = join(base, DECISION_FILE);
220388
220916
  const text = fs.read(file);
220389
220917
  if (text === void 0) continue;
220390
220918
  const low = normalize$1(text);
220391
220919
  const at = relative$1(root, file);
220392
- for (let i = 1; i <= cfg.roles.proponents; i++) if (!low.includes(`p${i}`)) add("waves.decision_missing_role", at, `no nombra la propuesta P${i} de la matriz`);
220393
- for (const axis of cfg.quorum.axes) if (!low.includes(normalize$1(axis))) add("waves.decision_missing_axis", at, `sin eje "${axis}" en la matriz`);
220920
+ const matrix = parseDecisionMatrix(text, {
220921
+ axes,
220922
+ proposals
220923
+ });
220924
+ for (const proposal of proposals) if (!matrix.presentProposals.includes(proposal)) add("waves.decision_missing_role", at, `no hay columna ${proposal} en la matriz de la ronda`);
220925
+ if (!matrix.present) add("waves.decision_without_quorum", at, "sin tabla de la matriz: la ronda no trae puntuaciones que auditar");
220926
+ else {
220927
+ for (const axis of axes) if (!matrix.axesFound.includes(axis)) add("waves.decision_missing_axis", at, `sin eje "${axis}" en la matriz`);
220928
+ for (const item of matrix.missing) add("waves.decision_matrix_incomplete", at, `eje "${item.axis}" · propuesta ${item.proposal}: celda vacia`);
220929
+ for (const proposal of proposals) {
220930
+ const declared = matrix.totals[proposal];
220931
+ const sum = decisionProposalSum(matrix, proposal);
220932
+ if (declared === void 0 || sum === void 0 || declared === sum) continue;
220933
+ add("waves.decision_total_mismatch", at, `${proposal}: total declarado ${declared} y recalculado ${sum} desde las celdas de la matriz`);
220934
+ }
220935
+ if (proposals.some((proposal) => matrix.medians[proposal] !== void 0)) {
220936
+ const verdictsDir = join(base, "04-verdicts");
220937
+ const recalc = decisionRecalculateMedians(Array.from({ length: judges }, (_, index) => {
220938
+ const name = `J${index + 1}.md`;
220939
+ return {
220940
+ file: `${relative$1(root, verdictsDir)}/${name}`,
220941
+ text: fs.read(join(verdictsDir, name)) ?? ""
220942
+ };
220943
+ }), {
220944
+ axes,
220945
+ proposals
220946
+ });
220947
+ for (const missing of recalc.missing) add("waves.decision_matrix_incomplete", missing, `sin tabla de ${axes.length} ejes por propuesta: no hay de donde recalcular la mediana`);
220948
+ if (recalc.missing.length === 0) {
220949
+ const citation = Array.from({ length: judges }, (_, index) => `04-verdicts/J${index + 1}.md`).join(", ");
220950
+ for (const proposal of proposals) {
220951
+ const declared = matrix.medians[proposal];
220952
+ const value = recalc.medians[proposal];
220953
+ if (declared === void 0 || value === void 0 || declared === value) continue;
220954
+ add("waves.decision_median_mismatch", at, `${proposal}: mediana declarada ${declared} y recalculada ${value} desde ${citation}`);
220955
+ }
220956
+ }
220957
+ }
220958
+ if (matrix.margins.length > 0) {
220959
+ const dissent = normalize$1(matrix.dissent ?? "");
220960
+ for (const row of matrix.margins) {
220961
+ if (row.margin >= 0 || dissent.includes(normalize$1(row.judge))) continue;
220962
+ add("waves.decision_undisclosed_dissent", at, `${row.judge}: margen ${row.margin} y no aparece en "## Disidencias"`);
220963
+ }
220964
+ }
220965
+ }
220394
220966
  if (!/^#{1,3}\s+\d*\.?\s*disidencias/im.test(text)) add("waves.decision_without_dissent", at, "falta la seccion \"## Disidencias\"");
220395
220967
  if (text.length < thresholds.minDecisionBytes) add("waves.decision_thin", at, `${text.length} B < ${thresholds.minDecisionBytes} B minimos`);
220396
220968
  const numbers = text.match(/\b\d+(?:[.,]\d+)?\b/g)?.length ?? 0;
@@ -220479,10 +221051,16 @@ function runWaveGate(fs, root, cfg) {
220479
221051
  if (tasks.length < thresholds.minTasks) add("tasks.count", null, `${tasks.length} tasks < ${thresholds.minTasks} minimas`);
220480
221052
  });
220481
221053
  block("ac", () => {
220482
- const vague = vocabulary.vagueWords.map((word) => normalize$1(word)).filter(Boolean);
220483
- const vaguePattern = vague.length === 0 ? void 0 : new RegExp(`(^|[^a-z0-9])(${vague.map(escapeRegExp$1).join("|")})[a-z]*([^a-z0-9]|$)`);
221054
+ const vague = vocabulary.vagueWords.map((word) => normalize$1(word)).filter(Boolean).map((word) => ({
221055
+ word,
221056
+ root: word.replace(/[aeiou]$/, "")
221057
+ })).filter((entry) => entry.root !== "");
221058
+ const units = asArray$1(record$1(state.raw["vocabulary"])["units"]).map(str$2).map(normalize$1).filter(Boolean);
221059
+ const falsifierLexicon = record$1(state.raw["falsifier"]);
221060
+ const stateVerbs = asArray$1(falsifierLexicon["stateVerbs"]).map(str$2).map(normalize$1).filter(Boolean);
221061
+ const stateObjects = asArray$1(falsifierLexicon["stateObjects"]).map(str$2).map(normalize$1).filter(Boolean);
220484
221062
  const withFalsifier = /* @__PURE__ */ new Set();
220485
- const waveDirs = waveDirectories(fs, root, cfg);
221063
+ const openWaves = new Set(state.wavesOpen);
220486
221064
  for (const task of tasks) {
220487
221065
  const taskId = str$2(task["id"]);
220488
221066
  for (const raw of asArray$1(task["ac"])) {
@@ -220502,31 +221080,67 @@ function runWaveGate(fs, root, cfg) {
220502
221080
  }
220503
221081
  const metric = str$2(ac["metric"]);
220504
221082
  if (metric !== "" && metric.length < thresholds.minMetricChars) add("ac.short_metric", at, `"metric" < ${thresholds.minMetricChars} caracteres`);
220505
- const prose = normalize$1(`${str$2(ac["then"])} ${str$2(ac["threshold"])} ${metric}`);
220506
- const hit = vaguePattern?.exec(prose)?.[2];
220507
- if (hit !== void 0) add("ac.vague_language", at, `"${hit}" no es medible: el AC no muerde`);
220508
- const thresholdText = str$2(ac["threshold"]).trim();
220509
- if (thresholdText !== "" && !hasMeasure(thresholdText)) add("ac.threshold_not_measurable", at, `"${thresholdText}" no declara unidad, operador ni magnitud comparada`);
221083
+ const threshold = str$2(ac["threshold"]);
221084
+ if (threshold !== "" && !measurableThreshold(threshold, units)) add("ac.threshold_not_measurable", at, `"${threshold}" no compara ninguna magnitud: falta operador con numero, numero con unidad o conjunto enumerable`);
221085
+ const prose = normalize$1(`${str$2(ac["then"])} ${threshold} ${metric}`);
221086
+ const vagueHit = vague.find((entry) => new RegExp(`\\b${escapeRegExp$1(entry.root)}[a-z]{0,10}\\b`).test(prose));
221087
+ if (vagueHit !== void 0) {
221088
+ const matched = new RegExp(`\\b${escapeRegExp$1(vagueHit.root)}[a-z]{0,10}\\b`).exec(prose)?.[0] ?? vagueHit.root;
221089
+ add("ac.vague_language", at, `"${matched}" contiene la raiz "${vagueHit.root}" de "${vagueHit.word}": no es medible`);
221090
+ }
220510
221091
  const verify = str$2(ac["verify"]).trim();
220511
221092
  if (verify !== "") {
220512
221093
  const command = verify.split(/\s+/)[0] ?? "";
220513
221094
  if (!vocabulary.verifyCommands.includes(command)) add("ac.verify_not_command", at, `"${command}" no es un comando de la lista permitida`);
220514
221095
  else {
220515
- const matched = verify.match(/\b((?:[\w.-]+\/)+[\w.-]+)/)?.[1];
220516
- if (matched === void 0) add("ac.verify_outside_test_plan", at, "el comando no apunta a ningun fichero o directorio verificable");
220517
- else if (!vocabulary.verifyPrefixes.some((prefix) => matched.startsWith(prefix))) add("ac.verify_outside_test_plan", at, `"${matched}" fuera del test plan declarado`);
220518
- else if (!thresholds.allowVerifyTargetMissing && !verifyTargetExists(waveDirs.get(str$2(task["wave"])) ?? root, matched)) add("ac.verify_target_missing", at, `"${matched}" no existe en el arbol: el verify saldria verde sin ejecutar nada`);
221096
+ let argv;
221097
+ try {
221098
+ argv = splitCommand(verify);
221099
+ } catch (error) {
221100
+ add("ac.verify_command_unsafe", at, error instanceof Error ? error.message : String(error));
221101
+ }
221102
+ if (argv !== void 0) {
221103
+ let escaped;
221104
+ for (const token of argv) {
221105
+ if (token.startsWith("-") || !token.includes("/")) continue;
221106
+ try {
221107
+ containedPath(root, token);
221108
+ } catch {
221109
+ escaped = token;
221110
+ break;
221111
+ }
221112
+ }
221113
+ if (escaped !== void 0) add("ac.verify_command_unsafe", at, `el token "${escaped}" escapa de la raiz del paquete`);
221114
+ else {
221115
+ const token = argv.slice(1).find((item) => !item.startsWith("-") && item.includes("/"));
221116
+ if (token === void 0) add("ac.verify_outside_test_plan", at, "el comando no apunta a ningun fichero o directorio verificable");
221117
+ else {
221118
+ const target = token.replace(/^\.\//, "");
221119
+ if (!vocabulary.verifyPrefixes.some((prefix) => target.startsWith(prefix))) add("ac.verify_outside_test_plan", at, `"${target}" fuera del test plan declarado`);
221120
+ else if (state.version >= 2 && !openWaves.has(str$2(task["wave"])) && !fs.exists(join(root, target))) add("ac.verify_target_missing", at, `el destino "${target}" no existe bajo la raiz del paquete`);
221121
+ const destination = join(root, target);
221122
+ const isTest = /(^|\/)test\//.test(target) || /\.test\./.test(target);
221123
+ const exempt = /(^|\/)scripts\//.test(target);
221124
+ if (isTest && !exempt && fs.exists(destination) && fs.isDirectory(destination) === false) {
221125
+ if ((fs.read(destination) ?? "").match(ASSERTION) === null) add("ac.verify_stub", at, `el destino "${target}" no tiene ninguna asercion: el verify no mide nada`);
221126
+ }
221127
+ }
221128
+ }
221129
+ }
220519
221130
  }
220520
221131
  }
220521
221132
  const falsifier = record$1(ac["falsifier"]);
220522
221133
  if (Object.keys(falsifier).length > 0) {
220523
221134
  const mutation = str$2(falsifier["mutation"]);
220524
221135
  const expect = str$2(falsifier["expect"]);
220525
- const script = str$2(falsifier["script"]);
220526
221136
  if (mutation.length < thresholds.minFieldChars) add("ac.falsifier_thin", at, "falsifier.mutation insuficiente");
220527
221137
  else if (expect.length < thresholds.minFieldChars) add("ac.falsifier_thin", at, "falsifier.expect insuficiente");
220528
- else if (script === "" && isDestructive(mutation)) add("ac.falsifier_not_reversible", at, `"${mutation}" necesita falsifier.script con apply y restore`);
220529
221138
  else withFalsifier.add(taskId);
221139
+ const normalizedMutation = normalize$1(mutation);
221140
+ const verb = stateVerbs.find((entry) => new RegExp(`\\b${escapeRegExp$1(entry)}[a-z]{0,4}\\b`).test(normalizedMutation));
221141
+ const object = stateObjects.find((entry) => new RegExp(`\\b${escapeRegExp$1(entry)}s?\\b`).test(normalizedMutation));
221142
+ const reversible = str$2(falsifier["script"]).trim() !== "" || str$2(falsifier["restore"]).trim() !== "";
221143
+ if (verb !== void 0 && object !== void 0 && !reversible) add("ac.falsifier_not_reversible", at, `la mutacion "${verb} ... ${object}" toca estado sin falsifier.script ni falsifier.restore`);
220530
221144
  }
220531
221145
  }
220532
221146
  }
@@ -220588,14 +221202,17 @@ function runWaveGate(fs, root, cfg) {
220588
221202
  waves: cfg.waves.length,
220589
221203
  epics: epics.length,
220590
221204
  tasks: tasks.length,
220591
- ac: totalAc
221205
+ ac: totalAc,
221206
+ notRun: NOT_RUN_BLOCKS
220592
221207
  }
220593
221208
  };
220594
221209
  }
220595
- var DECISION_FILE, AC_FIELDS, normalize$1, relative$1;
221210
+ var DECISION_FILE, BRIEF_TEMPLATE, AC_FIELDS, OPERATOR_WITH_NUMBER, EXACT_VALUE, ASSERTION, escapeRegExp$1, NOT_RUN_BLOCKS, normalize$1, relative$1, DECISION_SEPARATOR, DECISION_PROPOSAL, DECISION_LEADING_NUMBER, DECISION_JUDGE, DECISION_SIGNED_MARGIN;
220596
221211
  var init_checks = __esmMin((() => {
220597
221212
  init_config$6();
221213
+ init_runner();
220598
221214
  DECISION_FILE = "06-DECISION.md";
221215
+ BRIEF_TEMPLATE = "workflow/templates/brief.md";
220599
221216
  AC_FIELDS = [
220600
221217
  "id",
220601
221218
  "given",
@@ -220606,8 +221223,24 @@ var init_checks = __esmMin((() => {
220606
221223
  "verify",
220607
221224
  "falsifier"
220608
221225
  ];
221226
+ OPERATOR_WITH_NUMBER = /[=<>≥≤]\s*\d|\b(?:mayor|menor|igual|al menos|como maximo|como minimo)\b[^0-9]{0,20}\d/;
221227
+ EXACT_VALUE = /\b\d+(?:[.,]\d+)?\s+(?:exacto|exacta|exactos|exactas|fijo|fija|justo|justa)\b/;
221228
+ ASSERTION = /\bassert\b|\bexpect\s*\(|\bit\s*\(|\btest\s*\(|\bfailures\b|process\.exit\s*\(\s*(?!0\b)/g;
221229
+ escapeRegExp$1 = (value) => value.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&");
221230
+ NOT_RUN_BLOCKS = [{
221231
+ block: "regression",
221232
+ closedBy: "oracle"
221233
+ }, {
221234
+ block: "schemas",
221235
+ closedBy: "oracle"
221236
+ }];
220609
221237
  normalize$1 = (value) => value.normalize("NFD").replaceAll(/[\u0300-\u036F]/g, "").toLowerCase();
220610
221238
  relative$1 = (root, path) => path.startsWith(root) ? path.slice(root.length).replaceAll("\\", "/").replace(/^\/+/, "") : path;
221239
+ DECISION_SEPARATOR = /^:?-{2,}:?$/;
221240
+ DECISION_PROPOSAL = /^p\d+$/;
221241
+ DECISION_LEADING_NUMBER = /^[+-]?\d+(?:[.,]\d+)?/;
221242
+ DECISION_JUDGE = /^J\d+$/;
221243
+ DECISION_SIGNED_MARGIN = /^([+-]?\d+(?:[.,]\d+)?)$/;
220611
221244
  }));
220612
221245
  // ../../packages/agent-core-v2/src/features/wave/tools/gate/gateRunner.ts
220613
221246
  function runWaveGateForRoot(input) {
@@ -220629,6 +221262,7 @@ function runWaveGateForRoot(input) {
220629
221262
  };
220630
221263
  const lines = [`GATE — ${report.ok ? "verde" : "con hallazgos"}`, ""];
220631
221264
  for (const block of report.blocks) lines.push(`${block.ok ? "OK " : "FAIL"} ${block.name}`);
221265
+ for (const entry of report.summary.notRun) lines.push(`NO EJECUTADO ${entry.block} — cerrado por ${entry.closedBy}`);
220632
221266
  if (report.findings.length > 0) {
220633
221267
  lines.push("", `PENDIENTE: ${report.findings.length} hallazgo(s)`, "");
220634
221268
  for (const finding of report.findings) lines.push(` - ${finding.code}${finding.path === null ? "" : ` ${finding.path}`} — ${finding.message}`);
@@ -220687,168 +221321,37 @@ var init_verify$1 = __esmMin((() => {
220687
221321
  init_instantiation();
220688
221322
  WaveVerifyToolInputSchema = object$1({
220689
221323
  task: string().min(3),
220690
- root: string().optional()
221324
+ root: string().optional(),
221325
+ ac: string().min(3).optional(),
221326
+ dryRun: boolean$1().optional(),
221327
+ allowDeclared: boolean$1().optional()
220691
221328
  }).strict();
220692
221329
  IWaveVerifyTool = createDecorator("waveVerifyTool");
220693
221330
  }));
220694
- // ../../packages/agent-core-v2/src/features/wave/core/verify/runner.ts
220695
- function splitCommand(command) {
220696
- const tokens = [];
220697
- let current = "";
220698
- let quote;
220699
- for (const char of command) {
220700
- if (quote !== void 0) {
220701
- if (char === quote) quote = void 0;
220702
- else current += char;
220703
- continue;
220704
- }
220705
- if (char === "\"" || char === "'") {
220706
- quote = char;
220707
- continue;
220708
- }
220709
- if (SHELL_METACHARACTERS.has(char)) throw new Error(`metacaracter de shell prohibido en: ${command}`);
220710
- if (char === " " || char === " ") {
220711
- if (current !== "") {
220712
- tokens.push(current);
220713
- current = "";
220714
- }
220715
- continue;
220716
- }
220717
- current += char;
220718
- }
220719
- if (quote !== void 0) throw new Error(`comillas sin cerrar en: ${command}`);
220720
- if (current !== "") tokens.push(current);
220721
- if (tokens.length === 0) throw new Error("comando vacio");
220722
- return tokens;
220723
- }
220724
- function containPaths(argv, root) {
220725
- for (const token of argv) {
220726
- if (token.startsWith("-")) continue;
220727
- if (!token.includes("/") && !/\.(mjs|cjs|js|ts|sh)$/.test(token)) continue;
220728
- containedPath(root, token);
220729
- }
221331
+ // ../../packages/agent-core-v2/src/features/wave/tools/verify/verifyRunner.ts
221332
+ function failure$1(error) {
221333
+ return {
221334
+ output: error instanceof Error ? error.message : String(error),
221335
+ isError: true
221336
+ };
220730
221337
  }
220731
- function runCommand$2(command, options) {
220732
- let argv;
221338
+ function runDry(root, config, input) {
221339
+ let selected;
220733
221340
  try {
220734
- argv = splitCommand(command);
220735
- const binary = (argv[0] ?? "").replace(/\.exe$/i, "");
220736
- const allowed = options.allowBinaries.map((item) => item.replace(/\.exe$/i, ""));
220737
- if (!allowed.includes(binary)) throw new Error(`binario no permitido: ${binary} (permitidos: ${allowed.join(", ")})`);
220738
- containPaths(argv, options.root);
221341
+ selected = selectTaskCriteria(nodeWaveFs, root, config, input.task, input.ac);
220739
221342
  } catch (error) {
220740
- return {
220741
- ok: false,
220742
- output: `rechazado: ${error instanceof Error ? error.message : String(error)}`,
220743
- rejected: true
220744
- };
221343
+ return failure$1(error);
220745
221344
  }
220746
- const result = spawnSync(argv[0] ?? "", argv.slice(1), {
220747
- cwd: options.cwd ?? options.root,
220748
- encoding: "utf8",
220749
- timeout: options.timeoutMs ?? DEFAULT_TIMEOUT_MS$1,
220750
- stdio: "pipe",
220751
- shell: false
220752
- });
220753
- const output = `${result.stdout ?? ""}${result.stderr ?? ""}${result.error === void 0 ? "" : result.error.message}`.replaceAll(/\s+/g, " ").trim().slice(0, 300);
220754
- if (result.error !== void 0) return {
220755
- ok: false,
220756
- output: `no se pudo ejecutar: ${output}`,
220757
- rejected: false
220758
- };
220759
- return {
220760
- ok: result.status === 0,
220761
- output,
220762
- rejected: false
220763
- };
220764
- }
220765
- function evaluateMutant(ac, verifyCommand, options) {
220766
- const script = asText$1(asRecord$2(ac["falsifier"])["script"]);
220767
- if (script === "") return {
220768
- state: "declarado",
220769
- detail: "sin falsifier.script: el AC queda declarado y cuenta como FALLO"
220770
- };
220771
- const apply = runCommand$2(`${script} apply`, options);
220772
- if (apply.rejected) return {
220773
- state: "rechazado",
220774
- detail: `apply rechazado: ${apply.output}`
220775
- };
220776
- if (!apply.ok) return {
220777
- state: "error",
220778
- detail: `apply fallo: ${apply.output}`
220779
- };
220780
- const mutated = runCommand$2(verifyCommand, options);
220781
- const restore = runCommand$2(`${script} restore`, options);
220782
- const after = runCommand$2(verifyCommand, options);
220783
- if (restore.rejected || !restore.ok) return {
220784
- state: "error",
220785
- detail: `restore fallo, el arbol puede estar sucio: ${restore.output}`
220786
- };
220787
- if (mutated.ok) return {
220788
- state: "sobrevive",
220789
- detail: "el verify pasa con la mutacion aplicada: el AC no muerde"
220790
- };
220791
- if (!after.ok) return {
220792
- state: "error",
220793
- detail: "el verify no vuelve a verde tras restaurar"
220794
- };
220795
- return {
220796
- state: "muerto",
220797
- detail: "rojo con la mutacion, verde tras restaurar"
220798
- };
220799
- }
220800
- function verifyTask(fs, root, cfg, taskId, options) {
220801
- const tasksDir = containedPath(root, cfg.layout.backlog);
220802
- const file = walk$1(fs, join(tasksDir, "tasks"), (path) => path.endsWith(`${taskId}.json`))[0];
220803
- if (file === void 0) throw new Error(`no encuentro la task ${taskId} en ${cfg.layout.backlog}/tasks/`);
220804
- const task = asRecord$2(readJson$2(fs, file));
220805
- const runOptions = {
220806
- root,
220807
- allowBinaries: cfg.vocabulary.verifyCommands,
220808
- cwd: options?.cwd,
220809
- timeoutMs: options?.timeoutMs
220810
- };
220811
- const acs = [];
220812
- let declared = 0;
220813
- for (const raw of asArray$2(task["ac"])) {
220814
- const ac = asRecord$2(raw);
220815
- const verifyCommand = asText$1(ac["verify"]);
220816
- const verify = runCommand$2(verifyCommand, runOptions);
220817
- const mutant = evaluateMutant(ac, verifyCommand, runOptions);
220818
- if (mutant.state === "declarado") declared++;
220819
- const ok = verify.ok && mutant.state === "muerto";
220820
- acs.push({
220821
- acId: asText$1(ac["id"]),
220822
- verify,
220823
- mutant,
220824
- ok
220825
- });
221345
+ const lines = [`TASK ${selected.taskId} · dryRun: no se ejecuta ningun comando`, ""];
221346
+ for (const ac of selected.criteria) {
221347
+ const falsifier = asRecord$2(ac["falsifier"]);
221348
+ lines.push(`dry ${asText$1(ac["id"])}`);
221349
+ lines.push(` verify: ${asText$1(ac["verify"])}`);
221350
+ lines.push(` mutacion: ${asText$1(falsifier["mutation"])}`);
220826
221351
  }
220827
- return {
220828
- taskId,
220829
- acs,
220830
- declared,
220831
- ok: acs.length > 0 && acs.every((ac) => ac.ok)
220832
- };
221352
+ lines.push("", `criterios ${selected.criteria.length} · comandos 0`);
221353
+ return { output: lines.join("\n") };
220833
221354
  }
220834
- var SHELL_METACHARACTERS, DEFAULT_TIMEOUT_MS$1;
220835
- var init_runner = __esmMin((() => {
220836
- init_config$6();
220837
- init_fsJson();
220838
- SHELL_METACHARACTERS = /* @__PURE__ */ new Set([
220839
- "|",
220840
- "&",
220841
- ";",
220842
- "<",
220843
- ">",
220844
- "`",
220845
- "$",
220846
- "\n",
220847
- "\r"
220848
- ]);
220849
- DEFAULT_TIMEOUT_MS$1 = 6e5;
220850
- }));
220851
- // ../../packages/agent-core-v2/src/features/wave/tools/verify/verifyRunner.ts
220852
221355
  function runWaveVerifyForRoot(input) {
220853
221356
  const root = resolve(input.root);
220854
221357
  const config = readWaveConfig(nodeWaveFs, root);
@@ -220856,7 +221359,16 @@ function runWaveVerifyForRoot(input) {
220856
221359
  output: `no hay workflow.config.json en ${root}`,
220857
221360
  isError: true
220858
221361
  };
220859
- const result = verifyTask(nodeWaveFs, root, config, input.task);
221362
+ if (input.dryRun === true) return runDry(root, config, input);
221363
+ let result;
221364
+ try {
221365
+ result = verifyTask(nodeWaveFs, root, config, input.task, {
221366
+ ac: input.ac,
221367
+ allowDeclared: input.allowDeclared
221368
+ });
221369
+ } catch (error) {
221370
+ return failure$1(error);
221371
+ }
220860
221372
  const lines = [`TASK ${result.taskId}`, ""];
220861
221373
  for (const ac of result.acs) {
220862
221374
  lines.push(`${ac.ok ? "ok " : "FAIL"} ${ac.acId} verify ${ac.verify.ok ? "verde" : "rojo"} mutante ${ac.mutant.state}`);
@@ -220872,13 +221384,14 @@ function runWaveVerifyForRoot(input) {
220872
221384
  }
220873
221385
  var init_verifyRunner = __esmMin((() => {
220874
221386
  init_config$6();
221387
+ init_fsJson();
220875
221388
  init_nodeFs();
220876
221389
  init_runner();
220877
221390
  }));
220878
221391
  // ../../packages/agent-core-v2/src/features/wave/tools/verify/verify.md?raw
220879
221392
  var verify_default;
220880
221393
  var init_verify = __esmMin((() => {
220881
- verify_default = "Close a wave task with evidence: run each acceptance criterion's `verify` command and its falsifier.\n\nFor every criterion the tool runs `verify`, then `apply` of `falsifier.script`, then `verify` again (which must now fail), then `restore`, then `verify` once more (which must pass again). A criterion whose falsifier is only declared, whose mutant survives, or whose tree does not return to green is a failure — never a pass with a note.\n\nThe task id (`E01-T01`) is looked up in `backlog/tasks/`. Commands run without a shell and only with the binaries allowed by `vocabulary.verifyCommands`.\n";
221394
+ verify_default = "Close a wave task with evidence: run each acceptance criterion's `verify` command and its falsifier.\n\nFor every criterion the tool runs `verify`, then `apply` of `falsifier.script`, then `verify` again (which must now fail), then `restore`, then `verify` once more (which must pass again). A criterion whose falsifier is only declared, whose mutant survives, or whose tree does not return to green is a failure — never a pass with a note.\n\nThe task id (`E01-T01`) is looked up in `backlog/tasks/`. Commands run without a shell and only with the binaries allowed by `vocabulary.verifyCommands`.\n\nArguments:\n- `task`: task id to verify.\n- `root`: workspace root. Defaults to the current working directory.\n- `ac`: verify only this acceptance criterion instead of every criterion of the task. An id the task does not declare is rejected.\n- `dryRun`: list the criteria and the command each one would run without executing anything, so a coordinator can review before spending the run.\n";
220882
221395
  }));
220883
221396
  // ../../packages/agent-core-v2/src/features/wave/tools/verify/verifyTool.ts
220884
221397
  var WaveVerifyTool;
@@ -220904,7 +221417,10 @@ var init_verifyTool = __esmMin((() => {
220904
221417
  approvalRule: this.name,
220905
221418
  execute: () => Promise.resolve(runWaveVerifyForRoot({
220906
221419
  root: args.root ?? this.sessionContext.cwd,
220907
- task: args.task
221420
+ task: args.task,
221421
+ ac: args.ac,
221422
+ dryRun: args.dryRun,
221423
+ allowDeclared: args.allowDeclared
220908
221424
  }))
220909
221425
  };
220910
221426
  }
@@ -220995,7 +221511,7 @@ var init_flag$2 = __esmMin((() => {
220995
221511
  init_wave$3();
220996
221512
  init_waveFeature();
220997
221513
  waveFlag = {
220998
- id: WAVE_FLAG_ID$1,
221514
+ id: WAVE_FLAG_ID,
220999
221515
  title: "Wave workflow",
221000
221516
  description: "Enable the wave workflow: adversarial design rounds with isolated seats, the package gate, and task verification tools.",
221001
221517
  env: WAVE_FLAG_ENV,
@@ -394734,10 +395250,6 @@ function swarmArgumentCompletions(argumentPrefix) {
394734
395250
  function towerArgumentCompletions(argumentPrefix) {
394735
395251
  return completeLeadingArg(TOWER_ARG_COMPLETIONS, argumentPrefix);
394736
395252
  }
394737
- /** Argument autocompletion for the `/wave` command (subcommands). */
394738
- function waveArgumentCompletions(argumentPrefix) {
394739
- return completeLeadingArg(WAVE_ARG_COMPLETIONS, argumentPrefix);
394740
- }
394741
395253
  /** Argument autocompletion for the `/add-dir` command. */
394742
395254
  function addDirArgumentCompletions(argumentPrefix) {
394743
395255
  if (isPathLikeAddDirArgument(argumentPrefix)) return completeAddDirPath(argumentPrefix);
@@ -394817,7 +395329,7 @@ function resolveSlashCommandAvailability(command, args) {
394817
395329
  function sortSlashCommands(commands) {
394818
395330
  return [...commands].toSorted((a, b) => (b.priority ?? 0) - (a.priority ?? 0) || a.name.localeCompare(b.name));
394819
395331
  }
394820
- var GOAL_ARG_COMPLETIONS, GOAL_NEXT_ARG_COMPLETIONS, SWARM_ARG_COMPLETIONS, TOWER_ARG_COMPLETIONS, WAVE_ARG_COMPLETIONS, ADD_DIR_ARG_COMPLETIONS, BUILTIN_SLASH_COMMANDS;
395332
+ var GOAL_ARG_COMPLETIONS, GOAL_NEXT_ARG_COMPLETIONS, SWARM_ARG_COMPLETIONS, TOWER_ARG_COMPLETIONS, ADD_DIR_ARG_COMPLETIONS, BUILTIN_SLASH_COMMANDS;
394821
395333
  var init_registry$2 = __esmMin((() => {
394822
395334
  init_dist$5();
394823
395335
  init_complete_args();
@@ -394876,20 +395388,6 @@ var init_registry$2 = __esmMin((() => {
394876
395388
  description: "Turn tower mode off"
394877
395389
  }
394878
395390
  ];
394879
- WAVE_ARG_COMPLETIONS = [
394880
- {
394881
- value: "on",
394882
- description: "Enable the wave experiment"
394883
- },
394884
- {
394885
- value: "off",
394886
- description: "Disable the wave experiment"
394887
- },
394888
- {
394889
- value: "status",
394890
- description: "Report whether the wave experiment is on"
394891
- }
394892
- ];
394893
395391
  ADD_DIR_ARG_COMPLETIONS = [{
394894
395392
  value: "list",
394895
395393
  description: "Show configured additional workspace directories"
@@ -394954,8 +395452,7 @@ var init_registry$2 = __esmMin((() => {
394954
395452
  aliases: [],
394955
395453
  description: "Run a design wave: brief, adversarial swarm, gate and execution",
394956
395454
  priority: 90,
394957
- argumentHint: "[on|off|status] | <objetivo de la oleada>",
394958
- completeArgs: waveArgumentCompletions,
395455
+ argumentHint: "<objetivo de la oleada>",
394959
395456
  availability: "idle-only",
394960
395457
  experimentalFlag: "wave"
394961
395458
  },
@@ -477583,74 +478080,24 @@ function buildWavePrompt(args) {
477583
478080
  "",
477584
478081
  "Reglas de la oleada:",
477585
478082
  "- Usa las tools Wave: `WaveOpen` para abrirla, `WaveRound` para obtener el prompt de cada asiento, `WaveSubmit` para que cada asiento entregue su artefacto, `WaveDecide` para el quórum, `WaveGate` para el paquete y `WaveVerify` para cerrar tasks.",
477586
- "- Lanza un agente por asiento con `Agent` (o `AgentSwarm` si el usuario activó swarm) usando el prompt que devuelve `WaveRound`. No inventes el prompt ni cambies el rol.",
477587
- "- Un asiento nunca escribe con Write/Edit: entrega con `WaveSubmit`, que ata rol y ruta.",
478083
+ "- Lanza un agente por asiento con `Agent` (o `AgentSwarm` si el usuario activó swarm) pasando `subagent_type: \"wave-seat\"` y el prompt que devuelve `WaveRound`. No inventes el prompt ni cambies el rol.",
478084
+ "- El asiento es de solo lectura: `Read`, `Glob`, `Grep` y `WaveSubmit`. Nunca escribe con Write/Edit: entrega con `WaveSubmit`, que ata rol y ruta.",
477588
478085
  "- Si el brief no tiene las siete secciones, para y pide que se rellenen; no lo escribas tú.",
477589
478086
  "- Sin `adopt` de `WaveDecide`, lanza `refine` y después `audit`; con dos rondas sin quórum, adopta R1 con disidencia escrita.",
477590
478087
  "- La oleada no está cerrada hasta que `WaveGate` sale en verde. Si sale en rojo, dilo con los códigos de hallazgo."
477591
478088
  ].join("\n");
477592
478089
  }
477593
478090
  async function handleWaveCommand(host, args) {
477594
- const input = args.trim();
477595
- const sub = input.toLowerCase();
477596
- if (sub === "on" || sub === "off") {
477597
- await setWaveExperiment(host, sub === "on");
477598
- return;
477599
- }
477600
- if (sub === "" || sub === "status") {
477601
- await reportWaveStatus(host, sub === "");
477602
- return;
477603
- }
477604
- if (sub === "help") {
478091
+ const objective = args.trim();
478092
+ if (objective === "") {
477605
478093
  host.showNotice("Wave", WAVE_USAGE);
477606
478094
  return;
477607
478095
  }
477608
- host.sendNormalUserInput(buildWavePrompt(input));
478096
+ host.sendNormalUserInput(buildWavePrompt(objective));
477609
478097
  }
477610
- async function reportWaveStatus(host, asUsage) {
477611
- let enabled;
477612
- try {
477613
- enabled = (await host.harness.getExperimentalFeatures()).find((feature) => feature.id === WAVE_FLAG_ID)?.enabled === true;
477614
- } catch (error) {
477615
- host.showError(`Failed to read the wave experiment state: ${formatErrorMessage$2(error)}`);
477616
- return;
477617
- }
477618
- if (asUsage) {
477619
- host.showNotice(`Wave experiment: ${enabled ? "ON" : "OFF"}`, `${WAVE_USAGE}\n\n${enabled ? "Las tools Wave están disponibles en esta sesión." : "Actívalo con `/wave on`; no hace falta editar config.toml ni reiniciar."}`);
477620
- return;
477621
- }
477622
- host.showStatus(enabled ? "Wave experiment is on." : "Wave experiment is off. Run `/wave on` to enable it without a restart.");
477623
- }
477624
- async function setWaveExperiment(host, enabled) {
477625
- try {
477626
- await host.harness.setConfig({ experimental: { [WAVE_FLAG_ID]: enabled } });
477627
- setExperimentalFeatures(await host.harness.getExperimentalFeatures());
477628
- host.refreshSlashCommandAutocomplete();
477629
- } catch (error) {
477630
- host.showError(`Failed to ${enabled ? "enable" : "disable"} the wave experiment: ${formatErrorMessage$2(error)}`);
477631
- return;
477632
- }
477633
- if (!enabled) {
477634
- host.showStatus("Wave experiment off. `/wave` is hidden until you turn it back on.");
477635
- return;
477636
- }
477637
- if (host.session === void 0) {
477638
- host.showStatus("Wave experiment on. `/wave <objetivo>` is ready.", "success");
477639
- return;
477640
- }
477641
- try {
477642
- const reloadedSession = await host.harness.reloadSession({ id: host.session.id });
477643
- await host.reloadCurrentSessionView(reloadedSession, "Wave experiment on. Session reloaded — `/wave <objetivo>` is ready.");
477644
- } catch (error) {
477645
- host.showError(`Wave experiment is on, but the session could not be reloaded: ${formatErrorMessage$2(error)}`);
477646
- }
477647
- }
477648
- var WAVE_USAGE, WAVE_FLAG_ID;
478098
+ var WAVE_USAGE;
477649
478099
  var init_wave = __esmMin((() => {
477650
- init_event_payload();
477651
- init_experimental_flags();
477652
- WAVE_USAGE = "Uso: /wave <objetivo de la oleada> — abre una oleada de diseño, lanza el swarm adversarial y cierra con el gate. Los subcomandos `/wave on`, `/wave off` y `/wave status` activan o consultan el experimento.";
477653
- WAVE_FLAG_ID = "wave";
478100
+ WAVE_USAGE = "Uso: /wave <objetivo de la oleada> — abre una oleada de diseño, lanza el swarm adversarial y cierra con el gate. Requiere NEXGEN_EXPERIMENTAL_WAVE.";
477654
478101
  }));
477655
478102
  // src/tui/commands/tower.ts
477656
478103
  async function handleTowerCommand(host, args) {
@@ -484747,7 +485194,6 @@ var init_commands = __esmMin((() => {
484747
485194
  init_config();
484748
485195
  init_swarm();
484749
485196
  init_tower();
484750
- init_wave();
484751
485197
  init_info();
484752
485198
  init_plugins();
484753
485199
  init_reload();