pumuki 6.3.133 → 6.3.135

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/CHANGELOG.md CHANGED
@@ -6,6 +6,20 @@ This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [6.3.135] - 2026-05-03
10
+
11
+ ### Fixed
12
+
13
+ - **Bootstrap de pre-push por delta real:** cuando una rama no tiene upstream, el bootstrap de `PRE_PUSH` elige la base con menor delta real entre `main` y `develop`, evitando falsos positivos de atomicidad en branches nacidas de `main`.
14
+ - **Repin desbloqueable:** esta versión corrige el bloqueo que impedía publicar el repin de `Flux_training` aunque el diff efectivo del cambio fuese mínimo.
15
+
16
+ ## [6.3.134] - 2026-05-03
17
+
18
+ ### Fixed
19
+
20
+ - **Policy hash drift accionable:** `governanceObservationSnapshot`, `governanceNextAction` y el catálogo de remediación ya convierten la divergencia entre stages en una acción estricta y aplicable.
21
+ - **Release publicada y lista para repin:** esta versión ya está en npm y queda lista para repinear consumers activos como RuralGo con el fix real distribuido.
22
+
9
23
  ## [6.3.133] - 2026-05-03
10
24
 
11
25
  ### Fixed
@@ -4,6 +4,17 @@ This file tracks the active deterministic framework line used in this repository
4
4
  Canonical release chronology lives in `CHANGELOG.md`.
5
5
  This file keeps only the operational highlights and rollout notes that matter while running the framework.
6
6
 
7
+ ### 2026-05-03 (v6.3.135)
8
+
9
+ - **Bootstrap de pre-push por delta real:** en ramas sin upstream, `PRE_PUSH` elige la base con menor delta real entre `main` y `develop`, evitando que un rollout nacido de `main` se bloquee como si heredara todo `develop`.
10
+ - **Repin desbloqueable:** esta release corrige el falso positivo que impedía publicar el repin de `Flux_training` aunque el diff efectivo fuese mínimo.
11
+
12
+ ### 2026-05-03 (v6.3.134)
13
+
14
+ - **Policy hash drift accionable:** `governanceObservationSnapshot`, `governanceNextAction` y el catálogo de remediación ya convierten la divergencia entre stages en una acción estricta y aplicable.
15
+ - **Repin recomendado:** publicar `pumuki@6.3.134`, repinear RuralGo y revalidar `status`, `doctor`, `audit --stage=PRE_WRITE --json` y hooks gestionados.
16
+ - **Contrato de release estable:** no hace falta cerrar más gaps funcionales para este fix; la solución ya quedó validada localmente y la distribución de `6.3.134` ya está publicada.
17
+
7
18
  ### 2026-05-03 (v6.3.133)
8
19
 
9
20
  - **Skills hard-blocking end-to-end:** `skillsEnforcement`, `evaluateAiGate`, `runPlatformGate` y el flujo CLI ya no dejan pasar advisory para violations de skills.
@@ -66,8 +66,7 @@ Current enforcement scope:
66
66
  - curated template compilation (`skills.sources.json` -> `skills.lock.json`)
67
67
  - stage-aware policy resolution via `resolvePolicyForStage`
68
68
  - additive skills-derived rules merged through the shared gate runner
69
- - strict skills enforcement by default; `PUMUKI_SKILLS_ENFORCEMENT=advisory`
70
- is an explicit opt-in escape hatch, not the product baseline
69
+ - strict skills enforcement by default; advisory values are ignored by design
71
70
  - strict heuristics, TDD/BDD evidence, SDD completeness, and Git atomicity by
72
71
  default; their `advisory`/`0` env values are explicit legacy overrides, not
73
72
  the product baseline
@@ -1338,6 +1338,26 @@ const collectEvidencePolicyThresholdViolations = (params: {
1338
1338
  return [];
1339
1339
  };
1340
1340
 
1341
+ const collectTddBddBaselineViolations = (params: {
1342
+ evidenceResult: EvidenceReadResult;
1343
+ }): AiGateViolation[] => {
1344
+ if (params.evidenceResult.kind !== 'valid') {
1345
+ return [];
1346
+ }
1347
+
1348
+ const tddBdd = params.evidenceResult.evidence.snapshot.tdd_bdd;
1349
+ if (tddBdd?.status !== 'blocked') {
1350
+ return [];
1351
+ }
1352
+
1353
+ return [
1354
+ toErrorViolation(
1355
+ 'TDD_BDD_BASELINE_BLOCKED',
1356
+ 'TDD/BDD baseline snapshot is blocked. Fix the failing baseline tests and refresh evidence before continuing.'
1357
+ ),
1358
+ ];
1359
+ };
1360
+
1341
1361
  const toEvidenceSourceDescriptor = (
1342
1362
  result: EvidenceReadResult,
1343
1363
  repoRoot: string
@@ -1688,9 +1708,13 @@ export const evaluateAiGate = (
1688
1708
  evidenceResult,
1689
1709
  policy: resolvedPolicy.policy,
1690
1710
  });
1711
+ const tddBddBaselineViolations = collectTddBddBaselineViolations({
1712
+ evidenceResult,
1713
+ });
1691
1714
  const violations = [
1692
1715
  ...evidenceAssessment.violations,
1693
1716
  ...policyThresholdViolations,
1717
+ ...tddBddBaselineViolations,
1694
1718
  ...stageSkillsContractViolations,
1695
1719
  ...gitflowViolations,
1696
1720
  ...trackingViolations,
@@ -19,7 +19,7 @@ export const buildGovernanceValidateCommand = (stage: AiGateStage): string =>
19
19
  PRE_WRITE_VALIDATE_COMMAND.replace('PRE_WRITE', stage);
20
20
 
21
21
  export const buildGovernancePolicyReconcileCommand = (stage: AiGateStage): string =>
22
- `npx --yes --package pumuki@latest pumuki policy reconcile --strict --json && ${buildGovernanceValidateCommand(stage)}`;
22
+ `npx --yes --package pumuki@latest pumuki policy reconcile --strict --apply --json && ${buildGovernanceValidateCommand(stage)}`;
23
23
 
24
24
  const buildFallbackAction = (code: string): GovernanceCatalogAction => ({
25
25
  reason_code: code,
@@ -174,6 +174,17 @@ export const resolveGovernanceCatalogAction = (params: {
174
174
  command: buildGovernancePolicyReconcileCommand(stage),
175
175
  },
176
176
  };
177
+ case 'POLICY_HASH_DIVERGENCE':
178
+ return {
179
+ reason_code: 'POLICY_HASH_DIVERGENCE',
180
+ instruction: 'Reconcilia policy/skills y aplica el contrato canónico para reducir el drift entre stages.',
181
+ next_action: {
182
+ kind: 'run_command',
183
+ message:
184
+ 'Los hashes de policy difieren entre stages. Ejecuta reconcile estricto con apply y vuelve a validar.',
185
+ command: buildGovernancePolicyReconcileCommand(stage),
186
+ },
187
+ };
177
188
  case 'SKILLS_CONTRACT_SURFACE_INCOMPLETE':
178
189
  case 'EVIDENCE_SKILLS_CONTRACT_INCOMPLETE':
179
190
  return {
@@ -207,6 +218,16 @@ export const resolveGovernanceCatalogAction = (params: {
207
218
  command: validateCommand,
208
219
  },
209
220
  };
221
+ case 'TDD_BDD_BASELINE_BLOCKED':
222
+ return {
223
+ reason_code: 'TDD_BDD_BASELINE_BLOCKED',
224
+ instruction: 'Corrige el baseline TDD/BDD roto y regenera la evidencia antes de seguir.',
225
+ next_action: {
226
+ kind: 'run_command',
227
+ message: 'Revalida el stage actual tras reparar el baseline TDD/BDD y refrescar la evidencia.',
228
+ command: validateCommand,
229
+ },
230
+ };
210
231
  case 'ACTIVE_RULE_IDS_EMPTY_FOR_CODE_CHANGES_HIGH':
211
232
  case 'EVIDENCE_ACTIVE_RULE_IDS_EMPTY_FOR_CODE_CHANGES':
212
233
  return {
@@ -10,12 +10,14 @@ export const REMEDIATION_HINT_BY_CODE: Readonly<Record<string, string>> = {
10
10
  EVIDENCE_STALE: 'Refresca evidencia antes de continuar.',
11
11
  EVIDENCE_REPO_ROOT_MISMATCH: 'Regenera evidencia desde este mismo repositorio.',
12
12
  EVIDENCE_BRANCH_MISMATCH: 'Regenera evidencia en la rama actual y reintenta.',
13
+ TDD_BDD_BASELINE_BLOCKED:
14
+ 'Corrige el baseline TDD/BDD roto y regenera la evidencia antes de continuar.',
13
15
  EVIDENCE_RULES_COVERAGE_MISSING: 'Ejecuta auditoría completa para recalcular rules_coverage.',
14
16
  EVIDENCE_RULES_COVERAGE_INCOMPLETE: 'Asegura coverage_ratio=1 y unevaluated=0.',
15
17
  ACTIVE_RULE_IDS_EMPTY_FOR_CODE_CHANGES_HIGH:
16
- 'Reconcilia policy/skills y reintenta PRE_COMMIT: npx --yes --package pumuki@latest pumuki policy reconcile --strict --json && npx --yes --package pumuki@latest pumuki-pre-commit',
18
+ 'Reconcilia policy/skills y reintenta PRE_COMMIT: npx --yes --package pumuki@latest pumuki policy reconcile --strict --apply --json && npx --yes --package pumuki@latest pumuki-pre-commit',
17
19
  EVIDENCE_ACTIVE_RULE_IDS_EMPTY_FOR_CODE_CHANGES:
18
- 'Reconcilia policy/skills y revalida PRE_WRITE: npx --yes --package pumuki@latest pumuki policy reconcile --strict --json && npx --yes --package pumuki@latest pumuki sdd validate --stage=PRE_WRITE --json',
20
+ 'Reconcilia policy/skills y revalida PRE_WRITE: npx --yes --package pumuki@latest pumuki policy reconcile --strict --apply --json && npx --yes --package pumuki@latest pumuki sdd validate --stage=PRE_WRITE --json',
19
21
  GITFLOW_PROTECTED_BRANCH: 'Trabaja en feature/* y evita ramas protegidas.',
20
22
  GITFLOW_BRANCH_NAMING_INVALID:
21
23
  'Renombra o recrea la rama con un prefijo GitFlow válido (feature/*, bugfix/*, hotfix/*, release/*, chore/*, refactor/* o docs/*).',
@@ -26,6 +26,37 @@ const resolveDefaultCiBaseRef = (): string => {
26
26
  return 'HEAD';
27
27
  };
28
28
 
29
+ const resolveDiffFileCount = (fromRef: string): number | null => {
30
+ try {
31
+ return runGit(['diff', '--name-only', '--diff-filter=ACMR', `${fromRef}..HEAD`])
32
+ .split('\n')
33
+ .map((line) => line.trim())
34
+ .filter((line) => line.length > 0).length;
35
+ } catch {
36
+ return null;
37
+ }
38
+ };
39
+
40
+ const resolveBestBootstrapBaseRef = (): string | null => {
41
+ const candidates = ['origin/main', 'main', 'origin/develop', 'develop'];
42
+ let best: { ref: string; changedFiles: number } | undefined;
43
+
44
+ for (const candidate of candidates) {
45
+ if (!isResolvableRef(candidate)) {
46
+ continue;
47
+ }
48
+ const changedFiles = resolveDiffFileCount(candidate);
49
+ if (changedFiles === null) {
50
+ continue;
51
+ }
52
+ if (!best || changedFiles < best.changedFiles) {
53
+ best = { ref: candidate, changedFiles };
54
+ }
55
+ }
56
+
57
+ return best?.ref ?? null;
58
+ };
59
+
29
60
  export const resolveUpstreamRef = (): string | null => {
30
61
  try {
31
62
  return runGit(['rev-parse', '@{u}']);
@@ -88,12 +119,10 @@ export const resolveCiBaseRef = (): string => {
88
119
  };
89
120
 
90
121
  export const resolvePrePushBootstrapBaseRef = (): string => {
91
- const candidates = ['origin/develop', 'develop', resolveCiBaseRef()];
92
- for (const candidate of candidates) {
93
- if (isResolvableRef(candidate)) {
94
- return candidate;
95
- }
122
+ const best = resolveBestBootstrapBaseRef();
123
+ if (best) {
124
+ return best;
96
125
  }
97
126
 
98
- return 'HEAD';
127
+ return resolveDefaultCiBaseRef();
99
128
  };
@@ -9,20 +9,19 @@ const BLOCK_NEXT_ACTION_BY_CODE: Readonly<Record<string, string>> = {
9
9
  'git push --set-upstream origin <branch>',
10
10
  PRE_PUSH_UPSTREAM_MISALIGNED:
11
11
  'git branch --unset-upstream && git push --set-upstream origin <branch>',
12
- EVIDENCE_STALE:
13
- 'npx --yes --package pumuki@latest pumuki-pre-commit',
14
- EVIDENCE_BRANCH_MISMATCH:
15
- 'npx --yes --package pumuki@latest pumuki-pre-commit',
16
12
  GIT_ATOMICITY_TOO_MANY_FILES:
17
13
  'git restore --staged . && separa cambios en commits más pequeños',
18
14
  GIT_ATOMICITY_TOO_MANY_SCOPES:
19
15
  'Revisa scope_files del bloqueo y aplica: git restore --staged . && git add <scope>/ && git commit -m "<tipo>: <scope>"',
20
16
  ACTIVE_RULE_IDS_EMPTY_FOR_CODE_CHANGES_HIGH:
21
- 'Reconcilia policy/skills y reintenta PRE_COMMIT: npx --yes --package pumuki@latest pumuki policy reconcile --strict --json && npx --yes --package pumuki@latest pumuki-pre-commit',
17
+ 'Reconcilia policy/skills y reintenta PRE_COMMIT: npx --yes --package pumuki@latest pumuki policy reconcile --strict --apply --json && npx --yes --package pumuki@latest pumuki-pre-commit',
22
18
  SKILLS_SKILLS_FRONTEND_NO_SOLID_VIOLATIONS:
23
19
  'Aplica refactor incremental: extrae 1 componente/hook por commit y vuelve a ejecutar PRE_COMMIT.',
24
20
  };
25
21
 
22
+ const buildStageValidateCommand = (stage: 'PRE_COMMIT' | 'PRE_PUSH' | 'CI' | 'PRE_WRITE'): string =>
23
+ `npx --yes --package pumuki@latest pumuki sdd validate --stage=${stage} --json`;
24
+
26
25
  const severityWeight = (severity: string): number => {
27
26
  switch (severity.toUpperCase()) {
28
27
  case 'CRITICAL':
@@ -103,15 +102,21 @@ const formatFinding = (finding: Finding): string => {
103
102
  return `[${severity}] ${finding.ruleId}: ${finding.message} -> ${location}`;
104
103
  };
105
104
 
106
- export const printGateFindings = (findings: ReadonlyArray<Finding>): void => {
105
+ export const printGateFindings = (
106
+ findings: ReadonlyArray<Finding>,
107
+ params?: { stage?: 'PRE_WRITE' | 'PRE_COMMIT' | 'PRE_PUSH' | 'CI' }
108
+ ): void => {
107
109
  if (findings.length === 0) {
108
110
  return;
109
111
  }
110
112
  const orderedFindings = sortFindingsBySeverity(findings);
111
113
  const primary = resolvePrimaryFinding(orderedFindings);
114
+ const stage = params?.stage ?? 'PRE_COMMIT';
112
115
  const nextAction =
113
- BLOCK_NEXT_ACTION_BY_CODE[primary.code]
114
- ?? 'Corrige el bloqueante primario y vuelve a ejecutar el mismo comando.';
116
+ primary.code === 'EVIDENCE_STALE' || primary.code === 'EVIDENCE_BRANCH_MISMATCH'
117
+ ? buildStageValidateCommand(stage)
118
+ : BLOCK_NEXT_ACTION_BY_CODE[primary.code]
119
+ ?? 'Corrige el bloqueante primario y vuelve a ejecutar el mismo comando.';
115
120
  process.stdout.write(
116
121
  `[pumuki][block-summary] primary=${primary.code} severity=${primary.severity.toUpperCase()} rule=${primary.ruleId}\n`
117
122
  );
@@ -55,7 +55,7 @@ type LifecycleAuditDependencies = {
55
55
  };
56
56
 
57
57
  const POLICY_RECONCILE_HINT =
58
- 'If .pumuki/policy-as-code.json signatures drift after a pumuki upgrade, run: pumuki policy reconcile --apply';
58
+ 'If .pumuki/policy-as-code.json signatures drift after a pumuki upgrade, run: pumuki policy reconcile --strict --apply --json';
59
59
 
60
60
  const countUntrackedMatchingExtensions = (
61
61
  git: Pick<IGitService, 'resolveRepoRoot' | 'runGit'>,
@@ -1650,7 +1650,7 @@ const buildPreWriteValidateCommand = (params: {
1650
1650
  const buildPreWritePolicyReconcileCommand = (repoRoot?: string): string =>
1651
1651
  `${buildPinnedPumukiNpxCommand({
1652
1652
  repoRoot,
1653
- executableAndArgs: 'pumuki policy reconcile --strict --json',
1653
+ executableAndArgs: 'pumuki policy reconcile --strict --apply --json',
1654
1654
  })} && ${buildPreWriteValidateCommand({ repoRoot, stage: 'PRE_WRITE' })}`;
1655
1655
 
1656
1656
  type PreWriteValidationEnvelope = {
@@ -1838,11 +1838,13 @@ const PRE_WRITE_HINTS_BY_CODE: Readonly<Record<string, string>> = {
1838
1838
  EVIDENCE_RULES_COVERAGE_MISSING: 'Ejecuta auditoría completa para recalcular rules_coverage.',
1839
1839
  EVIDENCE_RULES_COVERAGE_INCOMPLETE: 'Asegura unevaluated=0 y coverage_ratio=1.',
1840
1840
  EVIDENCE_ACTIVE_RULE_IDS_EMPTY_FOR_CODE_CHANGES:
1841
- 'No hay active_rule_ids para plataforma de código detectada. Reconcilia policy/skills en modo estricto y revalida PRE_WRITE.',
1841
+ 'No hay active_rule_ids para plataforma de código detectada. Reconcilia policy/skills en modo estricto con apply y revalida PRE_WRITE.',
1842
1842
  EVIDENCE_PLATFORM_CRITICAL_SKILLS_RULES_MISSING:
1843
- 'Reconcilia policy/skills en modo estricto y materializa reglas críticas (p.ej. skills.ios.critical-test-quality).',
1843
+ 'Reconcilia policy/skills en modo estricto con apply y materializa reglas críticas (p.ej. skills.ios.critical-test-quality).',
1844
1844
  EVIDENCE_CROSS_PLATFORM_CRITICAL_ENFORCEMENT_INCOMPLETE:
1845
- 'Reconcilia policy/skills en modo estricto para completar enforcement crítico transversal.',
1845
+ 'Reconcilia policy/skills en modo estricto con apply para completar enforcement crítico transversal.',
1846
+ TDD_BDD_BASELINE_BLOCKED:
1847
+ 'Corrige el baseline TDD/BDD roto y regenera la evidencia antes de continuar.',
1846
1848
  EVIDENCE_SKILLS_CONTRACT_INCOMPLETE:
1847
1849
  'Completa contrato skills/policy para el stage actual y vuelve a validar.',
1848
1850
  EVIDENCE_PREWRITE_WORKTREE_OVER_LIMIT:
@@ -834,7 +834,7 @@ const buildPolicySignatureRemediation = (
834
834
  const mismatch = Object.values(policyValidation.stages).some(
835
835
  (stage) => stage.validationCode === 'POLICY_AS_CODE_SIGNATURE_MISMATCH'
836
836
  );
837
- return mismatch ? 'pumuki policy reconcile --apply' : undefined;
837
+ return mismatch ? 'pumuki policy reconcile --strict --apply --json' : undefined;
838
838
  };
839
839
 
840
840
  export const runLifecycleDoctor = (params?: {
@@ -91,6 +91,16 @@ const resolveBlockedAction = (
91
91
  message: 'La política efectiva todavía no es estricta en todos los stages requeridos.',
92
92
  };
93
93
  }
94
+ if (snapshot.attention_codes.includes('POLICY_HASH_DIVERGENCE')) {
95
+ return {
96
+ stage,
97
+ phase: 'RED',
98
+ action: 'ask',
99
+ confidence_pct: 58,
100
+ ...resolveGovernanceCatalogAction({ code: 'POLICY_HASH_DIVERGENCE', stage }),
101
+ message: 'Los hashes de policy difieren entre stages; reconcilia y aplica el contrato canónico.',
102
+ };
103
+ }
94
104
  if (!snapshot.contract_surface.skills_lock_json || !snapshot.contract_surface.skills_sources_json) {
95
105
  return {
96
106
  stage,
@@ -263,6 +263,12 @@ export const readGovernanceObservationSnapshot = (params: {
263
263
  if (!policyValidation.stages.CI.strict) {
264
264
  attention.push('POLICY_CI_NOT_STRICT');
265
265
  }
266
+ const policyHashes = new Set(
267
+ Object.values(policyValidation.stages).map((stage) => stage.hash)
268
+ );
269
+ if (policyHashes.size > 1) {
270
+ attention.push('POLICY_HASH_DIVERGENCE');
271
+ }
266
272
  if (onProtected) {
267
273
  attention.push('GITFLOW_PROTECTED_BRANCH_CONTEXT');
268
274
  }
@@ -44,14 +44,30 @@ const resolveCiBaseRefInRepo = (repoRoot: string): string => {
44
44
  };
45
45
 
46
46
  const resolvePrePushBootstrapBaseRefInRepo = (repoRoot: string): string => {
47
- const candidates = ['origin/develop', 'develop', resolveCiBaseRefInRepo(repoRoot)];
47
+ const candidates = ['origin/main', 'main', 'origin/develop', 'develop'];
48
+ let best: { ref: string; changedFiles: number } | undefined;
49
+
48
50
  for (const candidate of candidates) {
49
- if (runGit(repoRoot, ['rev-parse', '--verify', candidate])) {
50
- return candidate;
51
+ if (!runGit(repoRoot, ['rev-parse', '--verify', candidate])) {
52
+ continue;
53
+ }
54
+ try {
55
+ const changedFiles = runGit(
56
+ repoRoot,
57
+ ['diff', '--name-only', '--diff-filter=ACMR', `${candidate}..HEAD`]
58
+ )
59
+ .split('\n')
60
+ .map((line) => line.trim())
61
+ .filter((line) => line.length > 0).length;
62
+ if (!best || changedFiles < best.changedFiles) {
63
+ best = { ref: candidate, changedFiles };
64
+ }
65
+ } catch {
66
+ continue;
51
67
  }
52
68
  }
53
69
 
54
- return 'HEAD';
70
+ return best?.ref ?? resolveCiBaseRefInRepo(repoRoot);
55
71
  };
56
72
 
57
73
  const shouldAllowBootstrapPrePush = (rawInput: string): boolean => {
@@ -94,7 +94,7 @@ const nextActionFromViolation = (
94
94
  command:
95
95
  `${buildPinnedPumukiNpxCommand({
96
96
  repoRoot,
97
- executableAndArgs: 'pumuki policy reconcile --strict --json',
97
+ executableAndArgs: 'pumuki policy reconcile --strict --apply --json',
98
98
  })} && ${buildPinnedPumukiNpxCommand({
99
99
  repoRoot,
100
100
  executableAndArgs: 'pumuki sdd validate --stage=PRE_WRITE --json',
@@ -121,7 +121,7 @@ const nextActionFromViolation = (
121
121
  command:
122
122
  `${buildPinnedPumukiNpxCommand({
123
123
  repoRoot,
124
- executableAndArgs: 'pumuki policy reconcile --strict --json',
124
+ executableAndArgs: 'pumuki policy reconcile --strict --apply --json',
125
125
  })} && ${buildPinnedPumukiNpxCommand({
126
126
  repoRoot,
127
127
  executableAndArgs: 'pumuki sdd validate --stage=PRE_WRITE --json',
@@ -3,19 +3,30 @@ import {
3
3
  type GovernanceCatalogNextAction,
4
4
  } from '../gate/governanceActionCatalog';
5
5
  import { evaluateAiGate, type AiGateStage, type AiGateViolation } from '../gate/evaluateAiGate';
6
+ import { readEvidenceResult } from '../evidence/readEvidence';
6
7
  import { collectWorktreeAtomicSlices } from '../git/worktreeAtomicSlices';
7
8
  import { readLifecyclePolicyValidationSnapshot } from '../lifecycle/policyValidationSnapshot';
8
9
  import { resolveLearningContextExperimentalFeature } from '../policy/experimentalFeatures';
9
10
  import { resolvePreWriteEnforcement } from '../policy/preWriteEnforcement';
10
11
  import { readSddLearningContext, type SddLearningContext } from '../sdd/learningInsights';
11
12
 
13
+ const resolveTddStatus = (
14
+ repoRoot: string
15
+ ): 'skipped' | 'passed' | 'advisory' | 'blocked' | 'waived' | null => {
16
+ const evidenceResult = readEvidenceResult(repoRoot);
17
+ if (evidenceResult.kind !== 'valid') {
18
+ return null;
19
+ }
20
+ return evidenceResult.evidence.snapshot.tdd_bdd?.status ?? null;
21
+ };
22
+
12
23
  const ACTIONABLE_HINTS_BY_CODE: Readonly<Record<string, string>> = {
13
24
  EVIDENCE_MISSING:
14
25
  'Ejecuta una auditoría (1/2/3/4 u opciones de motor 11–14) para regenerar .ai_evidence.json.',
15
26
  EVIDENCE_INVALID: 'Regenera .ai_evidence.json desde una opción de auditoría.',
16
27
  EVIDENCE_INTEGRITY_MISSING: 'Refresca evidencia para regenerar metadatos de integridad.',
17
28
  EVIDENCE_ACTIVE_RULE_IDS_EMPTY_FOR_CODE_CHANGES:
18
- 'No hay active_rule_ids para plataforma de código detectada. Ejecuta reconcile --strict y revalida PRE_WRITE.',
29
+ 'No hay active_rule_ids para plataforma de código detectada. Ejecuta reconcile --strict --apply y revalida PRE_WRITE.',
19
30
  EVIDENCE_STALE: 'Refresca evidencia antes de continuar con commit/push.',
20
31
  EVIDENCE_TIMESTAMP_INVALID: 'Regenera evidencia para obtener un timestamp válido.',
21
32
  EVIDENCE_GATE_BLOCKED: 'Corrige primero las violaciones bloqueantes y vuelve a auditar.',
@@ -32,9 +43,11 @@ const ACTIONABLE_HINTS_BY_CODE: Readonly<Record<string, string>> = {
32
43
  EVIDENCE_PLATFORM_SKILLS_BUNDLES_MISSING:
33
44
  'Carga los bundles de skills requeridos por plataforma detectada y regenera evidencia.',
34
45
  EVIDENCE_PLATFORM_CRITICAL_SKILLS_RULES_MISSING:
35
- 'Ejecuta `pumuki policy reconcile --strict --json`, materializa reglas críticas (p.ej. skills.ios.critical-test-quality) y revalida PRE_WRITE.',
46
+ 'Ejecuta `pumuki policy reconcile --strict --apply --json`, materializa reglas críticas (p.ej. skills.ios.critical-test-quality) y revalida PRE_WRITE.',
36
47
  EVIDENCE_CROSS_PLATFORM_CRITICAL_ENFORCEMENT_INCOMPLETE:
37
- 'Reconcilia policy/skills en modo estricto para enforcement crítico transversal y vuelve a validar PRE_WRITE.',
48
+ 'Reconcilia policy/skills en modo estricto con apply para enforcement crítico transversal y vuelve a validar PRE_WRITE.',
49
+ TDD_BDD_BASELINE_BLOCKED:
50
+ 'Corrige el baseline TDD/BDD roto y regenera la evidencia antes de continuar.',
38
51
  EVIDENCE_SKILLS_CONTRACT_INCOMPLETE:
39
52
  'Completa el contrato skills/policy para el stage solicitado y vuelve a validar.',
40
53
  EVIDENCE_PREWRITE_WORKTREE_OVER_LIMIT:
@@ -161,7 +174,7 @@ export type EnterprisePreFlightCheckResult = {
161
174
  hints: ReadonlyArray<string>;
162
175
  learning_context: SddLearningContext | null;
163
176
  ast_analysis: null;
164
- tdd_status: null;
177
+ tdd_status: 'skipped' | 'passed' | 'advisory' | 'blocked' | 'waived' | null;
165
178
  };
166
179
  };
167
180
 
@@ -246,7 +259,7 @@ export const runEnterprisePreFlightCheck = (params: {
246
259
  hints,
247
260
  learning_context: learningContext,
248
261
  ast_analysis: null,
249
- tdd_status: null,
262
+ tdd_status: resolveTddStatus(params.repoRoot),
250
263
  },
251
264
  };
252
265
  };
@@ -1,15 +1,9 @@
1
- export type SkillsEnforcementMode = 'advisory' | 'strict';
2
-
3
1
  export type SkillsEnforcementResolution = {
4
- mode: SkillsEnforcementMode;
5
- source: 'default' | 'env';
6
2
  blocking: boolean;
7
3
  };
8
4
 
9
5
  export const resolveSkillsEnforcement = (): SkillsEnforcementResolution => {
10
6
  return {
11
- mode: 'strict',
12
- source: 'default',
13
7
  blocking: true,
14
8
  };
15
9
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pumuki",
3
- "version": "6.3.133",
3
+ "version": "6.3.135",
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": {