pumuki 6.3.94 → 6.3.95

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.
@@ -1,3 +1,7 @@
1
+ ## 2026-04-20 (v6.3.95)
2
+ - **Enforcement operativo de trazabilidad contractual**: `pumuki sdd evidence` exige ahora `--traceability-markdown=<path>` cuando el repositorio declara en `AGENTS.md` la matriz mínima `ARCHIVO | SKILL | REGLA | EVIDENCIA | ESTADO`; el comando falla si falta la cabecera exacta o si no existe al menos una fila real.
3
+ - **Rollout recomendado**: publicar `pumuki@6.3.95`, repin inmediato en `Flux_training` y repetir la doble repro de `pumuki sdd evidence`: sin `--traceability-markdown` debe bloquear, y con un markdown repo-local que contenga la matriz contractual debe pasar.
4
+
1
5
  ## 2026-04-20 (v6.3.94)
2
6
  - **Subtitle de bloqueo 100% en español**: la causa primaria visible de notificaciones ya traduce explícitamente los mensajes exactos de `console.log usage is not allowed in frontend code.` y `...backend code.` antes de construir el `subtitle`, evitando el último escape de copy inglés en bloqueos reales de frontend/backend.
3
7
  - **Rollout recomendado**: publicar `pumuki@6.3.94`, repin inmediato en `Flux_training` y repetir un rojo real de frontend con `PUMUKI_SKIP_CHAINED_PRE_WRITE=1 pnpm exec pumuki-pre-commit`, confirmando que el `subtitle` contiene `Se detectó uso de "console.log" en frontend.` y no la frase inglesa raw.
@@ -152,6 +152,7 @@ export type ParsedArgs = {
152
152
  sddEvidenceTestStatus?: SddEvidenceScaffoldTestStatus;
153
153
  sddEvidenceTestOutput?: string;
154
154
  sddEvidenceFromEvidence?: string;
155
+ sddEvidenceTraceabilityMarkdown?: string;
155
156
  sddStateSyncDryRun?: boolean;
156
157
  sddStateSyncScenarioId?: string;
157
158
  sddStateSyncStatus?: SddStateSyncStatus;
@@ -208,7 +209,7 @@ Pumuki lifecycle commands:
208
209
  pumuki sdd sync [--change=<change-id>] [--stage=PRE_WRITE|PRE_COMMIT|PRE_PUSH|CI] [--task=<task-id>] [--from-evidence=<path>] [--dry-run] [--json]
209
210
  pumuki sdd learn --change=<change-id> [--stage=PRE_WRITE|PRE_COMMIT|PRE_PUSH|CI] [--task=<task-id>] [--from-evidence=<path>] [--dry-run] [--json]
210
211
  pumuki sdd auto-sync --change=<change-id> [--stage=PRE_WRITE|PRE_COMMIT|PRE_PUSH|CI] [--task=<task-id>] [--from-evidence=<path>] [--dry-run] [--json]
211
- pumuki sdd evidence --scenario-id=<id> --test-command=<command> --test-status=passed|failed [--test-output=<path>] [--from-evidence=<path>] [--dry-run] [--json]
212
+ pumuki sdd evidence --scenario-id=<id> --test-command=<command> --test-status=passed|failed [--test-output=<path>] [--from-evidence=<path>] [--traceability-markdown=<path>] [--dry-run] [--json]
212
213
  pumuki sdd state-sync [--scenario-id=<id>] [--status=todo|in_progress|blocked|done] [--from-evidence=<path>] [--board-path=<path>] [--force] [--dry-run] [--json]
213
214
  aliases de --stage: RED=PRE_WRITE, GREEN=PRE_COMMIT, REFACTOR=PRE_PUSH, CLOSE=CI
214
215
  `.trim();
@@ -612,6 +613,7 @@ export const parseLifecycleCliArgs = (argv: ReadonlyArray<string>): ParsedArgs =
612
613
  let sddEvidenceTestStatus: ParsedArgs['sddEvidenceTestStatus'];
613
614
  let sddEvidenceTestOutput: ParsedArgs['sddEvidenceTestOutput'];
614
615
  let sddEvidenceFromEvidence: ParsedArgs['sddEvidenceFromEvidence'];
616
+ let sddEvidenceTraceabilityMarkdown: ParsedArgs['sddEvidenceTraceabilityMarkdown'];
615
617
  let sddStateSyncDryRun = false;
616
618
  let sddStateSyncScenarioId: ParsedArgs['sddStateSyncScenarioId'];
617
619
  let sddStateSyncStatus: ParsedArgs['sddStateSyncStatus'];
@@ -1095,6 +1097,17 @@ export const parseLifecycleCliArgs = (argv: ReadonlyArray<string>): ParsedArgs =
1095
1097
  sddEvidenceTestOutput = testOutputPath;
1096
1098
  continue;
1097
1099
  }
1100
+ if (arg.startsWith('--traceability-markdown=')) {
1101
+ if (sddCommand !== 'evidence') {
1102
+ throw new Error(`--traceability-markdown is only supported with "pumuki sdd evidence".\n\n${HELP_TEXT}`);
1103
+ }
1104
+ const traceabilityMarkdownPath = arg.slice('--traceability-markdown='.length).trim();
1105
+ if (traceabilityMarkdownPath.length === 0) {
1106
+ throw new Error(`Invalid --traceability-markdown value "${arg}".`);
1107
+ }
1108
+ sddEvidenceTraceabilityMarkdown = traceabilityMarkdownPath;
1109
+ continue;
1110
+ }
1098
1111
  if (arg.startsWith('--from-evidence=')) {
1099
1112
  if (sddCommand === 'sync-docs') {
1100
1113
  sddSyncDocsFromEvidence = parseSddEvidencePath(
@@ -1255,7 +1268,7 @@ export const parseLifecycleCliArgs = (argv: ReadonlyArray<string>): ParsedArgs =
1255
1268
  sddAutoSyncChange
1256
1269
  ) {
1257
1270
  throw new Error(
1258
- `"pumuki sdd evidence" only supports --scenario-id=<id> --test-command=<command> --test-status=passed|failed [--test-output=<path>] [--from-evidence=<path>] [--dry-run] [--json].\n\n${HELP_TEXT}`
1271
+ `"pumuki sdd evidence" only supports --scenario-id=<id> --test-command=<command> --test-status=passed|failed [--test-output=<path>] [--from-evidence=<path>] [--traceability-markdown=<path>] [--dry-run] [--json].\n\n${HELP_TEXT}`
1259
1272
  );
1260
1273
  }
1261
1274
  if (!sddEvidenceScenarioId) {
@@ -1278,6 +1291,7 @@ export const parseLifecycleCliArgs = (argv: ReadonlyArray<string>): ParsedArgs =
1278
1291
  sddEvidenceTestStatus,
1279
1292
  ...(sddEvidenceTestOutput ? { sddEvidenceTestOutput } : {}),
1280
1293
  ...(sddEvidenceFromEvidence ? { sddEvidenceFromEvidence } : {}),
1294
+ ...(sddEvidenceTraceabilityMarkdown ? { sddEvidenceTraceabilityMarkdown } : {}),
1281
1295
  };
1282
1296
  }
1283
1297
  if (sddCommand === 'state-sync') {
@@ -451,6 +451,7 @@ export const runSddCommand = async (parsed: ParsedArgs, activeDependencies: Life
451
451
  testStatus: parsed.sddEvidenceTestStatus,
452
452
  testOutputPath: parsed.sddEvidenceTestOutput,
453
453
  fromEvidencePath: parsed.sddEvidenceFromEvidence,
454
+ traceabilityMarkdownPath: parsed.sddEvidenceTraceabilityMarkdown,
454
455
  });
455
456
  if (parsed.json) {
456
457
  writeInfo(JSON.stringify(evidenceResult, null, 2));
@@ -1,5 +1,5 @@
1
1
  import { createHash } from 'node:crypto';
2
- import { mkdirSync, writeFileSync } from 'node:fs';
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
3
3
  import { basename, dirname, isAbsolute, relative, resolve } from 'node:path';
4
4
  import { readEvidenceResult, type EvidenceReadResult } from '../evidence/readEvidence';
5
5
 
@@ -15,6 +15,7 @@ export type SddEvidenceScaffoldResult = {
15
15
  testStatus: SddEvidenceScaffoldTestStatus;
16
16
  testOutputPath: string | null;
17
17
  fromEvidencePath: string | null;
18
+ traceabilityMarkdownPath: string | null;
18
19
  };
19
20
  output: {
20
21
  path: string;
@@ -44,6 +45,13 @@ export type SddEvidenceScaffoldResult = {
44
45
  source: 'pumuki-sdd-evidence';
45
46
  stack: 'sdd-evidence-scaffold';
46
47
  };
48
+ traceability: {
49
+ required: boolean;
50
+ path: string | null;
51
+ header_present: boolean;
52
+ rows: number;
53
+ status: 'valid';
54
+ };
47
55
  // Legacy fields kept for state-sync compatibility in existing consumers.
48
56
  scenario_id: string;
49
57
  test_run: {
@@ -65,10 +73,13 @@ export type SddEvidenceScaffoldResult = {
65
73
  const computeDigest = (value: string): string =>
66
74
  `sha256:${createHash('sha256').update(value, 'utf8').digest('hex')}`;
67
75
 
76
+ const TRACEABILITY_HEADER = '| ARCHIVO | SKILL | REGLA | EVIDENCIA | ESTADO |';
77
+ const TRACEABILITY_CONTRACT_MARKER = 'ARCHIVO | SKILL | REGLA | EVIDENCIA | ESTADO';
78
+
68
79
  const resolveRepoBoundPath = (params: {
69
80
  repoRoot: string;
70
81
  candidatePath: string;
71
- flagName: '--from-evidence' | '--test-output';
82
+ flagName: '--from-evidence' | '--test-output' | '--traceability-markdown';
72
83
  }): string => {
73
84
  const repoRootAbsolute = resolve(params.repoRoot);
74
85
  const resolved = isAbsolute(params.candidatePath)
@@ -141,6 +152,87 @@ const resolveScenarioReference = (scenarioId: string): string => {
141
152
  return `${normalized}.feature`;
142
153
  };
143
154
 
155
+ const repoRequiresTraceabilityMatrix = (repoRoot: string): boolean => {
156
+ const agentsPath = resolve(repoRoot, 'AGENTS.md');
157
+ if (!existsSync(agentsPath)) {
158
+ return false;
159
+ }
160
+ const contents = readFileSync(agentsPath, 'utf8');
161
+ return (
162
+ contents.includes('Plantilla obligatoria de trazabilidad por turno') ||
163
+ contents.includes(TRACEABILITY_CONTRACT_MARKER)
164
+ );
165
+ };
166
+
167
+ const validateTraceabilityMarkdown = (params: {
168
+ repoRoot: string;
169
+ required: boolean;
170
+ traceabilityMarkdownPath?: string;
171
+ }): {
172
+ path: string | null;
173
+ header_present: boolean;
174
+ rows: number;
175
+ } => {
176
+ if (!params.required) {
177
+ return {
178
+ path: params.traceabilityMarkdownPath?.trim() ? params.traceabilityMarkdownPath.trim() : null,
179
+ header_present: false,
180
+ rows: 0,
181
+ };
182
+ }
183
+ const candidate = params.traceabilityMarkdownPath?.trim() ?? '';
184
+ if (candidate.length === 0) {
185
+ throw new Error(
186
+ `[pumuki][sdd] evidence requires --traceability-markdown=<path> because this repository declares the contractual traceability matrix (${TRACEABILITY_HEADER}).`
187
+ );
188
+ }
189
+ const absolutePath = resolveRepoBoundPath({
190
+ repoRoot: params.repoRoot,
191
+ candidatePath: candidate,
192
+ flagName: '--traceability-markdown',
193
+ });
194
+ if (!existsSync(absolutePath)) {
195
+ throw new Error(
196
+ `[pumuki][sdd] traceability markdown file does not exist: ${candidate}. Add the contractual matrix and retry.`
197
+ );
198
+ }
199
+ const markdown = readFileSync(absolutePath, 'utf8');
200
+ const lines = markdown.split(/\r?\n/);
201
+ const headerIndex = lines.findIndex((line) => line.trim() === TRACEABILITY_HEADER);
202
+ if (headerIndex < 0) {
203
+ throw new Error(
204
+ `[pumuki][sdd] traceability markdown must include the contractual header ${TRACEABILITY_HEADER}.`
205
+ );
206
+ }
207
+ let rows = 0;
208
+ for (const line of lines.slice(headerIndex + 1)) {
209
+ const trimmed = line.trim();
210
+ if (!trimmed.startsWith('|')) {
211
+ if (rows > 0) {
212
+ break;
213
+ }
214
+ continue;
215
+ }
216
+ if (/^\|\s*-+\s*\|/.test(trimmed)) {
217
+ continue;
218
+ }
219
+ if (trimmed === TRACEABILITY_HEADER) {
220
+ continue;
221
+ }
222
+ rows += 1;
223
+ }
224
+ if (rows === 0) {
225
+ throw new Error(
226
+ '[pumuki][sdd] traceability markdown must include at least one data row under the contractual matrix header.'
227
+ );
228
+ }
229
+ return {
230
+ path: relative(params.repoRoot, absolutePath).split('\\').join('/'),
231
+ header_present: true,
232
+ rows,
233
+ };
234
+ };
235
+
144
236
  export const runSddEvidenceScaffold = (params?: {
145
237
  repoRoot?: string;
146
238
  dryRun?: boolean;
@@ -149,6 +241,7 @@ export const runSddEvidenceScaffold = (params?: {
149
241
  testStatus?: SddEvidenceScaffoldTestStatus;
150
242
  testOutputPath?: string;
151
243
  fromEvidencePath?: string;
244
+ traceabilityMarkdownPath?: string;
152
245
  outputPath?: string;
153
246
  now?: () => Date;
154
247
  evidenceReader?: (repoRoot: string) => EvidenceReadResult;
@@ -195,6 +288,12 @@ export const runSddEvidenceScaffold = (params?: {
195
288
  evidenceResult: evidenceReader(repoRoot),
196
289
  fromEvidencePath,
197
290
  });
291
+ const traceabilityRequired = repoRequiresTraceabilityMatrix(repoRoot);
292
+ const traceability = validateTraceabilityMarkdown({
293
+ repoRoot,
294
+ required: traceabilityRequired,
295
+ traceabilityMarkdownPath: params?.traceabilityMarkdownPath,
296
+ });
198
297
 
199
298
  const now = params?.now ?? (() => new Date());
200
299
  const generatedAt = now().toISOString();
@@ -232,6 +331,13 @@ export const runSddEvidenceScaffold = (params?: {
232
331
  source: 'pumuki-sdd-evidence',
233
332
  stack: 'sdd-evidence-scaffold',
234
333
  },
334
+ traceability: {
335
+ required: traceabilityRequired,
336
+ path: traceability.path,
337
+ header_present: traceability.header_present,
338
+ rows: traceability.rows,
339
+ status: 'valid',
340
+ },
235
341
  scenario_id: scenarioId,
236
342
  test_run: {
237
343
  command: testCommand,
@@ -267,6 +373,7 @@ export const runSddEvidenceScaffold = (params?: {
267
373
  testStatus,
268
374
  testOutputPath,
269
375
  fromEvidencePath,
376
+ traceabilityMarkdownPath: traceability.path,
270
377
  },
271
378
  output: {
272
379
  path: outputRelativePath,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pumuki",
3
- "version": "6.3.94",
3
+ "version": "6.3.95",
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": {