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,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,31 +190,30 @@ 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]}`;
216
209
  }
217
- const force = action === 'ask' && confidencePct < 50;
210
+ const force = action === 'ask';
218
211
 
219
212
  return {
220
213
  tool: 'auto_execute_ai_start',
221
214
  dryRun: true,
222
215
  executed: true,
223
- success: true,
216
+ success: evaluation.allowed,
224
217
  result: {
225
218
  action,
226
219
  phase,
@@ -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,