pumuki 6.3.249 → 6.3.251
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,12 +6,19 @@ This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
-
## [6.3.
|
|
9
|
+
## [6.3.251] - 2026-05-14
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **SDD session refresh tracking parity:** `pumuki sdd session --refresh` now realigns the session with the single active task in `docs/RURALGO_SEGUIMIENTO.md` when the matching OpenSpec change exists, and blocks with an actionable error instead of silently refreshing a stale task.
|
|
14
|
+
|
|
15
|
+
## [6.3.250] - 2026-05-14
|
|
10
16
|
|
|
11
17
|
### Fixed
|
|
12
18
|
|
|
13
19
|
- **Zero-violation gate contract:** runtime findings are blocking regardless of severity; `pumuki audit` no longer degrades findings to non-blocking warnings just because the runner returned exit 0.
|
|
14
20
|
- **AST-actionable findings contract:** scoped `skills.*` and `heuristics.*` matches without line, range or semantic node attribution are no longer emitted as advisory findings. A runtime finding must be actionable by AST/location evidence or it is not a gate finding.
|
|
21
|
+
- **Synthetic coverage findings removed from runtime gates:** cross-platform critical coverage gaps are no longer emitted as `.ai_evidence.json` findings. Coverage remains diagnostic metadata; runtime findings must be source-actionable.
|
|
15
22
|
|
|
16
23
|
## [6.3.248] - 2026-05-14
|
|
17
24
|
|
|
@@ -6,13 +6,20 @@ 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-05-14 (v6.3.
|
|
9
|
+
### 2026-05-14 (v6.3.251)
|
|
10
|
+
|
|
11
|
+
- **Refresh SDD alineado con tracking activo:** `pumuki sdd session --refresh` deja de reutilizar silenciosamente una sesión antigua cuando `docs/RURALGO_SEGUIMIENTO.md` ya marca otra task activa.
|
|
12
|
+
- **Bloqueo accionable si falta OpenSpec:** si el tracking activo no existe en `openspec/changes`, el refresh falla con causa concreta en vez de producir una sesión válida pero semánticamente stale.
|
|
13
|
+
- **Rollout recomendado:** publicar `pumuki@6.3.251`, repinear RuralGo y revalidar `pumuki sdd session --refresh --ttl-minutes=90` sobre el slice Android activo.
|
|
14
|
+
|
|
15
|
+
### 2026-05-14 (v6.3.250)
|
|
10
16
|
|
|
11
17
|
- **Normalización iOS mode-aware:** la línea activa conserva reglas iOS automatizables con evidencia concreta y deja como declarativas las reglas greenfield/brownfield que requieren contexto de adopción, baseline o migración.
|
|
12
18
|
- **Package smoke estable para fixtures Git:** los commits y pushes internos de preparación del consumer smoke no disparan hooks del paquete bajo prueba; el gate real sigue validándose en los pasos explícitos del smoke.
|
|
13
19
|
- **Smokes no interactivos sin diálogos macOS:** `PUMUKI_SYSTEM_NOTIFICATIONS=0` y `PUMUKI_NOTIFICATIONS=0` vuelven a apagar el canal de sistema, evitando bloqueos por Swift dialog en validaciones de release.
|
|
14
20
|
- **Zero-violation real:** cualquier finding runtime emitido por regla activa bloquea; los matches scoped sin línea/rango/nodo AST dejan de publicarse como findings advisory.
|
|
15
|
-
- **
|
|
21
|
+
- **AST accionable obligatorio:** los gaps sintéticos de cobertura cross-platform dejan de emitirse como findings sobre `.ai_evidence.json`; la cobertura queda como metadato diagnóstico y las findings runtime deben apuntar a código accionable.
|
|
22
|
+
- **Rollout recomendado:** publicar `pumuki@6.3.250` tras el test suite global verde; `validation:package-smoke`, metadata local y `PRE_WRITE` strict/advisory quedan alineados para esta versión.
|
|
16
23
|
|
|
17
24
|
### 2026-04-25 (v6.3.116)
|
|
18
25
|
|
|
@@ -693,13 +693,7 @@ const collectPreWriteCrossPlatformCriticalViolations = (params: {
|
|
|
693
693
|
return [];
|
|
694
694
|
}
|
|
695
695
|
|
|
696
|
-
return [
|
|
697
|
-
toSkillsViolation(
|
|
698
|
-
params.skillsEnforcement,
|
|
699
|
-
'EVIDENCE_CROSS_PLATFORM_CRITICAL_ENFORCEMENT_INCOMPLETE',
|
|
700
|
-
`Cross-platform critical enforcement incomplete in PRE_WRITE: ${missingCriticalCoverage.join(' | ')}.`
|
|
701
|
-
),
|
|
702
|
-
];
|
|
696
|
+
return [];
|
|
703
697
|
};
|
|
704
698
|
|
|
705
699
|
const toSkillsContractAssessment = (params: {
|
|
@@ -945,15 +939,6 @@ const toSkillsContractAssessment = (params: {
|
|
|
945
939
|
)
|
|
946
940
|
);
|
|
947
941
|
}
|
|
948
|
-
if (!transversalCriticalCovered && requiredAnyTransversalCriticalRuleIds.length > 0) {
|
|
949
|
-
violations.push(
|
|
950
|
-
toSkillsViolation(
|
|
951
|
-
params.skillsEnforcement,
|
|
952
|
-
'EVIDENCE_CROSS_PLATFORM_CRITICAL_ENFORCEMENT_INCOMPLETE',
|
|
953
|
-
`Skills contract missing transversal critical coverage for ${platform}: required_any=[${requiredAnyTransversalCriticalRuleIds.join(', ')}].`
|
|
954
|
-
)
|
|
955
|
-
);
|
|
956
|
-
}
|
|
957
942
|
}
|
|
958
943
|
|
|
959
944
|
return {
|
|
@@ -794,50 +794,7 @@ const toCrossPlatformCriticalEnforcementBlockingFinding = (params: {
|
|
|
794
794
|
return undefined;
|
|
795
795
|
}
|
|
796
796
|
|
|
797
|
-
|
|
798
|
-
const gaps: string[] = [];
|
|
799
|
-
|
|
800
|
-
for (const platform of detectedPlatformKeys) {
|
|
801
|
-
const rulePrefix = PLATFORM_SKILLS_RULE_PREFIXES[platform];
|
|
802
|
-
const criticalSkillRules = params.skillsRules
|
|
803
|
-
.filter(
|
|
804
|
-
(rule) =>
|
|
805
|
-
rule.id.startsWith(rulePrefix) &&
|
|
806
|
-
isCriticalProfileSeverity(rule.severity)
|
|
807
|
-
)
|
|
808
|
-
.map((rule) => rule.id)
|
|
809
|
-
.sort();
|
|
810
|
-
|
|
811
|
-
if (criticalSkillRules.length === 0) {
|
|
812
|
-
gaps.push(`${platform}{critical_profile_rules=missing}`);
|
|
813
|
-
continue;
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
const evaluatedCriticalSkillRules = criticalSkillRules.filter((ruleId) =>
|
|
817
|
-
evaluatedRuleIds.has(ruleId)
|
|
818
|
-
);
|
|
819
|
-
if (evaluatedCriticalSkillRules.length === 0) {
|
|
820
|
-
gaps.push(
|
|
821
|
-
`${platform}{critical_profile_rules=${criticalSkillRules.length}; evaluated=0}`
|
|
822
|
-
);
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
if (gaps.length === 0) {
|
|
827
|
-
return undefined;
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
return {
|
|
831
|
-
ruleId: 'governance.skills.cross-platform-critical.incomplete',
|
|
832
|
-
severity: 'ERROR',
|
|
833
|
-
code: 'SKILLS_CROSS_PLATFORM_CRITICAL_INCOMPLETE_S0',
|
|
834
|
-
message:
|
|
835
|
-
`Cross-platform critical enforcement incomplete at ${params.stage}: ${gaps.join(' | ')}. ` +
|
|
836
|
-
'Ensure each detected platform has critical-profile skill rules active and evaluated.',
|
|
837
|
-
filePath: '.ai_evidence.json',
|
|
838
|
-
matchedBy: 'SkillsCrossPlatformCriticalGuard',
|
|
839
|
-
source: 'skills-cross-platform-critical',
|
|
840
|
-
};
|
|
797
|
+
return undefined;
|
|
841
798
|
};
|
|
842
799
|
|
|
843
800
|
const shouldBlockFromFinding = (finding: Finding | undefined): boolean => {
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
type OpenSpecCompatibilityMigrationResult,
|
|
9
9
|
} from './openSpecBootstrap';
|
|
10
10
|
import { getCurrentPumukiPackageName } from './packageInfo';
|
|
11
|
+
import { emitGateBlockedNotification } from '../notifications/emitAuditSummaryNotification';
|
|
11
12
|
|
|
12
13
|
export type LifecycleUpdateResult = {
|
|
13
14
|
repoRoot: string;
|
|
@@ -28,6 +29,7 @@ export const runLifecycleUpdate = (params?: {
|
|
|
28
29
|
targetSpec?: string;
|
|
29
30
|
git?: ILifecycleGitService;
|
|
30
31
|
npm?: ILifecycleNpmService;
|
|
32
|
+
notifyGateBlocked?: typeof emitGateBlockedNotification;
|
|
31
33
|
}): LifecycleUpdateResult => {
|
|
32
34
|
const git = params?.git ?? new LifecycleGitService();
|
|
33
35
|
const npm = params?.npm ?? new LifecycleNpmService();
|
|
@@ -61,6 +63,7 @@ export const runLifecycleUpdate = (params?: {
|
|
|
61
63
|
cwd: doctorReport.repoRoot,
|
|
62
64
|
git,
|
|
63
65
|
bootstrapOpenSpec: false,
|
|
66
|
+
notifyGateBlocked: params?.notifyGateBlocked,
|
|
64
67
|
});
|
|
65
68
|
return {
|
|
66
69
|
repoRoot: installResult.repoRoot,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, readdirSync } from 'node:fs';
|
|
1
|
+
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
2
2
|
import { resolve } from 'node:path';
|
|
3
3
|
import {
|
|
4
4
|
LifecycleGitService,
|
|
@@ -16,6 +16,11 @@ const SDD_KEYS = {
|
|
|
16
16
|
|
|
17
17
|
const DEFAULT_TTL_MINUTES = 45;
|
|
18
18
|
|
|
19
|
+
const TRACKING_CANDIDATE_FILES = [
|
|
20
|
+
'docs/RURALGO_SEGUIMIENTO.md',
|
|
21
|
+
'RURALGO_SEGUIMIENTO.md',
|
|
22
|
+
] as const;
|
|
23
|
+
|
|
19
24
|
const resolveRepoRoot = (cwd: string, git: ILifecycleGitService): string =>
|
|
20
25
|
git.resolveRepoRoot(cwd);
|
|
21
26
|
|
|
@@ -32,6 +37,51 @@ const parsePositiveMinutes = (value?: number): number =>
|
|
|
32
37
|
const normalizeChangeId = (value: string): string =>
|
|
33
38
|
value.trim().toLowerCase();
|
|
34
39
|
|
|
40
|
+
const collectActiveTrackingChangeIds = (markdown: string): ReadonlyArray<string> => {
|
|
41
|
+
const changeIds: string[] = [];
|
|
42
|
+
const lines = markdown.split(/\r?\n/u);
|
|
43
|
+
for (const line of lines) {
|
|
44
|
+
const boardRowMatch = line.match(/^\|\s*🚧\s*\|\s*([A-Z0-9-]+)\s*\|/u);
|
|
45
|
+
if (boardRowMatch?.[1]) {
|
|
46
|
+
changeIds.push(normalizeChangeId(boardRowMatch[1]));
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
const tableMatch = line.match(
|
|
50
|
+
/^\|\s*\d+\s*\|\s*`([^`]+)`\s*\|.*\|\s*🚧(?:\s+reported\s+activo|\s+En construcción|\s+En construccion)?\s*\|/u
|
|
51
|
+
);
|
|
52
|
+
if (tableMatch?.[1]) {
|
|
53
|
+
changeIds.push(normalizeChangeId(tableMatch[1]));
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const bulletMatch = line.match(/^- 🚧 (`?[A-Z0-9][0-9A-Za-z.-]*`?)/u);
|
|
57
|
+
if (bulletMatch?.[1]) {
|
|
58
|
+
changeIds.push(normalizeChangeId(bulletMatch[1].replace(/`/gu, '')));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return changeIds.filter((changeId) => changeId.length > 0);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const resolveSingleActiveTrackingChangeId = (repoRoot: string): string | undefined => {
|
|
65
|
+
for (const candidate of TRACKING_CANDIDATE_FILES) {
|
|
66
|
+
const candidatePath = resolve(repoRoot, candidate);
|
|
67
|
+
if (!existsSync(candidatePath)) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const changeIds = collectActiveTrackingChangeIds(readFileSync(candidatePath, 'utf8'));
|
|
71
|
+
const uniqueChangeIds = [...new Set(changeIds)];
|
|
72
|
+
if (uniqueChangeIds.length === 1) {
|
|
73
|
+
return uniqueChangeIds[0];
|
|
74
|
+
}
|
|
75
|
+
if (uniqueChangeIds.length > 1) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
`Multiple active tracking changes found in ${candidate}: ${uniqueChangeIds.join(', ')}. ` +
|
|
78
|
+
'Keep exactly one active task before refreshing the SDD session.'
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return undefined;
|
|
83
|
+
};
|
|
84
|
+
|
|
35
85
|
export const listActiveOpenSpecChangeIds = (repoRoot: string): ReadonlyArray<string> => {
|
|
36
86
|
const changesRoot = resolve(repoRoot, 'openspec', 'changes');
|
|
37
87
|
if (!existsSync(changesRoot)) {
|
|
@@ -178,6 +228,24 @@ export const refreshSddSession = (params?: {
|
|
|
178
228
|
if (!current.changeId) {
|
|
179
229
|
throw new Error('No active SDD session to refresh. Run `pumuki sdd session --open --change=<id>` first.');
|
|
180
230
|
}
|
|
231
|
+
const trackingChangeId = resolveSingleActiveTrackingChangeId(repoRoot);
|
|
232
|
+
if (trackingChangeId && trackingChangeId !== current.changeId) {
|
|
233
|
+
const trackingChangeState = ensureChangePath(repoRoot, trackingChangeId);
|
|
234
|
+
if (!trackingChangeState.exists) {
|
|
235
|
+
throw new Error(
|
|
236
|
+
`Active tracking change "${trackingChangeId}" does not exist in openspec/changes. ` +
|
|
237
|
+
`Current SDD session points to "${current.changeId}". ` +
|
|
238
|
+
`Create openspec/changes/${trackingChangeId} or run ` +
|
|
239
|
+
`\`pumuki sdd session --open --change=${trackingChangeId}\` after creating it.`
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
if (trackingChangeState.archived) {
|
|
243
|
+
throw new Error(
|
|
244
|
+
`Active tracking change "${trackingChangeId}" is archived and cannot refresh the SDD session.`
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
git.applyLocalConfig(repoRoot, SDD_KEYS.change, trackingChangeId);
|
|
248
|
+
}
|
|
181
249
|
const ttlMinutes = parsePositiveMinutes(params?.ttlMinutes ?? current.ttlMinutes);
|
|
182
250
|
git.applyLocalConfig(repoRoot, SDD_KEYS.updatedAt, nowIso());
|
|
183
251
|
git.applyLocalConfig(repoRoot, SDD_KEYS.expiresAt, addMinutesIso(ttlMinutes));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pumuki",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.251",
|
|
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": {
|