pumuki 6.3.269 → 6.3.271

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 (35) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/VERSION +1 -1
  3. package/core/facts/detectors/text/android.test.ts +538 -0
  4. package/core/facts/detectors/text/android.ts +436 -0
  5. package/core/facts/detectors/text/ios.test.ts +328 -1
  6. package/core/facts/detectors/text/ios.ts +241 -0
  7. package/core/facts/detectors/typescript/index.test.ts +393 -0
  8. package/core/facts/detectors/typescript/index.ts +316 -0
  9. package/core/facts/extractHeuristicFacts.ts +70 -1
  10. package/core/rules/presets/heuristics/android.test.ts +91 -1
  11. package/core/rules/presets/heuristics/android.ts +360 -0
  12. package/core/rules/presets/heuristics/ios.test.ts +54 -1
  13. package/core/rules/presets/heuristics/ios.ts +243 -2
  14. package/core/rules/presets/heuristics/typescript.test.ts +50 -2
  15. package/core/rules/presets/heuristics/typescript.ts +162 -0
  16. package/docs/operations/RELEASE_NOTES.md +4 -0
  17. package/integrations/config/skillsDetectorRegistry.ts +501 -0
  18. package/integrations/config/skillsRuleClassification.ts +127 -3
  19. package/integrations/context/contextGate.ts +192 -0
  20. package/integrations/git/runPlatformGate.ts +4 -1
  21. package/integrations/lifecycle/preWriteAutomation.ts +1 -0
  22. package/integrations/lifecycle/preWriteLease.ts +41 -4
  23. package/package.json +2 -1
  24. package/scripts/classify-skills-rules.ts +2 -2
  25. package/scripts/framework-menu-consumer-actions-lib.ts +9 -9
  26. package/scripts/framework-menu-consumer-runtime-actions.ts +53 -117
  27. package/scripts/framework-menu-consumer-runtime-audit.ts +66 -0
  28. package/scripts/framework-menu-consumer-runtime-menu.ts +4 -4
  29. package/scripts/framework-menu-gate-lib.ts +86 -1
  30. package/scripts/framework-menu-layout-data.ts +3 -3
  31. package/scripts/framework-menu-legacy-audit-render-sections.ts +6 -0
  32. package/scripts/framework-menu.ts +10 -6
  33. package/scripts/package-install-smoke-consumer-npm-lib.ts +10 -4
  34. package/scripts/package-install-smoke-lifecycle-lib.ts +19 -0
  35. package/scripts/package-manifest-lib.ts +1 -0
@@ -3,7 +3,7 @@ import { resolveMappedHeuristicRuleIdsForCompiledRule } from './skillsDetectorRe
3
3
 
4
4
  export type SkillsRuleClassificationStatus =
5
5
  | 'AST_IMPLEMENTED'
6
- | 'IMPLEMENTABLE_AHORA'
6
+ | 'IMPLEMENTAR_DETECTOR'
7
7
  | 'REQUIERE_ESTUDIO'
8
8
  | 'NO_ES_REGLA_DE_CODIGO';
9
9
 
@@ -28,7 +28,7 @@ export type SkillsRuleClassificationSummary = {
28
28
 
29
29
  const emptyStatusCounts = (): Record<SkillsRuleClassificationStatus, number> => ({
30
30
  AST_IMPLEMENTED: 0,
31
- IMPLEMENTABLE_AHORA: 0,
31
+ IMPLEMENTAR_DETECTOR: 0,
32
32
  REQUIERE_ESTUDIO: 0,
33
33
  NO_ES_REGLA_DE_CODIGO: 0,
34
34
  });
@@ -55,11 +55,106 @@ const NON_CODE_RULE_PATTERNS: ReadonlyArray<RegExp> = [
55
55
  /\bci\/cd\b/,
56
56
  ];
57
57
 
58
+ const CODE_RULE_WITH_DIRECT_DETECTOR_PATTERNS: ReadonlyArray<RegExp> = [
59
+ /\baccessibility\b/,
60
+ /\bcontentdescription\b/,
61
+ /\bsemantic\b/,
62
+ /\btest tag\b/,
63
+ /\basynctask\b/,
64
+ /\bcallback\b/,
65
+ /\bglobal ?scope\b/,
66
+ /\brunblocking\b/,
67
+ /\bthread\.?sleep\b/,
68
+ /\bdispatch(queue|group|semaphore)\b/,
69
+ /\bforce\b.*\b(unwrap|try|cast)\b/,
70
+ /\btry!\b/,
71
+ /\bas!\b/,
72
+ /\bany\b/,
73
+ /\bconsole\b/,
74
+ /\blog\b/,
75
+ /\bprint\b/,
76
+ /\bsecret\b/,
77
+ /\btoken\b/,
78
+ /\bpassword\b/,
79
+ /\bapi key\b/,
80
+ /\bhardcoded\b/,
81
+ /\bmagic number\b/,
82
+ /\bcolor\b/,
83
+ /\bdimension\b/,
84
+ /\bgod\b/,
85
+ /\blarge\b.*\b(class|component|view|service|function|file)\b/,
86
+ /\bsolid\b/,
87
+ /\bsrp\b/,
88
+ /\bdependency\b.*\binjection\b/,
89
+ /\bconstructor\b.*\b(param|dependency)/,
90
+ /\bsharedpreferences\b/,
91
+ /\buserdefaults\b/,
92
+ /\bappstorage\b/,
93
+ /\bkeychain\b/,
94
+ /\braw sql\b/,
95
+ /\bsql\b.*\b(template|injection)\b/,
96
+ /\bempty catch\b/,
97
+ /\bcatch\b.*\b(empty|silenc)/,
98
+ /\bmock\b/,
99
+ /\bspy\b/,
100
+ /\bjunit4\b/,
101
+ /\bxctassert\b/,
102
+ /\bxctunwrap\b/,
103
+ /\bxctest\b/,
104
+ /\bquick\b/,
105
+ /\bnimble\b/,
106
+ /\bwaitforexpectations\b/,
107
+ /\bexpectation\(description\b/,
108
+ /\bnavigationview\b/,
109
+ /\bgeometryreader\b/,
110
+ /\banyview\b/,
111
+ /\bforegroundcolor\b/,
112
+ /\bcornerradius\b/,
113
+ /\bsheet\b.*\bispresented\b/,
114
+ /\bscrollview\b.*\bshowsindicators\b/,
115
+ /\bforeach\b.*\b(indices|index)\b/,
116
+ /\bonchange\b/,
117
+ /\bontapgesture\b/,
118
+ /\buiscreen\.main\.bounds\b/,
119
+ /\bstring\(format\b/,
120
+ /\bobservableobject\b/,
121
+ /\bstateobject\b/,
122
+ /\bobservable\b/,
123
+ /\blivedata\b/,
124
+ /\bstateflow\b/,
125
+ /\bsharedflow\b/,
126
+ /\bremember\b/,
127
+ /\blaunched(effect)?\b/,
128
+ /\blazy(column|row|vstack|hstack)\b/,
129
+ /\bwindow ?size ?class\b/,
130
+ /\bpadding\b/,
131
+ /\bframe\b/,
132
+ /\bfont\b/,
133
+ /\broute\b/,
134
+ /\bnavigation\b/,
135
+ ];
136
+
137
+ const STUDY_BEFORE_DETECTOR_RULE_IDS = new Set<string>([
138
+ 'skills.android.guideline.android.adaptive-layouts-responsive-design-windowsizeclass',
139
+ 'skills.android.guideline.android.color-contrast-wcag-aa-mi-nimo',
140
+ 'skills.android.guideline.android.play-console-production-deployment',
141
+ 'skills.backend.guideline.backend.dependency-injection-injectable-inject-providers-array',
142
+ 'skills.backend.guideline.backend.event-store-log-de-eventos-para-auditori-a',
143
+ ]);
144
+
145
+ const requiresStudyBeforeDetector = (rule: SkillsCompiledRule): boolean =>
146
+ STUDY_BEFORE_DETECTOR_RULE_IDS.has(rule.id);
147
+
58
148
  const isNonCodeRule = (rule: SkillsCompiledRule): boolean => {
59
149
  const haystack = normalizeText(`${rule.id} ${rule.description} ${rule.sourceSkill}`);
60
150
  return NON_CODE_RULE_PATTERNS.some((pattern) => pattern.test(haystack));
61
151
  };
62
152
 
153
+ const hasDirectCodeDetectorCandidate = (rule: SkillsCompiledRule): boolean => {
154
+ const haystack = normalizeText(`${rule.id} ${rule.description} ${rule.sourceSkill}`);
155
+ return CODE_RULE_WITH_DIRECT_DETECTOR_PATTERNS.some((pattern) => pattern.test(haystack));
156
+ };
157
+
63
158
  const classifyRule = (rule: SkillsCompiledRule): ClassifiedSkillsRule => {
64
159
  const evaluationMode = rule.evaluationMode ?? 'AUTO';
65
160
  const astNodeIds = resolveMappedHeuristicRuleIdsForCompiledRule(rule);
@@ -86,12 +181,27 @@ const classifyRule = (rule: SkillsCompiledRule): ClassifiedSkillsRule => {
86
181
  sourcePath: rule.sourcePath,
87
182
  evaluationMode,
88
183
  severity: rule.severity,
89
- status: 'IMPLEMENTABLE_AHORA',
184
+ status: 'IMPLEMENTAR_DETECTOR',
90
185
  reason: 'AUTO rule without AST/nodal detector binding; implement detector mapping.',
91
186
  astNodeIds,
92
187
  };
93
188
  }
94
189
 
190
+ if (requiresStudyBeforeDetector(rule)) {
191
+ return {
192
+ ruleId: rule.id,
193
+ platform: rule.platform,
194
+ sourceSkill: rule.sourceSkill,
195
+ sourcePath: rule.sourcePath,
196
+ evaluationMode,
197
+ severity: rule.severity,
198
+ status: 'REQUIERE_ESTUDIO',
199
+ reason:
200
+ 'Rule has code relevance, but the AST/nodal signal needs explicit design before implementation to avoid poor umbrella detectors.',
201
+ astNodeIds,
202
+ };
203
+ }
204
+
95
205
  if (isNonCodeRule(rule)) {
96
206
  return {
97
207
  ruleId: rule.id,
@@ -106,6 +216,20 @@ const classifyRule = (rule: SkillsCompiledRule): ClassifiedSkillsRule => {
106
216
  };
107
217
  }
108
218
 
219
+ if (hasDirectCodeDetectorCandidate(rule)) {
220
+ return {
221
+ ruleId: rule.id,
222
+ platform: rule.platform,
223
+ sourceSkill: rule.sourceSkill,
224
+ sourcePath: rule.sourcePath,
225
+ evaluationMode,
226
+ severity: rule.severity,
227
+ status: 'IMPLEMENTAR_DETECTOR',
228
+ reason: 'Code rule has a direct AST/nodal detection surface; implement detector and blocking evidence.',
229
+ astNodeIds,
230
+ };
231
+ }
232
+
109
233
  return {
110
234
  ruleId: rule.id,
111
235
  platform: rule.platform,
@@ -0,0 +1,192 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import type { Finding } from '../../core/gate/Finding';
4
+ import type { GateStage } from '../../core/gate/GateStage';
5
+
6
+ export type ContextGateStatus = 'applied' | 'blocked' | 'disabled';
7
+
8
+ export type ContextGateResult = {
9
+ status: ContextGateStatus;
10
+ finding?: Finding;
11
+ };
12
+
13
+ export const CONTEXT_FILE = '.pumuki/context/context.json';
14
+ const CONTEXT_GATE_ENV = 'PUMUKI_CONTEXT_GATE';
15
+ const CONTEXT_SCHEMA_VERSION = 1;
16
+
17
+ const isStrictContextGate = (): boolean => {
18
+ const normalized = process.env[CONTEXT_GATE_ENV]?.trim().toLowerCase();
19
+ return normalized !== 'off' && normalized !== '0' && normalized !== 'false' && normalized !== 'disabled';
20
+ };
21
+
22
+ type LocalContextDocument = {
23
+ schema_version: number;
24
+ repo_root: string;
25
+ generated_at: string;
26
+ contract: {
27
+ status: 'applied';
28
+ required_before_gate: true;
29
+ agent_instruction: string;
30
+ user_remediation: string;
31
+ };
32
+ };
33
+
34
+ export type ContextLifecycleResult = {
35
+ status: 'ok' | 'blocked';
36
+ path: string;
37
+ message: string;
38
+ document?: LocalContextDocument;
39
+ };
40
+
41
+ const buildContextDocument = (repoRoot: string): LocalContextDocument => ({
42
+ schema_version: CONTEXT_SCHEMA_VERSION,
43
+ repo_root: repoRoot,
44
+ generated_at: new Date().toISOString(),
45
+ contract: {
46
+ status: 'applied',
47
+ required_before_gate: true,
48
+ agent_instruction:
49
+ 'STOP si este contexto no se puede leer o validar antes de editar, auditar, commitear o pushear.',
50
+ user_remediation:
51
+ 'Ejecuta `npx pumuki context repair` y reejecuta el gate antes de continuar.',
52
+ },
53
+ });
54
+
55
+ const contextPathForRepo = (repoRoot: string): string => join(repoRoot, CONTEXT_FILE);
56
+
57
+ const parseContextDocument = (repoRoot: string): LocalContextDocument => {
58
+ const contextPath = contextPathForRepo(repoRoot);
59
+ const parsed: unknown = JSON.parse(readFileSync(contextPath, 'utf8'));
60
+ if (!parsed || typeof parsed !== 'object') {
61
+ throw new Error(`${CONTEXT_FILE} no contiene un objeto JSON valido`);
62
+ }
63
+ const candidate = parsed as Partial<LocalContextDocument>;
64
+ if (candidate.schema_version !== CONTEXT_SCHEMA_VERSION) {
65
+ throw new Error(`${CONTEXT_FILE} tiene schema_version invalido`);
66
+ }
67
+ if (candidate.repo_root !== repoRoot) {
68
+ throw new Error(`${CONTEXT_FILE} pertenece a otro repo_root`);
69
+ }
70
+ if (
71
+ !candidate.contract ||
72
+ candidate.contract.status !== 'applied' ||
73
+ candidate.contract.required_before_gate !== true ||
74
+ typeof candidate.contract.agent_instruction !== 'string' ||
75
+ candidate.contract.agent_instruction.trim().length === 0 ||
76
+ typeof candidate.contract.user_remediation !== 'string' ||
77
+ candidate.contract.user_remediation.trim().length === 0
78
+ ) {
79
+ throw new Error(`${CONTEXT_FILE} no declara contrato aplicado para usuario y agente IA`);
80
+ }
81
+ return candidate as LocalContextDocument;
82
+ };
83
+
84
+ export const writeLocalContext = (repoRoot: string): ContextLifecycleResult => {
85
+ const contextPath = contextPathForRepo(repoRoot);
86
+ const document = buildContextDocument(repoRoot);
87
+ mkdirSync(join(repoRoot, '.pumuki/context'), { recursive: true });
88
+ writeFileSync(contextPath, `${JSON.stringify(document, null, 2)}\n`, 'utf8');
89
+ return {
90
+ status: 'ok',
91
+ path: contextPath,
92
+ message: 'Contexto local Pumuki inicializado y aplicado.',
93
+ document,
94
+ };
95
+ };
96
+
97
+ export const readLocalContextStatus = (repoRoot: string): ContextLifecycleResult => {
98
+ const contextPath = contextPathForRepo(repoRoot);
99
+ if (!existsSync(contextPath)) {
100
+ return {
101
+ status: 'blocked',
102
+ path: contextPath,
103
+ message:
104
+ `Pumuki bloqueado: falta ${CONTEXT_FILE}. Ejecuta ` +
105
+ '`npx pumuki context init` o `npx pumuki context repair`.',
106
+ };
107
+ }
108
+ try {
109
+ const document = parseContextDocument(repoRoot);
110
+ return {
111
+ status: 'ok',
112
+ path: contextPath,
113
+ message: 'Contexto local Pumuki leido, validado y aplicado.',
114
+ document,
115
+ };
116
+ } catch (error) {
117
+ const reason = error instanceof Error ? error.message : 'context validation error';
118
+ return {
119
+ status: 'blocked',
120
+ path: contextPath,
121
+ message:
122
+ `Pumuki bloqueado: contexto local invalido. Causa: ${reason}. Ejecuta ` +
123
+ '`npx pumuki context repair`.',
124
+ };
125
+ }
126
+ };
127
+
128
+ export const repairLocalContext = (repoRoot: string): ContextLifecycleResult =>
129
+ writeLocalContext(repoRoot);
130
+
131
+ const contextBlockedFinding = (params: {
132
+ stage: GateStage;
133
+ code: string;
134
+ cause: string;
135
+ evidence: string;
136
+ }): Finding => ({
137
+ ruleId: 'governance.context.local-context-not-applied',
138
+ severity: 'ERROR',
139
+ code: params.code,
140
+ message:
141
+ `Pumuki bloqueado: contexto local obligatorio no aplicado en ${params.stage}. ` +
142
+ `Causa: ${params.cause}. ` +
143
+ `Evidencia: ${params.evidence}. ` +
144
+ 'Usuario: ejecuta `npx pumuki context init` si el repo no tiene contexto, o `npx pumuki context repair` si existe pero no valida. ' +
145
+ 'Agente IA: STOP. No edites archivos ni continues la tarea hasta leer, validar y aplicar el contexto local del repo.',
146
+ filePath: CONTEXT_FILE,
147
+ matchedBy: 'PumukiContextGate',
148
+ source: 'pumuki-context-store',
149
+ why: 'Pumuki no puede garantizar continuidad entre sesiones ni aplicar el contrato activo del repo sin contexto local validado.',
150
+ impact: 'El agente y el usuario podrian trabajar con task, contrato o evidencia stale.',
151
+ expected_fix:
152
+ 'Inicializa o repara el contexto local: `npx pumuki context init` o `npx pumuki context repair`, y reejecuta el gate.',
153
+ });
154
+
155
+ export const evaluateContextGate = (params: {
156
+ repoRoot: string;
157
+ stage: GateStage;
158
+ }): ContextGateResult => {
159
+ if (!isStrictContextGate()) {
160
+ return { status: 'disabled' };
161
+ }
162
+
163
+ const contextPath = contextPathForRepo(params.repoRoot);
164
+ if (!existsSync(contextPath)) {
165
+ return {
166
+ status: 'blocked',
167
+ finding: contextBlockedFinding({
168
+ stage: params.stage,
169
+ code: 'CONTEXT_NOT_APPLIED',
170
+ cause: `No existe ${CONTEXT_FILE}`,
171
+ evidence: contextPath,
172
+ }),
173
+ };
174
+ }
175
+
176
+ try {
177
+ parseContextDocument(params.repoRoot);
178
+ } catch (error) {
179
+ const reason = error instanceof Error ? error.message : 'JSON parse error';
180
+ return {
181
+ status: 'blocked',
182
+ finding: contextBlockedFinding({
183
+ stage: params.stage,
184
+ code: 'CONTEXT_INVALID',
185
+ cause: reason,
186
+ evidence: contextPath,
187
+ }),
188
+ };
189
+ }
190
+
191
+ return { status: 'applied' };
192
+ };
@@ -286,6 +286,9 @@ const toSkillsDeclarativeRulesClassificationFinding = (params: {
286
286
  declarativeRuleIds: ReadonlyArray<string>;
287
287
  facts: ReadonlyArray<Fact>;
288
288
  }): Finding | undefined => {
289
+ if (process.env.PUMUKI_ENFORCE_DECLARATIVE_RULE_CLASSIFICATION !== '1') {
290
+ return undefined;
291
+ }
289
292
  if (params.declarativeRuleIds.length === 0) {
290
293
  return undefined;
291
294
  }
@@ -302,7 +305,7 @@ const toSkillsDeclarativeRulesClassificationFinding = (params: {
302
305
  message:
303
306
  `Skills declarative rules require AST/nodal classification at ${params.stage}: ` +
304
307
  `total=${params.declarativeRuleIds.length} sample_rule_ids=[${sampleRuleIds}]. ` +
305
- 'Classify every rule as IMPLEMENTABLE_AHORA, REQUIERE_ESTUDIO, or NO_ES_REGLA_DE_CODIGO; code skills cannot remain as hidden declarative/advisory rules.',
308
+ 'Classify every rule as IMPLEMENTAR_DETECTOR, REQUIERE_ESTUDIO, or NO_ES_REGLA_DE_CODIGO; code skills cannot remain as hidden declarative/advisory rules.',
306
309
  filePath: 'skills.lock.json',
307
310
  matchedBy: 'SkillsDeclarativeRulesClassificationGuard',
308
311
  source: 'skills-declarative-rules-classification',
@@ -250,6 +250,7 @@ export const buildPreWriteAutomationTrace = async (params: {
250
250
  const leaseResult = activeDependencies.writePreWriteLease({
251
251
  repoRoot: params.repoRoot,
252
252
  git,
253
+ allowExistingCodeChanges: true,
253
254
  });
254
255
  trace.actions.push({
255
256
  action: 'write_prewrite_lease',
@@ -8,6 +8,7 @@ import { DEFAULT_FACT_FILE_EXTENSIONS } from '../git/runPlatformGateFacts';
8
8
  export type PreWriteLease = {
9
9
  version: '1';
10
10
  kind: 'pumuki-pre-write-lease';
11
+ validation_mode?: 'clean-prewrite' | 'validated-diff';
11
12
  repo_root: string;
12
13
  head: string;
13
14
  branch: string | null;
@@ -119,6 +120,10 @@ const parseLease = (raw: string): PreWriteLease | undefined => {
119
120
  return {
120
121
  version: '1',
121
122
  kind: 'pumuki-pre-write-lease',
123
+ validation_mode:
124
+ value.validation_mode === 'validated-diff' || value.validation_mode === 'clean-prewrite'
125
+ ? value.validation_mode
126
+ : undefined,
122
127
  repo_root: value.repo_root,
123
128
  head: value.head,
124
129
  branch: typeof value.branch === 'string' ? value.branch : null,
@@ -186,6 +191,33 @@ export const readPreWriteLeaseStatus = (params: {
186
191
  };
187
192
  }
188
193
 
194
+ if (lease.validation_mode === 'validated-diff') {
195
+ const expectedPaths = [...lease.pre_change_code_paths].sort((left, right) => left.localeCompare(right));
196
+ const currentPaths = [...changedCodePaths].sort((left, right) => left.localeCompare(right));
197
+ if (
198
+ lease.pre_change_code_changes_count !== expectedPaths.length ||
199
+ expectedPaths.length !== currentPaths.length ||
200
+ expectedPaths.some((path, index) => path !== currentPaths[index])
201
+ ) {
202
+ return {
203
+ valid: false,
204
+ code: 'PRE_WRITE_LEASE_DIRTY_AT_ISSUE',
205
+ path,
206
+ lease,
207
+ changedCodePaths,
208
+ message: 'PRE_WRITE lease was issued for a different validated code diff.',
209
+ };
210
+ }
211
+
212
+ return {
213
+ valid: true,
214
+ code: 'PRE_WRITE_LEASE_VALID',
215
+ path,
216
+ lease,
217
+ changedCodePaths,
218
+ };
219
+ }
220
+
189
221
  if (lease.pre_change_code_changes_count !== 0 || lease.pre_change_code_paths.length !== 0) {
190
222
  return {
191
223
  valid: false,
@@ -210,10 +242,11 @@ export const writePreWriteLease = (params: {
210
242
  repoRoot: string;
211
243
  git: Pick<IGitService, 'runGit'>;
212
244
  now?: Date;
245
+ allowExistingCodeChanges?: boolean;
213
246
  }): PreWriteLeaseWriteResult => {
214
247
  const path = resolvePreWriteLeasePath(params.repoRoot);
215
248
  const changedCodePaths = collectPreWriteCodeChangePaths(params);
216
- if (changedCodePaths.length > 0) {
249
+ if (changedCodePaths.length > 0 && !params.allowExistingCodeChanges) {
217
250
  return {
218
251
  path,
219
252
  written: false,
@@ -229,13 +262,14 @@ export const writePreWriteLease = (params: {
229
262
  const lease: PreWriteLease = {
230
263
  version: '1',
231
264
  kind: 'pumuki-pre-write-lease',
265
+ validation_mode: changedCodePaths.length > 0 ? 'validated-diff' : 'clean-prewrite',
232
266
  repo_root: params.repoRoot,
233
267
  head: resolveHead(params),
234
268
  branch: resolveBranch(params),
235
269
  issued_at: issuedAt.toISOString(),
236
270
  expires_at: new Date(issuedAt.getTime() + PRE_WRITE_LEASE_TTL_MS).toISOString(),
237
- pre_change_code_changes_count: 0,
238
- pre_change_code_paths: [],
271
+ pre_change_code_changes_count: changedCodePaths.length,
272
+ pre_change_code_paths: changedCodePaths,
239
273
  };
240
274
  mkdirSync(dirname(path), { recursive: true });
241
275
  writeFileSync(path, `${JSON.stringify(lease, null, 2)}\n`, 'utf8');
@@ -245,7 +279,10 @@ export const writePreWriteLease = (params: {
245
279
  valid: true,
246
280
  code: 'PRE_WRITE_LEASE_WRITTEN',
247
281
  changedCodePaths,
248
- message: 'PRE_WRITE lease written for clean code state.',
282
+ message:
283
+ changedCodePaths.length > 0
284
+ ? 'PRE_WRITE lease written for validated code diff.'
285
+ : 'PRE_WRITE lease written for clean code state.',
249
286
  lease,
250
287
  };
251
288
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pumuki",
3
- "version": "6.3.269",
3
+ "version": "6.3.271",
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": {
@@ -239,6 +239,7 @@
239
239
  "core/rules/presets/*.ts",
240
240
  "core/rules/presets/heuristics/*.ts",
241
241
  "integrations/config/*.ts",
242
+ "integrations/context/*.ts",
242
243
  "integrations/evidence/*.ts",
243
244
  "integrations/gate/*.ts",
244
245
  "integrations/git/*.ts",
@@ -19,8 +19,8 @@ process.stdout.write(`${JSON.stringify({
19
19
  AST_IMPLEMENTED: summary.rules
20
20
  .filter((rule) => rule.status === 'AST_IMPLEMENTED')
21
21
  .slice(0, 12),
22
- IMPLEMENTABLE_AHORA: summary.rules
23
- .filter((rule) => rule.status === 'IMPLEMENTABLE_AHORA')
22
+ IMPLEMENTAR_DETECTOR: summary.rules
23
+ .filter((rule) => rule.status === 'IMPLEMENTAR_DETECTOR')
24
24
  .slice(0, 12),
25
25
  REQUIERE_ESTUDIO: summary.rules
26
26
  .filter((rule) => rule.status === 'REQUIERE_ESTUDIO')
@@ -26,22 +26,22 @@ export const createConsumerLegacyMenuActions = (
26
26
  return [
27
27
  {
28
28
  id: '1',
29
- label: 'Consumer preflight + gate: ALL tracked files (PRE_COMMIT · writes evidence)',
29
+ label: 'Full audit (repo analysis)',
30
30
  execute: params.runFullAudit,
31
31
  },
32
32
  {
33
33
  id: '2',
34
- label: 'Consumer preflight + gate: REPO+index contract (PRE_PUSH · disk skip risk if evidence tracked)',
34
+ label: 'Strict REPO+STAGING (CI/CD)',
35
35
  execute: params.runStrictRepoAndStaged,
36
36
  },
37
37
  {
38
38
  id: '3',
39
- label: 'Consumer preflight + gate: STAGED only (PRE_COMMIT)',
39
+ label: 'Strict STAGING only (dev)',
40
40
  execute: params.runStrictStagedOnly,
41
41
  },
42
42
  {
43
43
  id: '4',
44
- label: 'Consumer preflight + gate: working tree (PRE_PUSH policy · disk skip risk if evidence tracked)',
44
+ label: 'Standard CRITICAL/HIGH',
45
45
  execute: params.runStandardCriticalHigh,
46
46
  },
47
47
  {
@@ -66,27 +66,27 @@ export const createConsumerLegacyMenuActions = (
66
66
  },
67
67
  {
68
68
  id: '5',
69
- label: 'Legacy read-only pattern checks snapshot',
69
+ label: 'Pattern checks',
70
70
  execute: params.runPatternChecks,
71
71
  },
72
72
  {
73
73
  id: '6',
74
- label: 'Legacy read-only ESLint evidence snapshot',
74
+ label: 'ESLint Admin+Web',
75
75
  execute: params.runEslintAudit,
76
76
  },
77
77
  {
78
78
  id: '7',
79
- label: 'Legacy read-only AST snapshot',
79
+ label: 'AST Intelligence',
80
80
  execute: params.runAstIntelligence,
81
81
  },
82
82
  {
83
83
  id: '8',
84
- label: 'Export legacy read-only evidence snapshot',
84
+ label: 'Export Markdown',
85
85
  execute: params.runExportMarkdown,
86
86
  },
87
87
  {
88
88
  id: '9',
89
- label: 'Legacy read-only file diagnostics snapshot',
89
+ label: 'File diagnostics',
90
90
  execute: params.runFileDiagnostics,
91
91
  },
92
92
  {