@andresmassello/uscha 1.53.0 → 1.55.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.
@@ -2419,9 +2419,9 @@ def cmd_flag_blocker(args):
2419
2419
  raise SystemExit("[qa_ledger] flag-blocker requires --note describing the breach.")
2420
2420
  if args.resolve and not args.escape_analysis:
2421
2421
  raise SystemExit(
2422
- "[qa_ledger] resolver un BLOCKER exige --escape-analysis: ¿que gate/test "
2423
- "debio atraparlo y que se hizo al respecto (test nuevo / gate nuevo / "
2424
- "busqueda de hermanos)? Encontrar cada bug UNA sola vez es la regla.")
2422
+ "[qa_ledger] resolving a BLOCKER requires --escape-analysis: which gate/test "
2423
+ "should have caught it, and what was done about it (new test / new gate / "
2424
+ "sibling hunt)? Finding each bug ONCE is the rule.")
2425
2425
  rec = _append_gate_record(ledger, node, args.repo, tool, args.iteration,
2426
2426
  failing, 1, args.note)
2427
2427
  if args.resolve:
@@ -2432,7 +2432,7 @@ def cmd_flag_blocker(args):
2432
2432
  f"(readiness capped <=65, convergence blocked until --resolve)")
2433
2433
  else:
2434
2434
  print(f"[qa_ledger] {args.repo}/{tool}: resolved — gate cleared "
2435
- f"(step #{rec['n']}, escape analysis registrada)")
2435
+ f"(step #{rec['n']}, escape analysis recorded)")
2436
2436
 
2437
2437
 
2438
2438
  def _converged(node, k, qa_order=None):
@@ -2595,16 +2595,16 @@ def cmd_phase(args):
2595
2595
  "spike_branch": spike, "satisfied": ok},
2596
2596
  indent=2, ensure_ascii=False))
2597
2597
  sys.exit(0 if ok else 1)
2598
- print(f"PHASE {args.repo}: {state} (derivado del ledger — no declarado)")
2598
+ print(f"PHASE {args.repo}: {state} (derived from the ledger — not declared)")
2599
2599
  for e in evidence:
2600
2600
  print(f" · {e}")
2601
2601
  if spike:
2602
- print(f" ! rama '{spike}': codigo de SPIKE, descartable por contrato "
2603
- f"(Tip 21) — el output legitimo es un ADR con las lecciones, "
2604
- f"jamas un PR. Escribi el ADR y arranca limpio en una rama normal.")
2602
+ print(f" ! branch '{spike}': SPIKE code, disposable by contract "
2603
+ f"(Tip 21) — its legitimate output is an ADR with the lessons, "
2604
+ f"never a PR. Write the ADR and start clean on a normal branch.")
2605
2605
  elif args.require and not ok:
2606
- print(f" ! se requiere '{args.require}' y los HECHOS dicen '{state}' — "
2607
- f"el estado no se negocia, se construye")
2606
+ print(f" ! '{args.require}' is required and the FACTS say '{state}' — "
2607
+ f"state is not negotiated, it is built")
2608
2608
  sys.exit(0 if ok else 1)
2609
2609
 
2610
2610
 
@@ -4003,34 +4003,34 @@ def cmd_readiness(args):
4003
4003
  # sin ella no hay % honesto que mostrar. Informativo: jamas gatea (kit 1.28.0).
4004
4004
  if acc_traceable and total:
4005
4005
  print(f" acceptance medido: {out['acceptance']['measured_pct']}% "
4006
- f"({len(measured_closed)}/{total} criterios cerrados por test verde "
4007
- f"— medido, no gatea)")
4006
+ f"({len(measured_closed)}/{total} criteria closed by a green test "
4007
+ f"— measured, does not gate)")
4008
4008
  if not acc_found:
4009
4009
  print(f" ! acceptance file not found: {acc_path or '(unset)'} — ADR dimension = 0")
4010
4010
  if acc_found and not total:
4011
- print(f" ! acceptance encontrado pero 0 criterios en scope "
4012
- f"(--section {args.section!r} sin match en el archivo?) — "
4013
- f"dimensiones adr/acceptance en 0")
4011
+ print(f" ! acceptance found but 0 criteria in scope "
4012
+ f"(--section {args.section!r} matched nothing in the file?) — "
4013
+ f"adr/acceptance dimensions at 0")
4014
4014
  if acc_found and total and not acc_traceable:
4015
- print(" ! acceptance sin IDs trazables ('- [ ] AC-01 — ...') — la "
4016
- "dimension acceptance cae al ratio de checkboxes (RELATO, no medido)")
4015
+ print(" ! acceptance has no traceable IDs ('- [ ] AC-01 — ...') — the "
4016
+ "acceptance dimension falls back to the checkbox ratio (NARRATED, not measured)")
4017
4017
  if dupe_ids:
4018
- print(f" ! IDs duplicados en acceptance (normalizados): {', '.join(dupe_ids)} "
4019
- f"— cada ID cuenta UNA sola vez en la dimension acceptance")
4018
+ print(f" ! duplicate IDs in acceptance (normalized): {', '.join(dupe_ids)} "
4019
+ f"— each ID counts ONCE in the acceptance dimension")
4020
4020
  if legacy_weights_cfg and not acc_traceable:
4021
- print(" ! config con readiness_weights explicitos de kit <=1.9.0 sin "
4022
- "'acceptance' — dimension excluida (peso 0) hasta agregar AC-IDs "
4023
- "o el peso explicito en config.defaults.readiness_weights")
4021
+ print(" ! config carries explicit readiness_weights from kit <=1.9.0 without "
4022
+ "'acceptance' — dimension excluded (weight 0) until you add AC-IDs "
4023
+ "or the explicit weight in config.defaults.readiness_weights")
4024
4024
  if narrated_only:
4025
- print(f" ! narrated-only: {', '.join(narrated_only)} — checkbox marcado "
4026
- f"SIN testcase verde 'AC-n' en los reportes (measured beats "
4027
- f"narrated: NO cierra)")
4025
+ print(f" ! narrated-only: {', '.join(narrated_only)} — checkbox ticked "
4026
+ f"WITHOUT a green 'AC-n' testcase in the reports (measured beats "
4027
+ f"narrated: does NOT close)")
4028
4028
  if measured_unchecked:
4029
- print(f" · medido sin marcar: {', '.join(measured_unchecked)} — hay "
4030
- f"testcase verde; marca el checkbox si el criterio esta completo")
4029
+ print(f" · measured but unticked: {', '.join(measured_unchecked)} — there is "
4030
+ f"a green testcase; tick the checkbox if the criterion is done")
4031
4031
  if acc_traceable and ac_untagged:
4032
- print(f" · {ac_untagged} criterio(s) sin AC-ID — no pueden cerrar "
4033
- f"MEDIDO (cuentan como abiertos en la dimension acceptance)")
4032
+ print(f" · {ac_untagged} criterion(s) without an AC-ID — cannot close "
4033
+ f"MEASURED (they count as open in the acceptance dimension)")
4034
4034
  if unmeasured_repos:
4035
4035
  print(f" ! static gate NEVER ran in: {', '.join(unmeasured_repos)} — "
4036
4036
  f"dimension scored UNMEASURED (0.0), silence is not success")
@@ -4045,42 +4045,42 @@ def cmd_readiness(args):
4045
4045
  f"measured 0%. Instrument it, or DECLARE the exemption with "
4046
4046
  f"defaults.readiness_weights.coverage = 0 (config = requirement)")
4047
4047
  if stale_reports:
4048
- print(f" ! {len(stale_reports)} reporte(s) JUnit STALE descartados "
4049
- f"(codigo mas nuevo que la evidencia) — los AC que dependian solo "
4050
- f"de ellos quedan UNMEASURED; re-corre los tests")
4048
+ print(f" ! {len(stale_reports)} STALE JUnit report(s) discarded "
4049
+ f"(code newer than the evidence) — the ACs that relied only "
4050
+ f"on them go UNMEASURED; re-run the tests")
4051
4051
  if production_open:
4052
4052
  labels = ", ".join(f"{p.get('id')}({p.get('severity')})" for p in production_open[:3])
4053
- more = "" if len(production_open) <= 3 else f" +{len(production_open)-3} mas"
4054
- print(f" ! production findings open: {labels}{more} -- feedback real de "
4055
- f"produccion; reabrir discovery/SPEC en el proximo ciclo")
4053
+ more = "" if len(production_open) <= 3 else f" +{len(production_open)-3} more"
4054
+ print(f" ! production findings open: {labels}{more} -- real field "
4055
+ f"feedback; reopen discovery/SPEC in the next cycle")
4056
4056
  if spec_doubts_open:
4057
4057
  labels = ", ".join(f"{s.get('id')}({s.get('kind')}/{s.get('severity')})"
4058
4058
  for s in spec_doubts_open[:3])
4059
- more = "" if len(spec_doubts_open) <= 3 else f" +{len(spec_doubts_open)-3} mas"
4060
- print(f" ! spec-doubt open: {labels}{more} -- no codifiques alrededor "
4061
- f"de una SPEC dudosa; requiere revision humana")
4059
+ more = "" if len(spec_doubts_open) <= 3 else f" +{len(spec_doubts_open)-3} more"
4060
+ print(f" ! spec-doubt open: {labels}{more} -- do not code around a "
4061
+ f"doubtful SPEC; this needs human review")
4062
4062
  if spec_change_requests_open:
4063
4063
  labels = ", ".join(f"{r.get('id')}({r.get('source')})"
4064
4064
  for r in spec_change_requests_open[:3])
4065
- more = "" if len(spec_change_requests_open) <= 3 else f" +{len(spec_change_requests_open)-3} mas"
4066
- print(f" ! spec-change-request open: {labels}{more} -- el contrato "
4067
- f"requiere decision humana y SPEC/ADR amended")
4065
+ more = "" if len(spec_change_requests_open) <= 3 else f" +{len(spec_change_requests_open)-3} more"
4066
+ print(f" ! spec-change-request open: {labels}{more} -- the contract "
4067
+ f"needs a human decision and an amended SPEC/ADR")
4068
4068
  if stalled_repos:
4069
- print(f" ! stall: {', '.join(stalled_repos)} — findings gateados planos o "
4070
- f"subiendo {STALL_WINDOW} ciclos: iterar mas no esta acercando la "
4071
- f"solucion. Probable problema de diseño/SPEC — volver a ADR / "
4072
- f"re-planear con el humano (advisory)")
4069
+ print(f" ! stall: {', '.join(stalled_repos)} — gated findings flat or "
4070
+ f"rising for {STALL_WINDOW} cycles: iterating more is not getting "
4071
+ f"closer. Likely a design/SPEC problem go back to the ADR / "
4072
+ f"re-plan with the human (advisory)")
4073
4073
  # rubrica (1.23.0): el ultimo grade por repo, siempre visible — guess
4074
4074
  # estructurado que aconseja; si esta gateado ya bloqueo por el ledger
4075
4075
  for rname, rnode in ledger["repos"].items():
4076
4076
  rub = _latest_static_by_tool(rnode).get("rubric:grade")
4077
4077
  if rub:
4078
4078
  m_ = "!" if rub.get("gated_reported") else "·"
4079
- print(f" {m_} rubrica {rname}: {rub.get('note', 's/d')}")
4079
+ print(f" {m_} rubric {rname}: {rub.get('note', 'n/a')}")
4080
4080
  if stop_signal:
4081
- print(" · stop-signal: todos los repos convergieron y no queda ningun "
4082
- "fact bloqueante lo que falta es deuda medible (coverage/"
4083
- "acceptance), no findings: candidato a cortar e ir a PR (advisory)")
4081
+ print(" · stop-signal: every repo converged and no blocking fact "
4082
+ "remainswhat is left is measurable debt (coverage/"
4083
+ "acceptance), not findings: a candidate to stop and go to PR (advisory)")
4084
4084
  # veredicto unico (1.25.0, anti-ceremonia): los gates persistidos se colapsan a
4085
4085
  # UNA linea bajo el KPI. El detalle (dimensiones, acceptance, churn, by-repo) es
4086
4086
  # ruido de auditoria en el caso normal -> detras de --verbose. Las lineas de
@@ -4089,12 +4089,12 @@ def cmd_readiness(args):
4089
4089
  if gate_roll:
4090
4090
  blocking = [g for g in gate_roll if g["blocking"]]
4091
4091
  n_ok = len(gate_roll) - len(blocking)
4092
- hint = "" if args.verbose else " (readiness --verbose para el detalle)"
4092
+ hint = "" if args.verbose else " (readiness --verbose for the detail)"
4093
4093
  if blocking:
4094
4094
  names = ", ".join(f"{g['repo']}/{g['tool']}" for g in blocking)
4095
- print(f"--- gates: {n_ok} ok · {len(blocking)} bloqueando ({names}){hint}")
4095
+ print(f"--- gates: {n_ok} ok · {len(blocking)} blocking ({names}){hint}")
4096
4096
  else:
4097
- print(f"--- gates: {n_ok} ok, ninguno bloqueando{hint}")
4097
+ print(f"--- gates: {n_ok} ok, none blocking{hint}")
4098
4098
  if not args.verbose:
4099
4099
  return
4100
4100
  print("--- dimensions (weight | raw | contribution) ---")
@@ -4643,7 +4643,7 @@ def cmd_simplicity_check(args):
4643
4643
  sys.exit(0 if verdict != "OVERBUILT" else 1)
4644
4644
 
4645
4645
  print(f"SIMPLICITY: {score}/100 — {verdict}")
4646
- print("--- métricas (valor / presupuesto · * = declarado por el humano) ---")
4646
+ print("--- metrics (value / budget · * = declared by the human) ---")
4647
4647
  rows = [
4648
4648
  ("lines_added", m["lines_added"], b["max_lines_added"], "max_lines_added"),
4649
4649
  ("net_lines", m["net_lines"], b["max_net_lines"], "max_net_lines"),
@@ -4657,21 +4657,21 @@ def cmd_simplicity_check(args):
4657
4657
  mark = "*" if key in declared else ""
4658
4658
  print(f" {name:17s} {str(val):>7s} / {bud}{mark}")
4659
4659
  if not declared:
4660
- print(" (todos los presupuestos son defaults del kit — opinion, no "
4661
- "requerimiento: declara los tuyos en config.defaults.simplicity)")
4662
- print(f" (new_functions: {m['new_functions']} — informativo, no gateado)")
4660
+ print(" (every budget is a kit defaultan opinion, not a "
4661
+ "requirement: declare yours in config.defaults.simplicity)")
4662
+ print(f" (new_functions: {m['new_functions']} — informational, not gated)")
4663
4663
  if m.get("test_files_changed"):
4664
- print(f" (tests FUERA del presupuesto: +{m['test_lines_added']} lineas "
4665
- f"en {m['test_files_changed']} archivo(s) de test escribir tests "
4666
- f"nunca penaliza este gate)")
4664
+ print(f" (tests OUTSIDE the budget: +{m['test_lines_added']} lines "
4665
+ f"in {m['test_files_changed']} test file(s) — writing tests "
4666
+ f"never penalizes this gate)")
4667
4667
  if m.get("files_skipped"):
4668
- print(f" ({m['files_skipped']} archivo(s) no-código salteados: docs/config/resources)")
4668
+ print(f" ({m['files_skipped']} non-code file(s) skipped: docs/config/resources)")
4669
4669
  if flags:
4670
- print("--- flags (Reduce: qué recortar) ---")
4670
+ print("--- flags (Reduce: what to cut) ---")
4671
4671
  for fl in flags:
4672
4672
  print(f" ! {fl}")
4673
4673
  else:
4674
- print(" dentro de presupuesto nada que recortar")
4674
+ print(" within budgetnothing to cut")
4675
4675
  sys.exit(0 if verdict != "OVERBUILT" else 1)
4676
4676
 
4677
4677
 
@@ -4955,23 +4955,23 @@ def cmd_waste_check(args):
4955
4955
  print(json.dumps(out, indent=2, ensure_ascii=False))
4956
4956
  sys.exit(exit_code)
4957
4957
 
4958
- mode = "gate declarado" if gate else "advisory (declara defaults.waste.gate para gatear)"
4958
+ mode = "declared gate" if gate else "advisory (declare defaults.waste.gate to make it block)"
4959
4959
  print(f"WASTE: {score}/100 — {verdict} ({mode})")
4960
- print(f"--- ventanas de {b['window_size']} lineas normalizadas sobre {m['sig_lines_added']} "
4961
- f"lineas agregadas ({m['added_windows']} ventana(s), {m['files_added_to']} archivo(s)) ---")
4960
+ print(f"--- windows of {b['window_size']} normalized lines over {m['sig_lines_added']} "
4961
+ f"added lines ({m['added_windows']} window(s), {m['files_added_to']} file(s)) ---")
4962
4962
  print(f" dup_vs_repo: {m['dup_windows_vs_repo']} (budget {b['max_dup_windows_vs_repo']}"
4963
4963
  f"{'*' if 'max_dup_windows_vs_repo' in declared else ''}) "
4964
- f"dup_interno: {m['dup_windows_internal']} dup_ratio: {m['dup_ratio']}")
4965
- print(f" (call_density: {m['call_density']}/100 — informativo, no puntua ni gatea)")
4964
+ f"dup_internal: {m['dup_windows_internal']} dup_ratio: {m['dup_ratio']}")
4965
+ print(f" (call_density: {m['call_density']}/100 — informational, neither scored nor gated)")
4966
4966
  if not declared:
4967
- print(" (todos los presupuestos son defaults del kit — opinion, no "
4968
- "requerimiento: declara los tuyos en config.defaults.waste)")
4967
+ print(" (every budget is a kit defaultan opinion, not a "
4968
+ "requirement: declare yours in config.defaults.waste)")
4969
4969
  if flags:
4970
- print("--- flags (REUSE-FIRST: que reusar en vez de clonar) ---")
4970
+ print("--- flags (REUSE-FIRST: what to reuse instead of cloning) ---")
4971
4971
  for fl in flags:
4972
4972
  print(f" ! {fl}")
4973
- print("--- honesto: proxy Type-1/Type-2 sobre lineas normalizadas, NO clones "
4974
- "semanticos (Type-3/4) ni CC por AST ---")
4973
+ print("--- honest: a Type-1/Type-2 proxy over normalized lines, NOT semantic "
4974
+ "clones (Type-3/4) nor AST-based CC ---")
4975
4975
  sys.exit(exit_code)
4976
4976
 
4977
4977
 
@@ -5003,17 +5003,17 @@ def cmd_pit_check(args):
5003
5003
 
5004
5004
  print(f"MUTATION: {m['mutation_score']}/100 — {verdict} "
5005
5005
  f"(gate {args.min_score}, test-strength {m['test_strength']})")
5006
- print("--- efectividad (NO es coverage: mide que los tests ASERTEN) ---")
5007
- print(f" mutantes {m['total']} · matados {m['killed']} · "
5008
- f"sobreviven {m['survived']} · sin cobertura {m['no_coverage']}")
5006
+ print("--- effectiveness (NOT coverage: it measures that the tests ASSERT) ---")
5007
+ print(f" mutants {m['total']} · killed {m['killed']} · "
5008
+ f"survived {m['survived']} · uncovered {m['no_coverage']}")
5009
5009
  if m.get("excluded"):
5010
5010
  print(f" ({m['excluded']} excluidos NON_VIABLE/RUN_ERROR — fuera del score, como PIT)")
5011
5011
  if hotspots:
5012
- print("--- hotspots (más mutantes vivos = tests débiles ahí) ---")
5012
+ print("--- hotspots (more surviving mutants = weaker tests there) ---")
5013
5013
  for f, c in hotspots:
5014
5014
  print(f" ! {f}: {c['survived']} sobreviven, {c['no_coverage']} sin cubrir")
5015
5015
  else:
5016
- print(" sin sobrevivienteslos tests atrapan las mutaciones")
5016
+ print(" no survivorsthe tests catch the mutations")
5017
5017
  if m["no_coverage"]:
5018
5018
  print(f" nota: {m['no_coverage']} mutante(s) sin NINGÚN test "
5019
5019
  f"(coverage-gap, distinto de assertion-gap)")
@@ -5261,26 +5261,26 @@ def cmd_gate_check(args):
5261
5261
  if items:
5262
5262
  uniq = sorted(set(items))
5263
5263
  tail = " ..." if len(uniq) > 5 else ""
5264
- print(f" ! {label}: {len(items)} en {', '.join(uniq[:5])}{tail}")
5264
+ print(f" ! {label}: {len(items)} in {', '.join(uniq[:5])}{tail}")
5265
5265
 
5266
- _show("tests borrados (BLOCKER)", removed_tests)
5267
- _show("tests deshabilitados (BLOCKER)", disabled_tests)
5266
+ _show("tests deleted (BLOCKER)", removed_tests)
5267
+ _show("tests disabled (BLOCKER)", disabled_tests)
5268
5268
  if thresholds:
5269
- print(f" ! thresholds bajados/borrados (BLOCKER): {'; '.join(thresholds)}")
5269
+ print(f" ! thresholds lowered/removed (BLOCKER): {'; '.join(thresholds)}")
5270
5270
  if secrets:
5271
- print(f" ! secretos agregados (BLOCKER): {'; '.join(sorted(set(secrets)))}")
5272
- _show("literales tipo password/token agregados (revisar)", secret_literals)
5273
- _show("reglas de scrub del golden editadas (revisar: pueden enmascarar divergencia)", scrub_edits)
5274
- _show("supresiones de lint agregadas (revisar)", suppressions)
5275
- _show("dependencias nuevas (revisar: 0 deps nuevas sin aprobacion)", new_deps)
5271
+ print(f" ! secrets added (BLOCKER): {'; '.join(sorted(set(secrets)))}")
5272
+ _show("password/token-shaped literals added (review)", secret_literals)
5273
+ _show("golden scrub rules edited (review: they can mask divergence)", scrub_edits)
5274
+ _show("lint suppressions added (review)", suppressions)
5275
+ _show("new dependencies (review: 0 new deps without approval)", new_deps)
5276
5276
  if assertions_removed:
5277
- print(f" ~ asserts removidos en tests: {assertions_removed} (revisar)")
5277
+ print(f" ~ asserts removed from tests: {assertions_removed} (review)")
5278
5278
  if test_count_drop:
5279
- print(f" ~ conteo de tests ejecutados cayó: {test_count_drop} (medido en snapshots — revisar)")
5279
+ print(f" ~ executed-test count dropped: {test_count_drop} (measured in snapshots — review)")
5280
5280
  if verdict == "CLEAN":
5281
- print(" el cambio no debilita el aparato de medición")
5281
+ print(" the change does not weaken the measuring apparatus")
5282
5282
  elif not blocker:
5283
- print(" señales blandas: requieren justificación (--strict para gatear)")
5283
+ print(" soft signals: they require justification (--strict to gate)")
5284
5284
  sys.exit(1 if blocker else 0)
5285
5285
 
5286
5286
 
@@ -5359,20 +5359,20 @@ def cmd_regression_check(args):
5359
5359
  "has_assertion": has_assert},
5360
5360
  indent=2, ensure_ascii=False))
5361
5361
  sys.exit(1 if fail else 0)
5362
- print(f"REGRESSION-CAPTURE: {verdict} ({fixed} finding(s) cerrados · "
5363
- f"+{test_added} lineas de test en {len(test_files)} archivo(s))")
5362
+ print(f"REGRESSION-CAPTURE: {verdict} ({fixed} finding(s) closed · "
5363
+ f"+{test_added} test lines in {len(test_files)} file(s))")
5364
5364
  if verdict == "NARRATED":
5365
- print(" ! cierre NARRADO: desaparecieron findings sin tocar un solo test — "
5366
- "¿que test reproduce el bug que decis haber arreglado? (Tip 31: el "
5367
- "test que falla va ANTES del fix; Tip 94: cada bug se encuentra UNA vez)")
5365
+ print(" ! NARRATED close: findings vanished without touching a single test — "
5366
+ "which test reproduces the bug you say you fixed? (Tip 31: the "
5367
+ "failing test comes BEFORE the fix; Tip 94: each bug is found ONCE)")
5368
5368
  elif verdict == "MEASURED":
5369
- print(" cierre con evidencia: el diff que arregla trae tests")
5369
+ print(" close with evidence: the fixing diff brings tests")
5370
5370
  if not (has_testdef or has_assert):
5371
- print(" · evidencia DEBIL: las lineas de test agregadas no traen ni "
5372
- "definicion de test ni assert — el tripwire no juzga calidad "
5373
- "(eso es pit-check), pero esto merece un ojo humano")
5371
+ print(" · WEAK evidence: the added test lines carry neither a test "
5372
+ "definition nor an assert — this tripwire does not judge quality "
5373
+ "(that is pit-check), but it deserves a human eye")
5374
5374
  else:
5375
- print(" nada cerrado en la ultima iteracionnada que exigir")
5375
+ print(" nothing closed in the last iterationnothing to demand")
5376
5376
  sys.exit(1 if fail else 0)
5377
5377
 
5378
5378
 
@@ -5592,7 +5592,7 @@ def cmd_rubric_ingest(args):
5592
5592
  blockers = _rubric_structure(rubric_path)
5593
5593
  if blockers:
5594
5594
  for b in blockers:
5595
- print(f"[qa_ledger] rubrica invalida: {b}", file=sys.stderr)
5595
+ print(f"[qa_ledger] invalid rubric: {b}", file=sys.stderr)
5596
5596
  sys.exit(2)
5597
5597
  items, threshold, _found = _parse_rubric(rubric_path)
5598
5598
  by_id = {i["id"]: i for i in items}
@@ -5673,15 +5673,15 @@ def cmd_rubric_ingest(args):
5673
5673
  sys.exit(1 if failing else 0)
5674
5674
  print(f"RUBRIC: {'PASS' if passed else 'BELOW'} {note}")
5675
5675
  if failed_pos:
5676
- print(f" ! criterios sin cerrar: {', '.join(sorted(failed_pos))}")
5676
+ print(f" ! criteria not closed: {', '.join(sorted(failed_pos))}")
5677
5677
  if unsupported:
5678
- print(f" ! veredictos SIN evidencia (no puntuan): {', '.join(sorted(unsupported))} "
5679
- f"— evidence-or-nothing, el juicio sin cita no cuenta")
5678
+ print(f" ! verdicts WITHOUT evidence (they do not score): {', '.join(sorted(unsupported))} "
5679
+ f"— evidence-or-nothing, a judgement without a citation does not count")
5680
5680
  if failing:
5681
- print(" el gate esta DECLARADO (config/--gate): bloquea convergencia y "
5682
- "capea readiness <=65 hasta un grade limpio")
5681
+ print(" the gate is DECLARED (config/--gate): it blocks convergence and "
5682
+ "caps readiness <=65 until a clean grade")
5683
5683
  elif not passed:
5684
- print(" advisory: no gateadeclaralo en defaults.rubric.gate para que bloquee")
5684
+ print(" advisory: it does not gate declare it in defaults.rubric.gate to make it block")
5685
5685
  sys.exit(1 if failing else 0)
5686
5686
 
5687
5687
 
@@ -5740,13 +5740,13 @@ def cmd_spec_check(args):
5740
5740
  print(f" · {c[:80]}")
5741
5741
  if m["stack_hits"]:
5742
5742
  terms = sorted({t for _, t in m["stack_hits"]})
5743
- print(f" ~ stack nombrado en criterios ({len(m['stack_hits'])}): {', '.join(terms[:6])} — el 'cómo' va al ADR, no al SPEC")
5743
+ print(f" ~ stack named in criteria ({len(m['stack_hits'])}): {', '.join(terms[:6])} — the 'how' belongs in the ADR, not the SPEC")
5744
5744
  if m["non_ears"] and m["n_criteria"]:
5745
- print(f" · advisory: {m['non_ears']}/{m['n_criteria']} criterios no siguen patrón EARS (When/If/While … shall)")
5746
- print(" i consistencia: INFERENCIAL (checker no-correlacionado), no este lint · "
5747
- "estructura = FACT (bloquea) · prosa = advisory (--strict para gatear)")
5745
+ print(f" · advisory: {m['non_ears']}/{m['n_criteria']} criteria do not follow the EARS pattern (When/If/While … shall)")
5746
+ print(" i consistency: INFERENTIAL (an uncorrelated checker), not this lint · "
5747
+ "structure = FACT (blocks) · prose = advisory (--strict to gate)")
5748
5748
  if verdict == "OK":
5749
- print(" estructura completa y criterios testables")
5749
+ print(" structure complete and criteria testable")
5750
5750
  sys.exit(1 if fail else 0)
5751
5751
 
5752
5752
 
@@ -5796,8 +5796,8 @@ def _load_scrub_rules(root):
5796
5796
  rules.append((re.compile(r["pattern"]), r["replace"], r["pattern"]))
5797
5797
  return rules
5798
5798
  except (json.JSONDecodeError, re.error, KeyError, TypeError) as exc:
5799
- print(f"[qa_ledger] {path} invalido ({exc}) — el scrub no se saltea en "
5800
- f"silencio: arregla el archivo o borralo.", file=sys.stderr)
5799
+ print(f"[qa_ledger] {path} invalid ({exc}) — the scrub is not skipped in "
5800
+ f"silence: fix the file or delete it.", file=sys.stderr)
5801
5801
  sys.exit(2)
5802
5802
 
5803
5803
 
@@ -5829,7 +5829,7 @@ def _load_golden_labels(path):
5829
5829
  with open(path, "r", encoding="utf-8") as fh:
5830
5830
  raw = json.load(fh)
5831
5831
  except (OSError, json.JSONDecodeError) as exc:
5832
- print(f"[qa_ledger] golden labels invalidos ({exc})", file=sys.stderr)
5832
+ print(f"[qa_ledger] invalid golden labels ({exc})", file=sys.stderr)
5833
5833
  sys.exit(2)
5834
5834
  fixtures = raw.get("fixtures", raw) if isinstance(raw, dict) else None
5835
5835
  if not isinstance(fixtures, dict):
@@ -5914,7 +5914,7 @@ def cmd_golden_diff(args):
5914
5914
  ab = fa.read()
5915
5915
  except OSError as exc:
5916
5916
  fixture["result"] = "read_error"
5917
- diverged.append((rec, f"no se pudo leer: {exc}"))
5917
+ diverged.append((rec, f"could not read: {exc}"))
5918
5918
  continue
5919
5919
  if rb == ab:
5920
5920
  fixture["result"] = "matched"
@@ -5941,8 +5941,8 @@ def cmd_golden_diff(args):
5941
5941
  "note": "no .received fixtures found — run the capture first"},
5942
5942
  indent=2, ensure_ascii=False))
5943
5943
  else:
5944
- print("GOLDEN-DIFF: NOT-RUN — sin fixtures .received, nada que comparar "
5945
- "(corré la captura primero; ausencia NO es verde)")
5944
+ print("GOLDEN-DIFF: NOT-RUN — no .received fixtures, nothing to compare "
5945
+ "(run the capture first; absence is NOT green)")
5946
5946
  sys.exit(2)
5947
5947
  verdict = "CLEAN" if passed else "DIVERGE"
5948
5948
  if args.json:
@@ -5957,20 +5957,20 @@ def cmd_golden_diff(args):
5957
5957
  }, indent=2, ensure_ascii=False))
5958
5958
  sys.exit(0 if passed else 1)
5959
5959
  scrub_str = f" · {matched_scrubbed} via scrub" if matched_scrubbed else ""
5960
- print(f"GOLDEN-DIFF: {verdict} ({matched} matchean{scrub_str} · {len(diverged)} divergen)")
5960
+ print(f"GOLDEN-DIFF: {verdict} ({matched} match{scrub_str} · {len(diverged)} diverge)")
5961
5961
  if rules:
5962
5962
  subs = sum(scrub_counts.values())
5963
- print(f" · scrub activo: {len(rules)} regla(s) de {GOLDEN_SCRUB_FILE}, "
5964
- f"{subs} sustitucion(es) — el masking es visible, no magia")
5963
+ print(f" · scrub active: {len(rules)} rule(s) from {GOLDEN_SCRUB_FILE}, "
5964
+ f"{subs} substitution(s) — the masking is visible, not magic")
5965
5965
  for f, r in diverged[:12]:
5966
5966
  print(f" ! {f}: {r}")
5967
5967
  if len(diverged) > 12:
5968
- print(f" ... y {len(diverged) - 12} más")
5968
+ print(f" ... and {len(diverged) - 12} more")
5969
5969
  if passed:
5970
- print(" el comportamiento matchea el golden aprobado byte a byte"
5970
+ print(" behaviour matches the approved golden byte for byte"
5971
5971
  + (" (volatiles declarados enmascarados)" if matched_scrubbed else ""))
5972
5972
  else:
5973
- print(" el .approved es verdad de campo: si el diff es correcto, lo aprueba un HUMANOel agente no lo toca")
5973
+ print(" the .approved is field truth: if the diff is right a HUMAN approves itthe agent never touches it")
5974
5974
  sys.exit(0 if passed else 1)
5975
5975
 
5976
5976
 
@@ -6000,8 +6000,8 @@ DOCTOR_FIX = {
6000
6000
  "go": "https://go.dev/dl/",
6001
6001
  "cargo": "https://rustup.rs",
6002
6002
  "dotnet": "https://dotnet.microsoft.com/download",
6003
- "ctest": "https://cmake.org/download/ (ctest viene con CMake)",
6004
- "gradle": "https://gradle.org/install/ (o usa el gradlew del repo)",
6003
+ "ctest": "https://cmake.org/download/ (ctest ships with CMake)",
6004
+ "gradle": "https://gradle.org/install/ (or use the repo gradlew)",
6005
6005
  "swift": "https://www.swift.org/install/",
6006
6006
  }
6007
6007
  # Two valid hooks: the installer wires the portable .py; the plugin flow ships the .ps1.
@@ -6042,13 +6042,13 @@ def cmd_doctor(args):
6042
6042
  if py >= (3, 8):
6043
6043
  ok(f"Python {py.major}.{py.minor}.{py.micro} (>= 3.8)")
6044
6044
  else:
6045
- err(f"Python {py.major}.{py.minor} - el kit requiere 3.8+",
6046
- "instalar: https://www.python.org/downloads/")
6045
+ err(f"Python {py.major}.{py.minor} - the kit requires 3.8+",
6046
+ "install: https://www.python.org/downloads/")
6047
6047
  if shutil.which("git"):
6048
- ok("git en PATH")
6048
+ ok("git on PATH")
6049
6049
  else:
6050
- err("git no esta en PATH - gate-check/simplicity/regression lo usan (--from-git)",
6051
- "instalar: https://git-scm.com/downloads")
6050
+ err("git is not on PATH - gate-check/simplicity/regression use it (--from-git)",
6051
+ "install: https://git-scm.com/downloads")
6052
6052
 
6053
6053
  engine_dir = os.path.dirname(os.path.abspath(__file__))
6054
6054
  home_skills = os.path.join(os.path.expanduser("~"), ".claude", "skills")
@@ -6064,7 +6064,7 @@ def cmd_doctor(args):
6064
6064
  is_global = (not is_plugin) and _under(engine_dir, home_skills)
6065
6065
  mode = ("instalacion como PLUGIN (~/.claude/plugins, 1.24.0)" if is_plugin
6066
6066
  else "instalacion global (~/.claude/skills)" if is_global
6067
- else "instalacion por proyecto")
6067
+ else "per-project installation")
6068
6068
  ok(f"engine: {os.path.abspath(__file__)}", mode)
6069
6069
 
6070
6070
  # --- skills: hermanas del engine ---------------------------------------
@@ -6083,14 +6083,14 @@ def cmd_doctor(args):
6083
6083
  except OSError:
6084
6084
  missing.append(s)
6085
6085
  if not missing and not mismatched:
6086
- ok(f"skills {len(USCHA_SKILLS)}/{len(USCHA_SKILLS)} junto al engine",
6086
+ ok(f"skills {len(USCHA_SKILLS)}/{len(USCHA_SKILLS)} next to the engine",
6087
6087
  ", ".join(USCHA_SKILLS))
6088
6088
  else:
6089
6089
  if missing:
6090
6090
  err(f"skills faltantes en {skills_root}: {', '.join(missing)}",
6091
- "instalar: copia uscha-kit/.claude/skills/* a ~/.claude/skills/ "
6092
- "(README del kit > Instalacion, opcion B) - del zip uscha-kit-X.Y.Z "
6093
- "o de tu checkout del repo uscha")
6091
+ "install: copy uscha-kit/.claude/skills/* to ~/.claude/skills/ "
6092
+ "(kit README > Installation, option B) - from the uscha-kit-X.Y.Z zip "
6093
+ "or from your checkout of the uscha repo")
6094
6094
  if mismatched:
6095
6095
  err(f"SKILL.md con frontmatter name distinto al directorio: {', '.join(mismatched)}")
6096
6096
 
@@ -6115,18 +6115,18 @@ def cmd_doctor(args):
6115
6115
  needs_ps = bool(registered and registered.endswith(".ps1"))
6116
6116
  ps_ok = (not needs_ps) or bool(shutil.which("powershell") or shutil.which("pwsh"))
6117
6117
  if hook_file and registered and ps_ok:
6118
- ok("hook INV-GOLDEN-01: presente, registrado (PreToolUse) e interpretable",
6118
+ ok("hook INV-GOLDEN-01: present, registered (PreToolUse) and interpretable",
6119
6119
  f"{hook_file} ({registered})")
6120
6120
  elif not hook_file:
6121
- warn("hook INV-GOLDEN-01 no encontrado (~/.claude/hooks ni <kit>/hooks)",
6122
- f"instalar: `uscha install` copia {HOOK_NAMES[0]} a ~/.claude/hooks/ y lo "
6123
- f"registra en settings.json - sin el, el agente PUEDE escribir .approved "
6124
- f"(obligatorio para migraciones, perfil E)")
6121
+ warn("INV-GOLDEN-01 hook not found (neither ~/.claude/hooks nor <kit>/hooks)",
6122
+ f"install: `uscha install` copies {HOOK_NAMES[0]} to ~/.claude/hooks/ and "
6123
+ f"registers it in settings.json - without it the agent CAN write a .approved "
6124
+ f"(mandatory for migrations, profile E)")
6125
6125
  elif not registered:
6126
- warn("hook presente pero NO registrado en settings.json (PreToolUse)",
6127
- "re-corre `uscha install` para escribir el snippet de PreToolUse")
6126
+ warn("hook present but NOT registered in settings.json (PreToolUse)",
6127
+ "re-run `uscha install` to write the PreToolUse snippet")
6128
6128
  else:
6129
- warn(f"hook .ps1 registrado pero sin powershell/pwsh en PATH",
6129
+ warn(f"hook .ps1 registered but no powershell/pwsh on PATH",
6130
6130
  "instala pwsh, o usa el hook .py (portable) via `uscha install`: "
6131
6131
  "https://learn.microsoft.com/powershell/scripting/install/installing-powershell")
6132
6132
 
@@ -6138,22 +6138,22 @@ def cmd_doctor(args):
6138
6138
  cfg = _load(cfg_path)
6139
6139
  defaults = cfg.get("defaults", {})
6140
6140
  repos = cfg.get("repos", [])
6141
- ok(f"proyecto: {cfg_path} v{cfg.get('version', '?')} ({len(repos)} repo(s))")
6141
+ ok(f"project: {cfg_path} v{cfg.get('version', '?')} ({len(repos)} repo(s))")
6142
6142
  acc = defaults.get("acceptance_file")
6143
6143
  if acc and os.path.isfile(acc):
6144
6144
  items, _found = _parse_acceptance_items(acc)
6145
6145
  ids = sum(1 for i in items if i["id"])
6146
6146
  if items and ids:
6147
- ok(f"ACCEPTANCE: {len(items)} criterio(s), {ids} con AC-ID trazable")
6147
+ ok(f"ACCEPTANCE: {len(items)} criterion(s), {ids} with a traceable AC-ID")
6148
6148
  elif items:
6149
- warn(f"ACCEPTANCE sin AC-IDs trazables ({len(items)} criterio(s))",
6150
- "generala con /uscha-discovery o /uscha-adr-refine "
6151
- "(formato '- [ ] AC-01 - ...') - sin IDs la dimension dominante "
6152
- "del readiness cae al ratio de checkboxes")
6149
+ warn(f"ACCEPTANCE without traceable AC-IDs ({len(items)} criterion(s))",
6150
+ "generate it with /uscha-discovery or /uscha-adr-refine "
6151
+ "(format '- [ ] AC-01 - ...') - without IDs the dominant readiness "
6152
+ "dimension falls back to the checkbox ratio")
6153
6153
  else:
6154
- warn(f"ACCEPTANCE {acc} sin criterios (cero checkboxes)")
6154
+ warn(f"ACCEPTANCE {acc} has no criteria (zero checkboxes)")
6155
6155
  elif acc:
6156
- warn(f"acceptance_file declarado pero ausente: {acc}")
6156
+ warn(f"acceptance_file declared but missing: {acc}")
6157
6157
  qa_order = defaults.get("qa_tools_order", qa_order)
6158
6158
  for r in repos:
6159
6159
  tool = DOCTOR_TOOLS.get(r.get("type", ""))
@@ -6164,11 +6164,11 @@ def cmd_doctor(args):
6164
6164
  or os.path.isfile(os.path.join(r.get("path", "."), "gradlew.bat"))):
6165
6165
  ok(f"toolchain {r['name']} (gradle): gradlew del repo")
6166
6166
  elif shutil.which(tool):
6167
- ok(f"toolchain {r['name']} ({r.get('type')}): {tool} en PATH")
6167
+ ok(f"toolchain {r['name']} ({r.get('type')}): {tool} on PATH")
6168
6168
  else:
6169
- warn(f"toolchain {r['name']} ({r.get('type')}): {tool} NO esta en PATH",
6170
- f"instalar: {DOCTOR_FIX.get(tool, 'ver docs del toolchain')} "
6171
- f"(o puede vivir solo en CI)")
6169
+ warn(f"toolchain {r['name']} ({r.get('type')}): {tool} NO esta on PATH",
6170
+ f"install: {DOCTOR_FIX.get(tool, 'see the toolchain docs')} "
6171
+ f"(or it may live only in CI)")
6172
6172
  rub_cfg = defaults.get("rubric", {}) if isinstance(defaults.get("rubric"), dict) else {}
6173
6173
  if rub_cfg.get("file"):
6174
6174
  if os.path.isfile(rub_cfg["file"]):
@@ -6178,24 +6178,24 @@ def cmd_doctor(args):
6178
6178
  "; ".join(rb_block) + " - ver templates/RUBRIC.md")
6179
6179
  else:
6180
6180
  ok(f"rubrica declarada: {rub_cfg['file']}"
6181
- + (" (GATE declarado)" if rub_cfg.get("gate") is True else " (advisory)"))
6181
+ + (" (GATE declared)" if rub_cfg.get("gate") is True else " (advisory)"))
6182
6182
  else:
6183
6183
  warn(f"rubrica declarada pero ausente: {rub_cfg['file']}",
6184
- "instalar: crea desde templates/RUBRIC.md (criterios RB-nn "
6185
- "ponderados + threshold)")
6184
+ "install: create it from templates/RUBRIC.md (weighted RB-nn "
6185
+ "criteria + threshold)")
6186
6186
  ledger_path = args.ledger
6187
6187
  if os.path.isfile(ledger_path):
6188
6188
  try:
6189
6189
  _load(ledger_path)
6190
- ok(f"ledger {ledger_path}: carga e integridad OK")
6190
+ ok(f"ledger {ledger_path}: loads, integrity OK")
6191
6191
  except SystemExit as exc:
6192
- err(f"ledger {ledger_path} corrupto o mutado", str(exc))
6192
+ err(f"ledger {ledger_path} corrupt or mutated", str(exc))
6193
6193
  except SystemExit as exc:
6194
- err(f"{cfg_path} invalido", str(exc))
6194
+ err(f"{cfg_path} invalid", str(exc))
6195
6195
  else:
6196
- warn(f"sin {cfg_path} en este directorio",
6197
- "instalar: copia uscha.config.json del kit a la raiz del repo y declara "
6198
- "tus repos/types y tu quality bar - necesario solo para CORRER el loop aca")
6196
+ warn(f"no {cfg_path} in this directory",
6197
+ "install: copy the kit uscha.config.json to the repo root and declare "
6198
+ "your repos/types and your quality bar - only needed to RUN the loop here")
6199
6199
 
6200
6200
  # --- skills de QA del loop (externas al kit, se orquestan sin traerlas) --
6201
6201
  # sin ellas la fase 3 (QA loop) no corre; chequeables con o sin config.
@@ -6206,9 +6206,9 @@ def cmd_doctor(args):
6206
6206
  ok(f"skills de QA del loop instaladas: {', '.join(qa_order)}")
6207
6207
  else:
6208
6208
  warn(f"skills de QA no encontradas como archivo: {', '.join(missing_qa)}",
6209
- "instalar tus skills de QA en ~/.claude/skills/ o declarar otras en "
6210
- "config.defaults.qa_tools_order; si es una built-in del harness "
6211
- "(p.ej. code-review), ignora este aviso")
6209
+ "install your QA skills in ~/.claude/skills/ or declare others in "
6210
+ "config.defaults.qa_tools_order; if it is a harness built-in "
6211
+ "(e.g. code-review), ignore this notice")
6212
6212
 
6213
6213
  # --- veredicto ----------------------------------------------------------
6214
6214
  n_ok = sum(1 for lv, _, _ in checks if lv == "ok")
@@ -6223,14 +6223,14 @@ def cmd_doctor(args):
6223
6223
  for lv, t, d in checks]},
6224
6224
  indent=2, ensure_ascii=True))
6225
6225
  sys.exit(1 if n_err else 0)
6226
- print("USCHA DOCTOR - diagnostico de la instalacion")
6226
+ print("USCHA DOCTOR - installation diagnosis")
6227
6227
  mark = {"ok": "[OK]", "warn": "[ !]", "error": "[ X]"}
6228
6228
  for lv, t, d in checks:
6229
6229
  print(f" {mark[lv]} {t}")
6230
6230
  if d:
6231
6231
  print(f" {d}")
6232
- print(f"RESULTADO: {n_ok} ok - {n_warn} aviso(s) - {n_err} error(es)"
6233
- + (" -> instalacion sana" if not n_err else " -> hay errores que corregir"))
6232
+ print(f"RESULT: {n_ok} ok - {n_warn} warning(s) - {n_err} error(s)"
6233
+ + (" -> installation healthy" if not n_err else " -> errors to fix"))
6234
6234
  sys.exit(1 if n_err else 0)
6235
6235
 
6236
6236