trackops 2.0.6 → 2.2.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 (60) hide show
  1. package/README.md +307 -701
  2. package/bin/trackops.js +24 -16
  3. package/lib/config.js +265 -58
  4. package/lib/control.js +830 -292
  5. package/lib/init.js +46 -16
  6. package/lib/opera-bootstrap.js +85 -45
  7. package/lib/opera-phase-dod.js +485 -0
  8. package/lib/opera.js +8 -5
  9. package/lib/plans.js +1329 -0
  10. package/lib/quality-assert.js +49 -0
  11. package/lib/quality.js +1759 -0
  12. package/lib/release.js +18 -11
  13. package/lib/server.js +504 -192
  14. package/lib/skills.js +94 -41
  15. package/locales/en.json +249 -15
  16. package/locales/es.json +249 -15
  17. package/package.json +3 -2
  18. package/scripts/quality-unit-tests.js +130 -0
  19. package/scripts/skills-marketplace-smoke.js +156 -124
  20. package/scripts/smoke-tests.js +378 -71
  21. package/scripts/sync-skill-version.js +29 -19
  22. package/scripts/validate-skill.js +188 -103
  23. package/skills/trackops/SKILL.md +25 -7
  24. package/skills/trackops/locales/en/SKILL.md +25 -7
  25. package/skills/trackops/locales/en/references/activation.md +3 -3
  26. package/skills/trackops/locales/en/references/workflow.md +5 -4
  27. package/skills/trackops/references/activation.md +3 -3
  28. package/skills/trackops/references/workflow.md +5 -4
  29. package/skills/trackops/skill.json +29 -29
  30. package/skills/trackops-quality-guard/SKILL.md +78 -0
  31. package/skills/trackops-quality-guard/agents/openai.yaml +7 -0
  32. package/skills/trackops-quality-guard/locales/en/SKILL.md +78 -0
  33. package/skills/trackops-quality-guard/locales/en/references/commands.md +36 -0
  34. package/skills/trackops-quality-guard/locales/en/references/decision-policy.md +16 -0
  35. package/skills/trackops-quality-guard/locales/en/references/output-format.md +24 -0
  36. package/skills/trackops-quality-guard/references/commands.md +36 -0
  37. package/skills/trackops-quality-guard/references/decision-policy.md +16 -0
  38. package/skills/trackops-quality-guard/references/output-format.md +24 -0
  39. package/skills/trackops-quality-guard/skill.json +28 -0
  40. package/templates/skills/opera-skill/SKILL.md +12 -0
  41. package/templates/skills/opera-skill/locales/en/SKILL.md +12 -0
  42. package/templates/skills/trackops-quality-guard/SKILL.md +72 -0
  43. package/templates/skills/trackops-quality-guard/locales/en/SKILL.md +72 -0
  44. package/templates/skills/trackops-quality-guard/locales/en/references/commands.md +30 -0
  45. package/templates/skills/trackops-quality-guard/locales/en/references/decision-policy.md +14 -0
  46. package/templates/skills/trackops-quality-guard/locales/en/references/output-format.md +21 -0
  47. package/templates/skills/trackops-quality-guard/references/commands.md +30 -0
  48. package/templates/skills/trackops-quality-guard/references/decision-policy.md +14 -0
  49. package/templates/skills/trackops-quality-guard/references/output-format.md +21 -0
  50. package/ui/js/api.js +93 -26
  51. package/ui/js/app.js +13 -7
  52. package/ui/js/filters.js +49 -29
  53. package/ui/js/time-tracker.js +41 -28
  54. package/ui/js/views/board.js +22 -14
  55. package/ui/js/views/dashboard.js +206 -49
  56. package/ui/js/views/execution.js +7 -3
  57. package/ui/js/views/plans.js +284 -0
  58. package/ui/js/views/scrum.js +25 -13
  59. package/ui/js/views/sidebar.js +9 -8
  60. package/ui/js/views/tasks.js +238 -134
package/locales/es.json CHANGED
@@ -42,10 +42,12 @@
42
42
  "doc.label.completedTasks": "Completadas",
43
43
  "doc.label.inProgressTasks": "En progreso",
44
44
  "doc.label.inReviewTasks": "En revision",
45
- "doc.label.pendingTasks": "Pendientes",
46
- "doc.label.blockedTasks": "Bloqueadas",
47
- "doc.label.findingOpen": "abierto",
48
- "doc.label.findingResolved": "resuelto",
45
+ "doc.label.pendingTasks": "Pendientes",
46
+ "doc.label.blockedTasks": "Bloqueadas",
47
+ "doc.label.awaitingUserTasks": "Esperando confirmacion del usuario",
48
+ "doc.label.agentInboxPending": "Instrucciones pendientes para el agente",
49
+ "doc.label.findingOpen": "abierto",
50
+ "doc.label.findingResolved": "resuelto",
49
51
  "doc.label.findingStatus": "Estado",
50
52
  "doc.label.findingDetail": "Detalle",
51
53
  "doc.label.findingImpact": "Impacto",
@@ -60,7 +62,8 @@
60
62
  "doc.label.noActiveTasks": "No hay tareas en progreso en este momento.",
61
63
  "doc.label.noReviewTasks": "No hay tareas en revision.",
62
64
  "doc.label.noActiveBlockers": "Sin bloqueadores activos.",
63
- "doc.label.noHistory": "Sin movimientos registrados.",
65
+ "doc.label.noHistory": "Sin movimientos registrados.",
66
+ "doc.label.noAgentInbox": "Sin instrucciones pendientes para el agente.",
64
67
 
65
68
  "cli.runtimeUpdated": "Runtime actualizado en {path}",
66
69
  "cli.docsSynced": "Documentacion operativa sincronizada.",
@@ -93,9 +96,11 @@
93
96
  "cli.status.noReadyTasks": "No hay tareas listas; revisar bloqueadores o dependencias.",
94
97
  "cli.status.blockers": "Bloqueadores:",
95
98
  "cli.status.noBlockers": "Sin bloqueadores.",
96
- "cli.status.decisions": "Decisiones externas:",
97
- "cli.status.noDecisions": "Ninguna.",
98
- "cli.status.repo": "Repositorio:",
99
+ "cli.status.decisions": "Decisiones externas:",
100
+ "cli.status.noDecisions": "Ninguna.",
101
+ "cli.status.agentInbox": "Bandeja del agente:",
102
+ "cli.status.noAgentInbox": "Sin instrucciones pendientes.",
103
+ "cli.status.repo": "Repositorio:",
99
104
  "cli.status.branch": "Rama: {branch} | Arbol: {treeStatus}",
100
105
  "cli.status.treeClean": "limpio",
101
106
  "cli.status.treeDirty": "con cambios ({staged} preparados, {unstaged} sin preparar, {untracked} nuevos)",
@@ -122,6 +127,8 @@
122
127
  "cli.next.priority": "prioridad",
123
128
  "cli.next.stream": "stream",
124
129
  "cli.next.summary": "resumen",
130
+ "cli.next.activeTasks": "Trabajo activo:",
131
+ "cli.next.readyQueue": "Siguientes tareas listas en cola:",
125
132
  "cli.sync.dryRunWouldUpdate": "Se actualizarian: {files}",
126
133
  "cli.sync.dryRunInSync": "Toda la documentacion ya esta sincronizada.",
127
134
 
@@ -200,11 +207,22 @@
200
207
  "server.invalidJson": "JSON invalido.",
201
208
  "server.sessionNotFound": "Sesion no encontrada.",
202
209
  "server.noActiveSession": "No hay sesion activa para iniciar stream.",
203
- "server.commandRequired": "Se requiere un comando.",
204
- "server.projectNotResolved": "No se pudo resolver el proyecto.",
205
- "server.invalidLocale": "Idioma invalido: {value}.",
206
-
207
- "init.welcome": "Proyecto inicializado con trackops.",
210
+ "server.commandRequired": "Se requiere un comando.",
211
+ "server.projectNotResolved": "No se pudo resolver el proyecto.",
212
+ "server.invalidLocale": "Idioma invalido: {value}.",
213
+ "server.timeTaskRequired": "Debes indicar una tarea para iniciar el cronometro.",
214
+ "server.timeTaskNotFound": "No existe la tarea '{taskId}'.",
215
+ "server.timeParentTask": "Solo puedes registrar tiempo en tareas hoja. Divide el trabajo y arranca el timer en una subtarea accionable.",
216
+ "server.timeStartedNote": "Cronometro iniciado desde el dashboard.",
217
+ "server.timeEntryNotFound": "No se encontro el registro de tiempo.",
218
+ "server.agentExecutionStarted": "Ejecucion del agente iniciada desde la consola.",
219
+
220
+ "agentInbox.awaitUser.message": "La tarea '{title}' ({taskId}) queda en {status} desde {source}. El agente debe esperar la confirmacion del usuario y continuar cuando haya resultado real.",
221
+ "agentInbox.awaitUser.resolved": "La espera de confirmacion del usuario ha quedado resuelta.",
222
+ "agentInbox.verify.message": "El usuario ha dejado la tarea '{title}' ({taskId}) en estado {status} desde {source}. El agente debe comprobar el estado real antes de continuar.",
223
+ "agentInbox.verify.resolved": "La verificacion del estado real ya ha sido atendida.",
224
+
225
+ "init.welcome": "Proyecto inicializado con trackops.",
208
226
  "init.opsExists": "Ops ya esta instalado en este proyecto.",
209
227
  "init.created": "Creado {file}",
210
228
  "init.updated": "Actualizado {file}",
@@ -572,5 +590,221 @@
572
590
  "handoff.instruction.closure": "Cuando termines de escribir los archivos, dile al usuario exactamente esto: 'Los archivos estan listos. Vuelve a la terminal y ejecuta: trackops opera bootstrap --resume'. No ofrezcas scaffolding, creacion de carpetas ni ningun paso adicional. Tu trabajo termina con los archivos de bootstrap.",
573
591
  "handoff.label.knownIntention": "Intencion conocida del proyecto",
574
592
  "handoff.section.intakeSchema": "Schema obligatorio de intake.json",
575
- "handoff.instruction.intakeRequired": "El archivo `ops/bootstrap/intake.json` DEBE incluir TODOS estos campos. Los campos faltantes bloquean el bootstrap. Usa null solo si es genuinamente desconocido tras el descubrimiento."
576
- }
593
+ "handoff.instruction.intakeRequired": "El archivo `ops/bootstrap/intake.json` DEBE incluir TODOS estos campos. Los campos faltantes bloquean el bootstrap. Usa null solo si es genuinamente desconocido tras el descubrimiento.",
594
+
595
+ "cli.help.plan.desc": "Importa planes de implementacion externos en el control del proyecto con reconciliacion preview-first.",
596
+ "cli.help.quality.desc": "Inspeccion continua de calidad, evidencia de verificacion y gates de readiness para release/promocion.",
597
+
598
+ "quality.status.pass": "correcto",
599
+ "quality.status.fail": "fallando",
600
+ "quality.status.warn": "atencion",
601
+ "quality.status.skip": "omitido",
602
+ "quality.status.passed": "superado",
603
+ "quality.status.failed": "fallido",
604
+ "quality.status.skipped": "omitido",
605
+ "quality.status.not_configured": "sin configurar",
606
+ "quality.status.ready": "listo",
607
+ "quality.status.blocked": "bloqueado",
608
+ "quality.status.attention": "atencion",
609
+ "quality.status.unknown": "desconocido",
610
+ "quality.scope.test": "tests",
611
+ "quality.scope.build": "build",
612
+ "quality.scope.smoke": "smoke",
613
+ "quality.scope.review": "revision",
614
+ "quality.scope.all": "todos",
615
+ "quality.value.unknown": "desconocido",
616
+ "quality.value.missing": "faltante",
617
+ "quality.source.intake": "intake.json",
618
+ "quality.source.bootstrap": "bootstrap",
619
+ "quality.source.inferred": "inferencia",
620
+ "quality.source.unknown": "desconocido",
621
+ "quality.message.ok": "{subject}: correcto.",
622
+ "quality.message.notApplicable": "{subject}: no aplica.",
623
+ "quality.message.failDetail": "{subject}: {detail}.",
624
+ "quality.message.fileMissing": "{subject}: falta {path}.",
625
+ "quality.message.missingItems": "{subject}: faltan {items}.",
626
+ "quality.message.outOfSync": "{subject}: desincronizado ({detail}).",
627
+ "quality.message.bridgeMissing": "{subject}: falta el bridge de app.",
628
+ "quality.message.bridgeMismatch": "{subject}: claves desincronizadas ({items}).",
629
+ "quality.message.dirtyTree": "{subject}: hay cambios sin commit.",
630
+ "quality.message.behindRemote": "{subject}: la rama local va {count} commit(s) por detras del remoto.",
631
+ "quality.message.countFail": "{subject}: {count} incidencia(s).",
632
+ "quality.message.staleTasks": "{subject}: {count} tarea(s) sin actividad reciente.",
633
+ "quality.message.recordedFrom": "{subject}: registrado desde {source}.",
634
+ "quality.verification.blocker.missing": "Falta evidencia reciente para {scope}.",
635
+ "quality.verification.blocker.failed": "La ultima verificacion de {scope} no supero el check.",
636
+ "quality.verification.blocker.stale": "La evidencia de {scope} esta desactualizada respecto a la configuracion actual.",
637
+ "quality.verification.blocker.recommendation": "Ejecuta 'trackops quality verify --scope {scope}' antes de continuar.",
638
+ "quality.verify.reviewNoteRequired": "La revision manual requiere --note.",
639
+ "quality.readiness.phase": "Readiness de fase ({phase})",
640
+ "quality.readiness.release": "Readiness de release",
641
+ "quality.readiness.promotion": "Readiness de promocion ({target})",
642
+ "quality.section.blockers": "Bloqueadores:",
643
+ "quality.section.warnings": "Advertencias:",
644
+ "quality.section.waived": "Dispensas:",
645
+ "quality.section.status": "Estado de calidad",
646
+ "quality.section.currentPhase": "Fase actual",
647
+ "quality.section.profiles": "Perfiles",
648
+ "quality.section.probes": "Probes",
649
+ "quality.section.findings": "Hallazgos",
650
+ "quality.section.verification": "Verificacion",
651
+ "quality.usage.title": "Uso:",
652
+ "quality.usage.status": "trackops quality status [--json] [--domain <name>] [--phase current|O|P|E|R|A]",
653
+ "quality.usage.verify": "trackops quality verify [--scope test|build|smoke|review|all] [--json] [--note \"...\"]",
654
+ "quality.usage.phaseReadiness": "trackops quality phase-readiness [--phase current|O|P|E|R|A] [--json]",
655
+ "quality.usage.releaseReadiness": "trackops quality release-readiness [--json]",
656
+ "quality.usage.promoteReadiness": "trackops quality promote-readiness --target staging|production [--json]",
657
+ "quality.usage.waiverList": "trackops quality waiver list [--json]",
658
+ "quality.usage.waiverAdd": "trackops quality waiver add <probe-id> --scope release|promotion --reason ... --approved-by ... --expires-at ...",
659
+ "quality.waivers.none": "No hay dispensas de calidad.",
660
+ "quality.waivers.approvedBy": "aprobado por {approvedBy}",
661
+ "quality.waivers.created": "Dispensa creada: {id}",
662
+ "quality.error.waiverProbeRequired": "quality waiver add requiere un probe id.",
663
+ "quality.error.waiverScope": "quality waiver scope debe ser 'release' o 'promotion'.",
664
+ "quality.error.waiverReason": "quality waiver add requiere --reason.",
665
+ "quality.error.waiverApprovedBy": "quality waiver add requiere --approved-by.",
666
+ "quality.error.waiverExpiry": "Las dispensas de promotion requieren --expires-at.",
667
+ "quality.statusBlock.title": "Calidad",
668
+ "quality.statusBlock.status": "estado",
669
+ "quality.statusBlock.phaseReadiness": "readiness de fase",
670
+ "quality.statusBlock.releaseReadiness": "readiness de release",
671
+ "quality.statusBlock.blocker": "bloqueador",
672
+ "quality.promotion.versionControlRequired": "La promocion a produccion requiere metadata de control de versiones.",
673
+ "quality.promotion.versionControlRecommendation": "Completa versionControl.remote/provider/developmentBranch/releaseBranch en intake.json.",
674
+ "quality.promotion.deploymentRequired": "La promocion a produccion requiere metadata completa de deployment.",
675
+ "quality.promotion.deploymentRecommendation": "Completa deployment.mode/target/smokeCommand en intake.json.",
676
+ "quality.promotion.policyApprovalRequired": "La promocion a produccion requiere una regla explicita de aprobacion en policy.",
677
+ "quality.promotion.policyApprovalRecommendation": "Restaura production_deploy_requires_approval=true en autonomy.json.",
678
+ "quality.phase.O.title": "Orquestar",
679
+ "quality.phase.P.title": "Probar",
680
+ "quality.phase.E.title": "Estructurar",
681
+ "quality.phase.R.title": "Refinar",
682
+ "quality.phase.A.title": "Automatizar",
683
+ "quality.phase.message.pass": "{title}: correcto.",
684
+ "quality.phase.message.fail": "{title}: no cumple.",
685
+ "quality.phase.message.failDetail": "{title}: no cumple ({detail}).",
686
+ "quality.phase.message.warn": "{title}: requiere atencion.",
687
+ "quality.phase.message.warnDetail": "{title}: requiere atencion ({detail}).",
688
+ "quality.phase.message.skip": "{title}: no aplica.",
689
+ "quality.phase.check.bootstrapComplete.title": "Bootstrap completado",
690
+ "quality.phase.check.bootstrapComplete.recommendation": "Completa o reanuda el bootstrap de OPERA antes de cerrar la fase.",
691
+ "quality.phase.check.discoveryCore.title": "Discovery principal definido",
692
+ "quality.phase.check.discoveryCore.recommendation": "Completa problema, usuario, resultado singular y fuente de verdad.",
693
+ "quality.phase.check.bootstrapArtifacts.title": "Artefactos base de bootstrap consistentes",
694
+ "quality.phase.check.bootstrapArtifacts.recommendation": "Asegura intake.json, spec-dossier.md, operating-contract.json y quality-report sin faltantes bloqueantes.",
695
+ "quality.phase.check.versionControl.title": "Metadata de versionado registrada",
696
+ "quality.phase.check.versionControl.recommendation": "Registra remote, provider, developmentBranch y releaseBranch.",
697
+ "quality.phase.check.deployment.title": "Metadata de despliegue registrada",
698
+ "quality.phase.check.deployment.recommendation": "Registra mode, target y smokeCommand.",
699
+ "quality.phase.check.schemas.title": "Schemas de input/output definidos",
700
+ "quality.phase.check.schemas.recommendation": "Define inputSchema y outputSchema con estructura minima util.",
701
+ "quality.phase.check.behaviorRules.title": "Reglas de comportamiento documentadas",
702
+ "quality.phase.check.behaviorRules.recommendation": "Documenta behaviorRules en bootstrap o contrato.",
703
+ "quality.phase.check.taskPlanSynced.title": "task_plan sincronizado",
704
+ "quality.phase.check.taskPlanSynced.recommendation": "Ejecuta 'trackops sync' y revisa task_plan.md.",
705
+ "quality.phase.check.envKeys.title": "Credenciales y entorno verificados",
706
+ "quality.phase.check.envKeys.recommendation": "Completa las claves requeridas en .env y sincroniza el bridge.",
707
+ "quality.phase.check.verificationEvidence.title": "Evidencia de verificacion disponible",
708
+ "quality.phase.check.verificationEvidence.recommendation": "Ejecuta tests o smoke para demostrar viabilidad tecnica real.",
709
+ "quality.phase.check.criticalFindings.title": "Sin findings criticos abiertos",
710
+ "quality.phase.check.criticalFindings.recommendation": "Resuelve o acepta explicitamente cualquier finding critical.",
711
+ "quality.phase.check.blockedTasksDocumented.title": "Bloqueos documentados",
712
+ "quality.phase.check.blockedTasksDocumented.recommendation": "Añade razon de bloqueo a todas las tareas bloqueadas.",
713
+ "quality.phase.check.backlogIntegrity.title": "Backlog sin ciclos ni referencias fantasma",
714
+ "quality.phase.check.backlogIntegrity.recommendation": "Corrige dependencias, padres y ciclos del backlog.",
715
+ "quality.phase.check.requiredAcceptance.title": "Tareas requeridas con acceptance",
716
+ "quality.phase.check.requiredAcceptance.recommendation": "Añade acceptance criteria a cada tarea hoja requerida.",
717
+ "quality.phase.check.taskTracking.title": "Trabajo trazado con TrackOps",
718
+ "quality.phase.check.taskTracking.recommendation": "Inicia, mueve y cierra tareas desde TrackOps para dejar rastro operativo.",
719
+ "quality.phase.check.docsSynced.title": "Docs generados sincronizados",
720
+ "quality.phase.check.docsSynced.recommendation": "Ejecuta 'trackops sync' y revisa drift documental.",
721
+ "quality.phase.check.repoCommitted.title": "Cambios materializados en git",
722
+ "quality.phase.check.repoCommitted.recommendation": "Haz commit de los cambios relevantes antes de cerrar la fase.",
723
+ "quality.phase.check.findingsHighClosed.title": "Sin findings critical/high abiertos",
724
+ "quality.phase.check.findingsHighClosed.recommendation": "Resuelve o acepta los findings de severidad alta antes de cerrar refinement.",
725
+ "quality.phase.check.contractConsistent.title": "Contrato coherente con los artefactos",
726
+ "quality.phase.check.contractConsistent.recommendation": "Ejecuta el auditor o corrige contradicciones entre contrato y bootstrap.",
727
+ "quality.phase.check.reviewEvidence.title": "Revision registrada",
728
+ "quality.phase.check.reviewEvidence.recommendation": "Registra la revision manual con 'trackops quality verify --scope review --note ...'.",
729
+ "quality.phase.check.tmpClean.title": ".tmp limpio",
730
+ "quality.phase.check.tmpClean.recommendation": "Limpia residuos temporales no necesarios antes de automatizar o liberar.",
731
+ "quality.phase.check.deploymentReady.title": "Deployment preparado",
732
+ "quality.phase.check.deploymentReady.recommendation": "Completa la metadata y el destino de despliegue antes de automatizar.",
733
+ "quality.phase.check.smokePassed.title": "Smoke test superado",
734
+ "quality.phase.check.smokePassed.recommendation": "Ejecuta y registra un smoke test valido en el destino.",
735
+ "quality.phase.check.remotePushed.title": "Cambios pusheados al remoto",
736
+ "quality.phase.check.remotePushed.recommendation": "Haz push al remoto cuando versionControl.remote sea true.",
737
+ "quality.phase.check.repoClean.title": "Working tree limpio",
738
+ "quality.phase.check.repoClean.recommendation": "Deja el working tree limpio antes de cerrar automatizacion/release.",
739
+ "quality.probe.bootstrap.complete.title": "Bootstrap",
740
+ "quality.probe.bootstrap.complete.recommendation": "Completa el bootstrap antes de confiar en readiness de fase o release.",
741
+ "quality.probe.contract.exists.title": "Contrato operativo",
742
+ "quality.probe.contract.exists.recommendation": "Genera o restaura ops/contract/operating-contract.json.",
743
+ "quality.probe.contract.valid-json.title": "JSON del contrato",
744
+ "quality.probe.contract.valid-json.recommendation": "Corrige operating-contract.json para que parsee sin error.",
745
+ "quality.probe.contract.intent-complete.title": "Intent del contrato",
746
+ "quality.probe.contract.intent-complete.recommendation": "Completa los campos de intent faltantes.",
747
+ "quality.probe.policy.exists.title": "Politica de autonomia",
748
+ "quality.probe.policy.exists.recommendation": "Genera o restaura ops/policy/autonomy.json.",
749
+ "quality.probe.policy.valid-json.title": "JSON de policy",
750
+ "quality.probe.policy.valid-json.recommendation": "Corrige autonomy.json para que parsee sin error.",
751
+ "quality.probe.docs.drift.title": "Docs generados",
752
+ "quality.probe.docs.drift.recommendation": "Ejecuta 'trackops sync' y revisa los documentos generados.",
753
+ "quality.probe.docs.genesis-exists.title": "genesis.md",
754
+ "quality.probe.docs.genesis-exists.recommendation": "Regenera genesis.md desde el contrato y el bootstrap actuales.",
755
+ "quality.probe.env.missing-keys.title": "Claves de entorno requeridas",
756
+ "quality.probe.env.missing-keys.recommendation": "Completa las claves faltantes en .env.",
757
+ "quality.probe.env.bridge-sync.title": "Bridge de entorno app",
758
+ "quality.probe.env.bridge-sync.recommendation": "Ejecuta 'trackops env sync' para regenerar el bridge.",
759
+ "quality.probe.repo.clean.title": "Working tree",
760
+ "quality.probe.repo.clean.recommendation": "Haz commit, stash o limpia cambios no relacionados antes de liberar.",
761
+ "quality.probe.repo.behind-remote.title": "Sincronizacion con remoto",
762
+ "quality.probe.repo.behind-remote.recommendation": "Trae los ultimos cambios remotos antes de liberar o promocionar.",
763
+ "quality.probe.tasks.circular-deps.title": "Dependencias circulares",
764
+ "quality.probe.tasks.circular-deps.recommendation": "Rompe la cadena circular de dependencias.",
765
+ "quality.probe.tasks.phantom-deps.title": "Dependencias fantasma",
766
+ "quality.probe.tasks.phantom-deps.recommendation": "Corrige referencias a tareas inexistentes.",
767
+ "quality.probe.tasks.phantom-parents.title": "Padres fantasma",
768
+ "quality.probe.tasks.phantom-parents.recommendation": "Reasigna o limpia parentId que apunten a tareas inexistentes.",
769
+ "quality.probe.tasks.hierarchy-cycles.title": "Ciclos jerarquicos",
770
+ "quality.probe.tasks.hierarchy-cycles.recommendation": "Rompe los ciclos padre-hijo del arbol de tareas.",
771
+ "quality.probe.tasks.stale-in-progress.title": "Tareas estancadas en progreso",
772
+ "quality.probe.tasks.stale-in-progress.recommendation": "Actualiza, bloquea o completa las tareas sin actividad reciente.",
773
+ "quality.probe.tasks.blocked-without-note.title": "Bloqueos sin nota",
774
+ "quality.probe.tasks.blocked-without-note.recommendation": "Documenta la razon de bloqueo en cada tarea bloqueada.",
775
+ "quality.probe.tasks.required-without-acceptance.title": "Acceptance faltante",
776
+ "quality.probe.tasks.required-without-acceptance.recommendation": "Añade acceptance criteria a todas las tareas requeridas.",
777
+ "quality.probe.findings.open-critical.title": "Findings criticos abiertos",
778
+ "quality.probe.findings.open-critical.recommendation": "Resuelve o dispensa explicitamente los findings critical antes de release.",
779
+ "quality.probe.findings.open-high.title": "Findings high abiertos",
780
+ "quality.probe.findings.open-high.recommendation": "Resuelve o sigue de forma explicita los findings high antes de cerrar refinement.",
781
+ "quality.probe.bootstrap.version-control.title": "Metadata de versionado en bootstrap",
782
+ "quality.probe.bootstrap.version-control.recommendation": "Captura versionControl.remote/provider/developmentBranch/releaseBranch en intake.json.",
783
+ "quality.probe.bootstrap.deployment.title": "Metadata de deployment en bootstrap",
784
+ "quality.probe.bootstrap.deployment.recommendation": "Captura deployment.mode/target/smokeCommand en intake.json.",
785
+ "quality.probe.verification.configured.title": "Configuracion de verificacion",
786
+ "quality.probe.verification.configured.recommendation": "Declara comandos explicitos en meta.quality.verification si los defaults no bastan.",
787
+ "ui.dashboard.tabQuality": "Calidad",
788
+ "ui.dashboard.quality.kpiQuality": "Calidad",
789
+ "ui.dashboard.quality.failingProbes": "{count} probes fallando",
790
+ "ui.dashboard.quality.kpiPhase": "Readiness de fase",
791
+ "ui.dashboard.quality.blockerCount": "{count} bloqueadores",
792
+ "ui.dashboard.quality.kpiRelease": "Readiness de release",
793
+ "ui.dashboard.quality.kpiPromotion": "Promocion a produccion",
794
+ "ui.dashboard.quality.domainStatus": "Estado por dominio",
795
+ "ui.dashboard.quality.domainCounts": "pass {pass} · fail {fail} · skip {skip}",
796
+ "ui.dashboard.quality.noDomainData": "Sin datos por dominio.",
797
+ "ui.dashboard.quality.latestVerification": "Ultima verificacion",
798
+ "ui.dashboard.quality.noVerificationRuns": "No hay verificaciones registradas.",
799
+ "ui.dashboard.quality.topBlockers": "Bloqueadores principales",
800
+ "ui.dashboard.quality.noFailingProbes": "No hay probes fallando.",
801
+ "ui.dashboard.quality.readinessDetail": "Detalle de readiness",
802
+ "ui.dashboard.quality.kind.phase": "fase",
803
+ "ui.dashboard.quality.kind.release": "release",
804
+ "ui.dashboard.quality.kind.promotion": "promocion",
805
+ "ui.dashboard.quality.kind.readiness": "readiness",
806
+ "ui.dashboard.quality.noBlockers": "Sin bloqueadores",
807
+ "ui.dashboard.quality.activeWaivers": "Dispensas activas",
808
+ "ui.dashboard.quality.waiverSummary": "{scope} · aprobado por {approvedBy}",
809
+ "ui.dashboard.quality.noWaivers": "No hay dispensas activas."
810
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trackops",
3
- "version": "2.0.6",
3
+ "version": "2.2.0",
4
4
  "description": "Local control and coordination system for AI-agent software development",
5
5
  "main": "lib/control.js",
6
6
  "bin": {
@@ -51,7 +51,8 @@
51
51
  },
52
52
  "scripts": {
53
53
  "postinstall": "node scripts/postinstall-locale.js",
54
- "test": "node scripts/smoke-tests.js",
54
+ "test": "node --test scripts/quality-unit-tests.js && node scripts/smoke-tests.js",
55
+ "test:unit": "node --test scripts/quality-unit-tests.js",
55
56
  "test:smoke": "node scripts/smoke-tests.js",
56
57
  "skill:sync-version": "node scripts/sync-skill-version.js",
57
58
  "skill:validate": "node scripts/validate-skill.js",
@@ -0,0 +1,130 @@
1
+ #!/usr/bin/env node
2
+
3
+ const assert = require("assert");
4
+ const fs = require("fs");
5
+ const os = require("os");
6
+ const path = require("path");
7
+ const { spawnSync } = require("child_process");
8
+ const test = require("node:test");
9
+
10
+ const ROOT = path.resolve(__dirname, "..");
11
+ const BIN = path.join(ROOT, "bin", "trackops.js");
12
+
13
+ function runNode(args, cwd, envOverrides = {}) {
14
+ const result = spawnSync(process.execPath, args, {
15
+ cwd,
16
+ encoding: "utf8",
17
+ env: { ...process.env, ...envOverrides },
18
+ });
19
+ assert.strictEqual(result.status, 0, result.stderr || result.stdout || `fallo ejecutando ${args.join(" ")}`);
20
+ return result.stdout;
21
+ }
22
+
23
+ function runNodeResult(args, cwd, envOverrides = {}) {
24
+ return spawnSync(process.execPath, args, {
25
+ cwd,
26
+ encoding: "utf8",
27
+ env: { ...process.env, ...envOverrides },
28
+ });
29
+ }
30
+
31
+ function writeJson(filePath, data) {
32
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
33
+ fs.writeFileSync(filePath, `${JSON.stringify(data, null, 2)}\n`, "utf8");
34
+ }
35
+
36
+ function createProject(name) {
37
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), `trackops-quality-${name}-`));
38
+ runNode([BIN, "init", "--locale", "en"], dir);
39
+ return dir;
40
+ }
41
+
42
+ function readProbe(projectRoot, probeId) {
43
+ const output = runNode([BIN, "quality", "status", "--json"], projectRoot);
44
+ const report = JSON.parse(output);
45
+ return report.probes.find((probe) => probe.id === probeId);
46
+ }
47
+
48
+ test("quality read-only commands do not create ops/quality storage", () => {
49
+ const projectRoot = createProject("readonly");
50
+ const qualityDir = path.join(projectRoot, "ops", "quality");
51
+
52
+ const commands = [
53
+ [BIN, "quality", "status", "--json"],
54
+ [BIN, "quality", "phase-readiness", "--json"],
55
+ [BIN, "quality", "release-readiness", "--json"],
56
+ [BIN, "quality", "promote-readiness", "--target", "production", "--json"],
57
+ [BIN, "quality", "waiver", "list", "--json"],
58
+ ];
59
+
60
+ for (const command of commands) {
61
+ runNodeResult(command, projectRoot);
62
+ assert.ok(!fs.existsSync(qualityDir), `el comando ${command.join(" ")} no debe crear ops/quality`);
63
+ }
64
+ });
65
+
66
+ test("quality verify build without commands fails with not_configured", () => {
67
+ const projectRoot = createProject("verify-build");
68
+ writeJson(path.join(projectRoot, "app", "package.json"), {
69
+ name: "verify-build",
70
+ version: "1.0.0",
71
+ scripts: {
72
+ test: `node -e "console.log('test ok')"`,
73
+ },
74
+ });
75
+
76
+ const result = runNodeResult([BIN, "quality", "verify", "--scope", "build", "--json"], projectRoot);
77
+ assert.notStrictEqual(result.status, 0);
78
+ const payload = JSON.parse(result.stdout);
79
+ assert.strictEqual(payload.status, "failed");
80
+ assert.strictEqual(payload.results[0].scope, "build");
81
+ assert.strictEqual(payload.results[0].status, "not_configured");
82
+ });
83
+
84
+ test("quality verify smoke uses app package cwd when inferred from release:check", () => {
85
+ const projectRoot = createProject("verify-smoke");
86
+ writeJson(path.join(projectRoot, "app", "package.json"), {
87
+ name: "verify-smoke",
88
+ version: "1.0.0",
89
+ scripts: {
90
+ "release:check": `node -e "console.log(process.cwd())"`,
91
+ },
92
+ });
93
+
94
+ const result = runNodeResult([BIN, "quality", "verify", "--scope", "smoke", "--json"], projectRoot);
95
+ assert.strictEqual(result.status, 0, result.stderr || result.stdout);
96
+ const payload = JSON.parse(result.stdout);
97
+ assert.strictEqual(payload.status, "passed");
98
+ assert.strictEqual(payload.results[0].scope, "smoke");
99
+ assert.strictEqual(payload.results[0].status, "passed");
100
+ assert.strictEqual(payload.results[0].commands[0].cwd, path.join(projectRoot, "app"));
101
+ assert.match(payload.results[0].commands[0].stdout, /trackops-quality-verify-smoke-.*[\\/]app/);
102
+ });
103
+
104
+ test("phase-readiness is advisory and returns structured DoD checks", () => {
105
+ const projectRoot = createProject("phase-readiness");
106
+ runNode([BIN, "opera", "install", "--locale", "en", "--no-bootstrap"], projectRoot);
107
+
108
+ const result = runNodeResult([BIN, "quality", "phase-readiness", "--json"], projectRoot);
109
+ assert.strictEqual(result.status, 0);
110
+ const payload = JSON.parse(result.stdout);
111
+ assert.strictEqual(payload.kind, "phase");
112
+ assert.ok(payload.definition);
113
+ assert.ok(Array.isArray(payload.checks));
114
+ assert.ok(payload.checks.length > 0);
115
+ });
116
+
117
+ test("env.bridge-sync reports pass and mismatch correctly", () => {
118
+ const projectRoot = createProject("bridge-sync");
119
+ fs.writeFileSync(path.join(projectRoot, ".env"), "OPENAI_API_KEY=test\n", "utf8");
120
+ fs.writeFileSync(path.join(projectRoot, ".env.example"), "OPENAI_API_KEY=\n", "utf8");
121
+ runNode([BIN, "env", "sync"], projectRoot);
122
+
123
+ const okProbe = readProbe(projectRoot, "env.bridge-sync");
124
+ assert.strictEqual(okProbe.status, "pass");
125
+
126
+ fs.rmSync(path.join(projectRoot, "app", ".env"), { force: true });
127
+ fs.writeFileSync(path.join(projectRoot, "app", ".env"), "OPENAI_API_KEY=broken\n", "utf8");
128
+ const mismatchProbe = readProbe(projectRoot, "env.bridge-sync");
129
+ assert.strictEqual(mismatchProbe.status, "fail");
130
+ });