pumuki 6.3.97 → 6.3.98

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 (95) hide show
  1. package/AGENTS.md +269 -0
  2. package/CHANGELOG.md +688 -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 +9 -78
  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/preWriteAutomation.ts +7 -77
  36. package/integrations/lifecycle/state.ts +1 -8
  37. package/integrations/lifecycle/status.ts +2 -29
  38. package/integrations/mcp/aiGateCheck.ts +26 -206
  39. package/integrations/mcp/autoExecuteAiStart.ts +85 -92
  40. package/integrations/mcp/enterpriseServer.ts +7 -23
  41. package/integrations/mcp/enterpriseStdioServer.cli.ts +4 -31
  42. package/integrations/mcp/preFlightCheck.ts +5 -51
  43. package/integrations/platform/detectPlatforms.ts +37 -0
  44. package/integrations/policy/experimentalFeatures.ts +1 -1
  45. package/integrations/sdd/evidenceScaffold.ts +2 -109
  46. package/package.json +10 -2
  47. package/scripts/check-tracking-single-active.sh +1 -1
  48. package/scripts/consumer-menu-matrix-baseline-report-lib.ts +13 -38
  49. package/scripts/consumer-postinstall-resolve-args.cjs +44 -0
  50. package/scripts/consumer-postinstall.cjs +76 -21
  51. package/scripts/framework-menu-advanced-view-lib.ts +0 -15
  52. package/scripts/framework-menu-consumer-actions-lib.ts +28 -4
  53. package/scripts/framework-menu-consumer-preflight-hints.ts +5 -2
  54. package/scripts/framework-menu-consumer-preflight-render.ts +0 -10
  55. package/scripts/framework-menu-consumer-preflight-run.ts +0 -23
  56. package/scripts/framework-menu-consumer-preflight-types.ts +0 -12
  57. package/scripts/framework-menu-consumer-runtime-actions.ts +87 -17
  58. package/scripts/framework-menu-consumer-runtime-audit.ts +36 -2
  59. package/scripts/framework-menu-consumer-runtime-evidence-classic.ts +140 -0
  60. package/scripts/framework-menu-consumer-runtime-lib.ts +2 -10
  61. package/scripts/framework-menu-consumer-runtime-menu.ts +4 -18
  62. package/scripts/framework-menu-consumer-runtime-types.ts +3 -3
  63. package/scripts/framework-menu-evidence-summary-lib.ts +1 -0
  64. package/scripts/framework-menu-evidence-summary-read.ts +57 -5
  65. package/scripts/framework-menu-evidence-summary-severity.ts +3 -1
  66. package/scripts/framework-menu-evidence-summary-types.ts +7 -0
  67. package/scripts/framework-menu-gate-lib.ts +9 -0
  68. package/scripts/framework-menu-layout-data.ts +5 -0
  69. package/scripts/framework-menu-matrix-baseline-lib.ts +15 -14
  70. package/scripts/framework-menu-matrix-canary-lib.ts +22 -1
  71. package/scripts/framework-menu-matrix-evidence-lib.ts +1 -0
  72. package/scripts/framework-menu-matrix-evidence-types.ts +13 -1
  73. package/scripts/framework-menu-matrix-runner-lib.ts +35 -0
  74. package/scripts/framework-menu-system-notifications-cause.ts +0 -24
  75. package/scripts/framework-menu-system-notifications-macos-swift-source.ts +24 -204
  76. package/scripts/framework-menu-system-notifications-macos.ts +4 -0
  77. package/scripts/framework-menu-system-notifications-payloads-blocked.ts +1 -1
  78. package/scripts/framework-menu-system-notifications-remediation.ts +13 -24
  79. package/scripts/framework-menu-system-notifications-text.ts +1 -7
  80. package/scripts/framework-menu.ts +3 -2
  81. package/scripts/package-install-smoke-consumer-git-repo-lib.ts +1 -10
  82. package/scripts/package-install-smoke-consumer-npm-lib.ts +9 -46
  83. package/scripts/pumuki-full-surface-smoke-lib.ts +37 -0
  84. package/scripts/pumuki-full-surface-smoke.ts +346 -0
  85. package/scripts/pumuki-smoke-installed-wrapper.cjs +31 -0
  86. package/integrations/evidence/trackingContract.ts +0 -150
  87. package/integrations/gate/governanceActionCatalog.ts +0 -275
  88. package/integrations/lifecycle/bootstrapManifest.ts +0 -248
  89. package/integrations/lifecycle/cliGovernanceConsole.ts +0 -69
  90. package/integrations/lifecycle/governanceNextAction.ts +0 -164
  91. package/integrations/lifecycle/governanceObservationSnapshot.ts +0 -613
  92. package/integrations/mcp/alignedPlatformGate.ts +0 -232
  93. package/integrations/mcp/readMcpPrePushStdin.ts +0 -7
  94. package/scripts/build-ruralgo-s1-evidence-pack.ts +0 -85
  95. package/scripts/ruralgo-s1-evidence-pack-lib.ts +0 -200
@@ -1,20 +1,10 @@
1
- import { existsSync } from 'node:fs';
2
- import { join } from 'node:path';
3
1
  import { evaluateAiGate, type AiGateStage } from '../gate/evaluateAiGate';
4
2
  import { resolveRemediationHintForViolationCode } from '../gate/remediationCatalog';
5
3
  import { resolveLearningContextExperimentalFeature } from '../policy/experimentalFeatures';
6
4
  import { readSddLearningContext, type SddLearningContext } from '../sdd/learningInsights';
7
- import { runMcpAlignedPlatformGate } from './alignedPlatformGate';
8
5
 
9
6
  const PROTECTED_BRANCHES = new Set(['main', 'master', 'develop', 'dev']);
10
7
 
11
- type PlatformGateAlignment = {
12
- mode: 'full' | 'policy';
13
- exit_code: number;
14
- aligned: boolean;
15
- skip_reason: string | null;
16
- };
17
-
18
8
  export type EnterpriseAiGateCheckResult = {
19
9
  tool: 'ai_gate_check';
20
10
  dryRun: true;
@@ -41,7 +31,6 @@ export type EnterpriseAiGateCheckResult = {
41
31
  reason_code: 'HOOK_RUNNER_CAN_REFRESH_EVIDENCE' | null;
42
32
  message: string;
43
33
  };
44
- platform_gate_alignment?: PlatformGateAlignment;
45
34
  };
46
35
  };
47
36
 
@@ -50,22 +39,25 @@ const HOOK_STAGE_SET = new Set<AiGateStage>(['PRE_COMMIT', 'PRE_PUSH', 'CI']);
50
39
  const isHookRefreshableEvidenceCode = (code: string): boolean =>
51
40
  code.startsWith('EVIDENCE_');
52
41
 
53
- const MCP_ENTERPRISE_ADAPTER_MISSING_CODE = 'MCP_ENTERPRISE_ADAPTER_MISSING';
54
-
55
42
  type AiGateCheckDependencies = {
56
43
  evaluateAiGate: typeof evaluateAiGate;
57
- runMcpAlignedPlatformGate: typeof runMcpAlignedPlatformGate;
58
44
  };
59
45
 
60
46
  const defaultDependencies: AiGateCheckDependencies = {
61
47
  evaluateAiGate,
62
- runMcpAlignedPlatformGate,
63
48
  };
64
49
 
65
50
  const buildConsistencyHint = (
66
- evaluation: ReturnType<typeof evaluateAiGate>,
67
- platform?: { exitCode: number; aligned: boolean; skipReason: string | null }
51
+ evaluation: ReturnType<typeof evaluateAiGate>
68
52
  ): EnterpriseAiGateCheckResult['result']['consistency_hint'] => {
53
+ if (!HOOK_STAGE_SET.has(evaluation.stage)) {
54
+ return {
55
+ comparable_with_hook_runner: true,
56
+ reason_code: null,
57
+ message: 'Stage is directly comparable with ai_gate_check semantics.',
58
+ };
59
+ }
60
+
69
61
  const hasRefreshableEvidenceViolation = evaluation.violations.some((violation) =>
70
62
  isHookRefreshableEvidenceCode(violation.code)
71
63
  );
@@ -80,24 +72,6 @@ const buildConsistencyHint = (
80
72
  };
81
73
  }
82
74
 
83
- if (platform?.aligned) {
84
- return {
85
- comparable_with_hook_runner: true,
86
- reason_code: null,
87
- message:
88
- `ai_gate_check ejecutó runPlatformGate después de leer la evidencia actual (exit_code=${platform.exitCode}); ` +
89
- 'alineación hook-like habilitada explícitamente para este stage.',
90
- };
91
- }
92
-
93
- if (!HOOK_STAGE_SET.has(evaluation.stage)) {
94
- return {
95
- comparable_with_hook_runner: true,
96
- reason_code: null,
97
- message: 'Stage is directly comparable with ai_gate_check semantics.',
98
- };
99
- }
100
-
101
75
  return {
102
76
  comparable_with_hook_runner: true,
103
77
  reason_code: null,
@@ -124,9 +98,6 @@ const buildAutoFixes = (
124
98
  learningContext: SddLearningContext | null
125
99
  ): ReadonlyArray<string> => {
126
100
  const fixes: string[] = [];
127
- if (evaluation.violations.some((violation) => violation.code === MCP_ENTERPRISE_ADAPTER_MISSING_CODE)) {
128
- fixes.push('Regenera .pumuki/adapter.json con `pnpm exec pumuki install` antes de volver a validar el gate MCP.');
129
- }
130
101
  const emittedCodes = new Set<string>();
131
102
  for (const violation of evaluation.violations) {
132
103
  if (emittedCodes.has(violation.code)) {
@@ -147,14 +118,7 @@ const buildAutoFixes = (
147
118
  return fixes;
148
119
  };
149
120
 
150
- const buildMessage = (
151
- evaluation: ReturnType<typeof evaluateAiGate>,
152
- platform?: { exitCode: number; skipReason: string | null }
153
- ): string => {
154
- if (platform && platform.exitCode !== 0) {
155
- const suffix = platform.skipReason ? ` (${platform.skipReason})` : '';
156
- return `🔴 runPlatformGate exit_code=${platform.exitCode}${suffix}.`;
157
- }
121
+ const buildMessage = (evaluation: ReturnType<typeof evaluateAiGate>): string => {
158
122
  if (evaluation.allowed) {
159
123
  return `✅ Gate ${evaluation.stage} ALLOWED.`;
160
124
  }
@@ -165,59 +129,6 @@ const buildMessage = (
165
129
  return `🔴 ${firstViolation.code}: ${firstViolation.message}`;
166
130
  };
167
131
 
168
- const resolveAiGateCheckMode = (): PlatformGateAlignment['mode'] => {
169
- const raw = process.env.PUMUKI_MCP_AI_GATE_CHECK_MODE?.trim().toLowerCase();
170
- return raw === 'full' || raw === 'aligned' ? 'full' : 'policy';
171
- };
172
-
173
- const hasMcpAdapter = (repoRoot: string): boolean =>
174
- existsSync(join(repoRoot, '.pumuki', 'adapter.json'));
175
-
176
- const withRequiredMcpAdapter = (
177
- evaluation: ReturnType<typeof evaluateAiGate>,
178
- repoRoot: string,
179
- requireMcpReceipt: boolean
180
- ): ReturnType<typeof evaluateAiGate> => {
181
- if (!requireMcpReceipt || hasMcpAdapter(repoRoot)) {
182
- return evaluation;
183
- }
184
- if (evaluation.violations.some((violation) => violation.code === MCP_ENTERPRISE_ADAPTER_MISSING_CODE)) {
185
- return {
186
- ...evaluation,
187
- allowed: false,
188
- status: 'BLOCKED',
189
- };
190
- }
191
- return {
192
- ...evaluation,
193
- allowed: false,
194
- status: 'BLOCKED',
195
- violations: [
196
- {
197
- code: MCP_ENTERPRISE_ADAPTER_MISSING_CODE,
198
- severity: 'ERROR',
199
- message: 'Missing .pumuki/adapter.json. MCP-dependent validation cannot prove a real MCP invocation.',
200
- },
201
- ...evaluation.violations,
202
- ],
203
- };
204
- };
205
-
206
- const toPlatformGateAlignment = (
207
- mode: PlatformGateAlignment['mode'],
208
- platform?: { exitCode: number; aligned: boolean; skipReason: string | null }
209
- ): PlatformGateAlignment | undefined => {
210
- if (mode !== 'full' || !platform) {
211
- return undefined;
212
- }
213
- return {
214
- mode,
215
- exit_code: platform.exitCode,
216
- aligned: platform.aligned,
217
- skip_reason: platform.skipReason,
218
- };
219
- };
220
-
221
132
  export const runEnterpriseAiGateCheck = (params: {
222
133
  repoRoot: string;
223
134
  stage: AiGateStage;
@@ -232,131 +143,40 @@ export const runEnterpriseAiGateCheck = (params: {
232
143
  stage: params.stage,
233
144
  requireMcpReceipt: params.requireMcpReceipt ?? false,
234
145
  });
235
- const normalizedEvaluation = withRequiredMcpAdapter(
236
- evaluation,
237
- params.repoRoot,
238
- params.requireMcpReceipt ?? false
239
- );
240
- const branch = normalizedEvaluation.repo_state.git.branch;
241
- const timestamp = normalizedEvaluation.evidence.source.generated_at;
242
- const learningContextFeature = resolveLearningContextExperimentalFeature();
243
- const learningContext = learningContextFeature.mode === 'off'
244
- ? null
245
- : readSddLearningContext({
246
- repoRoot: params.repoRoot,
247
- });
248
- const warnings = buildWarnings(normalizedEvaluation);
249
- const autoFixes = buildAutoFixes(normalizedEvaluation, learningContext);
250
- const message = buildMessage(normalizedEvaluation);
251
-
252
- return {
253
- tool: 'ai_gate_check',
254
- dryRun: true,
255
- executed: true,
256
- success: normalizedEvaluation.allowed,
257
- result: {
258
- allowed: normalizedEvaluation.allowed,
259
- status: normalizedEvaluation.status,
260
- timestamp,
261
- branch,
262
- message,
263
- stage: normalizedEvaluation.stage,
264
- policy: normalizedEvaluation.policy,
265
- violations: normalizedEvaluation.violations,
266
- warnings,
267
- auto_fixes: autoFixes,
268
- learning_context: learningContext,
269
- evidence: normalizedEvaluation.evidence,
270
- mcp_receipt: normalizedEvaluation.mcp_receipt,
271
- skills_contract: normalizedEvaluation.skills_contract,
272
- repo_state: normalizedEvaluation.repo_state,
273
- consistency_hint: buildConsistencyHint(normalizedEvaluation),
274
- },
275
- };
276
- };
277
-
278
- export const runEnterpriseAiGateCheckAsync = async (params: {
279
- repoRoot: string;
280
- stage: AiGateStage;
281
- requireMcpReceipt?: boolean;
282
- }, dependencies: Partial<AiGateCheckDependencies> = {}): Promise<EnterpriseAiGateCheckResult> => {
283
- const mode = resolveAiGateCheckMode();
284
- const activeDependencies: AiGateCheckDependencies = {
285
- ...defaultDependencies,
286
- ...dependencies,
287
- };
288
- const evaluation = activeDependencies.evaluateAiGate({
289
- repoRoot: params.repoRoot,
290
- stage: params.stage,
291
- requireMcpReceipt: params.requireMcpReceipt ?? false,
292
- });
293
- const normalizedEvaluation = withRequiredMcpAdapter(
294
- evaluation,
295
- params.repoRoot,
296
- params.requireMcpReceipt ?? false
297
- );
298
-
299
- let platform:
300
- | { exitCode: number; aligned: boolean; skipReason: string | null }
301
- | undefined;
302
- if (mode === 'full') {
303
- platform = await activeDependencies.runMcpAlignedPlatformGate({
304
- repoRoot: params.repoRoot,
305
- stage: params.stage,
306
- });
307
- }
308
-
309
- const platformBlocks = Boolean(platform && platform.exitCode !== 0);
310
- const allowed = normalizedEvaluation.allowed && !platformBlocks;
311
- const status: 'ALLOWED' | 'BLOCKED' = allowed ? 'ALLOWED' : 'BLOCKED';
312
- const violations = platformBlocks && platform
313
- ? [
314
- ...normalizedEvaluation.violations,
315
- {
316
- code: 'PLATFORM_GATE_EXIT_NON_ZERO',
317
- message:
318
- `runPlatformGate devolvió exit_code=${platform.exitCode}` +
319
- (platform.skipReason ? ` (${platform.skipReason})` : ''),
320
- severity: 'ERROR' as const,
321
- },
322
- ]
323
- : normalizedEvaluation.violations;
324
- const branch = normalizedEvaluation.repo_state.git.branch;
325
- const timestamp = normalizedEvaluation.evidence.source.generated_at;
146
+ const branch = evaluation.repo_state.git.branch;
147
+ const timestamp = evaluation.evidence.source.generated_at;
326
148
  const learningContextFeature = resolveLearningContextExperimentalFeature();
327
149
  const learningContext = learningContextFeature.mode === 'off'
328
150
  ? null
329
151
  : readSddLearningContext({
330
152
  repoRoot: params.repoRoot,
331
153
  });
332
- const evaluationForHints = { ...normalizedEvaluation, allowed, status, violations };
333
- const warnings = buildWarnings(evaluationForHints);
334
- const autoFixes = buildAutoFixes(evaluationForHints, learningContext);
335
- const message = buildMessage(evaluationForHints, platform);
154
+ const warnings = buildWarnings(evaluation);
155
+ const autoFixes = buildAutoFixes(evaluation, learningContext);
156
+ const message = buildMessage(evaluation);
336
157
 
337
158
  return {
338
159
  tool: 'ai_gate_check',
339
160
  dryRun: true,
340
161
  executed: true,
341
- success: allowed,
162
+ success: evaluation.allowed,
342
163
  result: {
343
- allowed,
344
- status,
164
+ allowed: evaluation.allowed,
165
+ status: evaluation.status,
345
166
  timestamp,
346
167
  branch,
347
168
  message,
348
- stage: normalizedEvaluation.stage,
349
- policy: normalizedEvaluation.policy,
350
- violations,
169
+ stage: evaluation.stage,
170
+ policy: evaluation.policy,
171
+ violations: evaluation.violations,
351
172
  warnings,
352
173
  auto_fixes: autoFixes,
353
174
  learning_context: learningContext,
354
- evidence: normalizedEvaluation.evidence,
355
- mcp_receipt: normalizedEvaluation.mcp_receipt,
356
- skills_contract: normalizedEvaluation.skills_contract,
357
- repo_state: normalizedEvaluation.repo_state,
358
- consistency_hint: buildConsistencyHint(evaluationForHints, platform),
359
- platform_gate_alignment: toPlatformGateAlignment(mode, platform),
175
+ evidence: evaluation.evidence,
176
+ mcp_receipt: evaluation.mcp_receipt,
177
+ skills_contract: evaluation.skills_contract,
178
+ repo_state: evaluation.repo_state,
179
+ consistency_hint: buildConsistencyHint(evaluation),
360
180
  },
361
181
  };
362
182
  };
@@ -1,7 +1,3 @@
1
- import {
2
- buildGovernanceValidateCommand,
3
- resolveGovernanceCatalogAction,
4
- } from '../gate/governanceActionCatalog';
5
1
  import { evaluateAiGate, type AiGateStage, type AiGateViolation } from '../gate/evaluateAiGate';
6
2
  import { collectWorktreeAtomicSlices } from '../git/worktreeAtomicSlices';
7
3
  import { resolveLearningContextExperimentalFeature } from '../policy/experimentalFeatures';
@@ -54,98 +50,96 @@ const confidenceFromViolation = (violationCode: string | null): number => {
54
50
  if (isEvidenceCode(violationCode)) {
55
51
  return 65;
56
52
  }
57
- if (
58
- violationCode === 'GITFLOW_PROTECTED_BRANCH'
59
- || violationCode === 'GITFLOW_BRANCH_NAMING_INVALID'
60
- ) {
53
+ if (violationCode === 'GITFLOW_PROTECTED_BRANCH') {
61
54
  return 40;
62
55
  }
63
56
  return 50;
64
57
  };
65
58
 
66
- const normalizeGovernanceCatalogCode = (code: string): string => {
67
- switch (code) {
59
+ const nextActionFromViolation = (
60
+ violation: AiGateViolation | undefined,
61
+ repoRoot: string
62
+ ): AutoExecuteNextAction => {
63
+ if (!violation) {
64
+ return {
65
+ kind: 'info',
66
+ message: 'Gate listo. Puedes continuar con implementación.',
67
+ };
68
+ }
69
+ switch (violation.code) {
70
+ case 'EVIDENCE_MISSING':
68
71
  case 'EVIDENCE_INVALID':
69
72
  case 'EVIDENCE_CHAIN_INVALID':
70
- return 'EVIDENCE_INVALID_OR_CHAIN';
73
+ case 'EVIDENCE_STALE':
74
+ return {
75
+ kind: 'run_command',
76
+ message: 'Regenera o refresca evidencia y vuelve a evaluar PRE_WRITE.',
77
+ command: 'npx --yes --package pumuki@latest pumuki sdd validate --stage=PRE_WRITE --json',
78
+ };
79
+ case 'EVIDENCE_ACTIVE_RULE_IDS_EMPTY_FOR_CODE_CHANGES':
80
+ return {
81
+ kind: 'run_command',
82
+ message:
83
+ 'No hay active_rule_ids para plataforma de código detectada. Reconciliación strict de policy/skills y revalidación PRE_WRITE.',
84
+ command:
85
+ 'npx --yes --package pumuki@latest pumuki policy reconcile --strict --json && npx --yes --package pumuki@latest pumuki sdd validate --stage=PRE_WRITE --json',
86
+ };
87
+ case 'EVIDENCE_PLATFORM_SKILLS_SCOPE_INCOMPLETE':
88
+ case 'EVIDENCE_PLATFORM_SKILLS_BUNDLES_MISSING':
89
+ case 'EVIDENCE_SKILLS_CONTRACT_INCOMPLETE':
90
+ return {
91
+ kind: 'run_command',
92
+ message:
93
+ 'Completa cobertura de skills por plataforma (prefijos + bundles) y revalida PRE_WRITE.',
94
+ command: 'npx --yes --package pumuki@latest pumuki sdd validate --stage=PRE_WRITE --json',
95
+ };
96
+ case 'EVIDENCE_PLATFORM_CRITICAL_SKILLS_RULES_MISSING':
97
+ case 'EVIDENCE_CROSS_PLATFORM_CRITICAL_ENFORCEMENT_INCOMPLETE':
98
+ return {
99
+ kind: 'run_command',
100
+ message:
101
+ 'Reconcilia policy/skills en modo estricto (incluida skills.ios.critical-test-quality cuando aplique) y revalida PRE_WRITE.',
102
+ command:
103
+ 'npx --yes --package pumuki@latest pumuki policy reconcile --strict --json && npx --yes --package pumuki@latest pumuki sdd validate --stage=PRE_WRITE --json',
104
+ };
105
+ case 'EVIDENCE_PREWRITE_WORKTREE_OVER_LIMIT':
106
+ case 'EVIDENCE_PREWRITE_WORKTREE_WARN':
107
+ {
108
+ const plan = collectWorktreeAtomicSlices({
109
+ repoRoot,
110
+ maxSlices: 3,
111
+ maxFilesPerSlice: 4,
112
+ });
113
+ if (plan.slices.length > 0) {
114
+ const firstSlice = plan.slices[0];
115
+ return {
116
+ kind: 'run_command',
117
+ message:
118
+ `Particiona el worktree en slices atómicos por scope. Primer lote sugerido: ${firstSlice?.scope ?? 'scope-desconocido'}.`,
119
+ command:
120
+ `${firstSlice?.staged_command ?? 'git add -p'} && npx --yes --package pumuki@latest pumuki sdd validate --stage=PRE_WRITE --json`,
121
+ };
122
+ }
123
+ }
124
+ return {
125
+ kind: 'run_command',
126
+ message:
127
+ 'Particiona el worktree en slices atómicos y revalida PRE_WRITE para continuar sin fricción.',
128
+ command:
129
+ 'git status --short && git add -p && npx --yes --package pumuki@latest pumuki sdd validate --stage=PRE_WRITE --json',
130
+ };
71
131
  case 'GITFLOW_PROTECTED_BRANCH':
72
- return 'GITFLOW_PROTECTED_BRANCH_CONTEXT';
73
- case 'GITFLOW_BRANCH_NAMING_INVALID':
74
- return 'GITFLOW_BRANCH_NAMING_INVALID_CONTEXT';
132
+ return {
133
+ kind: 'run_command',
134
+ message: 'Cambia a una rama feature/* antes de continuar.',
135
+ command: 'git checkout -b feature/<descripcion-kebab-case>',
136
+ };
75
137
  default:
76
- return code;
77
- }
78
- };
79
-
80
- const resolveAutoExecuteRemediation = (params: {
81
- violation: AiGateViolation | undefined;
82
- repoRoot: string;
83
- stage: AiGateStage;
84
- allowed: boolean;
85
- }): {
86
- instruction: string;
87
- nextAction: AutoExecuteNextAction;
88
- } => {
89
- if (!params.violation) {
90
- const readyAction = resolveGovernanceCatalogAction({
91
- code: 'READY',
92
- stage: params.stage,
93
- });
94
- return {
95
- instruction: readyAction.instruction,
96
- nextAction: readyAction.next_action,
97
- };
98
- }
99
-
100
- if (
101
- params.violation.code === 'EVIDENCE_PREWRITE_WORKTREE_OVER_LIMIT'
102
- || params.violation.code === 'EVIDENCE_PREWRITE_WORKTREE_WARN'
103
- ) {
104
- const validateCommand = buildGovernanceValidateCommand(params.stage);
105
- const plan = collectWorktreeAtomicSlices({
106
- repoRoot: params.repoRoot,
107
- maxSlices: 3,
108
- maxFilesPerSlice: 4,
109
- });
110
- if (plan.slices.length > 0) {
111
- const firstSlice = plan.slices[0];
112
138
  return {
113
- instruction: 'Particiona el worktree en slices atómicos antes de continuar.',
114
- nextAction: {
115
- kind: params.allowed ? 'info' : 'run_command',
116
- message:
117
- `Particiona el worktree en slices atómicos por scope. Primer lote sugerido: ${firstSlice?.scope ?? 'scope-desconocido'}.`,
118
- command: params.allowed
119
- ? undefined
120
- : `${firstSlice?.staged_command ?? 'git add -p'} && ${validateCommand}`,
121
- },
139
+ kind: 'info',
140
+ message: 'Corrige la violación bloqueante y vuelve a ejecutar auto_execute_ai_start.',
122
141
  };
123
- }
124
- return {
125
- instruction: 'Particiona el worktree en slices atómicos antes de continuar.',
126
- nextAction: {
127
- kind: params.allowed ? 'info' : 'run_command',
128
- message: 'Particiona el worktree en slices atómicos y revalida PRE_WRITE para continuar sin fricción.',
129
- command: params.allowed
130
- ? undefined
131
- : `git status --short && git add -p && ${validateCommand}`,
132
- },
133
- };
134
142
  }
135
-
136
- const governanceAction = resolveGovernanceCatalogAction({
137
- code: normalizeGovernanceCatalogCode(params.violation.code),
138
- stage: params.stage,
139
- });
140
- return {
141
- instruction: governanceAction.instruction,
142
- nextAction: params.allowed
143
- ? {
144
- kind: 'info',
145
- message: governanceAction.next_action.message,
146
- }
147
- : governanceAction.next_action,
148
- };
149
143
  };
150
144
 
151
145
  export type EnterpriseAutoExecuteAiStartResult = {
@@ -196,20 +190,19 @@ export const runEnterpriseAutoExecuteAiStart = (params: {
196
190
  const action: AutoExecuteAction = evaluation.allowed ? 'proceed' : 'ask';
197
191
  const phase = toAutoExecutePhase(action);
198
192
  const confidencePct = confidenceFromViolation(firstViolation?.code ?? null);
199
- const remediation = resolveAutoExecuteRemediation({
200
- violation: firstViolation,
201
- repoRoot: params.repoRoot,
202
- stage,
203
- allowed: evaluation.allowed,
204
- });
205
- const nextAction = remediation.nextAction;
193
+ const nextAction = evaluation.allowed
194
+ ? {
195
+ kind: 'info' as const,
196
+ message: 'Gate en verde. Continúa con la implementación.',
197
+ }
198
+ : nextActionFromViolation(firstViolation, params.repoRoot);
206
199
 
207
200
  let message = toHumanMessage({
208
201
  action,
209
202
  confidencePct,
210
203
  reasonCode,
211
204
  });
212
- let instruction = remediation.instruction;
205
+ let instruction = nextAction.message;
213
206
  if (learningContext?.recommended_actions[0]) {
214
207
  message = `${message} Learning: ${learningContext.recommended_actions[0]}`;
215
208
  instruction = `${instruction} Learning: ${learningContext.recommended_actions[0]}`;
@@ -9,7 +9,7 @@ import { resolveMcpEnterpriseExperimentalFeature } from '../policy/experimentalF
9
9
  import { evaluateSddPolicy, readSddStatus } from '../sdd';
10
10
  import type { SddStage } from '../sdd';
11
11
  import { toStatusPayload } from './evidencePayloads';
12
- import { runEnterpriseAiGateCheckAsync } from './aiGateCheck';
12
+ import { runEnterpriseAiGateCheck } from './aiGateCheck';
13
13
  import { runEnterprisePreFlightCheck } from './preFlightCheck';
14
14
  import { runEnterpriseAutoExecuteAiStart } from './autoExecuteAiStart';
15
15
  import { writeMcpAiGateReceipt } from './aiGateReceipt';
@@ -39,14 +39,6 @@ type EnterpriseStatusPayload = {
39
39
  evidence: ReturnType<typeof toStatusPayload>;
40
40
  };
41
41
 
42
- type EnterpriseHealthPayload = {
43
- status: 'ok';
44
- repoRoot: string;
45
- experimentalFeatures: {
46
- mcp_enterprise: ReturnType<typeof resolveMcpEnterpriseExperimentalFeature>;
47
- };
48
- };
49
-
50
42
  const ENTERPRISE_RESOURCES = [
51
43
  'evidence://status',
52
44
  'gitflow://state',
@@ -390,16 +382,16 @@ const evaluateCriticalToolGuard = (
390
382
  }
391
383
  };
392
384
 
393
- const executeEnterpriseTool = async (
385
+ const executeEnterpriseTool = (
394
386
  repoRoot: string,
395
387
  toolName: EnterpriseToolName,
396
388
  args: Record<string, string | number | boolean | bigint | symbol | null | Date | object>,
397
389
  dryRun: boolean
398
- ): Promise<EnterpriseToolExecution> => {
390
+ ): EnterpriseToolExecution => {
399
391
  switch (toolName) {
400
392
  case 'ai_gate_check': {
401
393
  const stage = toSddStage(args.stage, 'PRE_COMMIT');
402
- const execution = await runEnterpriseAiGateCheckAsync({
394
+ const execution = runEnterpriseAiGateCheck({
403
395
  repoRoot,
404
396
  stage,
405
397
  });
@@ -658,14 +650,6 @@ const buildStatusPayload = (repoRoot: string): EnterpriseStatusPayload => ({
658
650
  evidence: toStatusPayload(repoRoot),
659
651
  });
660
652
 
661
- const buildHealthPayload = (repoRoot: string): EnterpriseHealthPayload => ({
662
- status: 'ok',
663
- repoRoot,
664
- experimentalFeatures: {
665
- mcp_enterprise: readMcpEnterpriseExperimentalState(),
666
- },
667
- });
668
-
669
653
  export const startEnterpriseMcpServer = (
670
654
  options: EnterpriseServerOptions = {}
671
655
  ): EnterpriseServerHandle => {
@@ -696,7 +680,7 @@ export const startEnterpriseMcpServer = (
696
680
  sendJson(res, 405, { error: 'Method not allowed' });
697
681
  return;
698
682
  }
699
- sendJson(res, 200, buildHealthPayload(repoRoot));
683
+ sendJson(res, 200, { status: 'ok' });
700
684
  return;
701
685
  }
702
686
 
@@ -757,7 +741,7 @@ export const startEnterpriseMcpServer = (
757
741
  return;
758
742
  }
759
743
  void readJsonBody(req)
760
- .then(async (body) => {
744
+ .then((body) => {
761
745
  if (typeof body !== 'object' || body === null) {
762
746
  sendJson(res, 400, {
763
747
  error: 'Invalid request body.',
@@ -830,7 +814,7 @@ export const startEnterpriseMcpServer = (
830
814
  }
831
815
  let result: EnterpriseToolExecution;
832
816
  try {
833
- result = await executeEnterpriseTool(
817
+ result = executeEnterpriseTool(
834
818
  repoRoot,
835
819
  toolName,
836
820
  args,
@@ -1,6 +1,5 @@
1
1
  import { Socket, createServer } from 'node:net';
2
2
  import { startEnterpriseMcpServer } from './enterpriseServer';
3
- import { resolveMcpEnterpriseExperimentalFeature } from '../policy/experimentalFeatures';
4
3
 
5
4
  type JsonRpcId = string | number | null;
6
5
 
@@ -99,32 +98,11 @@ const fetchJson = async (url: string, options?: RequestInit): Promise<unknown> =
99
98
  }
100
99
  };
101
100
 
102
- type EnterpriseHealthPayload = {
103
- status?: string;
104
- repoRoot?: string;
105
- experimentalFeatures?: {
106
- mcp_enterprise?: {
107
- mode?: string;
108
- };
109
- };
110
- };
111
-
112
- const canReuseEnterpriseHttp = (
113
- health: EnterpriseHealthPayload,
114
- repoRoot: string,
115
- expectedMcpMode: string
116
- ): boolean =>
117
- health.status === 'ok'
118
- && health.repoRoot === repoRoot
119
- && health.experimentalFeatures?.mcp_enterprise?.mode === expectedMcpMode;
120
-
121
101
  const startOrReuseEnterpriseHttp = async (): Promise<{
122
102
  host: string;
123
103
  port: number;
124
104
  startedByThisProcess: boolean;
125
105
  }> => {
126
- const repoRoot = process.cwd();
127
- const expectedMcpMode = resolveMcpEnterpriseExperimentalFeature().mode;
128
106
  const host = process.env.PUMUKI_ENTERPRISE_MCP_HOST ?? '127.0.0.1';
129
107
  const parsedPort = Number.parseInt(process.env.PUMUKI_ENTERPRISE_MCP_PORT ?? '', 10);
130
108
  const preferredPort = Number.isFinite(parsedPort) ? parsedPort : 7391;
@@ -132,8 +110,8 @@ const startOrReuseEnterpriseHttp = async (): Promise<{
132
110
 
133
111
  const healthUrl = `http://${host}:${requestedPort}/health`;
134
112
  try {
135
- const health = (await fetchJson(healthUrl)) as EnterpriseHealthPayload;
136
- if (canReuseEnterpriseHttp(health, repoRoot, expectedMcpMode)) {
113
+ const health = (await fetchJson(healthUrl)) as { status?: string };
114
+ if (health.status === 'ok') {
137
115
  return {
138
116
  host,
139
117
  port: requestedPort,
@@ -141,12 +119,7 @@ const startOrReuseEnterpriseHttp = async (): Promise<{
141
119
  };
142
120
  }
143
121
  } catch (error) {
144
- if (process.env.PUMUKI_DEBUG_MCP === '1') {
145
- const message = error instanceof Error ? error.message : String(error);
146
- process.stderr.write(
147
- `[pumuki-mcp-enterprise-stdio] health probe reuse skipped: ${message}\n`
148
- );
149
- }
122
+ void error;
150
123
  }
151
124
 
152
125
  const portInUse = await isPortInUse(host, requestedPort);
@@ -154,7 +127,7 @@ const startOrReuseEnterpriseHttp = async (): Promise<{
154
127
  startEnterpriseMcpServer({
155
128
  host,
156
129
  port: resolvedPort,
157
- repoRoot,
130
+ repoRoot: process.cwd(),
158
131
  });
159
132
 
160
133
  return {