pumuki 6.3.140 → 6.3.141

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,12 @@ This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [6.3.141] - 2026-05-05
10
+
11
+ ### Fixed
12
+
13
+ - **PRE_PUSH en ramas actualizadas desde base:** el guard de atomicidad ignora commits heredados de `main`/`develop` y commits merge al validar trazabilidad y límites por commit, evitando bloqueos falsos en rollouts que solo resolvieron conflictos con la rama base.
14
+
9
15
  ## [6.3.140] - 2026-05-05
10
16
 
11
17
  ### Fixed
@@ -4,6 +4,12 @@ This file tracks the active deterministic framework line used in this repository
4
4
  Canonical release chronology lives in `CHANGELOG.md`.
5
5
  This file keeps only the operational highlights and rollout notes that matter while running the framework.
6
6
 
7
+ ### 2026-05-05 (v6.3.141)
8
+
9
+ - **PRE_PUSH sin falso bloqueo por historial base:** ramas de rollout que integran `main`/`develop` dejan de fallar por commits merge heredados como `Merge pull request ...`.
10
+ - **Flux/SAAS follow-up:** esta patch desbloquea el push de las resoluciones de conflicto de los PRs de repin abiertos tras `6.3.140`.
11
+ - **Rollout:** publicar `pumuki@6.3.141`, repinear primero RuralGo y repetir Flux/SAAS sin bypass.
12
+
7
13
  ### 2026-05-05 (v6.3.140)
8
14
 
9
15
  - **PRE_PUSH compatible con ramas largas:** la atomicidad se valida por commit individual, no por diff agregado de rama.
@@ -32,6 +32,14 @@ const ATOMICITY_CONFIG_FILE = '.pumuki/git-atomicity.json';
32
32
  const DEFAULT_COMMIT_PATTERN =
33
33
  '^(feat|fix|chore|refactor|docs|test|perf|build|ci|revert)(\\([^)]+\\))?:\\s.+$';
34
34
  const MANAGED_EVIDENCE_PATHS = new Set(['.ai_evidence.json', '.AI_EVIDENCE.json']);
35
+ const BASELINE_BRANCH_REFS = [
36
+ 'origin/main',
37
+ 'origin/develop',
38
+ 'upstream/main',
39
+ 'upstream/develop',
40
+ 'main',
41
+ 'develop',
42
+ ];
35
43
 
36
44
  const defaultConfig: GitAtomicityConfig = {
37
45
  enabled: true,
@@ -252,33 +260,90 @@ const collectCommitSubjects = (params: {
252
260
  fromRef?: string;
253
261
  toRef?: string;
254
262
  }): ReadonlyArray<string> => {
255
- if (!params.fromRef || !params.toRef) {
256
- return [];
257
- }
263
+ return collectCommitRecords(params)
264
+ .filter((record) => shouldEvaluateCommitRecord({ git: params.git, repoRoot: params.repoRoot, record }))
265
+ .map((record) => record.subject);
266
+ };
267
+
268
+ type CommitRecord = {
269
+ hash: string;
270
+ parents: ReadonlyArray<string>;
271
+ subject: string;
272
+ };
273
+
274
+ const parseCommitRecords = (value: string): ReadonlyArray<CommitRecord> =>
275
+ value
276
+ .split('\n')
277
+ .map((line) => line.trim())
278
+ .filter((line) => line.length > 0)
279
+ .map((line) => {
280
+ const [hash = '', parents = '', subject = ''] = line.split('\u0001');
281
+ return {
282
+ hash: hash.trim(),
283
+ parents: parents.split(' ').map((parent) => parent.trim()).filter((parent) => parent.length > 0),
284
+ subject: subject.trim(),
285
+ };
286
+ })
287
+ .filter((record) => record.hash.length > 0);
288
+
289
+ const isCommitReachableFromRef = (params: {
290
+ git: IGitService;
291
+ repoRoot: string;
292
+ commitHash: string;
293
+ ref: string;
294
+ }): boolean => {
258
295
  try {
259
- return parseLines(
260
- params.git.runGit(['log', '--format=%s', `${params.fromRef}..${params.toRef}`], params.repoRoot)
261
- );
262
- } catch (error) {
263
- if (isUnresolvableRevisionError(error)) {
264
- return [];
265
- }
266
- throw error;
296
+ params.git.runGit(['merge-base', '--is-ancestor', params.commitHash, params.ref], params.repoRoot);
297
+ return true;
298
+ } catch {
299
+ return false;
267
300
  }
268
301
  };
269
302
 
270
- const collectCommitHashes = (params: {
303
+ const isInheritedBaselineCommit = (params: {
304
+ git: IGitService;
305
+ repoRoot: string;
306
+ commitHash: string;
307
+ }): boolean =>
308
+ BASELINE_BRANCH_REFS.some((ref) =>
309
+ isCommitReachableFromRef({
310
+ git: params.git,
311
+ repoRoot: params.repoRoot,
312
+ commitHash: params.commitHash,
313
+ ref,
314
+ })
315
+ );
316
+
317
+ const shouldEvaluateCommitRecord = (params: {
318
+ git: IGitService;
319
+ repoRoot: string;
320
+ record: CommitRecord;
321
+ }): boolean => {
322
+ if (params.record.parents.length > 1) {
323
+ return false;
324
+ }
325
+ return !isInheritedBaselineCommit({
326
+ git: params.git,
327
+ repoRoot: params.repoRoot,
328
+ commitHash: params.record.hash,
329
+ });
330
+ };
331
+
332
+ const collectCommitRecords = (params: {
271
333
  git: IGitService;
272
334
  repoRoot: string;
273
335
  fromRef?: string;
274
336
  toRef?: string;
275
- }): ReadonlyArray<string> => {
337
+ }): ReadonlyArray<CommitRecord> => {
276
338
  if (!params.fromRef || !params.toRef) {
277
339
  return [];
278
340
  }
279
341
  try {
280
- return parseLines(
281
- params.git.runGit(['log', '--format=%H', '--reverse', `${params.fromRef}..${params.toRef}`], params.repoRoot)
342
+ return parseCommitRecords(
343
+ params.git.runGit(
344
+ ['log', '--format=%H%x01%P%x01%s', '--reverse', `${params.fromRef}..${params.toRef}`],
345
+ params.repoRoot
346
+ )
282
347
  );
283
348
  } catch (error) {
284
349
  if (isUnresolvableRevisionError(error)) {
@@ -367,29 +432,29 @@ const buildPrePushCommitPathLimitViolations = (params: {
367
432
  fromRef?: string;
368
433
  toRef?: string;
369
434
  }): GitAtomicityViolation[] | undefined => {
370
- const commitHashes = collectCommitHashes({
435
+ const commitRecords = collectCommitRecords({
371
436
  git: params.git,
372
437
  repoRoot: params.repoRoot,
373
438
  fromRef: params.fromRef,
374
439
  toRef: params.toRef,
375
- });
376
- if (commitHashes.length === 0) {
440
+ }).filter((record) => shouldEvaluateCommitRecord({ git: params.git, repoRoot: params.repoRoot, record }));
441
+ if (commitRecords.length === 0) {
377
442
  return undefined;
378
443
  }
379
444
 
380
445
  const violations: GitAtomicityViolation[] = [];
381
- for (const commitHash of commitHashes) {
446
+ for (const commitRecord of commitRecords) {
382
447
  const changedPaths = collectCommitChangedPaths({
383
448
  git: params.git,
384
449
  repoRoot: params.repoRoot,
385
- commitHash,
450
+ commitHash: commitRecord.hash,
386
451
  }).filter((path) => !isManagedEvidencePath(path));
387
452
  violations.push(
388
453
  ...buildPathLimitViolations({
389
454
  changedPaths,
390
455
  config: params.config,
391
456
  stage: 'PRE_PUSH',
392
- label: `commit=${commitHash.slice(0, 12)}`,
457
+ label: `commit=${commitRecord.hash.slice(0, 12)}`,
393
458
  })
394
459
  );
395
460
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pumuki",
3
- "version": "6.3.140",
3
+ "version": "6.3.141",
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": {