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,232 +0,0 @@
|
|
|
1
|
-
import type { AiGateStage } from '../gate/evaluateAiGate';
|
|
2
|
-
import { resolvePolicyForStage } from '../gate/stagePolicies';
|
|
3
|
-
import type { SddDecision } from '../sdd';
|
|
4
|
-
import { GitService } from '../git/GitService';
|
|
5
|
-
import { runPlatformGate } from '../git/runPlatformGate';
|
|
6
|
-
import type { GateScope } from '../git/runPlatformGateFacts';
|
|
7
|
-
import { readMcpPrePushStdin } from './readMcpPrePushStdin';
|
|
8
|
-
|
|
9
|
-
const ZERO_HASH = /^0+$/;
|
|
10
|
-
|
|
11
|
-
const runGit = (repoRoot: string, args: ReadonlyArray<string>): string | null => {
|
|
12
|
-
try {
|
|
13
|
-
return new GitService().runGit(args, repoRoot).trim();
|
|
14
|
-
} catch {
|
|
15
|
-
return null;
|
|
16
|
-
}
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const resolveUpstreamRefInRepo = (repoRoot: string): string | null =>
|
|
20
|
-
runGit(repoRoot, ['rev-parse', '@{u}']);
|
|
21
|
-
|
|
22
|
-
const resolveHeadOidInRepo = (repoRoot: string): string | null =>
|
|
23
|
-
runGit(repoRoot, ['rev-parse', 'HEAD']);
|
|
24
|
-
|
|
25
|
-
const resolveCiBaseRefInRepo = (repoRoot: string): string => {
|
|
26
|
-
const fromEnv = process.env.GITHUB_BASE_REF?.trim();
|
|
27
|
-
if (fromEnv) {
|
|
28
|
-
if (runGit(repoRoot, ['rev-parse', '--verify', fromEnv])) {
|
|
29
|
-
return fromEnv;
|
|
30
|
-
}
|
|
31
|
-
const remoteRef = `origin/${fromEnv}`;
|
|
32
|
-
if (runGit(repoRoot, ['rev-parse', '--verify', remoteRef])) {
|
|
33
|
-
return remoteRef;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
for (const candidate of ['origin/main', 'main', 'HEAD']) {
|
|
38
|
-
if (runGit(repoRoot, ['rev-parse', '--verify', candidate])) {
|
|
39
|
-
return candidate;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return 'HEAD';
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
const resolvePrePushBootstrapBaseRefInRepo = (repoRoot: string): string => {
|
|
47
|
-
const candidates = ['origin/develop', 'develop', resolveCiBaseRefInRepo(repoRoot)];
|
|
48
|
-
for (const candidate of candidates) {
|
|
49
|
-
if (runGit(repoRoot, ['rev-parse', '--verify', candidate])) {
|
|
50
|
-
return candidate;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return 'HEAD';
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
const shouldAllowBootstrapPrePush = (rawInput: string): boolean => {
|
|
58
|
-
const lines = rawInput
|
|
59
|
-
.split('\n')
|
|
60
|
-
.map((line) => line.trim())
|
|
61
|
-
.filter((line) => line.length > 0);
|
|
62
|
-
|
|
63
|
-
for (const line of lines) {
|
|
64
|
-
const [localRef, localOid, remoteRef, remoteOid] = line.split(/\s+/);
|
|
65
|
-
if (!localRef || !localOid || !remoteRef || !remoteOid) {
|
|
66
|
-
continue;
|
|
67
|
-
}
|
|
68
|
-
const localIsBranch = localRef.startsWith('refs/heads/');
|
|
69
|
-
const remoteIsBranch = remoteRef.startsWith('refs/heads/');
|
|
70
|
-
const localIsDeletion = ZERO_HASH.test(localOid);
|
|
71
|
-
const remoteIsNewBranch = ZERO_HASH.test(remoteOid);
|
|
72
|
-
|
|
73
|
-
if (localIsBranch && remoteIsBranch && !localIsDeletion && remoteIsNewBranch) {
|
|
74
|
-
return true;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return false;
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
const resolveExplicitPrePushRange = (
|
|
82
|
-
rawInput: string
|
|
83
|
-
): { fromRef: string; toRef: string } | undefined => {
|
|
84
|
-
const lines = rawInput
|
|
85
|
-
.split('\n')
|
|
86
|
-
.map((line) => line.trim())
|
|
87
|
-
.filter((line) => line.length > 0);
|
|
88
|
-
|
|
89
|
-
const eligibleRanges = lines
|
|
90
|
-
.map((line) => {
|
|
91
|
-
const [localRef, localOid, remoteRef, remoteOid] = line.split(/\s+/);
|
|
92
|
-
if (!localRef || !localOid || !remoteRef || !remoteOid) {
|
|
93
|
-
return undefined;
|
|
94
|
-
}
|
|
95
|
-
const localIsDeletion = ZERO_HASH.test(localOid);
|
|
96
|
-
const remoteIsNewBranch = ZERO_HASH.test(remoteOid);
|
|
97
|
-
if (localIsDeletion || remoteIsNewBranch) {
|
|
98
|
-
return undefined;
|
|
99
|
-
}
|
|
100
|
-
return {
|
|
101
|
-
fromRef: remoteOid,
|
|
102
|
-
toRef: localOid,
|
|
103
|
-
};
|
|
104
|
-
})
|
|
105
|
-
.filter((value): value is { fromRef: string; toRef: string } => Boolean(value));
|
|
106
|
-
|
|
107
|
-
if (eligibleRanges.length !== 1) {
|
|
108
|
-
return undefined;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return eligibleRanges[0];
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
type PrePushScopeResolution =
|
|
115
|
-
| { kind: 'scope'; scope: GateScope; sddDecisionOverride?: Pick<SddDecision, 'allowed' | 'code' | 'message'> }
|
|
116
|
-
| { kind: 'upstream_missing' };
|
|
117
|
-
|
|
118
|
-
const resolvePrePushScopeForMcp = (params: { repoRoot: string }): PrePushScopeResolution => {
|
|
119
|
-
const prePushInput = readMcpPrePushStdin();
|
|
120
|
-
const upstreamRef = resolveUpstreamRefInRepo(params.repoRoot);
|
|
121
|
-
if (!upstreamRef) {
|
|
122
|
-
const bootstrapBaseRef = resolvePrePushBootstrapBaseRefInRepo(params.repoRoot);
|
|
123
|
-
const bootstrapByPrePushStdIn = shouldAllowBootstrapPrePush(prePushInput);
|
|
124
|
-
const bootstrapByFallbackBase = !bootstrapByPrePushStdIn && bootstrapBaseRef !== 'HEAD';
|
|
125
|
-
const manualInvocationFallback =
|
|
126
|
-
!bootstrapByPrePushStdIn &&
|
|
127
|
-
!bootstrapByFallbackBase &&
|
|
128
|
-
prePushInput.trim().length === 0;
|
|
129
|
-
if (bootstrapByPrePushStdIn || bootstrapByFallbackBase) {
|
|
130
|
-
return {
|
|
131
|
-
kind: 'scope',
|
|
132
|
-
scope: {
|
|
133
|
-
kind: 'range',
|
|
134
|
-
fromRef: bootstrapBaseRef,
|
|
135
|
-
toRef: 'HEAD',
|
|
136
|
-
},
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
if (manualInvocationFallback) {
|
|
140
|
-
return { kind: 'scope', scope: { kind: 'workingTree' } };
|
|
141
|
-
}
|
|
142
|
-
return { kind: 'upstream_missing' };
|
|
143
|
-
}
|
|
144
|
-
const explicitPrePushRange = resolveExplicitPrePushRange(prePushInput);
|
|
145
|
-
const prePushFromRef = explicitPrePushRange?.fromRef ?? upstreamRef;
|
|
146
|
-
const prePushToRef = explicitPrePushRange?.toRef ?? 'HEAD';
|
|
147
|
-
const headOid = resolveHeadOidInRepo(params.repoRoot);
|
|
148
|
-
const sddDecisionOverride =
|
|
149
|
-
explicitPrePushRange && headOid && explicitPrePushRange.toRef !== headOid
|
|
150
|
-
? ({
|
|
151
|
-
allowed: true,
|
|
152
|
-
code: 'ALLOWED',
|
|
153
|
-
message:
|
|
154
|
-
`SDD enforcement suspended for PRE_PUSH historical publish targeting ${explicitPrePushRange.toRef.slice(0, 12)} ` +
|
|
155
|
-
`instead of current HEAD ${headOid.slice(0, 12)}.`,
|
|
156
|
-
} as Pick<SddDecision, 'allowed' | 'code' | 'message'>)
|
|
157
|
-
: undefined;
|
|
158
|
-
return {
|
|
159
|
-
kind: 'scope',
|
|
160
|
-
scope: {
|
|
161
|
-
kind: 'range',
|
|
162
|
-
fromRef: prePushFromRef,
|
|
163
|
-
toRef: prePushToRef,
|
|
164
|
-
},
|
|
165
|
-
sddDecisionOverride,
|
|
166
|
-
};
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
type RunAlignedParams = {
|
|
170
|
-
repoRoot: string;
|
|
171
|
-
stage: AiGateStage;
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
export const runMcpAlignedPlatformGate = async (
|
|
175
|
-
params: RunAlignedParams
|
|
176
|
-
): Promise<{ exitCode: number; aligned: boolean; skipReason: string | null }> => {
|
|
177
|
-
const git = new GitService();
|
|
178
|
-
const resolved = resolvePolicyForStage(params.stage, params.repoRoot);
|
|
179
|
-
if (params.stage === 'PRE_WRITE') {
|
|
180
|
-
const exitCode = await runPlatformGate({
|
|
181
|
-
policy: resolved.policy,
|
|
182
|
-
policyTrace: resolved.trace,
|
|
183
|
-
scope: { kind: 'workingTree' },
|
|
184
|
-
silent: true,
|
|
185
|
-
services: { git },
|
|
186
|
-
});
|
|
187
|
-
return { exitCode, aligned: true, skipReason: null };
|
|
188
|
-
}
|
|
189
|
-
if (params.stage === 'PRE_COMMIT') {
|
|
190
|
-
const exitCode = await runPlatformGate({
|
|
191
|
-
policy: resolved.policy,
|
|
192
|
-
policyTrace: resolved.trace,
|
|
193
|
-
scope: { kind: 'staged' },
|
|
194
|
-
silent: true,
|
|
195
|
-
services: { git },
|
|
196
|
-
});
|
|
197
|
-
return { exitCode, aligned: true, skipReason: null };
|
|
198
|
-
}
|
|
199
|
-
if (params.stage === 'CI') {
|
|
200
|
-
const ciBaseRef = resolveCiBaseRefInRepo(params.repoRoot);
|
|
201
|
-
const exitCode = await runPlatformGate({
|
|
202
|
-
policy: resolved.policy,
|
|
203
|
-
policyTrace: resolved.trace,
|
|
204
|
-
scope: {
|
|
205
|
-
kind: 'range',
|
|
206
|
-
fromRef: ciBaseRef,
|
|
207
|
-
toRef: 'HEAD',
|
|
208
|
-
},
|
|
209
|
-
silent: true,
|
|
210
|
-
services: { git },
|
|
211
|
-
});
|
|
212
|
-
return { exitCode, aligned: true, skipReason: null };
|
|
213
|
-
}
|
|
214
|
-
if (params.stage === 'PRE_PUSH') {
|
|
215
|
-
const scopeResolution = resolvePrePushScopeForMcp({ repoRoot: params.repoRoot });
|
|
216
|
-
if (scopeResolution.kind === 'upstream_missing') {
|
|
217
|
-
return { exitCode: 1, aligned: false, skipReason: 'PRE_PUSH_UPSTREAM_MISSING' };
|
|
218
|
-
}
|
|
219
|
-
const exitCode = await runPlatformGate({
|
|
220
|
-
policy: resolved.policy,
|
|
221
|
-
policyTrace: resolved.trace,
|
|
222
|
-
scope: scopeResolution.scope,
|
|
223
|
-
silent: true,
|
|
224
|
-
services: { git },
|
|
225
|
-
...(scopeResolution.sddDecisionOverride
|
|
226
|
-
? { sddDecisionOverride: scopeResolution.sddDecisionOverride }
|
|
227
|
-
: {}),
|
|
228
|
-
});
|
|
229
|
-
return { exitCode, aligned: true, skipReason: null };
|
|
230
|
-
}
|
|
231
|
-
throw new Error(`Unsupported MCP aligned stage: ${String(params.stage)}`);
|
|
232
|
-
};
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
buildRuralGoS1EvidencePackMarkdown,
|
|
3
|
-
writeRuralGoS1EvidencePack,
|
|
4
|
-
} from './ruralgo-s1-evidence-pack-lib';
|
|
5
|
-
|
|
6
|
-
type CliOptions = {
|
|
7
|
-
consumerRoot: string;
|
|
8
|
-
outFile: string;
|
|
9
|
-
packageVersion: string;
|
|
10
|
-
generatedAt: string;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
const parseArgs = (argv: ReadonlyArray<string>): CliOptions => {
|
|
14
|
-
const options: CliOptions = {
|
|
15
|
-
consumerRoot: '<RURALGO_REPO_ROOT>',
|
|
16
|
-
outFile: '.audit-reports/ruralgo-s1/ruralgo-s1-evidence-pack.md',
|
|
17
|
-
packageVersion: 'unknown',
|
|
18
|
-
generatedAt: new Date().toISOString(),
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
22
|
-
const token = argv[index];
|
|
23
|
-
const next = argv[index + 1];
|
|
24
|
-
switch (token) {
|
|
25
|
-
case '--consumer-root':
|
|
26
|
-
if (!next) {
|
|
27
|
-
throw new Error('missing value for --consumer-root');
|
|
28
|
-
}
|
|
29
|
-
options.consumerRoot = next;
|
|
30
|
-
index += 1;
|
|
31
|
-
break;
|
|
32
|
-
case '--out':
|
|
33
|
-
if (!next) {
|
|
34
|
-
throw new Error('missing value for --out');
|
|
35
|
-
}
|
|
36
|
-
options.outFile = next;
|
|
37
|
-
index += 1;
|
|
38
|
-
break;
|
|
39
|
-
case '--package-version':
|
|
40
|
-
if (!next) {
|
|
41
|
-
throw new Error('missing value for --package-version');
|
|
42
|
-
}
|
|
43
|
-
options.packageVersion = next;
|
|
44
|
-
index += 1;
|
|
45
|
-
break;
|
|
46
|
-
case '--generated-at':
|
|
47
|
-
if (!next) {
|
|
48
|
-
throw new Error('missing value for --generated-at');
|
|
49
|
-
}
|
|
50
|
-
options.generatedAt = next;
|
|
51
|
-
index += 1;
|
|
52
|
-
break;
|
|
53
|
-
default:
|
|
54
|
-
throw new Error(`unknown argument: ${token}`);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return options;
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
const main = (): number => {
|
|
62
|
-
const cwd = process.cwd();
|
|
63
|
-
const options = parseArgs(process.argv.slice(2));
|
|
64
|
-
const markdown = buildRuralGoS1EvidencePackMarkdown({
|
|
65
|
-
cwd,
|
|
66
|
-
consumerRoot: options.consumerRoot,
|
|
67
|
-
packageVersion: options.packageVersion,
|
|
68
|
-
generatedAt: options.generatedAt,
|
|
69
|
-
});
|
|
70
|
-
const outputPath = writeRuralGoS1EvidencePack({
|
|
71
|
-
cwd,
|
|
72
|
-
outFile: options.outFile,
|
|
73
|
-
markdown,
|
|
74
|
-
});
|
|
75
|
-
process.stdout.write(`ruralgo s1 evidence pack generated at ${outputPath}\n`);
|
|
76
|
-
return 0;
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
try {
|
|
80
|
-
process.exitCode = main();
|
|
81
|
-
} catch (error) {
|
|
82
|
-
const message = error instanceof Error ? error.message : 'unknown error';
|
|
83
|
-
process.stderr.write(`ruralgo s1 evidence pack failed: ${message}\n`);
|
|
84
|
-
process.exitCode = 1;
|
|
85
|
-
}
|
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
2
|
-
import { dirname, resolve } from 'node:path';
|
|
3
|
-
|
|
4
|
-
type RuralGoS1EvidenceEntry = {
|
|
5
|
-
title: string;
|
|
6
|
-
mode: 'shell' | 'mcp';
|
|
7
|
-
command: string;
|
|
8
|
-
capture: ReadonlyArray<string>;
|
|
9
|
-
expectedFragments: ReadonlyArray<string>;
|
|
10
|
-
incs: ReadonlyArray<string>;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export type RuralGoS1EvidencePackOptions = {
|
|
14
|
-
cwd: string;
|
|
15
|
-
consumerRoot: string;
|
|
16
|
-
packageVersion: string;
|
|
17
|
-
generatedAt: string;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const resolvePumukiPackageSelector = (packageVersion: string): string => {
|
|
21
|
-
const trimmed = packageVersion.trim();
|
|
22
|
-
const isStableSemver = /^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(trimmed);
|
|
23
|
-
if (isStableSemver) {
|
|
24
|
-
return `pumuki@${trimmed}`;
|
|
25
|
-
}
|
|
26
|
-
return 'pumuki@latest';
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const EVIDENCE_ENTRIES: ReadonlyArray<RuralGoS1EvidenceEntry> = [
|
|
30
|
-
{
|
|
31
|
-
title: 'Lifecycle status',
|
|
32
|
-
mode: 'shell',
|
|
33
|
-
command: 'npm run pumuki:status',
|
|
34
|
-
capture: [
|
|
35
|
-
'Bloque `governance truth` completo.',
|
|
36
|
-
'Indicadores de contrato efectivo, rama y skills surface.',
|
|
37
|
-
],
|
|
38
|
-
expectedFragments: [
|
|
39
|
-
'governance truth',
|
|
40
|
-
'governance_effective',
|
|
41
|
-
'contract_surface',
|
|
42
|
-
'current_branch',
|
|
43
|
-
],
|
|
44
|
-
incs: ['PUMUKI-INC-070', 'PUMUKI-INC-071', 'PUMUKI-INC-073', 'PUMUKI-INC-076'],
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
title: 'Lifecycle doctor',
|
|
48
|
-
mode: 'shell',
|
|
49
|
-
command: 'npm run pumuki:doctor',
|
|
50
|
-
capture: [
|
|
51
|
-
'Veredicto humano final.',
|
|
52
|
-
'Bloque `governance truth` con `next_action` visible.',
|
|
53
|
-
],
|
|
54
|
-
expectedFragments: [
|
|
55
|
-
'governance truth',
|
|
56
|
-
'next_action',
|
|
57
|
-
'reason_code',
|
|
58
|
-
'WARN',
|
|
59
|
-
],
|
|
60
|
-
incs: ['PUMUKI-INC-070', 'PUMUKI-INC-071', 'PUMUKI-INC-073'],
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
title: 'PRE_WRITE canónico',
|
|
64
|
-
mode: 'shell',
|
|
65
|
-
command: 'npx --yes --package pumuki@latest pumuki sdd validate --stage=PRE_WRITE --json',
|
|
66
|
-
capture: [
|
|
67
|
-
'Salida JSON completa.',
|
|
68
|
-
'Campos de session/mode y remediación inmediata.',
|
|
69
|
-
],
|
|
70
|
-
expectedFragments: [
|
|
71
|
-
'"stage":"PRE_WRITE"',
|
|
72
|
-
'"decision"',
|
|
73
|
-
'"next_action"',
|
|
74
|
-
],
|
|
75
|
-
incs: ['PUMUKI-INC-070', 'PUMUKI-INC-072'],
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
title: 'Hook pre-commit / gate',
|
|
79
|
-
mode: 'shell',
|
|
80
|
-
command: 'git commit --allow-empty -m "test: pumuki s1 validation"',
|
|
81
|
-
capture: [
|
|
82
|
-
'Bloque de gate con `reason_code`, `instruction` y `next_action`.',
|
|
83
|
-
'Si bloquea, conservar `NEXT:` y `REMEDIATION:`.',
|
|
84
|
-
],
|
|
85
|
-
expectedFragments: [
|
|
86
|
-
'reason_code=',
|
|
87
|
-
'instruction=',
|
|
88
|
-
'next_action=',
|
|
89
|
-
],
|
|
90
|
-
incs: ['PUMUKI-INC-071', 'PUMUKI-INC-073', 'PUMUKI-INC-076'],
|
|
91
|
-
},
|
|
92
|
-
{
|
|
93
|
-
title: 'MCP pre_flight_check',
|
|
94
|
-
mode: 'mcp',
|
|
95
|
-
command: 'mcp::pre_flight_check(stage=PRE_WRITE)',
|
|
96
|
-
capture: [
|
|
97
|
-
'Payload completo de `result`.',
|
|
98
|
-
'Campos `reason_code`, `instruction`, `next_action`, `hints`.',
|
|
99
|
-
],
|
|
100
|
-
expectedFragments: [
|
|
101
|
-
'reason_code',
|
|
102
|
-
'instruction',
|
|
103
|
-
'next_action',
|
|
104
|
-
'hints',
|
|
105
|
-
],
|
|
106
|
-
incs: ['PUMUKI-INC-071', 'PUMUKI-INC-072', 'PUMUKI-INC-073'],
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
title: 'MCP auto_execute_ai_start',
|
|
110
|
-
mode: 'mcp',
|
|
111
|
-
command: 'mcp::auto_execute_ai_start(stage=PRE_WRITE)',
|
|
112
|
-
capture: [
|
|
113
|
-
'Payload completo de `result`.',
|
|
114
|
-
'Campos `action`, `reason_code`, `next_action`, `confidence_pct`.',
|
|
115
|
-
],
|
|
116
|
-
expectedFragments: [
|
|
117
|
-
'action',
|
|
118
|
-
'reason_code',
|
|
119
|
-
'next_action',
|
|
120
|
-
'confidence_pct',
|
|
121
|
-
],
|
|
122
|
-
incs: ['PUMUKI-INC-071', 'PUMUKI-INC-073', 'PUMUKI-INC-076'],
|
|
123
|
-
},
|
|
124
|
-
];
|
|
125
|
-
|
|
126
|
-
const renderEntry = (params: {
|
|
127
|
-
entry: RuralGoS1EvidenceEntry;
|
|
128
|
-
consumerRoot: string;
|
|
129
|
-
}): string => {
|
|
130
|
-
const command = params.entry.mode === 'shell'
|
|
131
|
-
? `cd ${params.consumerRoot} && ${params.entry.command}`
|
|
132
|
-
: params.entry.command;
|
|
133
|
-
|
|
134
|
-
return [
|
|
135
|
-
`### ${params.entry.title}`,
|
|
136
|
-
'',
|
|
137
|
-
`- mode: ${params.entry.mode}`,
|
|
138
|
-
`- command: \`${command}\``,
|
|
139
|
-
`- incs: ${params.entry.incs.join(', ')}`,
|
|
140
|
-
'- capture:',
|
|
141
|
-
...params.entry.capture.map((item) => ` - ${item}`),
|
|
142
|
-
'- expected_fragments:',
|
|
143
|
-
...params.entry.expectedFragments.map((item) => ` - ${item}`),
|
|
144
|
-
'',
|
|
145
|
-
].join('\n');
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
export const buildRuralGoS1EvidencePackMarkdown = (
|
|
149
|
-
options: RuralGoS1EvidencePackOptions
|
|
150
|
-
): string => {
|
|
151
|
-
const pumukiPackageSelector = resolvePumukiPackageSelector(options.packageVersion);
|
|
152
|
-
const evidenceEntries: ReadonlyArray<RuralGoS1EvidenceEntry> = EVIDENCE_ENTRIES.map((entry) =>
|
|
153
|
-
entry.title === 'PRE_WRITE canónico'
|
|
154
|
-
? {
|
|
155
|
-
...entry,
|
|
156
|
-
command: `npx --yes --package ${pumukiPackageSelector} pumuki sdd validate --stage=PRE_WRITE --json`,
|
|
157
|
-
}
|
|
158
|
-
: entry
|
|
159
|
-
);
|
|
160
|
-
|
|
161
|
-
return [
|
|
162
|
-
'# RuralGo S1 Evidence Pack',
|
|
163
|
-
'',
|
|
164
|
-
`- generated_at: ${options.generatedAt}`,
|
|
165
|
-
`- consumer_root: \`${options.consumerRoot}\``,
|
|
166
|
-
`- package_version: ${options.packageVersion}`,
|
|
167
|
-
'- objective: validar S1 contra PUMUKI-INC-071/073/076 y reunir soporte adicional para 070/072.',
|
|
168
|
-
'',
|
|
169
|
-
'## Uso',
|
|
170
|
-
'',
|
|
171
|
-
'- Ejecuta los comandos shell desde el consumer real tras repinear la semver publicada.',
|
|
172
|
-
'- Captura las respuestas MCP desde una sesión conectada al servidor enterprise.',
|
|
173
|
-
'- No muevas un INC a `FIXED` si falta convergencia entre lifecycle, hooks y MCP.',
|
|
174
|
-
'',
|
|
175
|
-
...evidenceEntries.map((entry) =>
|
|
176
|
-
renderEntry({
|
|
177
|
-
entry,
|
|
178
|
-
consumerRoot: options.consumerRoot,
|
|
179
|
-
})
|
|
180
|
-
),
|
|
181
|
-
'## Criterio rápido de cierre',
|
|
182
|
-
'',
|
|
183
|
-
'- `PUMUKI-INC-071`: candidato a FIXED si lifecycle, hooks y MCP exponen contrato efectivo del repo.',
|
|
184
|
-
'- `PUMUKI-INC-073`: candidato a FIXED si el verde parcial desaparece y se ve governance real.',
|
|
185
|
-
'- `PUMUKI-INC-076`: candidato a FIXED si hooks y surfaces muestran GitFlow/naming como parte del gate.',
|
|
186
|
-
'- `PUMUKI-INC-072`: no cerrar salvo que el pre-edit gate aparezca de forma homogénea y automática.',
|
|
187
|
-
'',
|
|
188
|
-
].join('\n');
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
export const writeRuralGoS1EvidencePack = (params: {
|
|
192
|
-
cwd: string;
|
|
193
|
-
outFile: string;
|
|
194
|
-
markdown: string;
|
|
195
|
-
}): string => {
|
|
196
|
-
const outputPath = resolve(params.cwd, params.outFile);
|
|
197
|
-
mkdirSync(dirname(outputPath), { recursive: true });
|
|
198
|
-
writeFileSync(outputPath, params.markdown, 'utf8');
|
|
199
|
-
return outputPath;
|
|
200
|
-
};
|