pumuki 6.3.113 → 6.3.115

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 (99) hide show
  1. package/CHANGELOG.md +52 -5
  2. package/README.md +4 -2
  3. package/VERSION +1 -1
  4. package/core/facts/detectors/typescript/index.test.ts +0 -229
  5. package/core/facts/detectors/typescript/index.ts +0 -278
  6. package/core/facts/extractHeuristicFacts.ts +0 -4
  7. package/core/rules/presets/heuristics/typescript.test.ts +1 -21
  8. package/core/rules/presets/heuristics/typescript.ts +0 -72
  9. package/docs/README.md +13 -9
  10. package/docs/codex-skills/backend-enterprise-rules.md +3 -3
  11. package/docs/operations/RELEASE_NOTES.md +41 -4
  12. package/docs/product/API_REFERENCE.md +1 -1
  13. package/docs/product/HOW_IT_WORKS.md +6 -0
  14. package/docs/product/INSTALLATION.md +1 -1
  15. package/docs/product/USAGE.md +42 -5
  16. package/docs/tracking/plan-curso-pumuki-stack-my-architecture.md +100 -44
  17. package/docs/validation/README.md +6 -3
  18. package/integrations/config/skillsDetectorRegistry.ts +0 -24
  19. package/integrations/config/skillsMarkdownRules.ts +0 -57
  20. package/integrations/evidence/buildEvidence.ts +0 -24
  21. package/integrations/evidence/repoState.ts +9 -7
  22. package/integrations/evidence/schema.ts +0 -18
  23. package/integrations/evidence/writeEvidence.ts +0 -24
  24. package/integrations/gate/evaluateAiGate.ts +8 -251
  25. package/integrations/gate/remediationCatalog.ts +0 -8
  26. package/integrations/git/GitService.ts +44 -5
  27. package/integrations/git/aiGateRepoPolicyFindings.ts +86 -17
  28. package/integrations/git/runPlatformGate.ts +1 -9
  29. package/integrations/git/runPlatformGateFacts.ts +19 -1
  30. package/integrations/git/runPlatformGateOutput.ts +41 -42
  31. package/integrations/lifecycle/adapter.templates.json +1 -0
  32. package/integrations/lifecycle/adapter.ts +0 -24
  33. package/integrations/lifecycle/audit.ts +101 -0
  34. package/integrations/lifecycle/cli.ts +120 -99
  35. package/integrations/lifecycle/cliSdd.ts +4 -26
  36. package/integrations/lifecycle/doctor.ts +40 -102
  37. package/integrations/lifecycle/index.ts +2 -0
  38. package/integrations/lifecycle/install.ts +0 -21
  39. package/integrations/lifecycle/packageInfo.ts +1 -118
  40. package/integrations/lifecycle/state.ts +1 -8
  41. package/integrations/lifecycle/status.ts +40 -59
  42. package/integrations/lifecycle/watch.ts +1 -1
  43. package/integrations/mcp/aiGateCheck.ts +10 -194
  44. package/integrations/mcp/autoExecuteAiStart.ts +116 -92
  45. package/integrations/mcp/enterpriseServer.ts +7 -23
  46. package/integrations/mcp/enterpriseStdioServer.cli.ts +4 -31
  47. package/integrations/mcp/preFlightCheck.ts +5 -67
  48. package/integrations/platform/detectPlatforms.ts +37 -0
  49. package/integrations/sdd/policy.ts +28 -20
  50. package/package.json +1 -1
  51. package/scripts/check-tracking-single-active.sh +1 -1
  52. package/scripts/consumer-menu-matrix-baseline-report-lib.ts +13 -38
  53. package/scripts/consumer-postinstall-resolve-args.cjs +44 -0
  54. package/scripts/consumer-postinstall.cjs +76 -21
  55. package/scripts/framework-menu-advanced-view-lib.ts +0 -49
  56. package/scripts/framework-menu-consumer-actions-lib.ts +28 -4
  57. package/scripts/framework-menu-consumer-preflight-hints.ts +5 -2
  58. package/scripts/framework-menu-consumer-preflight-render.ts +0 -10
  59. package/scripts/framework-menu-consumer-preflight-run.ts +0 -23
  60. package/scripts/framework-menu-consumer-preflight-types.ts +0 -12
  61. package/scripts/framework-menu-consumer-runtime-actions.ts +87 -17
  62. package/scripts/framework-menu-consumer-runtime-audit.ts +36 -2
  63. package/scripts/framework-menu-consumer-runtime-evidence-classic.ts +140 -0
  64. package/scripts/framework-menu-consumer-runtime-lib.ts +2 -38
  65. package/scripts/framework-menu-consumer-runtime-menu.ts +4 -31
  66. package/scripts/framework-menu-consumer-runtime-types.ts +3 -5
  67. package/scripts/framework-menu-evidence-summary-lib.ts +1 -0
  68. package/scripts/framework-menu-evidence-summary-read.ts +57 -5
  69. package/scripts/framework-menu-evidence-summary-severity.ts +3 -1
  70. package/scripts/framework-menu-evidence-summary-types.ts +7 -0
  71. package/scripts/framework-menu-gate-lib.ts +9 -0
  72. package/scripts/framework-menu-layout-data.ts +5 -0
  73. package/scripts/framework-menu-matrix-baseline-lib.ts +15 -14
  74. package/scripts/framework-menu-matrix-canary-lib.ts +22 -1
  75. package/scripts/framework-menu-matrix-evidence-lib.ts +1 -0
  76. package/scripts/framework-menu-matrix-evidence-types.ts +13 -1
  77. package/scripts/framework-menu-matrix-runner-lib.ts +35 -0
  78. package/scripts/framework-menu-system-notifications-cause.ts +0 -3
  79. package/scripts/framework-menu-system-notifications-macos-swift-source.ts +24 -204
  80. package/scripts/framework-menu-system-notifications-macos.ts +4 -0
  81. package/scripts/framework-menu-system-notifications-payloads-blocked.ts +1 -1
  82. package/scripts/framework-menu-system-notifications-text.ts +1 -7
  83. package/scripts/framework-menu.ts +3 -24
  84. package/scripts/package-install-smoke-consumer-git-repo-lib.ts +1 -10
  85. package/scripts/package-install-smoke-consumer-npm-lib.ts +9 -46
  86. package/scripts/pumuki-full-surface-smoke-lib.ts +37 -0
  87. package/scripts/pumuki-full-surface-smoke.ts +346 -0
  88. package/scripts/pumuki-smoke-installed-wrapper.cjs +31 -0
  89. package/integrations/evidence/trackingContract.ts +0 -17
  90. package/integrations/gate/governanceActionCatalog.ts +0 -275
  91. package/integrations/lifecycle/bootstrapManifest.ts +0 -248
  92. package/integrations/lifecycle/cliGovernanceConsole.ts +0 -69
  93. package/integrations/lifecycle/governanceNextAction.ts +0 -171
  94. package/integrations/lifecycle/governanceObservationSnapshot.ts +0 -369
  95. package/integrations/lifecycle/trackingState.ts +0 -403
  96. package/integrations/mcp/alignedPlatformGate.ts +0 -232
  97. package/integrations/mcp/readMcpPrePushStdin.ts +0 -7
  98. package/scripts/build-ruralgo-s1-evidence-pack.ts +0 -85
  99. package/scripts/ruralgo-s1-evidence-pack-lib.ts +0 -200
@@ -1,20 +1,10 @@
1
1
  import { evaluateAiGate, type AiGateStage } from '../gate/evaluateAiGate';
2
2
  import { resolveRemediationHintForViolationCode } from '../gate/remediationCatalog';
3
- import { readLifecyclePolicyValidationSnapshot } from '../lifecycle/policyValidationSnapshot';
4
3
  import { resolveLearningContextExperimentalFeature } from '../policy/experimentalFeatures';
5
- import { resolvePreWriteEnforcement } from '../policy/preWriteEnforcement';
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;
@@ -26,19 +16,6 @@ export type EnterpriseAiGateCheckResult = {
26
16
  timestamp: string | null;
27
17
  branch: string | null;
28
18
  message: string;
29
- reason_code: string;
30
- instruction: string;
31
- prewrite_effective: {
32
- mode: ReturnType<typeof resolvePreWriteEnforcement>['mode'];
33
- source: ReturnType<typeof resolvePreWriteEnforcement>['source'];
34
- blocking: boolean;
35
- strict_policy: boolean;
36
- };
37
- next_action: {
38
- kind: 'info';
39
- reason: string;
40
- message: string;
41
- };
42
19
  stage: ReturnType<typeof evaluateAiGate>['stage'];
43
20
  policy: ReturnType<typeof evaluateAiGate>['policy'];
44
21
  violations: ReturnType<typeof evaluateAiGate>['violations'];
@@ -54,7 +31,6 @@ export type EnterpriseAiGateCheckResult = {
54
31
  reason_code: 'HOOK_RUNNER_CAN_REFRESH_EVIDENCE' | null;
55
32
  message: string;
56
33
  };
57
- platform_gate_alignment?: PlatformGateAlignment;
58
34
  };
59
35
  };
60
36
 
@@ -65,18 +41,23 @@ const isHookRefreshableEvidenceCode = (code: string): boolean =>
65
41
 
66
42
  type AiGateCheckDependencies = {
67
43
  evaluateAiGate: typeof evaluateAiGate;
68
- runMcpAlignedPlatformGate: typeof runMcpAlignedPlatformGate;
69
44
  };
70
45
 
71
46
  const defaultDependencies: AiGateCheckDependencies = {
72
47
  evaluateAiGate,
73
- runMcpAlignedPlatformGate,
74
48
  };
75
49
 
76
50
  const buildConsistencyHint = (
77
- evaluation: ReturnType<typeof evaluateAiGate>,
78
- platform?: { exitCode: number; aligned: boolean; skipReason: string | null }
51
+ evaluation: ReturnType<typeof evaluateAiGate>
79
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
+
80
61
  const hasRefreshableEvidenceViolation = evaluation.violations.some((violation) =>
81
62
  isHookRefreshableEvidenceCode(violation.code)
82
63
  );
@@ -91,24 +72,6 @@ const buildConsistencyHint = (
91
72
  };
92
73
  }
93
74
 
94
- if (platform?.aligned) {
95
- return {
96
- comparable_with_hook_runner: true,
97
- reason_code: null,
98
- message:
99
- `ai_gate_check ejecutó runPlatformGate después de leer la evidencia actual (exit_code=${platform.exitCode}); ` +
100
- 'alineación hook-like habilitada explícitamente para este stage.',
101
- };
102
- }
103
-
104
- if (!HOOK_STAGE_SET.has(evaluation.stage)) {
105
- return {
106
- comparable_with_hook_runner: true,
107
- reason_code: null,
108
- message: 'Stage is directly comparable with ai_gate_check semantics.',
109
- };
110
- }
111
-
112
75
  return {
113
76
  comparable_with_hook_runner: true,
114
77
  reason_code: null,
@@ -155,31 +118,7 @@ const buildAutoFixes = (
155
118
  return fixes;
156
119
  };
157
120
 
158
- const buildReasonCode = (evaluation: ReturnType<typeof evaluateAiGate>): string =>
159
- evaluation.violations[0]?.code ?? 'AI_GATE_ALLOWED';
160
-
161
- const buildInstruction = (evaluation: ReturnType<typeof evaluateAiGate>): string =>
162
- evaluation.allowed
163
- ? 'Continúa con el stage actual manteniendo el vocabulario canónico de governance.'
164
- : 'Corrige el bloqueante primario y vuelve a ejecutar ai_gate_check en el mismo stage.';
165
-
166
- const buildNextAction = (
167
- evaluation: ReturnType<typeof evaluateAiGate>,
168
- autoFixes: ReadonlyArray<string>
169
- ): EnterpriseAiGateCheckResult['result']['next_action'] => ({
170
- kind: 'info',
171
- reason: buildReasonCode(evaluation),
172
- message: autoFixes[0] ?? buildInstruction(evaluation),
173
- });
174
-
175
- const buildMessage = (
176
- evaluation: ReturnType<typeof evaluateAiGate>,
177
- platform?: { exitCode: number; skipReason: string | null }
178
- ): string => {
179
- if (platform && platform.exitCode !== 0) {
180
- const suffix = platform.skipReason ? ` (${platform.skipReason})` : '';
181
- return `🔴 runPlatformGate exit_code=${platform.exitCode}${suffix}.`;
182
- }
121
+ const buildMessage = (evaluation: ReturnType<typeof evaluateAiGate>): string => {
183
122
  if (evaluation.allowed) {
184
123
  return `✅ Gate ${evaluation.stage} ALLOWED.`;
185
124
  }
@@ -190,26 +129,6 @@ const buildMessage = (
190
129
  return `🔴 ${firstViolation.code}: ${firstViolation.message}`;
191
130
  };
192
131
 
193
- const resolveAiGateCheckMode = (): PlatformGateAlignment['mode'] => {
194
- const raw = process.env.PUMUKI_MCP_AI_GATE_CHECK_MODE?.trim().toLowerCase();
195
- return raw === 'full' || raw === 'aligned' ? 'full' : 'policy';
196
- };
197
-
198
- const toPlatformGateAlignment = (
199
- mode: PlatformGateAlignment['mode'],
200
- platform?: { exitCode: number; aligned: boolean; skipReason: string | null }
201
- ): PlatformGateAlignment | undefined => {
202
- if (mode !== 'full' || !platform) {
203
- return undefined;
204
- }
205
- return {
206
- mode,
207
- exit_code: platform.exitCode,
208
- aligned: platform.aligned,
209
- skip_reason: platform.skipReason,
210
- };
211
- };
212
-
213
132
  export const runEnterpriseAiGateCheck = (params: {
214
133
  repoRoot: string;
215
134
  stage: AiGateStage;
@@ -232,8 +151,6 @@ export const runEnterpriseAiGateCheck = (params: {
232
151
  : readSddLearningContext({
233
152
  repoRoot: params.repoRoot,
234
153
  });
235
- const preWriteEnforcement = resolvePreWriteEnforcement();
236
- const policyValidation = readLifecyclePolicyValidationSnapshot(params.repoRoot);
237
154
  const warnings = buildWarnings(evaluation);
238
155
  const autoFixes = buildAutoFixes(evaluation, learningContext);
239
156
  const message = buildMessage(evaluation);
@@ -249,15 +166,6 @@ export const runEnterpriseAiGateCheck = (params: {
249
166
  timestamp,
250
167
  branch,
251
168
  message,
252
- reason_code: buildReasonCode(evaluation),
253
- instruction: buildInstruction(evaluation),
254
- prewrite_effective: {
255
- mode: preWriteEnforcement.mode,
256
- source: preWriteEnforcement.source,
257
- blocking: preWriteEnforcement.blocking,
258
- strict_policy: policyValidation.stages.PRE_WRITE.strict,
259
- },
260
- next_action: buildNextAction(evaluation, autoFixes),
261
169
  stage: evaluation.stage,
262
170
  policy: evaluation.policy,
263
171
  violations: evaluation.violations,
@@ -272,95 +180,3 @@ export const runEnterpriseAiGateCheck = (params: {
272
180
  },
273
181
  };
274
182
  };
275
-
276
- export const runEnterpriseAiGateCheckAsync = async (params: {
277
- repoRoot: string;
278
- stage: AiGateStage;
279
- requireMcpReceipt?: boolean;
280
- }, dependencies: Partial<AiGateCheckDependencies> = {}): Promise<EnterpriseAiGateCheckResult> => {
281
- const mode = resolveAiGateCheckMode();
282
- const activeDependencies: AiGateCheckDependencies = {
283
- ...defaultDependencies,
284
- ...dependencies,
285
- };
286
- const evaluation = activeDependencies.evaluateAiGate({
287
- repoRoot: params.repoRoot,
288
- stage: params.stage,
289
- requireMcpReceipt: params.requireMcpReceipt ?? false,
290
- });
291
-
292
- let platform:
293
- | { exitCode: number; aligned: boolean; skipReason: string | null }
294
- | undefined;
295
- if (mode === 'full') {
296
- platform = await activeDependencies.runMcpAlignedPlatformGate({
297
- repoRoot: params.repoRoot,
298
- stage: params.stage,
299
- });
300
- }
301
-
302
- const platformBlocks = Boolean(platform && platform.exitCode !== 0);
303
- const allowed = evaluation.allowed && !platformBlocks;
304
- const status: 'ALLOWED' | 'BLOCKED' = allowed ? 'ALLOWED' : 'BLOCKED';
305
- const violations = platformBlocks && platform
306
- ? [
307
- ...evaluation.violations,
308
- {
309
- code: 'PLATFORM_GATE_EXIT_NON_ZERO',
310
- message:
311
- `runPlatformGate devolvió exit_code=${platform.exitCode}` +
312
- (platform.skipReason ? ` (${platform.skipReason})` : ''),
313
- severity: 'ERROR' as const,
314
- },
315
- ]
316
- : evaluation.violations;
317
- const branch = evaluation.repo_state.git.branch;
318
- const timestamp = evaluation.evidence.source.generated_at;
319
- const learningContextFeature = resolveLearningContextExperimentalFeature();
320
- const learningContext = learningContextFeature.mode === 'off'
321
- ? null
322
- : readSddLearningContext({
323
- repoRoot: params.repoRoot,
324
- });
325
- const evaluationForHints = { ...evaluation, allowed, status, violations };
326
- const warnings = buildWarnings(evaluationForHints);
327
- const autoFixes = buildAutoFixes(evaluationForHints, learningContext);
328
- const message = buildMessage(evaluationForHints, platform);
329
- const preWriteEnforcement = resolvePreWriteEnforcement();
330
- const policyValidation = readLifecyclePolicyValidationSnapshot(params.repoRoot);
331
-
332
- return {
333
- tool: 'ai_gate_check',
334
- dryRun: true,
335
- executed: true,
336
- success: allowed,
337
- result: {
338
- allowed,
339
- status,
340
- timestamp,
341
- branch,
342
- message,
343
- reason_code: buildReasonCode(evaluationForHints),
344
- instruction: buildInstruction(evaluationForHints),
345
- prewrite_effective: {
346
- mode: preWriteEnforcement.mode,
347
- source: preWriteEnforcement.source,
348
- blocking: preWriteEnforcement.blocking,
349
- strict_policy: policyValidation.stages.PRE_WRITE.strict,
350
- },
351
- next_action: buildNextAction(evaluationForHints, autoFixes),
352
- stage: evaluation.stage,
353
- policy: evaluation.policy,
354
- violations,
355
- warnings,
356
- auto_fixes: autoFixes,
357
- learning_context: learningContext,
358
- evidence: evaluation.evidence,
359
- mcp_receipt: evaluation.mcp_receipt,
360
- skills_contract: evaluation.skills_contract,
361
- repo_state: evaluation.repo_state,
362
- consistency_hint: buildConsistencyHint(evaluationForHints, platform),
363
- platform_gate_alignment: toPlatformGateAlignment(mode, platform),
364
- },
365
- };
366
- };
@@ -1,9 +1,6 @@
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';
3
+ import { getCurrentPumukiVersion } from '../lifecycle/packageInfo';
7
4
  import { resolveLearningContextExperimentalFeature } from '../policy/experimentalFeatures';
8
5
  import { readSddLearningContext, type SddLearningContext } from '../sdd/learningInsights';
9
6
 
@@ -54,98 +51,126 @@ const confidenceFromViolation = (violationCode: string | null): number => {
54
51
  if (isEvidenceCode(violationCode)) {
55
52
  return 65;
56
53
  }
57
- if (
58
- violationCode === 'GITFLOW_PROTECTED_BRANCH'
59
- || violationCode === 'GITFLOW_BRANCH_NAMING_INVALID'
60
- ) {
54
+ if (violationCode === 'GITFLOW_PROTECTED_BRANCH') {
61
55
  return 40;
62
56
  }
63
57
  return 50;
64
58
  };
65
59
 
66
- const normalizeGovernanceCatalogCode = (code: string): string => {
67
- switch (code) {
68
- case 'EVIDENCE_INVALID':
69
- case 'EVIDENCE_CHAIN_INVALID':
70
- return 'EVIDENCE_INVALID_OR_CHAIN';
71
- case 'GITFLOW_PROTECTED_BRANCH':
72
- return 'GITFLOW_PROTECTED_BRANCH_CONTEXT';
73
- case 'GITFLOW_BRANCH_NAMING_INVALID':
74
- return 'GITFLOW_BRANCH_NAMING_INVALID_CONTEXT';
75
- default:
76
- return code;
77
- }
78
- };
79
-
80
- const resolveAutoExecuteRemediation = (params: {
81
- violation: AiGateViolation | undefined;
60
+ const buildPinnedPumukiNpxCommand = (params: {
82
61
  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
- });
62
+ executableAndArgs: string;
63
+ }): string =>
64
+ `npx --yes --package pumuki@${getCurrentPumukiVersion({ repoRoot: params.repoRoot })} ${params.executableAndArgs}`;
65
+
66
+ const nextActionFromViolation = (
67
+ violation: AiGateViolation | undefined,
68
+ repoRoot: string
69
+ ): AutoExecuteNextAction => {
70
+ if (!violation) {
94
71
  return {
95
- instruction: readyAction.instruction,
96
- nextAction: readyAction.next_action,
72
+ kind: 'info',
73
+ message: 'Gate listo. Puedes continuar con implementación.',
97
74
  };
98
75
  }
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];
76
+ switch (violation.code) {
77
+ case 'EVIDENCE_MISSING':
78
+ case 'EVIDENCE_INVALID':
79
+ case 'EVIDENCE_CHAIN_INVALID':
80
+ case 'EVIDENCE_STALE':
112
81
  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
- },
82
+ kind: 'run_command',
83
+ message: 'Regenera o refresca evidencia y vuelve a evaluar PRE_WRITE.',
84
+ command: buildPinnedPumukiNpxCommand({
85
+ repoRoot,
86
+ executableAndArgs: 'pumuki sdd validate --stage=PRE_WRITE --json',
87
+ }),
122
88
  };
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
- }
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,
89
+ case 'EVIDENCE_ACTIVE_RULE_IDS_EMPTY_FOR_CODE_CHANGES':
90
+ return {
91
+ kind: 'run_command',
92
+ message:
93
+ 'No hay active_rule_ids para plataforma de código detectada. Reconciliación strict de policy/skills y revalidación PRE_WRITE.',
94
+ command:
95
+ `${buildPinnedPumukiNpxCommand({
96
+ repoRoot,
97
+ executableAndArgs: 'pumuki policy reconcile --strict --json',
98
+ })} && ${buildPinnedPumukiNpxCommand({
99
+ repoRoot,
100
+ executableAndArgs: 'pumuki sdd validate --stage=PRE_WRITE --json',
101
+ })}`,
102
+ };
103
+ case 'EVIDENCE_PLATFORM_SKILLS_SCOPE_INCOMPLETE':
104
+ case 'EVIDENCE_PLATFORM_SKILLS_BUNDLES_MISSING':
105
+ case 'EVIDENCE_SKILLS_CONTRACT_INCOMPLETE':
106
+ return {
107
+ kind: 'run_command',
108
+ message:
109
+ 'Completa cobertura de skills por plataforma (prefijos + bundles) y revalida PRE_WRITE.',
110
+ command: buildPinnedPumukiNpxCommand({
111
+ repoRoot,
112
+ executableAndArgs: 'pumuki sdd validate --stage=PRE_WRITE --json',
113
+ }),
114
+ };
115
+ case 'EVIDENCE_PLATFORM_CRITICAL_SKILLS_RULES_MISSING':
116
+ case 'EVIDENCE_CROSS_PLATFORM_CRITICAL_ENFORCEMENT_INCOMPLETE':
117
+ return {
118
+ kind: 'run_command',
119
+ message:
120
+ 'Reconcilia policy/skills en modo estricto (incluida skills.ios.critical-test-quality cuando aplique) y revalida PRE_WRITE.',
121
+ command:
122
+ `${buildPinnedPumukiNpxCommand({
123
+ repoRoot,
124
+ executableAndArgs: 'pumuki policy reconcile --strict --json',
125
+ })} && ${buildPinnedPumukiNpxCommand({
126
+ repoRoot,
127
+ executableAndArgs: 'pumuki sdd validate --stage=PRE_WRITE --json',
128
+ })}`,
129
+ };
130
+ case 'EVIDENCE_PREWRITE_WORKTREE_OVER_LIMIT':
131
+ case 'EVIDENCE_PREWRITE_WORKTREE_WARN':
132
+ {
133
+ const plan = collectWorktreeAtomicSlices({
134
+ repoRoot,
135
+ maxSlices: 3,
136
+ maxFilesPerSlice: 4,
137
+ });
138
+ if (plan.slices.length > 0) {
139
+ const firstSlice = plan.slices[0];
140
+ return {
141
+ kind: 'run_command',
142
+ message:
143
+ `Particiona el worktree en slices atómicos por scope. Primer lote sugerido: ${firstSlice?.scope ?? 'scope-desconocido'}.`,
144
+ command:
145
+ `${firstSlice?.staged_command ?? 'git add -p'} && ${buildPinnedPumukiNpxCommand({
146
+ repoRoot,
147
+ executableAndArgs: 'pumuki sdd validate --stage=PRE_WRITE --json',
148
+ })}`,
149
+ };
150
+ }
146
151
  }
147
- : governanceAction.next_action,
148
- };
152
+ return {
153
+ kind: 'run_command',
154
+ message:
155
+ 'Particiona el worktree en slices atómicos y revalida PRE_WRITE para continuar sin fricción.',
156
+ command:
157
+ `git status --short && git add -p && ${buildPinnedPumukiNpxCommand({
158
+ repoRoot,
159
+ executableAndArgs: 'pumuki sdd validate --stage=PRE_WRITE --json',
160
+ })}`,
161
+ };
162
+ case 'GITFLOW_PROTECTED_BRANCH':
163
+ return {
164
+ kind: 'run_command',
165
+ message: 'Cambia a una rama feature/* antes de continuar.',
166
+ command: 'git checkout -b feature/<descripcion-kebab-case>',
167
+ };
168
+ default:
169
+ return {
170
+ kind: 'info',
171
+ message: 'Corrige la violación bloqueante y vuelve a ejecutar auto_execute_ai_start.',
172
+ };
173
+ }
149
174
  };
150
175
 
151
176
  export type EnterpriseAutoExecuteAiStartResult = {
@@ -196,20 +221,19 @@ export const runEnterpriseAutoExecuteAiStart = (params: {
196
221
  const action: AutoExecuteAction = evaluation.allowed ? 'proceed' : 'ask';
197
222
  const phase = toAutoExecutePhase(action);
198
223
  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;
224
+ const nextAction = evaluation.allowed
225
+ ? {
226
+ kind: 'info' as const,
227
+ message: 'Gate en verde. Continúa con la implementación.',
228
+ }
229
+ : nextActionFromViolation(firstViolation, params.repoRoot);
206
230
 
207
231
  let message = toHumanMessage({
208
232
  action,
209
233
  confidencePct,
210
234
  reasonCode,
211
235
  });
212
- let instruction = remediation.instruction;
236
+ let instruction = nextAction.message;
213
237
  if (learningContext?.recommended_actions[0]) {
214
238
  message = `${message} Learning: ${learningContext.recommended_actions[0]}`;
215
239
  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 {