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.
- package/AGENTS.md +269 -0
- package/CHANGELOG.md +697 -0
- package/README.md +4 -2
- package/VERSION +1 -1
- package/docs/README.md +13 -9
- package/docs/operations/RELEASE_NOTES.md +12 -76
- package/docs/product/HOW_IT_WORKS.md +6 -0
- package/docs/product/INSTALLATION.md +1 -1
- package/docs/product/USAGE.md +41 -4
- package/docs/tracking/plan-curso-pumuki-stack-my-architecture.md +118 -0
- package/docs/validation/README.md +6 -3
- package/integrations/config/skillsCustomRules.ts +18 -99
- package/integrations/evidence/buildEvidence.ts +0 -24
- package/integrations/evidence/repoState.ts +0 -3
- package/integrations/evidence/schema.ts +0 -18
- package/integrations/evidence/writeEvidence.ts +0 -24
- package/integrations/gate/evaluateAiGate.ts +15 -232
- package/integrations/gate/remediationCatalog.ts +0 -8
- package/integrations/git/GitService.ts +44 -5
- package/integrations/git/aiGateRepoPolicyFindings.ts +0 -4
- package/integrations/git/runPlatformGate.ts +1 -9
- package/integrations/git/runPlatformGateFacts.ts +19 -1
- package/integrations/git/runPlatformGateOutput.ts +27 -36
- package/integrations/lifecycle/adapter.templates.json +7 -13
- package/integrations/lifecycle/adapter.ts +0 -24
- package/integrations/lifecycle/artifacts.ts +1 -6
- package/integrations/lifecycle/audit.ts +101 -0
- package/integrations/lifecycle/cli.ts +110 -70
- package/integrations/lifecycle/cliSdd.ts +13 -8
- package/integrations/lifecycle/doctor.ts +16 -48
- package/integrations/lifecycle/hookManager.ts +0 -77
- package/integrations/lifecycle/index.ts +2 -0
- package/integrations/lifecycle/install.ts +0 -21
- package/integrations/lifecycle/npmService.ts +3 -155
- package/integrations/lifecycle/policyValidationSnapshot.ts +8 -2
- package/integrations/lifecycle/preWriteAutomation.ts +7 -77
- package/integrations/lifecycle/state.ts +1 -8
- package/integrations/lifecycle/status.ts +2 -29
- package/integrations/mcp/aiGateCheck.ts +26 -206
- package/integrations/mcp/autoExecuteAiStart.ts +87 -94
- package/integrations/mcp/enterpriseServer.ts +7 -23
- package/integrations/mcp/enterpriseStdioServer.cli.ts +4 -31
- package/integrations/mcp/preFlightCheck.ts +5 -51
- package/integrations/platform/detectPlatforms.ts +37 -0
- package/integrations/policy/experimentalFeatures.ts +1 -1
- package/integrations/sdd/evidenceScaffold.ts +2 -109
- package/package.json +10 -2
- package/scripts/check-tracking-single-active.sh +1 -1
- package/scripts/consumer-menu-matrix-baseline-report-lib.ts +13 -38
- package/scripts/consumer-postinstall-resolve-args.cjs +44 -0
- package/scripts/consumer-postinstall.cjs +76 -21
- package/scripts/framework-menu-advanced-view-lib.ts +0 -15
- package/scripts/framework-menu-consumer-actions-lib.ts +28 -4
- package/scripts/framework-menu-consumer-preflight-hints.ts +5 -2
- package/scripts/framework-menu-consumer-preflight-render.ts +0 -10
- package/scripts/framework-menu-consumer-preflight-run.ts +0 -23
- package/scripts/framework-menu-consumer-preflight-types.ts +0 -12
- package/scripts/framework-menu-consumer-runtime-actions.ts +87 -17
- package/scripts/framework-menu-consumer-runtime-audit.ts +36 -2
- package/scripts/framework-menu-consumer-runtime-evidence-classic.ts +140 -0
- package/scripts/framework-menu-consumer-runtime-lib.ts +2 -10
- package/scripts/framework-menu-consumer-runtime-menu.ts +4 -18
- package/scripts/framework-menu-consumer-runtime-types.ts +3 -3
- package/scripts/framework-menu-evidence-summary-lib.ts +1 -0
- package/scripts/framework-menu-evidence-summary-read.ts +57 -5
- package/scripts/framework-menu-evidence-summary-severity.ts +3 -1
- package/scripts/framework-menu-evidence-summary-types.ts +7 -0
- package/scripts/framework-menu-gate-lib.ts +9 -0
- package/scripts/framework-menu-layout-data.ts +5 -0
- package/scripts/framework-menu-matrix-baseline-lib.ts +15 -14
- package/scripts/framework-menu-matrix-canary-lib.ts +22 -1
- package/scripts/framework-menu-matrix-evidence-lib.ts +1 -0
- package/scripts/framework-menu-matrix-evidence-types.ts +13 -1
- package/scripts/framework-menu-matrix-runner-lib.ts +35 -0
- package/scripts/framework-menu-system-notifications-cause.ts +0 -24
- package/scripts/framework-menu-system-notifications-macos-swift-source.ts +24 -204
- package/scripts/framework-menu-system-notifications-macos.ts +4 -0
- package/scripts/framework-menu-system-notifications-payloads-blocked.ts +1 -1
- package/scripts/framework-menu-system-notifications-remediation.ts +13 -24
- package/scripts/framework-menu-system-notifications-text.ts +1 -7
- package/scripts/framework-menu.ts +3 -2
- package/scripts/package-install-smoke-consumer-git-repo-lib.ts +1 -10
- package/scripts/package-install-smoke-consumer-npm-lib.ts +9 -46
- package/scripts/pumuki-full-surface-smoke-lib.ts +37 -0
- package/scripts/pumuki-full-surface-smoke.ts +346 -0
- package/scripts/pumuki-smoke-installed-wrapper.cjs +31 -0
- package/integrations/evidence/trackingContract.ts +0 -150
- package/integrations/gate/governanceActionCatalog.ts +0 -275
- package/integrations/lifecycle/bootstrapManifest.ts +0 -248
- package/integrations/lifecycle/cliGovernanceConsole.ts +0 -69
- package/integrations/lifecycle/governanceNextAction.ts +0 -164
- package/integrations/lifecycle/governanceObservationSnapshot.ts +0 -613
- package/integrations/mcp/alignedPlatformGate.ts +0 -232
- package/integrations/mcp/readMcpPrePushStdin.ts +0 -7
- package/scripts/build-ruralgo-s1-evidence-pack.ts +0 -85
- 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
|
|
236
|
-
|
|
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
|
|
333
|
-
const
|
|
334
|
-
const
|
|
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:
|
|
349
|
-
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:
|
|
355
|
-
mcp_receipt:
|
|
356
|
-
skills_contract:
|
|
357
|
-
repo_state:
|
|
358
|
-
consistency_hint: buildConsistencyHint(
|
|
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
|
|
67
|
-
|
|
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
|
-
|
|
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
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
114
|
-
|
|
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
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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 =
|
|
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'
|
|
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:
|
|
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 {
|
|
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 =
|
|
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
|
-
):
|
|
390
|
+
): EnterpriseToolExecution => {
|
|
399
391
|
switch (toolName) {
|
|
400
392
|
case 'ai_gate_check': {
|
|
401
393
|
const stage = toSddStage(args.stage, 'PRE_COMMIT');
|
|
402
|
-
const execution =
|
|
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,
|
|
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(
|
|
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 =
|
|
817
|
+
result = executeEnterpriseTool(
|
|
834
818
|
repoRoot,
|
|
835
819
|
toolName,
|
|
836
820
|
args,
|