pumuki 6.3.58 → 6.3.59

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/VERSION CHANGED
@@ -1 +1 @@
1
- v6.3.58
1
+ v6.3.59
@@ -6,6 +6,22 @@ This file keeps only the operational highlights and rollout notes that matter wh
6
6
 
7
7
  ## 2026-03 (enterprise hardening updates)
8
8
 
9
+ ### 2026-03-14 (v6.3.59)
10
+
11
+ - PRE_WRITE tooling-only no-op:
12
+ - consumer deltas that only touch package/runtime alignment files no longer materialize a fake multi-platform skills contract,
13
+ - the result is now `skills_contract=NOT_APPLICABLE` even with `pending_changes > 0` when no code platform is active in the worktree delta.
14
+ - Policy packaging alignment:
15
+ - the published tarball now includes the `integrations/policy/*.ts` helpers required by `evaluateAiGate`, `stageRunners`, `watch` and `cli`.
16
+ - Operational impact:
17
+ - `SAAS · PUMUKI-021` can now be revalidated against the published package on a real tooling-only adoption slice.
18
+ - Validation evidence:
19
+ - `npm run -s typecheck` (`PASS`)
20
+ - `node --import tsx --test integrations/gate/__tests__/evaluateAiGate.test.ts` (`41 pass / 0 fail`)
21
+ - `node --import tsx --test --test-name-pattern "autocura evidence y receipt stale aunque la sesión SDD esté inválida|blocks AI gate violations in strict enforcement mode|blocks missing OpenSpec in strict enforcement mode|expone next_action de reconcile cuando active_rule_ids está vacío para código|expone next_action con slice atómico" integrations/lifecycle/__tests__/cli.test.ts` (`4 pass / 0 fail`)
22
+ - `npm run -s validation:package-manifest` (`PASS`)
23
+ - `npm pack --json --dry-run` (`PASS`)
24
+
9
25
  ### 2026-03-14 (v6.3.58)
10
26
 
11
27
  - PRE_WRITE no-op stability for consumer slices:
@@ -3,6 +3,7 @@ import { readEvidenceResult } from '../evidence/readEvidence';
3
3
  import { captureRepoState } from '../evidence/repoState';
4
4
  import type { RepoState } from '../evidence/schema';
5
5
  import { resolvePolicyForStage } from './stagePolicies';
6
+ import { execFileSync } from 'node:child_process';
6
7
  import { existsSync, realpathSync } from 'node:fs';
7
8
  import { resolve } from 'node:path';
8
9
  import type { SkillsLockV1, SkillsStage } from '../config/skillsLock';
@@ -353,6 +354,109 @@ const toRepoTreeDetectedPlatforms = (params: {
353
354
  });
354
355
  };
355
356
 
357
+ const normalizeChangedPath = (value: string): string =>
358
+ value.replace(/\\/g, '/').replace(/^"+|"+$/g, '').trim();
359
+
360
+ const parseChangedPath = (line: string): string | null => {
361
+ if (line.length < 4) {
362
+ return null;
363
+ }
364
+ const raw = line.slice(3).trim();
365
+ if (raw.length === 0) {
366
+ return null;
367
+ }
368
+ if (raw.includes(' -> ')) {
369
+ const renamed = raw.split(' -> ').pop();
370
+ if (!renamed) {
371
+ return null;
372
+ }
373
+ const normalizedRenamed = normalizeChangedPath(renamed);
374
+ return normalizedRenamed.length > 0 ? normalizedRenamed : null;
375
+ }
376
+ const normalized = normalizeChangedPath(raw);
377
+ return normalized.length > 0 ? normalized : null;
378
+ };
379
+
380
+ const collectWorktreeChangedPaths = (repoRoot: string): ReadonlyArray<string> => {
381
+ try {
382
+ const output = execFileSync(
383
+ 'git',
384
+ ['status', '--short', '--untracked-files=all'],
385
+ {
386
+ cwd: repoRoot,
387
+ encoding: 'utf8',
388
+ stdio: ['ignore', 'pipe', 'ignore'],
389
+ }
390
+ );
391
+ const files = output
392
+ .split('\n')
393
+ .map((line) => parseChangedPath(line))
394
+ .filter((line): line is string => typeof line === 'string' && line.length > 0);
395
+ return [...new Set(files)];
396
+ } catch {
397
+ return [];
398
+ }
399
+ };
400
+
401
+ const isPlatformPath = (platform: PreWriteSkillsPlatform, filePath: string): boolean => {
402
+ const normalized = normalizeChangedPath(filePath).toLowerCase();
403
+ if (platform === 'ios') {
404
+ return normalized.endsWith('.swift')
405
+ || normalized.startsWith('apps/ios/')
406
+ || normalized.startsWith('ios/');
407
+ }
408
+ if (platform === 'android') {
409
+ return normalized.endsWith('.kt')
410
+ || normalized.endsWith('.kts')
411
+ || normalized.startsWith('apps/android/')
412
+ || normalized.startsWith('android/');
413
+ }
414
+ if (platform === 'backend') {
415
+ const isTypeScriptOrJavaScript =
416
+ normalized.endsWith('.ts')
417
+ || normalized.endsWith('.js')
418
+ || normalized.endsWith('.mts')
419
+ || normalized.endsWith('.cts')
420
+ || normalized.endsWith('.mjs')
421
+ || normalized.endsWith('.cjs');
422
+ if (!isTypeScriptOrJavaScript) {
423
+ return false;
424
+ }
425
+ return normalized.startsWith('apps/backend/')
426
+ || /(^|\/)(backend|server|api)(\/|$)/.test(normalized);
427
+ }
428
+ const isReactExtension = normalized.endsWith('.tsx') || normalized.endsWith('.jsx');
429
+ if (isReactExtension) {
430
+ return true;
431
+ }
432
+ const isTypeScriptOrJavaScript =
433
+ normalized.endsWith('.ts')
434
+ || normalized.endsWith('.js')
435
+ || normalized.endsWith('.mts')
436
+ || normalized.endsWith('.cts')
437
+ || normalized.endsWith('.mjs')
438
+ || normalized.endsWith('.cjs');
439
+ if (!isTypeScriptOrJavaScript) {
440
+ return false;
441
+ }
442
+ return normalized.startsWith('apps/frontend/')
443
+ || normalized.startsWith('apps/web/')
444
+ || /(^|\/)(frontend|web|client)(\/|$)/.test(normalized);
445
+ };
446
+
447
+ const hasWorktreeCodePlatforms = (params: {
448
+ repoRoot: string;
449
+ requiredPlatforms: ReadonlyArray<PreWriteSkillsPlatform>;
450
+ }): boolean => {
451
+ const changedPaths = collectWorktreeChangedPaths(params.repoRoot);
452
+ if (changedPaths.length === 0) {
453
+ return false;
454
+ }
455
+ return changedPaths.some((filePath) =>
456
+ params.requiredPlatforms.some((platform) => isPlatformPath(platform, filePath))
457
+ );
458
+ };
459
+
356
460
  const toLockRequiredPlatforms = (
357
461
  requiredLock: SkillsLockV1 | undefined
358
462
  ): ReadonlyArray<PreWriteSkillsPlatform> => {
@@ -674,7 +778,16 @@ const toSkillsContractAssessment = (params: {
674
778
  : detectedPlatforms;
675
779
 
676
780
  if (requiredPlatforms.length > 0 && detectedPlatforms.length === 0) {
677
- if (params.stage === 'PRE_WRITE' && pendingChanges === 0) {
781
+ if (
782
+ params.stage === 'PRE_WRITE'
783
+ && (
784
+ pendingChanges === 0
785
+ || !hasWorktreeCodePlatforms({
786
+ repoRoot: params.repoRoot,
787
+ requiredPlatforms,
788
+ })
789
+ )
790
+ ) {
678
791
  return {
679
792
  stage: params.stage,
680
793
  enforced: false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pumuki",
3
- "version": "6.3.58",
3
+ "version": "6.3.59",
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": {