pumuki 6.3.29 → 6.3.30
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.
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
## Estado actual
|
|
8
8
|
- Plan activo: `docs/seguimiento-completo-validacion-ruralgo-03-03-2026.md`
|
|
9
9
|
- Estado del plan: EN CURSO
|
|
10
|
-
- Task activa (`🚧`): `P12.F1.
|
|
10
|
+
- Task activa (`🚧`): `P12.F1.T42` (sincronizar canónico RuralGO tras cierre de `#543` y actualizar refs reales en feedback/master plan).
|
|
11
11
|
|
|
12
12
|
## Historial resumido
|
|
13
13
|
- No se mantienen MDs históricos de seguimiento en este repositorio.
|
|
@@ -1243,13 +1243,13 @@ Criterio de salida F5:
|
|
|
1243
1243
|
- `gh issue create --title "[P2][OPS] Telemetría estructurada exportable (JSONL/OTel) para auditoría enterprise" ...`
|
|
1244
1244
|
- edición de `R_GO/docs/technical/08-validation/refactor/pumuki-integration-feedback.md`
|
|
1245
1245
|
- edición de `R_GO/ruralgo-master-plan.md`
|
|
1246
|
-
-
|
|
1247
|
-
-
|
|
1246
|
+
- ✅ `P12.F1.T40` Ejecutar implementación técnica de la mejora estratégica `#543` (policy-as-code versionada/firmada) en Pumuki con ciclo RED -> GREEN -> REFACTOR y trazabilidad E2E.
|
|
1247
|
+
- cierre ejecutado:
|
|
1248
1248
|
- rama de implementación: `feature/543-policy-as-code-signed`.
|
|
1249
1249
|
- commits técnicos:
|
|
1250
1250
|
- `a24bf6c` (`feat(gate): add policy-as-code trace metadata and strict validation guard`).
|
|
1251
1251
|
- `594a83c` (`feat(policy): add contract expiry validation and strict blocking coverage`).
|
|
1252
|
-
- PR
|
|
1252
|
+
- PR mergeada: `https://github.com/SwiftEnProfundidad/ast-intelligence-hooks/pull/545`.
|
|
1253
1253
|
- tests/verificación ejecutados en verde:
|
|
1254
1254
|
- `npx --yes tsx@4.21.0 --test integrations/gate/__tests__/stagePolicies.test.ts integrations/git/__tests__/runPlatformGate.test.ts integrations/git/__tests__/hookGateSummary.test.ts integrations/git/__tests__/EvidenceService.test.ts`
|
|
1255
1255
|
- `npm run -s typecheck`
|
|
@@ -1257,7 +1257,7 @@ Criterio de salida F5:
|
|
|
1257
1257
|
- contrato `policy-as-code` con validación runtime `valid/invalid/expired/unknown-source`.
|
|
1258
1258
|
- bloqueo strict (`PUMUKI_POLICY_STRICT=1`) con códigos explícitos de policy.
|
|
1259
1259
|
- documentación operativa mínima añadida en `README.md`.
|
|
1260
|
-
- bloqueo
|
|
1260
|
+
- contexto histórico de bloqueo (resuelto por bypass administrativo autorizado):
|
|
1261
1261
|
- checks remotos de GitHub Actions en `#545` fallan de forma sistémica y sin logs recuperables (`log not found` en `gh run view --log`).
|
|
1262
1262
|
- `security/snyk` reporta límite de cuota privada (`You have used your limit of private tests`).
|
|
1263
1263
|
- pendiente decisión de merge cuando se desbloquee infraestructura/checks remotos.
|
|
@@ -1266,7 +1266,10 @@ Criterio de salida F5:
|
|
|
1266
1266
|
- `gh pr checks 545 --json name,state,bucket,link,description`
|
|
1267
1267
|
- `gh run view 22647944444 --job 65640392393 --log`
|
|
1268
1268
|
|
|
1269
|
-
-
|
|
1269
|
+
- ✅ `P12.F1.T41` Desbloquear cierre administrativo de `#543` (merge PR `#545` + cierre issue `#543`) cuando los checks remotos vuelvan a estado operativo.
|
|
1270
|
+
- cierre ejecutado:
|
|
1271
|
+
- PR `#545` mergeada (commit: `71809fa5d281b2bab62c7e32d89ae4d1b49f0ea3`).
|
|
1272
|
+
- issue `#543` cerrada.
|
|
1270
1273
|
- issue operativa de soporte creada: `#546` (`ops: unblock remote PR checks (snyk quota + missing job logs)`).
|
|
1271
1274
|
- avance ejecutado:
|
|
1272
1275
|
- reintento de runs fallidos lanzado en lote (`gh run rerun <run_id> --failed`) para descartar flake.
|
|
@@ -1275,7 +1278,7 @@ Criterio de salida F5:
|
|
|
1275
1278
|
- jobs de `CI` con `steps_count=0` en attempt 2.
|
|
1276
1279
|
- ZIP de logs del run disponible, pero solo contiene `system.txt` (sin logs de pasos de ejecución).
|
|
1277
1280
|
- issue `#543`, issue `#546` y PR `#545` actualizadas con comentario de progreso y bloqueo remoto.
|
|
1278
|
-
- bloqueo reproducido (comando/error):
|
|
1281
|
+
- bloqueo reproducido (comando/error, histórico):
|
|
1279
1282
|
- comando: `gh run view 22648051050`
|
|
1280
1283
|
- error observado: `The job was not started because your account is locked due to a billing issue.` (anotación repetida en todos los jobs de `CI`).
|
|
1281
1284
|
- comando: `gh pr checks 545 --json name,state,bucket,link,description`
|
|
@@ -1310,8 +1313,11 @@ Criterio de salida F5:
|
|
|
1310
1313
|
- `npm run -s typecheck`
|
|
1311
1314
|
- `gh pr view 547 --json state,mergeStateStatus,statusCheckRollup,headRefOid,url`
|
|
1312
1315
|
|
|
1313
|
-
-
|
|
1314
|
-
-
|
|
1316
|
+
- ✅ `P12.F1.T44` Desbloquear cierre administrativo de `#544` (merge PR `#547` + cierre issue `#544`) cuando los checks remotos vuelvan a estado operativo.
|
|
1317
|
+
- cierre ejecutado:
|
|
1318
|
+
- PR `#547` mergeada (commit: `76e385fb43778b273ee347dbe9254bbf472f8063`).
|
|
1319
|
+
- issue `#544` cerrada.
|
|
1320
|
+
- bloqueo reproducido (comando/error, histórico):
|
|
1315
1321
|
- comando: `gh run view 22648407847`
|
|
1316
1322
|
- error observado: `The job was not started because your account is locked due to a billing issue.` (anotación repetida en jobs de `CI`).
|
|
1317
1323
|
- comando: `gh pr checks 547 --json name,state,bucket,link,description`
|
|
@@ -1320,7 +1326,7 @@ Criterio de salida F5:
|
|
|
1320
1326
|
- desbloquear billing de la cuenta/organización de GitHub Actions.
|
|
1321
1327
|
- restaurar cuota/permisos de Snyk o ajustar temporalmente ese check requerido.
|
|
1322
1328
|
|
|
1323
|
-
-
|
|
1329
|
+
- 🚧 `P12.F1.T42` Sincronizar canónico RuralGO tras cierre de `#543` (`REPORTED -> FIXED` en feedback + master plan con refs reales).
|
|
1324
1330
|
- salida esperada:
|
|
1325
1331
|
- `R_GO/docs/technical/08-validation/refactor/pumuki-integration-feedback.md` actualizado a `FIXED` para `PUMUKI-INC-055`.
|
|
1326
1332
|
- `R_GO/ruralgo-master-plan.md` con leyenda actualizada (solo 1 `🚧` activa) y referencia de issue/PR/commit.
|
|
@@ -1338,8 +1344,10 @@ Criterio de salida F5:
|
|
|
1338
1344
|
- `npx --yes --package pumuki@latest pumuki-pre-commit` (gate `ALLOW/PASS`)
|
|
1339
1345
|
- `npx --yes --package pumuki@latest pumuki-pre-push` (gate `ALLOW/PASS`)
|
|
1340
1346
|
|
|
1341
|
-
-
|
|
1342
|
-
-
|
|
1347
|
+
- ✅ `P12.F1.T47` Consolidar monitoreo único del bloqueo remoto para `#543/#544` en issue operativa `#546` y ejecutar cierre administrativo automático en cuanto los checks remotos queden operativos.
|
|
1348
|
+
- cierre ejecutado:
|
|
1349
|
+
- issue operativa `#546` cerrada tras merge y publicación.
|
|
1350
|
+
- publicación npm completada: `pumuki@6.3.29` (tag `v6.3.29`).
|
|
1343
1351
|
- issue operativa actualizada con seguimiento de `#547`:
|
|
1344
1352
|
- `https://github.com/SwiftEnProfundidad/ast-intelligence-hooks/issues/546#issuecomment-3994327893`
|
|
1345
1353
|
- `https://github.com/SwiftEnProfundidad/ast-intelligence-hooks/issues/546#issuecomment-3994336761`
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
import { appendFileSync, mkdirSync } from 'node:fs';
|
|
2
|
+
import { dirname, isAbsolute, join } from 'node:path';
|
|
3
|
+
import type { Finding } from '../../core/gate/Finding';
|
|
4
|
+
import type { GateOutcome } from '../../core/gate/GateOutcome';
|
|
5
|
+
import type { GateStage } from '../../core/gate/GateStage';
|
|
6
|
+
import type { Severity } from '../../core/rules/Severity';
|
|
7
|
+
import type { RepoState } from '../evidence/schema';
|
|
8
|
+
import type { ResolvedStagePolicy } from '../gate/stagePolicies';
|
|
9
|
+
import type { SddDecision } from '../sdd';
|
|
10
|
+
|
|
11
|
+
const TELEMETRY_EVENT_SCHEMA = 'telemetry_event_v1';
|
|
12
|
+
const TELEMETRY_EVENT_SCHEMA_VERSION = '1.0';
|
|
13
|
+
const DEFAULT_OTEL_SERVICE_NAME = 'pumuki';
|
|
14
|
+
const DEFAULT_OTEL_TIMEOUT_MS = 1500;
|
|
15
|
+
|
|
16
|
+
type PolicyTrace = ResolvedStagePolicy['trace'] & {
|
|
17
|
+
version?: string;
|
|
18
|
+
signature?: string;
|
|
19
|
+
policySource?: string;
|
|
20
|
+
validation?: {
|
|
21
|
+
status: 'valid' | 'invalid' | 'expired' | 'unknown-source';
|
|
22
|
+
code: string;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const toSeverityCounts = (
|
|
27
|
+
findings: ReadonlyArray<Finding>
|
|
28
|
+
): Record<Severity, number> => {
|
|
29
|
+
const counts: Record<Severity, number> = {
|
|
30
|
+
CRITICAL: 0,
|
|
31
|
+
ERROR: 0,
|
|
32
|
+
WARN: 0,
|
|
33
|
+
INFO: 0,
|
|
34
|
+
};
|
|
35
|
+
for (const finding of findings) {
|
|
36
|
+
counts[finding.severity] += 1;
|
|
37
|
+
}
|
|
38
|
+
return counts;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const toBlockingFindingsCount = (findings: ReadonlyArray<Finding>): number => {
|
|
42
|
+
let count = 0;
|
|
43
|
+
for (const finding of findings) {
|
|
44
|
+
if (finding.severity === 'CRITICAL' || finding.severity === 'ERROR') {
|
|
45
|
+
count += 1;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return count;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const toGateSeverityText = (outcome: GateOutcome): string => {
|
|
52
|
+
if (outcome === 'BLOCK') {
|
|
53
|
+
return 'ERROR';
|
|
54
|
+
}
|
|
55
|
+
if (outcome === 'WARN') {
|
|
56
|
+
return 'WARN';
|
|
57
|
+
}
|
|
58
|
+
return 'INFO';
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const toGateSeverityNumber = (outcome: GateOutcome): number => {
|
|
62
|
+
if (outcome === 'BLOCK') {
|
|
63
|
+
return 17;
|
|
64
|
+
}
|
|
65
|
+
if (outcome === 'WARN') {
|
|
66
|
+
return 13;
|
|
67
|
+
}
|
|
68
|
+
return 9;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const toOtelTimeoutMs = (value: string | undefined): number => {
|
|
72
|
+
const parsed = Number.parseInt(value ?? '', 10);
|
|
73
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
74
|
+
return DEFAULT_OTEL_TIMEOUT_MS;
|
|
75
|
+
}
|
|
76
|
+
return parsed;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const resolveTargetPath = (repoRoot: string, targetPath: string): string => {
|
|
80
|
+
if (isAbsolute(targetPath)) {
|
|
81
|
+
return targetPath;
|
|
82
|
+
}
|
|
83
|
+
return join(repoRoot, targetPath);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const defaultAppendJsonlLine = (targetPath: string, line: string): void => {
|
|
87
|
+
mkdirSync(dirname(targetPath), { recursive: true });
|
|
88
|
+
appendFileSync(targetPath, line, 'utf8');
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const defaultPostOtelPayload = async (params: {
|
|
92
|
+
endpoint: string;
|
|
93
|
+
payload: unknown;
|
|
94
|
+
timeoutMs: number;
|
|
95
|
+
}): Promise<void> => {
|
|
96
|
+
if (typeof fetch !== 'function') {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const controller = new AbortController();
|
|
100
|
+
const timeoutId = setTimeout(() => {
|
|
101
|
+
controller.abort();
|
|
102
|
+
}, params.timeoutMs);
|
|
103
|
+
try {
|
|
104
|
+
await fetch(params.endpoint, {
|
|
105
|
+
method: 'POST',
|
|
106
|
+
headers: {
|
|
107
|
+
'content-type': 'application/json',
|
|
108
|
+
},
|
|
109
|
+
body: JSON.stringify(params.payload),
|
|
110
|
+
signal: controller.signal,
|
|
111
|
+
});
|
|
112
|
+
} finally {
|
|
113
|
+
clearTimeout(timeoutId);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export type GateTelemetryEventV1 = {
|
|
118
|
+
schema: typeof TELEMETRY_EVENT_SCHEMA;
|
|
119
|
+
schema_version: typeof TELEMETRY_EVENT_SCHEMA_VERSION;
|
|
120
|
+
emitted_at: string;
|
|
121
|
+
stage: GateStage;
|
|
122
|
+
audit_mode: 'gate' | 'engine';
|
|
123
|
+
gate_outcome: GateOutcome;
|
|
124
|
+
files_scanned: number;
|
|
125
|
+
findings_total: number;
|
|
126
|
+
blocking_findings: number;
|
|
127
|
+
severity_counts: Record<Severity, number>;
|
|
128
|
+
repo: {
|
|
129
|
+
root: string;
|
|
130
|
+
branch: string | null;
|
|
131
|
+
upstream: string | null;
|
|
132
|
+
dirty: boolean;
|
|
133
|
+
staged: number;
|
|
134
|
+
unstaged: number;
|
|
135
|
+
ahead: number;
|
|
136
|
+
behind: number;
|
|
137
|
+
};
|
|
138
|
+
policy?: {
|
|
139
|
+
source: PolicyTrace['source'];
|
|
140
|
+
bundle: string;
|
|
141
|
+
hash: string;
|
|
142
|
+
version?: string;
|
|
143
|
+
signature?: string;
|
|
144
|
+
policy_source?: string;
|
|
145
|
+
validation_status?: 'valid' | 'invalid' | 'expired' | 'unknown-source';
|
|
146
|
+
validation_code?: string;
|
|
147
|
+
};
|
|
148
|
+
sdd?: {
|
|
149
|
+
allowed: boolean;
|
|
150
|
+
code: string;
|
|
151
|
+
message: string;
|
|
152
|
+
};
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
export type EmitGateTelemetryResult = {
|
|
156
|
+
skipped: boolean;
|
|
157
|
+
jsonl_path?: string;
|
|
158
|
+
otel_dispatched: boolean;
|
|
159
|
+
event: GateTelemetryEventV1;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
export type EmitGateTelemetryDependencies = {
|
|
163
|
+
env: NodeJS.ProcessEnv;
|
|
164
|
+
now: () => Date;
|
|
165
|
+
appendJsonlLine: (targetPath: string, line: string) => void;
|
|
166
|
+
postOtelPayload: (params: {
|
|
167
|
+
endpoint: string;
|
|
168
|
+
payload: unknown;
|
|
169
|
+
timeoutMs: number;
|
|
170
|
+
}) => Promise<void>;
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const defaultDependencies: EmitGateTelemetryDependencies = {
|
|
174
|
+
env: process.env,
|
|
175
|
+
now: () => new Date(),
|
|
176
|
+
appendJsonlLine: defaultAppendJsonlLine,
|
|
177
|
+
postOtelPayload: defaultPostOtelPayload,
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
const toTelemetryEvent = (params: {
|
|
181
|
+
stage: GateStage;
|
|
182
|
+
auditMode: 'gate' | 'engine';
|
|
183
|
+
gateOutcome: GateOutcome;
|
|
184
|
+
filesScanned: number;
|
|
185
|
+
findings: ReadonlyArray<Finding>;
|
|
186
|
+
repoRoot: string;
|
|
187
|
+
repoState: RepoState;
|
|
188
|
+
policyTrace?: PolicyTrace;
|
|
189
|
+
sddDecision?: Pick<SddDecision, 'allowed' | 'code' | 'message'>;
|
|
190
|
+
emittedAt: Date;
|
|
191
|
+
}): GateTelemetryEventV1 => {
|
|
192
|
+
return {
|
|
193
|
+
schema: TELEMETRY_EVENT_SCHEMA,
|
|
194
|
+
schema_version: TELEMETRY_EVENT_SCHEMA_VERSION,
|
|
195
|
+
emitted_at: params.emittedAt.toISOString(),
|
|
196
|
+
stage: params.stage,
|
|
197
|
+
audit_mode: params.auditMode,
|
|
198
|
+
gate_outcome: params.gateOutcome,
|
|
199
|
+
files_scanned: params.filesScanned,
|
|
200
|
+
findings_total: params.findings.length,
|
|
201
|
+
blocking_findings: toBlockingFindingsCount(params.findings),
|
|
202
|
+
severity_counts: toSeverityCounts(params.findings),
|
|
203
|
+
repo: {
|
|
204
|
+
root: params.repoRoot,
|
|
205
|
+
branch: params.repoState.git.branch,
|
|
206
|
+
upstream: params.repoState.git.upstream,
|
|
207
|
+
dirty: params.repoState.git.dirty,
|
|
208
|
+
staged: params.repoState.git.staged,
|
|
209
|
+
unstaged: params.repoState.git.unstaged,
|
|
210
|
+
ahead: params.repoState.git.ahead,
|
|
211
|
+
behind: params.repoState.git.behind,
|
|
212
|
+
},
|
|
213
|
+
...(params.policyTrace
|
|
214
|
+
? {
|
|
215
|
+
policy: {
|
|
216
|
+
source: params.policyTrace.source,
|
|
217
|
+
bundle: params.policyTrace.bundle,
|
|
218
|
+
hash: params.policyTrace.hash,
|
|
219
|
+
...(params.policyTrace.version ? { version: params.policyTrace.version } : {}),
|
|
220
|
+
...(params.policyTrace.signature ? { signature: params.policyTrace.signature } : {}),
|
|
221
|
+
...(params.policyTrace.policySource
|
|
222
|
+
? { policy_source: params.policyTrace.policySource }
|
|
223
|
+
: {}),
|
|
224
|
+
...(params.policyTrace.validation
|
|
225
|
+
? {
|
|
226
|
+
validation_status: params.policyTrace.validation.status,
|
|
227
|
+
validation_code: params.policyTrace.validation.code,
|
|
228
|
+
}
|
|
229
|
+
: {}),
|
|
230
|
+
},
|
|
231
|
+
}
|
|
232
|
+
: {}),
|
|
233
|
+
...(params.sddDecision
|
|
234
|
+
? {
|
|
235
|
+
sdd: {
|
|
236
|
+
allowed: params.sddDecision.allowed,
|
|
237
|
+
code: params.sddDecision.code,
|
|
238
|
+
message: params.sddDecision.message,
|
|
239
|
+
},
|
|
240
|
+
}
|
|
241
|
+
: {}),
|
|
242
|
+
};
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
const toOtelPayload = (params: {
|
|
246
|
+
event: GateTelemetryEventV1;
|
|
247
|
+
serviceName: string;
|
|
248
|
+
}): unknown => {
|
|
249
|
+
const eventBody = JSON.stringify(params.event);
|
|
250
|
+
return {
|
|
251
|
+
resourceLogs: [
|
|
252
|
+
{
|
|
253
|
+
resource: {
|
|
254
|
+
attributes: [
|
|
255
|
+
{
|
|
256
|
+
key: 'service.name',
|
|
257
|
+
value: { stringValue: params.serviceName },
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
key: 'pumuki.telemetry.schema',
|
|
261
|
+
value: { stringValue: params.event.schema },
|
|
262
|
+
},
|
|
263
|
+
],
|
|
264
|
+
},
|
|
265
|
+
scopeLogs: [
|
|
266
|
+
{
|
|
267
|
+
scope: {
|
|
268
|
+
name: 'pumuki.telemetry',
|
|
269
|
+
version: params.event.schema_version,
|
|
270
|
+
},
|
|
271
|
+
logRecords: [
|
|
272
|
+
{
|
|
273
|
+
timeUnixNano: `${Date.parse(params.event.emitted_at) * 1000000}`,
|
|
274
|
+
severityNumber: toGateSeverityNumber(params.event.gate_outcome),
|
|
275
|
+
severityText: toGateSeverityText(params.event.gate_outcome),
|
|
276
|
+
body: { stringValue: eventBody },
|
|
277
|
+
attributes: [
|
|
278
|
+
{
|
|
279
|
+
key: 'pumuki.stage',
|
|
280
|
+
value: { stringValue: params.event.stage },
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
key: 'pumuki.gate_outcome',
|
|
284
|
+
value: { stringValue: params.event.gate_outcome },
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
key: 'pumuki.policy_hash',
|
|
288
|
+
value: {
|
|
289
|
+
stringValue: params.event.policy?.hash ?? '',
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
],
|
|
293
|
+
},
|
|
294
|
+
],
|
|
295
|
+
},
|
|
296
|
+
],
|
|
297
|
+
},
|
|
298
|
+
],
|
|
299
|
+
};
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
export const emitGateTelemetryEvent = async (
|
|
303
|
+
params: {
|
|
304
|
+
stage: GateStage;
|
|
305
|
+
auditMode: 'gate' | 'engine';
|
|
306
|
+
gateOutcome: GateOutcome;
|
|
307
|
+
filesScanned: number;
|
|
308
|
+
findings: ReadonlyArray<Finding>;
|
|
309
|
+
repoRoot: string;
|
|
310
|
+
repoState: RepoState;
|
|
311
|
+
policyTrace?: PolicyTrace;
|
|
312
|
+
sddDecision?: Pick<SddDecision, 'allowed' | 'code' | 'message'>;
|
|
313
|
+
},
|
|
314
|
+
dependencies: Partial<EmitGateTelemetryDependencies> = {}
|
|
315
|
+
): Promise<EmitGateTelemetryResult> => {
|
|
316
|
+
const activeDependencies: EmitGateTelemetryDependencies = {
|
|
317
|
+
...defaultDependencies,
|
|
318
|
+
...dependencies,
|
|
319
|
+
};
|
|
320
|
+
const jsonlPathRaw = activeDependencies.env.PUMUKI_TELEMETRY_JSONL_PATH?.trim() ?? '';
|
|
321
|
+
const otelEndpoint = activeDependencies.env.PUMUKI_TELEMETRY_OTEL_ENDPOINT?.trim() ?? '';
|
|
322
|
+
const otelServiceName =
|
|
323
|
+
activeDependencies.env.PUMUKI_TELEMETRY_OTEL_SERVICE_NAME?.trim() ||
|
|
324
|
+
DEFAULT_OTEL_SERVICE_NAME;
|
|
325
|
+
const otelTimeoutMs = toOtelTimeoutMs(
|
|
326
|
+
activeDependencies.env.PUMUKI_TELEMETRY_OTEL_TIMEOUT_MS
|
|
327
|
+
);
|
|
328
|
+
|
|
329
|
+
const event = toTelemetryEvent({
|
|
330
|
+
...params,
|
|
331
|
+
emittedAt: activeDependencies.now(),
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
if (jsonlPathRaw.length === 0 && otelEndpoint.length === 0) {
|
|
335
|
+
return {
|
|
336
|
+
skipped: true,
|
|
337
|
+
otel_dispatched: false,
|
|
338
|
+
event,
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
let jsonlPath: string | undefined;
|
|
343
|
+
if (jsonlPathRaw.length > 0) {
|
|
344
|
+
jsonlPath = resolveTargetPath(params.repoRoot, jsonlPathRaw);
|
|
345
|
+
activeDependencies.appendJsonlLine(jsonlPath, `${JSON.stringify(event)}\n`);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
let otelDispatched = false;
|
|
349
|
+
if (otelEndpoint.length > 0) {
|
|
350
|
+
const payload = toOtelPayload({
|
|
351
|
+
event,
|
|
352
|
+
serviceName: otelServiceName,
|
|
353
|
+
});
|
|
354
|
+
try {
|
|
355
|
+
await activeDependencies.postOtelPayload({
|
|
356
|
+
endpoint: otelEndpoint,
|
|
357
|
+
payload,
|
|
358
|
+
timeoutMs: otelTimeoutMs,
|
|
359
|
+
});
|
|
360
|
+
otelDispatched = true;
|
|
361
|
+
} catch {
|
|
362
|
+
otelDispatched = false;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return {
|
|
367
|
+
skipped: false,
|
|
368
|
+
...(jsonlPath ? { jsonl_path: jsonlPath } : {}),
|
|
369
|
+
otel_dispatched: otelDispatched,
|
|
370
|
+
event,
|
|
371
|
+
};
|
|
372
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pumuki",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.30",
|
|
4
4
|
"description": "Enterprise-grade AST Intelligence System with multi-platform support (iOS, Android, Backend, Frontend) and Feature-First + DDD + Clean Architecture enforcement. Includes dynamic violations API for intelligent querying.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -202,6 +202,7 @@
|
|
|
202
202
|
"integrations/mcp/*.ts",
|
|
203
203
|
"integrations/notifications/*.ts",
|
|
204
204
|
"integrations/platform/*.ts",
|
|
205
|
+
"integrations/telemetry/*.ts",
|
|
205
206
|
"scripts/*.ts",
|
|
206
207
|
"scripts/adapters/*.ts",
|
|
207
208
|
"scripts/adapters/*.md",
|
|
@@ -10,6 +10,7 @@ export const REQUIRED_PACKAGE_PATHS = [
|
|
|
10
10
|
'core/rules/presets/heuristics/typescript.ts',
|
|
11
11
|
'scripts/package-install-smoke.ts',
|
|
12
12
|
'integrations/git/runPlatformGate.ts',
|
|
13
|
+
'integrations/telemetry/gateTelemetry.ts',
|
|
13
14
|
'integrations/lifecycle/cli.ts',
|
|
14
15
|
'integrations/notifications/emitAuditSummaryNotification.ts',
|
|
15
16
|
'integrations/evidence/buildEvidence.ts',
|