pumuki 6.3.97 → 6.3.99

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.
Files changed (96) hide show
  1. package/AGENTS.md +269 -0
  2. package/CHANGELOG.md +697 -0
  3. package/README.md +4 -2
  4. package/VERSION +1 -1
  5. package/docs/README.md +13 -9
  6. package/docs/operations/RELEASE_NOTES.md +12 -76
  7. package/docs/product/HOW_IT_WORKS.md +6 -0
  8. package/docs/product/INSTALLATION.md +1 -1
  9. package/docs/product/USAGE.md +41 -4
  10. package/docs/tracking/plan-curso-pumuki-stack-my-architecture.md +118 -0
  11. package/docs/validation/README.md +6 -3
  12. package/integrations/config/skillsCustomRules.ts +18 -99
  13. package/integrations/evidence/buildEvidence.ts +0 -24
  14. package/integrations/evidence/repoState.ts +0 -3
  15. package/integrations/evidence/schema.ts +0 -18
  16. package/integrations/evidence/writeEvidence.ts +0 -24
  17. package/integrations/gate/evaluateAiGate.ts +15 -232
  18. package/integrations/gate/remediationCatalog.ts +0 -8
  19. package/integrations/git/GitService.ts +44 -5
  20. package/integrations/git/aiGateRepoPolicyFindings.ts +0 -4
  21. package/integrations/git/runPlatformGate.ts +1 -9
  22. package/integrations/git/runPlatformGateFacts.ts +19 -1
  23. package/integrations/git/runPlatformGateOutput.ts +27 -36
  24. package/integrations/lifecycle/adapter.templates.json +7 -13
  25. package/integrations/lifecycle/adapter.ts +0 -24
  26. package/integrations/lifecycle/artifacts.ts +1 -6
  27. package/integrations/lifecycle/audit.ts +101 -0
  28. package/integrations/lifecycle/cli.ts +110 -70
  29. package/integrations/lifecycle/cliSdd.ts +13 -8
  30. package/integrations/lifecycle/doctor.ts +16 -48
  31. package/integrations/lifecycle/hookManager.ts +0 -77
  32. package/integrations/lifecycle/index.ts +2 -0
  33. package/integrations/lifecycle/install.ts +0 -21
  34. package/integrations/lifecycle/npmService.ts +3 -155
  35. package/integrations/lifecycle/policyValidationSnapshot.ts +8 -2
  36. package/integrations/lifecycle/preWriteAutomation.ts +7 -77
  37. package/integrations/lifecycle/state.ts +1 -8
  38. package/integrations/lifecycle/status.ts +2 -29
  39. package/integrations/mcp/aiGateCheck.ts +26 -206
  40. package/integrations/mcp/autoExecuteAiStart.ts +87 -94
  41. package/integrations/mcp/enterpriseServer.ts +7 -23
  42. package/integrations/mcp/enterpriseStdioServer.cli.ts +4 -31
  43. package/integrations/mcp/preFlightCheck.ts +5 -51
  44. package/integrations/platform/detectPlatforms.ts +37 -0
  45. package/integrations/policy/experimentalFeatures.ts +1 -1
  46. package/integrations/sdd/evidenceScaffold.ts +2 -109
  47. package/package.json +10 -2
  48. package/scripts/check-tracking-single-active.sh +1 -1
  49. package/scripts/consumer-menu-matrix-baseline-report-lib.ts +13 -38
  50. package/scripts/consumer-postinstall-resolve-args.cjs +44 -0
  51. package/scripts/consumer-postinstall.cjs +76 -21
  52. package/scripts/framework-menu-advanced-view-lib.ts +0 -15
  53. package/scripts/framework-menu-consumer-actions-lib.ts +28 -4
  54. package/scripts/framework-menu-consumer-preflight-hints.ts +5 -2
  55. package/scripts/framework-menu-consumer-preflight-render.ts +0 -10
  56. package/scripts/framework-menu-consumer-preflight-run.ts +0 -23
  57. package/scripts/framework-menu-consumer-preflight-types.ts +0 -12
  58. package/scripts/framework-menu-consumer-runtime-actions.ts +87 -17
  59. package/scripts/framework-menu-consumer-runtime-audit.ts +36 -2
  60. package/scripts/framework-menu-consumer-runtime-evidence-classic.ts +140 -0
  61. package/scripts/framework-menu-consumer-runtime-lib.ts +2 -10
  62. package/scripts/framework-menu-consumer-runtime-menu.ts +4 -18
  63. package/scripts/framework-menu-consumer-runtime-types.ts +3 -3
  64. package/scripts/framework-menu-evidence-summary-lib.ts +1 -0
  65. package/scripts/framework-menu-evidence-summary-read.ts +57 -5
  66. package/scripts/framework-menu-evidence-summary-severity.ts +3 -1
  67. package/scripts/framework-menu-evidence-summary-types.ts +7 -0
  68. package/scripts/framework-menu-gate-lib.ts +9 -0
  69. package/scripts/framework-menu-layout-data.ts +5 -0
  70. package/scripts/framework-menu-matrix-baseline-lib.ts +15 -14
  71. package/scripts/framework-menu-matrix-canary-lib.ts +22 -1
  72. package/scripts/framework-menu-matrix-evidence-lib.ts +1 -0
  73. package/scripts/framework-menu-matrix-evidence-types.ts +13 -1
  74. package/scripts/framework-menu-matrix-runner-lib.ts +35 -0
  75. package/scripts/framework-menu-system-notifications-cause.ts +0 -24
  76. package/scripts/framework-menu-system-notifications-macos-swift-source.ts +24 -204
  77. package/scripts/framework-menu-system-notifications-macos.ts +4 -0
  78. package/scripts/framework-menu-system-notifications-payloads-blocked.ts +1 -1
  79. package/scripts/framework-menu-system-notifications-remediation.ts +13 -24
  80. package/scripts/framework-menu-system-notifications-text.ts +1 -7
  81. package/scripts/framework-menu.ts +3 -2
  82. package/scripts/package-install-smoke-consumer-git-repo-lib.ts +1 -10
  83. package/scripts/package-install-smoke-consumer-npm-lib.ts +9 -46
  84. package/scripts/pumuki-full-surface-smoke-lib.ts +37 -0
  85. package/scripts/pumuki-full-surface-smoke.ts +346 -0
  86. package/scripts/pumuki-smoke-installed-wrapper.cjs +31 -0
  87. package/integrations/evidence/trackingContract.ts +0 -150
  88. package/integrations/gate/governanceActionCatalog.ts +0 -275
  89. package/integrations/lifecycle/bootstrapManifest.ts +0 -248
  90. package/integrations/lifecycle/cliGovernanceConsole.ts +0 -69
  91. package/integrations/lifecycle/governanceNextAction.ts +0 -164
  92. package/integrations/lifecycle/governanceObservationSnapshot.ts +0 -613
  93. package/integrations/mcp/alignedPlatformGate.ts +0 -232
  94. package/integrations/mcp/readMcpPrePushStdin.ts +0 -7
  95. package/scripts/build-ruralgo-s1-evidence-pack.ts +0 -85
  96. package/scripts/ruralgo-s1-evidence-pack-lib.ts +0 -200
@@ -1,613 +0,0 @@
1
- import { existsSync } from 'node:fs';
2
- import { join } from 'node:path';
3
- import { readEvidenceResult } from '../evidence/readEvidence';
4
- import { readRepoTrackingState } from '../evidence/trackingContract';
5
- import type { RepoTrackingState } from '../evidence/schema';
6
- import { loadRequiredSkillsLock } from '../config/skillsEffectiveLock';
7
- import {
8
- resolveSkillImportSourcesWithDiagnostics,
9
- type SkillImportSourceDiagnostic,
10
- } from '../config/skillsCustomRules';
11
- import { readSddStatus } from '../sdd';
12
- import type { SddStatusPayload } from '../sdd/types';
13
- import type { LifecycleExperimentalFeaturesSnapshot } from './experimentalFeaturesSnapshot';
14
- import type { ILifecycleGitService } from './gitService';
15
- import { LifecycleGitService } from './gitService';
16
- import type { LifecyclePolicyValidationSnapshot } from './policyValidationSnapshot';
17
- import { writeInfo } from './cliOutputs';
18
-
19
- const DEFAULT_PROTECTED_BRANCHES = new Set(['main', 'master', 'develop', 'dev']);
20
-
21
- export type GovernanceEvidenceSummary = {
22
- path: string;
23
- readable: 'missing' | 'invalid' | 'valid';
24
- snapshot_stage?: string;
25
- snapshot_outcome?: 'PASS' | 'WARN' | 'BLOCK';
26
- matched_warn_count?: number;
27
- matched_blocking_count?: number;
28
- findings_count?: number;
29
- ai_gate_status?: 'ALLOWED' | 'BLOCKED';
30
- human_summary_preview: string[];
31
- };
32
-
33
- type GovernanceSkillsContractPlatform = 'ios' | 'android' | 'backend' | 'frontend';
34
-
35
- type GovernanceSkillsContractViolation = {
36
- code: string;
37
- message: string;
38
- severity: 'ERROR' | 'WARN';
39
- };
40
-
41
- type GovernanceSkillsContractPlatformRequirement = {
42
- platform: GovernanceSkillsContractPlatform;
43
- required_rule_prefix: string;
44
- required_bundles: ReadonlyArray<string>;
45
- required_critical_rule_ids: ReadonlyArray<string>;
46
- required_any_transversal_critical_rule_ids: ReadonlyArray<string>;
47
- active_prefix_covered: boolean;
48
- evaluated_prefix_covered: boolean;
49
- missing_bundles: ReadonlyArray<string>;
50
- missing_critical_rule_ids: ReadonlyArray<string>;
51
- transversal_critical_covered: boolean;
52
- missing_any_transversal_critical_rule_ids: ReadonlyArray<string>;
53
- };
54
-
55
- export type GovernanceSkillsContractSummary = {
56
- stage: 'PRE_WRITE';
57
- enforced: boolean;
58
- status: 'PASS' | 'FAIL' | 'NOT_APPLICABLE';
59
- detected_platforms: ReadonlyArray<GovernanceSkillsContractPlatform>;
60
- requirements: ReadonlyArray<GovernanceSkillsContractPlatformRequirement>;
61
- source_diagnostics: ReadonlyArray<SkillImportSourceDiagnostic>;
62
- violations: ReadonlyArray<GovernanceSkillsContractViolation>;
63
- };
64
-
65
- export type GovernanceContractSurface = {
66
- agents_md: boolean;
67
- skills_lock_json: boolean;
68
- skills_sources_json: boolean;
69
- vendor_skills_dir: boolean;
70
- pumuki_adapter_json: boolean;
71
- };
72
-
73
- export type GovernanceObservationSnapshot = {
74
- schema_version: '1';
75
- sdd: {
76
- experimental_raw: string | null;
77
- effective_mode: 'off' | 'advisory' | 'strict';
78
- experimental_source: string;
79
- };
80
- sdd_session: {
81
- active: boolean;
82
- valid: boolean;
83
- change_id: string | null;
84
- remaining_seconds: number | null;
85
- };
86
- policy_strict: {
87
- pre_write: boolean;
88
- pre_commit: boolean;
89
- pre_push: boolean;
90
- ci: boolean;
91
- };
92
- enterprise_warn_as_block_env: boolean;
93
- evidence: GovernanceEvidenceSummary;
94
- skills_contract: GovernanceSkillsContractSummary;
95
- git: {
96
- current_branch: string | null;
97
- on_protected_branch_hint: boolean;
98
- };
99
- contract_surface: GovernanceContractSurface;
100
- tracking: RepoTrackingState;
101
- attention_codes: ReadonlyArray<string>;
102
- governance_effective: 'green' | 'attention' | 'blocked';
103
- agent_bootstrap_hints: ReadonlyArray<string>;
104
- };
105
-
106
- const GOVERNANCE_SKILLS_PLATFORMS = ['ios', 'android', 'backend', 'frontend'] as const;
107
- const GOVERNANCE_SKILLS_RULE_PREFIXES: Readonly<
108
- Record<GovernanceSkillsContractPlatform, string>
109
- > = {
110
- ios: 'skills.ios.',
111
- android: 'skills.android.',
112
- backend: 'skills.backend.',
113
- frontend: 'skills.frontend.',
114
- };
115
- const GOVERNANCE_REQUIRED_SKILLS_BUNDLES: Readonly<
116
- Record<GovernanceSkillsContractPlatform, ReadonlyArray<string>>
117
- > = {
118
- ios: ['ios-guidelines', 'ios-concurrency-guidelines', 'ios-swiftui-expert-guidelines'],
119
- android: ['android-guidelines'],
120
- backend: ['backend-guidelines'],
121
- frontend: ['frontend-guidelines'],
122
- };
123
- const GOVERNANCE_CRITICAL_SKILLS_RULES: Readonly<
124
- Record<GovernanceSkillsContractPlatform, ReadonlyArray<string>>
125
- > = {
126
- ios: ['skills.ios.critical-test-quality'],
127
- android: [],
128
- backend: [],
129
- frontend: [],
130
- };
131
- const GOVERNANCE_TRANSVERSAL_CRITICAL_SKILLS_RULES: Readonly<
132
- Record<GovernanceSkillsContractPlatform, ReadonlyArray<string>>
133
- > = {
134
- ios: [],
135
- android: ['skills.android.no-runblocking', 'skills.android.no-thread-sleep'],
136
- backend: ['skills.backend.no-empty-catch', 'skills.backend.avoid-explicit-any'],
137
- frontend: ['skills.frontend.no-empty-catch', 'skills.frontend.avoid-explicit-any'],
138
- };
139
-
140
- const truthyEnv = (value: string | undefined): boolean => {
141
- if (typeof value !== 'string') {
142
- return false;
143
- }
144
- const normalized = value.trim().toLowerCase();
145
- return normalized === '1' || normalized === 'true' || normalized === 'yes' || normalized === 'strict';
146
- };
147
-
148
- const readCurrentBranch = (git: ILifecycleGitService, repoRoot: string): string | null => {
149
- try {
150
- const branch = git.runGit(['rev-parse', '--abbrev-ref', 'HEAD'], repoRoot).trim();
151
- return branch.length > 0 ? branch : null;
152
- } catch {
153
- return null;
154
- }
155
- };
156
-
157
- const readSddStatusSafe = (repoRoot: string): SddStatusPayload => {
158
- try {
159
- return readSddStatus(repoRoot);
160
- } catch {
161
- return {
162
- repoRoot,
163
- openspec: {
164
- installed: false,
165
- projectInitialized: false,
166
- minimumVersion: '0.0.0',
167
- recommendedVersion: '0.0.0',
168
- compatible: false,
169
- },
170
- session: {
171
- repoRoot,
172
- active: false,
173
- valid: false,
174
- },
175
- };
176
- }
177
- };
178
-
179
- const buildContractSurface = (repoRoot: string): GovernanceContractSurface => ({
180
- agents_md: existsSync(join(repoRoot, 'AGENTS.md')),
181
- skills_lock_json: existsSync(join(repoRoot, 'skills.lock.json')),
182
- skills_sources_json: existsSync(join(repoRoot, 'skills.sources.json')),
183
- vendor_skills_dir: existsSync(join(repoRoot, 'vendor', 'skills')),
184
- pumuki_adapter_json: existsSync(join(repoRoot, '.pumuki', 'adapter.json')),
185
- });
186
-
187
- const toRequiredSkillsPlatforms = (repoRoot: string): GovernanceSkillsContractPlatform[] => {
188
- const requiredLock = loadRequiredSkillsLock(repoRoot);
189
- if (!requiredLock) {
190
- return [];
191
- }
192
- const detected = new Set<GovernanceSkillsContractPlatform>();
193
- for (const bundle of requiredLock.bundles) {
194
- for (const rule of bundle.rules) {
195
- if (
196
- rule.platform === 'ios'
197
- || rule.platform === 'android'
198
- || rule.platform === 'backend'
199
- || rule.platform === 'frontend'
200
- ) {
201
- detected.add(rule.platform);
202
- }
203
- }
204
- }
205
- return GOVERNANCE_SKILLS_PLATFORMS.filter((platform) => detected.has(platform));
206
- };
207
-
208
- const toCoverageDetectedPlatforms = (
209
- evidenceResult: ReturnType<typeof readEvidenceResult>
210
- ): GovernanceSkillsContractPlatform[] => {
211
- if (evidenceResult.kind !== 'valid') {
212
- return [];
213
- }
214
- const explicit = GOVERNANCE_SKILLS_PLATFORMS.filter((platform) => {
215
- const candidate = evidenceResult.evidence.platforms?.[platform];
216
- return candidate?.detected === true;
217
- });
218
- if (explicit.length > 0) {
219
- return explicit;
220
- }
221
- const coverage = evidenceResult.evidence.snapshot.rules_coverage;
222
- if (!coverage) {
223
- return [];
224
- }
225
- return GOVERNANCE_SKILLS_PLATFORMS.filter((platform) => {
226
- const prefix = GOVERNANCE_SKILLS_RULE_PREFIXES[platform];
227
- return (
228
- coverage.active_rule_ids.some((ruleId) => ruleId.startsWith(prefix))
229
- || coverage.evaluated_rule_ids.some((ruleId) => ruleId.startsWith(prefix))
230
- );
231
- });
232
- };
233
-
234
- const summarizeSkillsContract = (repoRoot: string): GovernanceSkillsContractSummary => {
235
- const requiredPlatforms = toRequiredSkillsPlatforms(repoRoot);
236
- const sourceDiagnostics = resolveSkillImportSourcesWithDiagnostics({ repoRoot }).diagnostics;
237
- const evidenceResult = readEvidenceResult(repoRoot);
238
- if (evidenceResult.kind !== 'valid') {
239
- return {
240
- stage: 'PRE_WRITE',
241
- enforced: requiredPlatforms.length > 0 || sourceDiagnostics.length > 0,
242
- status:
243
- requiredPlatforms.length > 0 || sourceDiagnostics.length > 0
244
- ? 'FAIL'
245
- : 'NOT_APPLICABLE',
246
- detected_platforms: [],
247
- requirements: [],
248
- source_diagnostics: sourceDiagnostics,
249
- violations: sourceDiagnostics.map((diagnostic) => ({
250
- severity: 'ERROR',
251
- code:
252
- diagnostic.issue === 'missing'
253
- ? 'SKILLS_REQUIRED_SOURCE_MISSING'
254
- : 'SKILLS_REQUIRED_SOURCE_UNREADABLE',
255
- message:
256
- `La skill requerida "${diagnostic.skillName}" no está disponible en ` +
257
- `${diagnostic.sourcePath}. ${diagnostic.resolution}`,
258
- })),
259
- };
260
- }
261
-
262
- const coverage = evidenceResult.evidence.snapshot.rules_coverage;
263
- const detectedPlatforms = toCoverageDetectedPlatforms(evidenceResult);
264
- const assessmentPlatforms = requiredPlatforms.length > 0 ? requiredPlatforms : detectedPlatforms;
265
- if (assessmentPlatforms.length === 0) {
266
- return {
267
- stage: 'PRE_WRITE',
268
- enforced: sourceDiagnostics.length > 0,
269
- status: sourceDiagnostics.length > 0 ? 'FAIL' : 'NOT_APPLICABLE',
270
- detected_platforms: [],
271
- requirements: [],
272
- source_diagnostics: sourceDiagnostics,
273
- violations: sourceDiagnostics.map((diagnostic) => ({
274
- severity: 'ERROR',
275
- code:
276
- diagnostic.issue === 'missing'
277
- ? 'SKILLS_REQUIRED_SOURCE_MISSING'
278
- : 'SKILLS_REQUIRED_SOURCE_UNREADABLE',
279
- message:
280
- `La skill requerida "${diagnostic.skillName}" no está disponible en ` +
281
- `${diagnostic.sourcePath}. ${diagnostic.resolution}`,
282
- })),
283
- };
284
- }
285
-
286
- const activeSkillsBundles = new Set(
287
- (evidenceResult.evidence.rulesets ?? [])
288
- .filter((ruleset) => ruleset.platform === 'skills')
289
- .map((ruleset) => {
290
- const [bundleName] = ruleset.bundle.split('@');
291
- return bundleName?.trim().toLowerCase() ?? '';
292
- })
293
- .filter((bundle) => bundle.length > 0)
294
- );
295
-
296
- const requirements: GovernanceSkillsContractPlatformRequirement[] = [];
297
- const violations: GovernanceSkillsContractViolation[] = [];
298
- for (const diagnostic of sourceDiagnostics) {
299
- violations.push({
300
- severity: 'ERROR',
301
- code:
302
- diagnostic.issue === 'missing'
303
- ? 'SKILLS_REQUIRED_SOURCE_MISSING'
304
- : 'SKILLS_REQUIRED_SOURCE_UNREADABLE',
305
- message:
306
- `La skill requerida "${diagnostic.skillName}" no está disponible en ` +
307
- `${diagnostic.sourcePath}. ${diagnostic.resolution}`,
308
- });
309
- }
310
- for (const platform of assessmentPlatforms) {
311
- const requiredRulePrefix = GOVERNANCE_SKILLS_RULE_PREFIXES[platform];
312
- const requiredBundles = [...GOVERNANCE_REQUIRED_SKILLS_BUNDLES[platform]];
313
- const requiredCriticalRuleIds = [...GOVERNANCE_CRITICAL_SKILLS_RULES[platform]];
314
- const requiredAnyTransversalCriticalRuleIds = [
315
- ...GOVERNANCE_TRANSVERSAL_CRITICAL_SKILLS_RULES[platform],
316
- ];
317
- const activePrefixCovered = coverage
318
- ? coverage.active_rule_ids.some((ruleId) => ruleId.startsWith(requiredRulePrefix))
319
- : false;
320
- const evaluatedPrefixCovered = coverage
321
- ? coverage.evaluated_rule_ids.some((ruleId) => ruleId.startsWith(requiredRulePrefix))
322
- : false;
323
- const missingBundles = requiredBundles.filter(
324
- (bundleName) => !activeSkillsBundles.has(bundleName.toLowerCase())
325
- );
326
- const missingCriticalRuleIds = coverage
327
- ? requiredCriticalRuleIds.filter((ruleId) => {
328
- const hasActive = coverage.active_rule_ids.includes(ruleId);
329
- const hasEvaluated = coverage.evaluated_rule_ids.includes(ruleId);
330
- return !hasActive || !hasEvaluated;
331
- })
332
- : [...requiredCriticalRuleIds];
333
- const transversalCriticalCovered =
334
- requiredAnyTransversalCriticalRuleIds.length === 0
335
- ? true
336
- : Boolean(
337
- coverage
338
- && requiredAnyTransversalCriticalRuleIds.some((ruleId) =>
339
- coverage.active_rule_ids.includes(ruleId)
340
- && coverage.evaluated_rule_ids.includes(ruleId)
341
- )
342
- );
343
- const missingAnyTransversalCriticalRuleIds = transversalCriticalCovered
344
- ? []
345
- : [...requiredAnyTransversalCriticalRuleIds];
346
-
347
- requirements.push({
348
- platform,
349
- required_rule_prefix: requiredRulePrefix,
350
- required_bundles: requiredBundles,
351
- required_critical_rule_ids: requiredCriticalRuleIds,
352
- required_any_transversal_critical_rule_ids: requiredAnyTransversalCriticalRuleIds,
353
- active_prefix_covered: activePrefixCovered,
354
- evaluated_prefix_covered: evaluatedPrefixCovered,
355
- missing_bundles: missingBundles,
356
- missing_critical_rule_ids: missingCriticalRuleIds,
357
- transversal_critical_covered: transversalCriticalCovered,
358
- missing_any_transversal_critical_rule_ids: missingAnyTransversalCriticalRuleIds,
359
- });
360
-
361
- if (!activePrefixCovered || !evaluatedPrefixCovered) {
362
- violations.push({
363
- severity: 'ERROR',
364
- code: 'EVIDENCE_PLATFORM_SKILLS_SCOPE_INCOMPLETE',
365
- message: `Skills contract scope coverage missing for ${platform}.`,
366
- });
367
- }
368
- if (missingBundles.length > 0) {
369
- violations.push({
370
- severity: 'ERROR',
371
- code: 'EVIDENCE_PLATFORM_SKILLS_BUNDLES_MISSING',
372
- message: `Skills contract missing bundles for ${platform}: [${missingBundles.join(', ')}].`,
373
- });
374
- }
375
- if (missingCriticalRuleIds.length > 0) {
376
- violations.push({
377
- severity: 'ERROR',
378
- code: 'EVIDENCE_PLATFORM_CRITICAL_SKILLS_RULES_MISSING',
379
- message: `Skills contract missing critical rule coverage for ${platform}: [${missingCriticalRuleIds.join(', ')}].`,
380
- });
381
- }
382
- if (!transversalCriticalCovered && requiredAnyTransversalCriticalRuleIds.length > 0) {
383
- violations.push({
384
- severity: 'ERROR',
385
- code: 'EVIDENCE_CROSS_PLATFORM_CRITICAL_ENFORCEMENT_INCOMPLETE',
386
- message:
387
- `Skills contract missing transversal critical coverage for ${platform}: ` +
388
- `[${requiredAnyTransversalCriticalRuleIds.join(', ')}].`,
389
- });
390
- }
391
- }
392
-
393
- return {
394
- stage: 'PRE_WRITE',
395
- enforced: true,
396
- status: violations.length === 0 ? 'PASS' : 'FAIL',
397
- detected_platforms: detectedPlatforms,
398
- requirements,
399
- source_diagnostics: sourceDiagnostics,
400
- violations,
401
- };
402
- };
403
-
404
- const summarizeEvidence = (repoRoot: string): GovernanceEvidenceSummary => {
405
- const evidenceResult = readEvidenceResult(repoRoot);
406
- const path = evidenceResult.source_descriptor.path;
407
- if (evidenceResult.kind === 'missing') {
408
- return { path, readable: 'missing', human_summary_preview: [] };
409
- }
410
- if (evidenceResult.kind === 'invalid') {
411
- return {
412
- path,
413
- readable: 'invalid',
414
- human_summary_preview: [evidenceResult.detail ?? evidenceResult.reason],
415
- };
416
- }
417
-
418
- const snapshot = evidenceResult.evidence.snapshot;
419
- const hints = evidenceResult.evidence.operational_hints?.human_summary_lines ?? [];
420
- const breakdown = evidenceResult.evidence.operational_hints?.rule_execution_breakdown;
421
- return {
422
- path,
423
- readable: 'valid',
424
- snapshot_stage: snapshot.stage,
425
- snapshot_outcome: snapshot.outcome,
426
- matched_warn_count: breakdown?.matched_warn_count,
427
- matched_blocking_count: breakdown?.matched_blocking_count,
428
- findings_count: Array.isArray(snapshot.findings) ? snapshot.findings.length : 0,
429
- ai_gate_status: evidenceResult.evidence.ai_gate.status,
430
- human_summary_preview: hints.slice(0, 5),
431
- };
432
- };
433
-
434
- const buildHints = (
435
- surface: GovernanceContractSurface,
436
- branch: string | null,
437
- protectedBranchHint: boolean,
438
- tracking: RepoTrackingState
439
- ): string[] => {
440
- const hints: string[] = [];
441
- if (surface.agents_md) {
442
- hints.push('AGENTS.md presente: aplica el contrato del repo antes de dar governance en verde.');
443
- }
444
- if (!surface.skills_lock_json) {
445
- hints.push('Falta skills.lock.json: genera lock canónico de skills antes de cerrar la gobernanza.');
446
- }
447
- if (!surface.pumuki_adapter_json) {
448
- hints.push('Falta .pumuki/adapter.json: instala el adaptador si quieres wiring IDE/MCP explícito.');
449
- }
450
- if (protectedBranchHint && branch) {
451
- hints.push(`La rama "${branch}" cae en el set protegido por defecto: usa feature/* o refactor/*.`);
452
- }
453
- if (tracking.conflict) {
454
- hints.push('Tracking canónico en conflicto: AGENTS.md y los README del repo no apuntan al mismo MD.');
455
- }
456
- if (tracking.enforced && !tracking.canonical_present) {
457
- hints.push(`Falta el tracking canónico declarado (${tracking.canonical_path ?? 'sin resolver'}).`);
458
- }
459
- if (tracking.enforced && tracking.single_in_progress_valid === false) {
460
- hints.push(
461
- `El tracking canónico debe dejar exactamente una 🚧 (actual=${tracking.in_progress_count ?? 'n/a'}).`
462
- );
463
- }
464
- hints.push('SDD/OpenSpec: usa PUMUKI_EXPERIMENTAL_SDD=advisory|strict cuando el loop SDD esté activo.');
465
- hints.push('WARN-as-BLOCK: activa PUMUKI_ENTERPRISE_STRICT_WARN_AS_BLOCK=1 si el repo exige promoción dura.');
466
- return hints;
467
- };
468
-
469
- export const readGovernanceObservationSnapshot = (params: {
470
- repoRoot: string;
471
- experimentalFeatures: LifecycleExperimentalFeaturesSnapshot;
472
- policyValidation: LifecyclePolicyValidationSnapshot;
473
- git?: ILifecycleGitService;
474
- }): GovernanceObservationSnapshot => {
475
- const git = params.git ?? new LifecycleGitService();
476
- const { repoRoot, experimentalFeatures, policyValidation } = params;
477
- const rawSdd = process.env.PUMUKI_EXPERIMENTAL_SDD?.trim();
478
- const sddStatus = readSddStatusSafe(repoRoot);
479
- const evidence = summarizeEvidence(repoRoot);
480
- const branch = readCurrentBranch(git, repoRoot);
481
- const onProtected = typeof branch === 'string' && DEFAULT_PROTECTED_BRANCHES.has(branch.trim().toLowerCase());
482
- const surface = buildContractSurface(repoRoot);
483
- const tracking = readRepoTrackingState(repoRoot);
484
- const warnAsBlock = truthyEnv(process.env.PUMUKI_ENTERPRISE_STRICT_WARN_AS_BLOCK);
485
- const skillsContract = summarizeSkillsContract(repoRoot);
486
-
487
- const attention: string[] = [];
488
- if (evidence.readable === 'invalid') {
489
- attention.push('EVIDENCE_INVALID_OR_CHAIN');
490
- }
491
- if (evidence.readable === 'valid' && evidence.ai_gate_status === 'BLOCKED') {
492
- attention.push('AI_GATE_BLOCKED');
493
- }
494
- if (evidence.readable === 'valid' && evidence.snapshot_outcome === 'WARN') {
495
- attention.push('EVIDENCE_SNAPSHOT_WARN');
496
- }
497
- if (evidence.readable === 'valid' && evidence.snapshot_outcome === 'BLOCK') {
498
- attention.push('EVIDENCE_SNAPSHOT_BLOCK');
499
- }
500
- if (sddStatus.session.active === true && sddStatus.session.valid !== true) {
501
- attention.push('SDD_SESSION_INVALID_OR_EXPIRED');
502
- }
503
- if (!policyValidation.stages.PRE_WRITE.strict) {
504
- attention.push('POLICY_PRE_WRITE_NOT_STRICT');
505
- }
506
- if (!policyValidation.stages.PRE_COMMIT.strict) {
507
- attention.push('POLICY_PRE_COMMIT_NOT_STRICT');
508
- }
509
- if (!policyValidation.stages.PRE_PUSH.strict) {
510
- attention.push('POLICY_PRE_PUSH_NOT_STRICT');
511
- }
512
- if (!policyValidation.stages.CI.strict) {
513
- attention.push('POLICY_CI_NOT_STRICT');
514
- }
515
- if (onProtected) {
516
- attention.push('GITFLOW_PROTECTED_BRANCH_CONTEXT');
517
- }
518
- if (tracking.conflict) {
519
- attention.push('TRACKING_CANONICAL_SOURCE_CONFLICT');
520
- }
521
- if (tracking.enforced && !tracking.canonical_present) {
522
- attention.push('TRACKING_CANONICAL_FILE_MISSING');
523
- }
524
- if (tracking.enforced && tracking.single_in_progress_valid === false) {
525
- attention.push('TRACKING_CANONICAL_IN_PROGRESS_INVALID');
526
- }
527
- if (skillsContract.status === 'FAIL') {
528
- attention.push('SKILLS_CONTRACT_INCOMPLETE');
529
- }
530
- if (skillsContract.source_diagnostics.length > 0) {
531
- attention.push('SKILLS_REQUIRED_SOURCE_INVALID');
532
- }
533
-
534
- let governanceEffective: GovernanceObservationSnapshot['governance_effective'] = 'green';
535
- if (
536
- evidence.readable === 'invalid'
537
- || (evidence.readable === 'valid' && evidence.ai_gate_status === 'BLOCKED')
538
- || (evidence.readable === 'valid' && evidence.snapshot_outcome === 'BLOCK')
539
- || skillsContract.source_diagnostics.length > 0
540
- ) {
541
- governanceEffective = 'blocked';
542
- } else if (attention.length > 0) {
543
- governanceEffective = 'attention';
544
- }
545
-
546
- return {
547
- schema_version: '1',
548
- sdd: {
549
- experimental_raw: rawSdd && rawSdd.length > 0 ? rawSdd : null,
550
- effective_mode: experimentalFeatures.features.sdd.mode,
551
- experimental_source: experimentalFeatures.features.sdd.source,
552
- },
553
- sdd_session: {
554
- active: sddStatus.session.active,
555
- valid: sddStatus.session.valid,
556
- change_id: sddStatus.session.changeId ?? null,
557
- remaining_seconds:
558
- typeof sddStatus.session.remainingSeconds === 'number' ? sddStatus.session.remainingSeconds : null,
559
- },
560
- policy_strict: {
561
- pre_write: policyValidation.stages.PRE_WRITE.strict,
562
- pre_commit: policyValidation.stages.PRE_COMMIT.strict,
563
- pre_push: policyValidation.stages.PRE_PUSH.strict,
564
- ci: policyValidation.stages.CI.strict,
565
- },
566
- enterprise_warn_as_block_env: warnAsBlock,
567
- evidence,
568
- skills_contract: skillsContract,
569
- git: {
570
- current_branch: branch,
571
- on_protected_branch_hint: onProtected,
572
- },
573
- contract_surface: surface,
574
- tracking,
575
- attention_codes: attention,
576
- governance_effective: governanceEffective,
577
- agent_bootstrap_hints: buildHints(surface, branch, onProtected, tracking),
578
- };
579
- };
580
-
581
- export const buildGovernanceObservationSummaryLines = (
582
- snapshot: GovernanceObservationSnapshot
583
- ): string[] => {
584
- const lines = [
585
- `Governance: ${snapshot.governance_effective.toUpperCase()}`,
586
- `Contract: AGENTS=${snapshot.contract_surface.agents_md ? 'yes' : 'no'} skills.lock=${snapshot.contract_surface.skills_lock_json ? 'yes' : 'no'} skills.sources=${snapshot.contract_surface.skills_sources_json ? 'yes' : 'no'} vendor/skills=${snapshot.contract_surface.vendor_skills_dir ? 'yes' : 'no'} adapter=${snapshot.contract_surface.pumuki_adapter_json ? 'yes' : 'no'}`,
587
- `SDD: env=${snapshot.sdd.experimental_raw ?? '(unset)'} effective=${snapshot.sdd.effective_mode} session_active=${snapshot.sdd_session.active} session_valid=${snapshot.sdd_session.valid} change=${snapshot.sdd_session.change_id ?? 'none'}`,
588
- `Evidence: readable=${snapshot.evidence.readable} stage=${snapshot.evidence.snapshot_stage ?? 'n/a'} outcome=${snapshot.evidence.snapshot_outcome ?? 'n/a'} ai_gate=${snapshot.evidence.ai_gate_status ?? 'n/a'} findings=${snapshot.evidence.findings_count ?? 'n/a'}`,
589
- `GitFlow: branch=${snapshot.git.current_branch ?? 'unknown'} protected_hint=${snapshot.git.on_protected_branch_hint ? 'yes' : 'no'}`,
590
- `Tracking: enforced=${snapshot.tracking.enforced} canonical=${snapshot.tracking.canonical_path ?? 'none'} present=${snapshot.tracking.canonical_present} single_active=${snapshot.tracking.single_in_progress_valid ?? 'n/a'} count=${snapshot.tracking.in_progress_count ?? 'n/a'} conflict=${snapshot.tracking.conflict}`,
591
- `Policy strict: PRE_WRITE=${snapshot.policy_strict.pre_write} PRE_COMMIT=${snapshot.policy_strict.pre_commit} PRE_PUSH=${snapshot.policy_strict.pre_push} CI=${snapshot.policy_strict.ci}`,
592
- ];
593
- if (snapshot.attention_codes.length > 0) {
594
- lines.push(`Attention: ${snapshot.attention_codes.join(', ')}`);
595
- }
596
- return lines;
597
- };
598
-
599
- export const printGovernanceObservationHuman = (snapshot: GovernanceObservationSnapshot): void => {
600
- writeInfo('[pumuki] governance truth (S1 / governance console baseline):');
601
- for (const line of buildGovernanceObservationSummaryLines(snapshot)) {
602
- writeInfo(`[pumuki] ${line}`);
603
- }
604
- for (const hint of snapshot.evidence.human_summary_preview) {
605
- writeInfo(`[pumuki] evidence hint: ${hint}`);
606
- }
607
- };
608
-
609
- export const doctorGovernanceIsBlocking = (snapshot: GovernanceObservationSnapshot): boolean =>
610
- snapshot.governance_effective === 'blocked';
611
-
612
- export const doctorGovernanceNeedsAttention = (snapshot: GovernanceObservationSnapshot): boolean =>
613
- snapshot.governance_effective !== 'green';