pumuki 6.3.102 → 6.3.104

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,22 @@ This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [6.3.104] - 2026-04-22
10
+
11
+ ### Fixed
12
+
13
+ - **Tracking canónico de RuralGo reconocido por el parser de repo-policy:** `appendTrackingActionableContext` ya inspecciona `docs/RURALGO_SEGUIMIENTO.md`, que es la ruta canónica real del consumer.
14
+ - **Filas `| 🚧 | TASK |` tratadas como entradas activas válidas:** el diagnóstico accionable cubre el formato de tabla usado por el hub de seguimiento de RuralGo además del backlog tabular de incidencias.
15
+ - **Cobertura de regresión para el hub canónico:** nuevas pruebas fijan parsing y priorización de `docs/RURALGO_SEGUIMIENTO.md` antes de otros archivos de tracking del consumer.
16
+
17
+ ## [6.3.103] - 2026-04-22
18
+
19
+ ### Fixed
20
+
21
+ - **Diagnóstico accionable del tracking canónico en consumers:** `status`, `doctor` y el gate repo-policy ya incluyen `TRACKING_CANONICAL_IN_PROGRESS_INVALID` junto con referencias a entradas activas y al board canónico del repo consumidor cuando existe.
22
+ - **Separación explícita entre blocker y warning secundario:** la salida de `PRE_WRITE` conserva un `block-summary` primario y añade `warning-summary` para warnings de higiene (`EVIDENCE_PREWRITE_WORKTREE_WARN`) cuando conviven con un bloqueo duro de tracking.
23
+ - **Cobertura de regresión del hotfix:** nuevas pruebas fijan el parsing de boards tabulares `🚧 reported activo` y la impresión del resumen jerárquico con warning secundario.
24
+
9
25
  ## [6.3.102] - 2026-04-22
10
26
 
11
27
  ### Fixed
package/VERSION CHANGED
@@ -1 +1 @@
1
- v6.3.102
1
+ v6.3.104
@@ -6,6 +6,18 @@ This file keeps only the operational highlights and rollout notes that matter wh
6
6
 
7
7
  ## 2026-04 (CLI stability and macOS notifications)
8
8
 
9
+ ### 2026-04-22 (v6.3.104)
10
+
11
+ - **RuralGo hub-aware diagnostics:** `TRACKING_CANONICAL_IN_PROGRESS_INVALID` pasa a enriquecer su mensaje usando `docs/RURALGO_SEGUIMIENTO.md` cuando ese hub es el board canónico del consumer.
12
+ - **Compatibilidad con tablas del hub:** el parser reconoce filas `| 🚧 | TASK_ID |` y las devuelve como entradas activas accionables.
13
+ - **Rollout recomendado:** publicar `pumuki@6.3.104`, repin inmediato en `RuralGo` y revalidar `status` / `doctor` / `pumuki-pre-write` con doble fila `🚧` temporal en el hub canónico.
14
+
15
+ ### 2026-04-22 (v6.3.103)
16
+
17
+ - **Tracking canónico accionable:** `status`, `doctor` y el gate repo-policy enriquecen `TRACKING_CANONICAL_IN_PROGRESS_INVALID` con referencias a las entradas activas detectadas en el board del consumer.
18
+ - **PRE_WRITE menos ambiguo:** cuando un warning de higiene de worktree convive con un bloqueo duro, el runtime imprime `warning-summary` separado del `block-summary`.
19
+ - **Rollout recomendado:** publicar `pumuki@6.3.103`, repin inmediato en `RuralGo` y revalidar `status` / `doctor` / `git commit` sobre un board con varias filas `🚧 reported activo`.
20
+
9
21
  ### 2026-04-22 (v6.3.102)
10
22
 
11
23
  - **Convergencia de policy efectiva:** `strict` deja de depender solo de `PUMUKI_POLICY_STRICT` cuando el contrato firmado ya lo declara por stage; `status`, `doctor` y runtime vuelven a hablar el mismo idioma.
@@ -1,3 +1,5 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
1
3
  import type { Finding } from '../../core/gate/Finding';
2
4
  import type { GateStage } from '../../core/gate/GateStage';
3
5
  import { evaluateAiGate, type AiGateStage } from '../gate/evaluateAiGate';
@@ -6,10 +8,91 @@ const AI_GATE_STAGES = new Set<AiGateStage>(['PRE_WRITE', 'PRE_COMMIT', 'PRE_PUS
6
8
 
7
9
  const REPO_POLICY_CODES = new Set<string>([
8
10
  'GITFLOW_PROTECTED_BRANCH',
11
+ 'TRACKING_CANONICAL_IN_PROGRESS_INVALID',
9
12
  'EVIDENCE_PREWRITE_WORKTREE_OVER_LIMIT',
10
13
  'EVIDENCE_PREWRITE_WORKTREE_WARN',
11
14
  ]);
12
15
 
16
+ const TRACKING_CANDIDATE_FILES = [
17
+ 'docs/technical/08-validation/refactor/pumuki-integration-feedback.md',
18
+ 'docs/RURALGO_SEGUIMIENTO.md',
19
+ 'docs/pumuki/PUMUKI_BUGS_MEJORAS.md',
20
+ 'docs/BUGS_Y_MEJORAS_PUMUKI.md',
21
+ 'PUMUKI-RESET-MASTER-PLAN.md',
22
+ 'RURALGO_SEGUIMIENTO.md',
23
+ ] as const;
24
+
25
+ type TrackingActiveEntry = {
26
+ taskId: string | null;
27
+ lineNumber: number;
28
+ };
29
+
30
+ export const collectTrackingActiveEntriesFromMarkdown = (
31
+ markdown: string
32
+ ): ReadonlyArray<TrackingActiveEntry> => {
33
+ const entries: TrackingActiveEntry[] = [];
34
+ const lines = markdown.split(/\r?\n/u);
35
+ for (const [index, line] of lines.entries()) {
36
+ const boardRowMatch = line.match(/^\|\s*🚧\s*\|\s*([A-Z0-9-]+)\s*\|/u);
37
+ if (boardRowMatch) {
38
+ entries.push({
39
+ taskId: boardRowMatch[1]!.trim(),
40
+ lineNumber: index + 1,
41
+ });
42
+ continue;
43
+ }
44
+ const tableMatch = line.match(
45
+ /^\|\s*\d+\s*\|\s*`([^`]+)`\s*\|.*\|\s*🚧(?:\s+reported\s+activo|\s+En construcción|\s+En construccion)?\s*\|/u
46
+ );
47
+ if (tableMatch) {
48
+ entries.push({
49
+ taskId: tableMatch[1]!.trim(),
50
+ lineNumber: index + 1,
51
+ });
52
+ continue;
53
+ }
54
+ const bulletMatch = line.match(/^- 🚧 (`?P[0-9A-Za-z.-]+`?)/u);
55
+ if (bulletMatch) {
56
+ entries.push({
57
+ taskId: bulletMatch[1]!.replaceAll('`', '').trim(),
58
+ lineNumber: index + 1,
59
+ });
60
+ continue;
61
+ }
62
+ if (/^- Estado:\s*🚧/u.test(line)) {
63
+ entries.push({
64
+ taskId: null,
65
+ lineNumber: index + 1,
66
+ });
67
+ }
68
+ }
69
+ return entries;
70
+ };
71
+
72
+ const formatTrackingEntry = (entry: TrackingActiveEntry): string =>
73
+ entry.taskId ? `${entry.taskId}@L${entry.lineNumber}` : `line_${entry.lineNumber}`;
74
+
75
+ export const appendTrackingActionableContext = (params: {
76
+ repoRoot: string;
77
+ message: string;
78
+ }): string => {
79
+ for (const candidate of TRACKING_CANDIDATE_FILES) {
80
+ const candidatePath = resolve(params.repoRoot, candidate);
81
+ if (!existsSync(candidatePath)) {
82
+ continue;
83
+ }
84
+ const source = readFileSync(candidatePath, 'utf8');
85
+ const entries = collectTrackingActiveEntriesFromMarkdown(source);
86
+ if (entries.length === 0) {
87
+ continue;
88
+ }
89
+ const preview = entries.slice(0, 3).map(formatTrackingEntry).join(', ');
90
+ const overflow = entries.length > 3 ? ` (+${entries.length - 3} more)` : '';
91
+ return `${params.message} active_entries=${preview}${overflow} tracking_source=${candidate}`;
92
+ }
93
+ return params.message;
94
+ };
95
+
13
96
  const toRepoPolicyFinding = (params: {
14
97
  code: string;
15
98
  message: string;
@@ -39,7 +122,13 @@ export const collectAiGateRepoPolicyFindings = (params: {
39
122
  .map((v) =>
40
123
  toRepoPolicyFinding({
41
124
  code: v.code,
42
- message: v.message,
125
+ message:
126
+ v.code === 'TRACKING_CANONICAL_IN_PROGRESS_INVALID'
127
+ ? appendTrackingActionableContext({
128
+ repoRoot: params.repoRoot,
129
+ message: v.message,
130
+ })
131
+ : v.message,
43
132
  severity: v.severity === 'ERROR' ? 'ERROR' : 'WARN',
44
133
  })
45
134
  );
@@ -52,6 +52,15 @@ const resolvePrimaryFinding = (findings: ReadonlyArray<Finding>): Finding => {
52
52
  return primary ?? findings[0]!;
53
53
  };
54
54
 
55
+ const sortFindingsBySeverity = (findings: ReadonlyArray<Finding>): ReadonlyArray<Finding> =>
56
+ [...findings].sort((left, right) => {
57
+ const severityDelta = severityWeight(right.severity) - severityWeight(left.severity);
58
+ if (severityDelta !== 0) {
59
+ return severityDelta;
60
+ }
61
+ return left.ruleId.localeCompare(right.ruleId);
62
+ });
63
+
55
64
  const normalizeAnchorLine = (lines: Finding['lines']): number => {
56
65
  if (Array.isArray(lines)) {
57
66
  const candidates = lines
@@ -98,7 +107,8 @@ export const printGateFindings = (findings: ReadonlyArray<Finding>): void => {
98
107
  if (findings.length === 0) {
99
108
  return;
100
109
  }
101
- const primary = resolvePrimaryFinding(findings);
110
+ const orderedFindings = sortFindingsBySeverity(findings);
111
+ const primary = resolvePrimaryFinding(orderedFindings);
102
112
  const nextAction =
103
113
  BLOCK_NEXT_ACTION_BY_CODE[primary.code]
104
114
  ?? 'Corrige el bloqueante primario y vuelve a ejecutar el mismo comando.';
@@ -106,7 +116,17 @@ export const printGateFindings = (findings: ReadonlyArray<Finding>): void => {
106
116
  `[pumuki][block-summary] primary=${primary.code} severity=${primary.severity.toUpperCase()} rule=${primary.ruleId}\n`
107
117
  );
108
118
  process.stdout.write(`[pumuki][block-summary] next_action=${nextAction}\n`);
109
- for (const finding of findings) {
119
+ const secondaryWarnings = orderedFindings.filter(
120
+ (finding) =>
121
+ finding !== primary &&
122
+ severityWeight(finding.severity) < severityWeight(primary.severity)
123
+ );
124
+ for (const finding of secondaryWarnings) {
125
+ process.stdout.write(
126
+ `[pumuki][warning-summary] secondary=${finding.code} severity=${finding.severity.toUpperCase()} rule=${finding.ruleId}\n`
127
+ );
128
+ }
129
+ for (const finding of orderedFindings) {
110
130
  process.stdout.write(`${formatFinding(finding)}\n`);
111
131
  }
112
132
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pumuki",
3
- "version": "6.3.102",
3
+ "version": "6.3.104",
4
4
  "description": "Enterprise-grade AST Intelligence System with multi-platform support (iOS, Android, Backend, Frontend) and Feature-First + DDD + Clean Architecture enforcement. Includes dynamic violations API for intelligent querying.",
5
5
  "main": "index.js",
6
6
  "bin": {