pumuki 6.3.90 → 6.3.91
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.91)
|
|
2
|
+
- **Observabilidad de skills alineada**: `status --json` y `doctor --json` dejan visible `governanceObservation.skills_contract` cuando la evidencia y el lock efectivo de skills muestran un contrato activo; el lifecycle ya no aparenta `NOT_APPLICABLE` mientras el gate real bloquea violaciones frontend/backend.
|
|
3
|
+
- **Rollout recomendado**: publicar `pumuki@6.3.91`, repin inmediato en `Flux_training` y repetir la repro con rojo frontend staged para confirmar `skills_contract.enforced=true`, `skills_contract.status=FAIL` y `attention_codes` con `SKILLS_CONTRACT_INCOMPLETE`.
|
|
4
|
+
|
|
1
5
|
## 2026-04-20 (v6.3.89)
|
|
2
6
|
- **Aislamiento por worktree**: `pumuki install` detecta worktrees sin `core.hooksPath` explícito y fija un `core.hooksPath` local a `.pumuki/git-hooks`, evitando que los hooks gestionados se escriban en `.git/hooks` del checkout principal compartido.
|
|
3
7
|
- **Rollback limpio**: `pumuki uninstall` retira ese `core.hooksPath` solo si fue creado por Pumuki para el worktree.
|
|
@@ -3,6 +3,7 @@ import { join } from 'node:path';
|
|
|
3
3
|
import { readEvidenceResult } from '../evidence/readEvidence';
|
|
4
4
|
import { readRepoTrackingState } from '../evidence/trackingContract';
|
|
5
5
|
import type { RepoTrackingState } from '../evidence/schema';
|
|
6
|
+
import { loadRequiredSkillsLock } from '../config/skillsEffectiveLock';
|
|
6
7
|
import { readSddStatus } from '../sdd';
|
|
7
8
|
import type { SddStatusPayload } from '../sdd/types';
|
|
8
9
|
import type { LifecycleExperimentalFeaturesSnapshot } from './experimentalFeaturesSnapshot';
|
|
@@ -25,6 +26,37 @@ export type GovernanceEvidenceSummary = {
|
|
|
25
26
|
human_summary_preview: string[];
|
|
26
27
|
};
|
|
27
28
|
|
|
29
|
+
type GovernanceSkillsContractPlatform = 'ios' | 'android' | 'backend' | 'frontend';
|
|
30
|
+
|
|
31
|
+
type GovernanceSkillsContractViolation = {
|
|
32
|
+
code: string;
|
|
33
|
+
message: string;
|
|
34
|
+
severity: 'ERROR' | 'WARN';
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
type GovernanceSkillsContractPlatformRequirement = {
|
|
38
|
+
platform: GovernanceSkillsContractPlatform;
|
|
39
|
+
required_rule_prefix: string;
|
|
40
|
+
required_bundles: ReadonlyArray<string>;
|
|
41
|
+
required_critical_rule_ids: ReadonlyArray<string>;
|
|
42
|
+
required_any_transversal_critical_rule_ids: ReadonlyArray<string>;
|
|
43
|
+
active_prefix_covered: boolean;
|
|
44
|
+
evaluated_prefix_covered: boolean;
|
|
45
|
+
missing_bundles: ReadonlyArray<string>;
|
|
46
|
+
missing_critical_rule_ids: ReadonlyArray<string>;
|
|
47
|
+
transversal_critical_covered: boolean;
|
|
48
|
+
missing_any_transversal_critical_rule_ids: ReadonlyArray<string>;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export type GovernanceSkillsContractSummary = {
|
|
52
|
+
stage: 'PRE_WRITE';
|
|
53
|
+
enforced: boolean;
|
|
54
|
+
status: 'PASS' | 'FAIL' | 'NOT_APPLICABLE';
|
|
55
|
+
detected_platforms: ReadonlyArray<GovernanceSkillsContractPlatform>;
|
|
56
|
+
requirements: ReadonlyArray<GovernanceSkillsContractPlatformRequirement>;
|
|
57
|
+
violations: ReadonlyArray<GovernanceSkillsContractViolation>;
|
|
58
|
+
};
|
|
59
|
+
|
|
28
60
|
export type GovernanceContractSurface = {
|
|
29
61
|
agents_md: boolean;
|
|
30
62
|
skills_lock_json: boolean;
|
|
@@ -54,6 +86,7 @@ export type GovernanceObservationSnapshot = {
|
|
|
54
86
|
};
|
|
55
87
|
enterprise_warn_as_block_env: boolean;
|
|
56
88
|
evidence: GovernanceEvidenceSummary;
|
|
89
|
+
skills_contract: GovernanceSkillsContractSummary;
|
|
57
90
|
git: {
|
|
58
91
|
current_branch: string | null;
|
|
59
92
|
on_protected_branch_hint: boolean;
|
|
@@ -65,6 +98,40 @@ export type GovernanceObservationSnapshot = {
|
|
|
65
98
|
agent_bootstrap_hints: ReadonlyArray<string>;
|
|
66
99
|
};
|
|
67
100
|
|
|
101
|
+
const GOVERNANCE_SKILLS_PLATFORMS = ['ios', 'android', 'backend', 'frontend'] as const;
|
|
102
|
+
const GOVERNANCE_SKILLS_RULE_PREFIXES: Readonly<
|
|
103
|
+
Record<GovernanceSkillsContractPlatform, string>
|
|
104
|
+
> = {
|
|
105
|
+
ios: 'skills.ios.',
|
|
106
|
+
android: 'skills.android.',
|
|
107
|
+
backend: 'skills.backend.',
|
|
108
|
+
frontend: 'skills.frontend.',
|
|
109
|
+
};
|
|
110
|
+
const GOVERNANCE_REQUIRED_SKILLS_BUNDLES: Readonly<
|
|
111
|
+
Record<GovernanceSkillsContractPlatform, ReadonlyArray<string>>
|
|
112
|
+
> = {
|
|
113
|
+
ios: ['ios-guidelines', 'ios-concurrency-guidelines', 'ios-swiftui-expert-guidelines'],
|
|
114
|
+
android: ['android-guidelines'],
|
|
115
|
+
backend: ['backend-guidelines'],
|
|
116
|
+
frontend: ['frontend-guidelines'],
|
|
117
|
+
};
|
|
118
|
+
const GOVERNANCE_CRITICAL_SKILLS_RULES: Readonly<
|
|
119
|
+
Record<GovernanceSkillsContractPlatform, ReadonlyArray<string>>
|
|
120
|
+
> = {
|
|
121
|
+
ios: ['skills.ios.critical-test-quality'],
|
|
122
|
+
android: [],
|
|
123
|
+
backend: [],
|
|
124
|
+
frontend: [],
|
|
125
|
+
};
|
|
126
|
+
const GOVERNANCE_TRANSVERSAL_CRITICAL_SKILLS_RULES: Readonly<
|
|
127
|
+
Record<GovernanceSkillsContractPlatform, ReadonlyArray<string>>
|
|
128
|
+
> = {
|
|
129
|
+
ios: [],
|
|
130
|
+
android: ['skills.android.no-runblocking', 'skills.android.no-thread-sleep'],
|
|
131
|
+
backend: ['skills.backend.no-empty-catch', 'skills.backend.avoid-explicit-any'],
|
|
132
|
+
frontend: ['skills.frontend.no-empty-catch', 'skills.frontend.avoid-explicit-any'],
|
|
133
|
+
};
|
|
134
|
+
|
|
68
135
|
const truthyEnv = (value: string | undefined): boolean => {
|
|
69
136
|
if (typeof value !== 'string') {
|
|
70
137
|
return false;
|
|
@@ -112,6 +179,186 @@ const buildContractSurface = (repoRoot: string): GovernanceContractSurface => ({
|
|
|
112
179
|
pumuki_adapter_json: existsSync(join(repoRoot, '.pumuki', 'adapter.json')),
|
|
113
180
|
});
|
|
114
181
|
|
|
182
|
+
const toRequiredSkillsPlatforms = (repoRoot: string): GovernanceSkillsContractPlatform[] => {
|
|
183
|
+
const requiredLock = loadRequiredSkillsLock(repoRoot);
|
|
184
|
+
if (!requiredLock) {
|
|
185
|
+
return [];
|
|
186
|
+
}
|
|
187
|
+
const detected = new Set<GovernanceSkillsContractPlatform>();
|
|
188
|
+
for (const bundle of requiredLock.bundles) {
|
|
189
|
+
for (const rule of bundle.rules) {
|
|
190
|
+
if (
|
|
191
|
+
rule.platform === 'ios'
|
|
192
|
+
|| rule.platform === 'android'
|
|
193
|
+
|| rule.platform === 'backend'
|
|
194
|
+
|| rule.platform === 'frontend'
|
|
195
|
+
) {
|
|
196
|
+
detected.add(rule.platform);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return GOVERNANCE_SKILLS_PLATFORMS.filter((platform) => detected.has(platform));
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const toCoverageDetectedPlatforms = (
|
|
204
|
+
evidenceResult: ReturnType<typeof readEvidenceResult>
|
|
205
|
+
): GovernanceSkillsContractPlatform[] => {
|
|
206
|
+
if (evidenceResult.kind !== 'valid') {
|
|
207
|
+
return [];
|
|
208
|
+
}
|
|
209
|
+
const explicit = GOVERNANCE_SKILLS_PLATFORMS.filter((platform) => {
|
|
210
|
+
const candidate = evidenceResult.evidence.platforms?.[platform];
|
|
211
|
+
return candidate?.detected === true;
|
|
212
|
+
});
|
|
213
|
+
if (explicit.length > 0) {
|
|
214
|
+
return explicit;
|
|
215
|
+
}
|
|
216
|
+
const coverage = evidenceResult.evidence.snapshot.rules_coverage;
|
|
217
|
+
if (!coverage) {
|
|
218
|
+
return [];
|
|
219
|
+
}
|
|
220
|
+
return GOVERNANCE_SKILLS_PLATFORMS.filter((platform) => {
|
|
221
|
+
const prefix = GOVERNANCE_SKILLS_RULE_PREFIXES[platform];
|
|
222
|
+
return (
|
|
223
|
+
coverage.active_rule_ids.some((ruleId) => ruleId.startsWith(prefix))
|
|
224
|
+
|| coverage.evaluated_rule_ids.some((ruleId) => ruleId.startsWith(prefix))
|
|
225
|
+
);
|
|
226
|
+
});
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
const summarizeSkillsContract = (repoRoot: string): GovernanceSkillsContractSummary => {
|
|
230
|
+
const requiredPlatforms = toRequiredSkillsPlatforms(repoRoot);
|
|
231
|
+
const evidenceResult = readEvidenceResult(repoRoot);
|
|
232
|
+
if (evidenceResult.kind !== 'valid') {
|
|
233
|
+
return {
|
|
234
|
+
stage: 'PRE_WRITE',
|
|
235
|
+
enforced: false,
|
|
236
|
+
status: 'NOT_APPLICABLE',
|
|
237
|
+
detected_platforms: [],
|
|
238
|
+
requirements: [],
|
|
239
|
+
violations: [],
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const coverage = evidenceResult.evidence.snapshot.rules_coverage;
|
|
244
|
+
const detectedPlatforms = toCoverageDetectedPlatforms(evidenceResult);
|
|
245
|
+
const assessmentPlatforms = requiredPlatforms.length > 0 ? requiredPlatforms : detectedPlatforms;
|
|
246
|
+
if (assessmentPlatforms.length === 0) {
|
|
247
|
+
return {
|
|
248
|
+
stage: 'PRE_WRITE',
|
|
249
|
+
enforced: false,
|
|
250
|
+
status: 'NOT_APPLICABLE',
|
|
251
|
+
detected_platforms: [],
|
|
252
|
+
requirements: [],
|
|
253
|
+
violations: [],
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const activeSkillsBundles = new Set(
|
|
258
|
+
(evidenceResult.evidence.rulesets ?? [])
|
|
259
|
+
.filter((ruleset) => ruleset.platform === 'skills')
|
|
260
|
+
.map((ruleset) => {
|
|
261
|
+
const [bundleName] = ruleset.bundle.split('@');
|
|
262
|
+
return bundleName?.trim().toLowerCase() ?? '';
|
|
263
|
+
})
|
|
264
|
+
.filter((bundle) => bundle.length > 0)
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
const requirements: GovernanceSkillsContractPlatformRequirement[] = [];
|
|
268
|
+
const violations: GovernanceSkillsContractViolation[] = [];
|
|
269
|
+
for (const platform of assessmentPlatforms) {
|
|
270
|
+
const requiredRulePrefix = GOVERNANCE_SKILLS_RULE_PREFIXES[platform];
|
|
271
|
+
const requiredBundles = [...GOVERNANCE_REQUIRED_SKILLS_BUNDLES[platform]];
|
|
272
|
+
const requiredCriticalRuleIds = [...GOVERNANCE_CRITICAL_SKILLS_RULES[platform]];
|
|
273
|
+
const requiredAnyTransversalCriticalRuleIds = [
|
|
274
|
+
...GOVERNANCE_TRANSVERSAL_CRITICAL_SKILLS_RULES[platform],
|
|
275
|
+
];
|
|
276
|
+
const activePrefixCovered = coverage
|
|
277
|
+
? coverage.active_rule_ids.some((ruleId) => ruleId.startsWith(requiredRulePrefix))
|
|
278
|
+
: false;
|
|
279
|
+
const evaluatedPrefixCovered = coverage
|
|
280
|
+
? coverage.evaluated_rule_ids.some((ruleId) => ruleId.startsWith(requiredRulePrefix))
|
|
281
|
+
: false;
|
|
282
|
+
const missingBundles = requiredBundles.filter(
|
|
283
|
+
(bundleName) => !activeSkillsBundles.has(bundleName.toLowerCase())
|
|
284
|
+
);
|
|
285
|
+
const missingCriticalRuleIds = coverage
|
|
286
|
+
? requiredCriticalRuleIds.filter((ruleId) => {
|
|
287
|
+
const hasActive = coverage.active_rule_ids.includes(ruleId);
|
|
288
|
+
const hasEvaluated = coverage.evaluated_rule_ids.includes(ruleId);
|
|
289
|
+
return !hasActive || !hasEvaluated;
|
|
290
|
+
})
|
|
291
|
+
: [...requiredCriticalRuleIds];
|
|
292
|
+
const transversalCriticalCovered =
|
|
293
|
+
requiredAnyTransversalCriticalRuleIds.length === 0
|
|
294
|
+
? true
|
|
295
|
+
: Boolean(
|
|
296
|
+
coverage
|
|
297
|
+
&& requiredAnyTransversalCriticalRuleIds.some((ruleId) =>
|
|
298
|
+
coverage.active_rule_ids.includes(ruleId)
|
|
299
|
+
&& coverage.evaluated_rule_ids.includes(ruleId)
|
|
300
|
+
)
|
|
301
|
+
);
|
|
302
|
+
const missingAnyTransversalCriticalRuleIds = transversalCriticalCovered
|
|
303
|
+
? []
|
|
304
|
+
: [...requiredAnyTransversalCriticalRuleIds];
|
|
305
|
+
|
|
306
|
+
requirements.push({
|
|
307
|
+
platform,
|
|
308
|
+
required_rule_prefix: requiredRulePrefix,
|
|
309
|
+
required_bundles: requiredBundles,
|
|
310
|
+
required_critical_rule_ids: requiredCriticalRuleIds,
|
|
311
|
+
required_any_transversal_critical_rule_ids: requiredAnyTransversalCriticalRuleIds,
|
|
312
|
+
active_prefix_covered: activePrefixCovered,
|
|
313
|
+
evaluated_prefix_covered: evaluatedPrefixCovered,
|
|
314
|
+
missing_bundles: missingBundles,
|
|
315
|
+
missing_critical_rule_ids: missingCriticalRuleIds,
|
|
316
|
+
transversal_critical_covered: transversalCriticalCovered,
|
|
317
|
+
missing_any_transversal_critical_rule_ids: missingAnyTransversalCriticalRuleIds,
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
if (!activePrefixCovered || !evaluatedPrefixCovered) {
|
|
321
|
+
violations.push({
|
|
322
|
+
severity: 'ERROR',
|
|
323
|
+
code: 'EVIDENCE_PLATFORM_SKILLS_SCOPE_INCOMPLETE',
|
|
324
|
+
message: `Skills contract scope coverage missing for ${platform}.`,
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
if (missingBundles.length > 0) {
|
|
328
|
+
violations.push({
|
|
329
|
+
severity: 'ERROR',
|
|
330
|
+
code: 'EVIDENCE_PLATFORM_SKILLS_BUNDLES_MISSING',
|
|
331
|
+
message: `Skills contract missing bundles for ${platform}: [${missingBundles.join(', ')}].`,
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
if (missingCriticalRuleIds.length > 0) {
|
|
335
|
+
violations.push({
|
|
336
|
+
severity: 'ERROR',
|
|
337
|
+
code: 'EVIDENCE_PLATFORM_CRITICAL_SKILLS_RULES_MISSING',
|
|
338
|
+
message: `Skills contract missing critical rule coverage for ${platform}: [${missingCriticalRuleIds.join(', ')}].`,
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
if (!transversalCriticalCovered && requiredAnyTransversalCriticalRuleIds.length > 0) {
|
|
342
|
+
violations.push({
|
|
343
|
+
severity: 'ERROR',
|
|
344
|
+
code: 'EVIDENCE_CROSS_PLATFORM_CRITICAL_ENFORCEMENT_INCOMPLETE',
|
|
345
|
+
message:
|
|
346
|
+
`Skills contract missing transversal critical coverage for ${platform}: ` +
|
|
347
|
+
`[${requiredAnyTransversalCriticalRuleIds.join(', ')}].`,
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return {
|
|
353
|
+
stage: 'PRE_WRITE',
|
|
354
|
+
enforced: true,
|
|
355
|
+
status: violations.length === 0 ? 'PASS' : 'FAIL',
|
|
356
|
+
detected_platforms: detectedPlatforms,
|
|
357
|
+
requirements,
|
|
358
|
+
violations,
|
|
359
|
+
};
|
|
360
|
+
};
|
|
361
|
+
|
|
115
362
|
const summarizeEvidence = (repoRoot: string): GovernanceEvidenceSummary => {
|
|
116
363
|
const evidenceResult = readEvidenceResult(repoRoot);
|
|
117
364
|
const path = evidenceResult.source_descriptor.path;
|
|
@@ -193,6 +440,7 @@ export const readGovernanceObservationSnapshot = (params: {
|
|
|
193
440
|
const surface = buildContractSurface(repoRoot);
|
|
194
441
|
const tracking = readRepoTrackingState(repoRoot);
|
|
195
442
|
const warnAsBlock = truthyEnv(process.env.PUMUKI_ENTERPRISE_STRICT_WARN_AS_BLOCK);
|
|
443
|
+
const skillsContract = summarizeSkillsContract(repoRoot);
|
|
196
444
|
|
|
197
445
|
const attention: string[] = [];
|
|
198
446
|
if (evidence.readable === 'invalid') {
|
|
@@ -234,6 +482,9 @@ export const readGovernanceObservationSnapshot = (params: {
|
|
|
234
482
|
if (tracking.enforced && tracking.single_in_progress_valid === false) {
|
|
235
483
|
attention.push('TRACKING_CANONICAL_IN_PROGRESS_INVALID');
|
|
236
484
|
}
|
|
485
|
+
if (skillsContract.status === 'FAIL') {
|
|
486
|
+
attention.push('SKILLS_CONTRACT_INCOMPLETE');
|
|
487
|
+
}
|
|
237
488
|
|
|
238
489
|
let governanceEffective: GovernanceObservationSnapshot['governance_effective'] = 'green';
|
|
239
490
|
if (
|
|
@@ -268,6 +519,7 @@ export const readGovernanceObservationSnapshot = (params: {
|
|
|
268
519
|
},
|
|
269
520
|
enterprise_warn_as_block_env: warnAsBlock,
|
|
270
521
|
evidence,
|
|
522
|
+
skills_contract: skillsContract,
|
|
271
523
|
git: {
|
|
272
524
|
current_branch: branch,
|
|
273
525
|
on_protected_branch_hint: onProtected,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pumuki",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.91",
|
|
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": {
|