pumuki 6.3.73 → 6.3.76

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 (44) hide show
  1. package/VERSION +1 -1
  2. package/docs/README.md +9 -7
  3. package/docs/operations/RELEASE_NOTES.md +0 -18
  4. package/docs/validation/README.md +3 -1
  5. package/integrations/evidence/buildEvidence.ts +14 -0
  6. package/integrations/evidence/repoState.ts +3 -0
  7. package/integrations/evidence/schema.ts +18 -0
  8. package/integrations/evidence/trackingContract.ts +146 -0
  9. package/integrations/evidence/writeEvidence.ts +14 -0
  10. package/integrations/gate/evaluateAiGate.ts +166 -3
  11. package/integrations/gate/governanceActionCatalog.ts +45 -0
  12. package/integrations/gate/remediationCatalog.ts +8 -0
  13. package/integrations/git/GitService.ts +0 -25
  14. package/integrations/git/aiGateRepoPolicyFindings.ts +4 -0
  15. package/integrations/git/runPlatformGateFacts.ts +0 -1
  16. package/integrations/lifecycle/adapter.templates.json +0 -3
  17. package/integrations/lifecycle/adapter.ts +24 -0
  18. package/integrations/lifecycle/bootstrapManifest.ts +248 -0
  19. package/integrations/lifecycle/cli.ts +30 -68
  20. package/integrations/lifecycle/cliSdd.ts +4 -3
  21. package/integrations/lifecycle/doctor.ts +7 -22
  22. package/integrations/lifecycle/governanceObservationSnapshot.ts +29 -2
  23. package/integrations/lifecycle/index.ts +0 -2
  24. package/integrations/lifecycle/install.ts +21 -0
  25. package/integrations/lifecycle/state.ts +8 -1
  26. package/integrations/mcp/aiGateCheck.ts +140 -10
  27. package/integrations/mcp/alignedPlatformGate.ts +232 -0
  28. package/integrations/mcp/autoExecuteAiStart.ts +6 -1
  29. package/integrations/mcp/enterpriseServer.ts +23 -7
  30. package/integrations/mcp/enterpriseStdioServer.cli.ts +32 -5
  31. package/integrations/mcp/preFlightCheck.ts +10 -0
  32. package/integrations/mcp/readMcpPrePushStdin.ts +7 -0
  33. package/integrations/platform/detectPlatforms.ts +0 -37
  34. package/integrations/policy/experimentalFeatures.ts +1 -1
  35. package/package.json +1 -10
  36. package/scripts/consumer-postinstall.cjs +1 -10
  37. package/AGENTS.md +0 -269
  38. package/CHANGELOG.md +0 -686
  39. package/docs/tracking/plan-curso-pumuki-stack-my-architecture.md +0 -62
  40. package/integrations/lifecycle/audit.ts +0 -101
  41. package/scripts/consumer-postinstall-resolve-args.cjs +0 -38
  42. package/scripts/pumuki-full-surface-smoke-lib.ts +0 -37
  43. package/scripts/pumuki-full-surface-smoke.ts +0 -261
  44. package/scripts/pumuki-smoke-installed-wrapper.cjs +0 -31
@@ -6,6 +6,10 @@ const AI_GATE_STAGES = new Set<AiGateStage>(['PRE_WRITE', 'PRE_COMMIT', 'PRE_PUS
6
6
 
7
7
  const REPO_POLICY_CODES = new Set<string>([
8
8
  'GITFLOW_PROTECTED_BRANCH',
9
+ 'GITFLOW_BRANCH_NAMING_INVALID',
10
+ 'TRACKING_CANONICAL_SOURCE_CONFLICT',
11
+ 'TRACKING_CANONICAL_FILE_MISSING',
12
+ 'TRACKING_CANONICAL_IN_PROGRESS_INVALID',
9
13
  'EVIDENCE_PREWRITE_WORKTREE_OVER_LIMIT',
10
14
  'EVIDENCE_PREWRITE_WORKTREE_WARN',
11
15
  ]);
@@ -27,7 +27,6 @@ export type GateScope =
27
27
  };
28
28
 
29
29
  const DEFAULT_EXTENSIONS = ['.swift', '.ts', '.tsx', '.js', '.jsx', '.kt', '.kts'];
30
- export const DEFAULT_FACT_FILE_EXTENSIONS = DEFAULT_EXTENSIONS;
31
30
 
32
31
  export const countScannedFilesFromFacts = (facts: ReadonlyArray<Fact>): number => {
33
32
  const contentPaths = new Set<string>();
@@ -2,7 +2,6 @@
2
2
  "codex": [
3
3
  {
4
4
  "path": ".pumuki/adapter.json",
5
- "mode": "json-merge",
6
5
  "payload": {
7
6
  "hooks": {
8
7
  "pre_write": {
@@ -32,7 +31,6 @@
32
31
  "repo": [
33
32
  {
34
33
  "path": ".pumuki/adapter.json",
35
- "mode": "json-merge",
36
34
  "payload": {
37
35
  "hooks": {
38
36
  "pre_write": {
@@ -145,7 +143,6 @@
145
143
  },
146
144
  {
147
145
  "path": ".pumuki/adapter.json",
148
- "mode": "json-merge",
149
146
  "payload": {
150
147
  "hooks": {
151
148
  "pre_write": {
@@ -2,6 +2,7 @@ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
2
  import { homedir } from 'node:os';
3
3
  import { dirname, isAbsolute, join, resolve } from 'node:path';
4
4
  import { LifecycleGitService, type ILifecycleGitService } from './gitService';
5
+ import { writeLifecycleBootstrapManifest } from './bootstrapManifest';
5
6
 
6
7
  export type AdapterAgent = string;
7
8
 
@@ -11,6 +12,10 @@ export type LifecycleAdapterInstallResult = {
11
12
  dryRun: boolean;
12
13
  written: boolean;
13
14
  changedFiles: ReadonlyArray<string>;
15
+ bootstrapManifest: {
16
+ path: string;
17
+ changed: boolean;
18
+ };
14
19
  };
15
20
 
16
21
  type AdapterTemplate = {
@@ -154,11 +159,30 @@ export const runLifecycleAdapterInstall = (params: {
154
159
  writeFileSync(absolutePath, nextContents, 'utf8');
155
160
  }
156
161
 
162
+ let bootstrapManifest = {
163
+ path: join(repoRoot, '.pumuki', 'bootstrap-manifest.json'),
164
+ changed: false,
165
+ };
166
+ if (!dryRun) {
167
+ const manifestWrite = writeLifecycleBootstrapManifest({
168
+ git,
169
+ repoRoot,
170
+ });
171
+ bootstrapManifest = {
172
+ path: manifestWrite.path,
173
+ changed: manifestWrite.changed,
174
+ };
175
+ if (manifestWrite.changed) {
176
+ changedFiles.push('.pumuki/bootstrap-manifest.json');
177
+ }
178
+ }
179
+
157
180
  return {
158
181
  repoRoot,
159
182
  agent: params.agent,
160
183
  dryRun,
161
184
  written: !dryRun,
162
185
  changedFiles,
186
+ bootstrapManifest,
163
187
  };
164
188
  };
@@ -0,0 +1,248 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
2
+ import { dirname, join } from 'node:path'
3
+ import { getPumukiHooksStatus, resolvePumukiHooksDirectory } from './hookManager'
4
+ import { LifecycleGitService, type ILifecycleGitService } from './gitService'
5
+ import {
6
+ readGovernanceObservationSnapshot,
7
+ type GovernanceContractSurface,
8
+ type GovernanceObservationSnapshot,
9
+ } from './governanceObservationSnapshot'
10
+ import { readGovernanceNextAction, type GovernanceNextActionSummary } from './governanceNextAction'
11
+ import { getCurrentPumukiPackageName, getCurrentPumukiVersion } from './packageInfo'
12
+ import { readLifecycleExperimentalFeaturesSnapshot } from './experimentalFeaturesSnapshot'
13
+ import { readLifecyclePolicyValidationSnapshot } from './policyValidationSnapshot'
14
+ import { readLifecycleState } from './state'
15
+
16
+ export const BOOTSTRAP_MANIFEST_RELATIVE_PATH = '.pumuki/bootstrap-manifest.json'
17
+
18
+ type AdapterCommandContract = {
19
+ path: string
20
+ present: boolean
21
+ hooks: {
22
+ pre_write?: string
23
+ pre_commit?: string
24
+ pre_push?: string
25
+ ci?: string
26
+ }
27
+ mcp: {
28
+ enterprise?: string
29
+ evidence?: string
30
+ }
31
+ }
32
+
33
+ export type LifecycleBootstrapManifest = {
34
+ schema_version: '1'
35
+ repo_root: string
36
+ package: {
37
+ name: string
38
+ version: string
39
+ }
40
+ lifecycle: {
41
+ installed: boolean
42
+ version: string | null
43
+ installed_at: string | null
44
+ managed_hooks: ReadonlyArray<string>
45
+ openspec_managed_artifacts: ReadonlyArray<string>
46
+ }
47
+ hooks_directory: {
48
+ path: string
49
+ source: 'git-rev-parse' | 'git-config' | 'default'
50
+ }
51
+ hook_status: Record<string, { managed_block_present: boolean; exists: boolean }>
52
+ contract_surface: GovernanceContractSurface
53
+ governance: {
54
+ effective: GovernanceObservationSnapshot['governance_effective']
55
+ attention_codes: ReadonlyArray<string>
56
+ next_action: GovernanceNextActionSummary
57
+ bootstrap_hints: ReadonlyArray<string>
58
+ }
59
+ sdd: {
60
+ effective_mode: GovernanceObservationSnapshot['sdd']['effective_mode']
61
+ experimental_source: string
62
+ session_active: boolean
63
+ session_valid: boolean
64
+ change_id: string | null
65
+ }
66
+ policy_strict: GovernanceObservationSnapshot['policy_strict']
67
+ git: GovernanceObservationSnapshot['git']
68
+ adapter: AdapterCommandContract
69
+ }
70
+
71
+ export type LifecycleBootstrapManifestWriteResult = {
72
+ path: string
73
+ changed: boolean
74
+ manifest: LifecycleBootstrapManifest
75
+ }
76
+
77
+ const isRecord = (value: unknown): value is Record<string, unknown> =>
78
+ typeof value === 'object' && value !== null && !Array.isArray(value)
79
+
80
+ const readOptionalCommand = (source: unknown): string | undefined => {
81
+ if (!isRecord(source)) {
82
+ return undefined
83
+ }
84
+ const command = source.command
85
+ return typeof command === 'string' && command.trim().length > 0 ? command.trim() : undefined
86
+ }
87
+
88
+ const readAdapterCommandContract = (repoRoot: string): AdapterCommandContract => {
89
+ const path = join(repoRoot, '.pumuki', 'adapter.json')
90
+ if (!existsSync(path)) {
91
+ return {
92
+ path: BOOTSTRAP_MANIFEST_RELATIVE_PATH.replace('bootstrap-manifest.json', 'adapter.json'),
93
+ present: false,
94
+ hooks: {},
95
+ mcp: {},
96
+ }
97
+ }
98
+
99
+ try {
100
+ const parsed = JSON.parse(readFileSync(path, 'utf8')) as unknown
101
+ const hooks = isRecord(parsed) && isRecord(parsed.hooks) ? parsed.hooks : {}
102
+ const mcp = isRecord(parsed) && isRecord(parsed.mcp) ? parsed.mcp : {}
103
+ return {
104
+ path: '.pumuki/adapter.json',
105
+ present: true,
106
+ hooks: {
107
+ pre_write: readOptionalCommand(isRecord(hooks) ? hooks.pre_write : undefined),
108
+ pre_commit: readOptionalCommand(isRecord(hooks) ? hooks.pre_commit : undefined),
109
+ pre_push: readOptionalCommand(isRecord(hooks) ? hooks.pre_push : undefined),
110
+ ci: readOptionalCommand(isRecord(hooks) ? hooks.ci : undefined),
111
+ },
112
+ mcp: {
113
+ enterprise: readOptionalCommand(isRecord(mcp) ? mcp.enterprise : undefined),
114
+ evidence: readOptionalCommand(isRecord(mcp) ? mcp.evidence : undefined),
115
+ },
116
+ }
117
+ } catch {
118
+ return {
119
+ path: '.pumuki/adapter.json',
120
+ present: false,
121
+ hooks: {},
122
+ mcp: {},
123
+ }
124
+ }
125
+ }
126
+
127
+ const parseManagedHooks = (raw?: string): string[] => {
128
+ if (typeof raw !== 'string' || raw.trim().length === 0) {
129
+ return []
130
+ }
131
+ return raw
132
+ .split(',')
133
+ .map((value) => value.trim())
134
+ .filter((value) => value.length > 0)
135
+ }
136
+
137
+ const parseManagedArtifacts = (raw?: string): string[] => {
138
+ if (typeof raw !== 'string' || raw.trim().length === 0) {
139
+ return []
140
+ }
141
+ return raw
142
+ .split(',')
143
+ .map((value) => value.trim())
144
+ .filter((value) => value.length > 0)
145
+ }
146
+
147
+ const toHookStatusSummary = (
148
+ hookStatus: ReturnType<typeof getPumukiHooksStatus>
149
+ ): Record<string, { managed_block_present: boolean; exists: boolean }> =>
150
+ Object.fromEntries(
151
+ Object.entries(hookStatus).map(([hook, entry]) => [
152
+ hook,
153
+ {
154
+ managed_block_present: entry.managedBlockPresent,
155
+ exists: entry.exists,
156
+ },
157
+ ])
158
+ )
159
+
160
+ export const buildLifecycleBootstrapManifest = (params: {
161
+ repoRoot: string
162
+ git?: ILifecycleGitService
163
+ }): LifecycleBootstrapManifest => {
164
+ const git = params.git ?? new LifecycleGitService()
165
+ const repoRoot = git.resolveRepoRoot(params.repoRoot)
166
+ const lifecycleState = readLifecycleState(git, repoRoot)
167
+ const experimentalFeatures = readLifecycleExperimentalFeaturesSnapshot()
168
+ const policyValidation = readLifecyclePolicyValidationSnapshot(repoRoot)
169
+ const governanceObservation = readGovernanceObservationSnapshot({
170
+ repoRoot,
171
+ experimentalFeatures,
172
+ policyValidation,
173
+ git,
174
+ })
175
+ const governanceNextAction = readGovernanceNextAction({
176
+ repoRoot,
177
+ stage: 'PRE_WRITE',
178
+ governanceObservation,
179
+ })
180
+ const hooksDirectory = resolvePumukiHooksDirectory(repoRoot)
181
+ const hookStatus = getPumukiHooksStatus(repoRoot)
182
+ const adapter = readAdapterCommandContract(repoRoot)
183
+
184
+ return {
185
+ schema_version: '1',
186
+ repo_root: repoRoot,
187
+ package: {
188
+ name: getCurrentPumukiPackageName(),
189
+ version: getCurrentPumukiVersion({ repoRoot }),
190
+ },
191
+ lifecycle: {
192
+ installed: lifecycleState.installed === 'true',
193
+ version: typeof lifecycleState.version === 'string' && lifecycleState.version.length > 0
194
+ ? lifecycleState.version
195
+ : null,
196
+ installed_at:
197
+ typeof lifecycleState.installedAt === 'string' && lifecycleState.installedAt.length > 0
198
+ ? lifecycleState.installedAt
199
+ : null,
200
+ managed_hooks: parseManagedHooks(lifecycleState.hooks),
201
+ openspec_managed_artifacts: parseManagedArtifacts(lifecycleState.openSpecManagedArtifacts),
202
+ },
203
+ hooks_directory: hooksDirectory,
204
+ hook_status: toHookStatusSummary(hookStatus),
205
+ contract_surface: governanceObservation.contract_surface,
206
+ governance: {
207
+ effective: governanceObservation.governance_effective,
208
+ attention_codes: governanceObservation.attention_codes,
209
+ next_action: governanceNextAction,
210
+ bootstrap_hints: governanceObservation.agent_bootstrap_hints,
211
+ },
212
+ sdd: {
213
+ effective_mode: governanceObservation.sdd.effective_mode,
214
+ experimental_source: governanceObservation.sdd.experimental_source,
215
+ session_active: governanceObservation.sdd_session.active,
216
+ session_valid: governanceObservation.sdd_session.valid,
217
+ change_id: governanceObservation.sdd_session.change_id,
218
+ },
219
+ policy_strict: governanceObservation.policy_strict,
220
+ git: governanceObservation.git,
221
+ adapter,
222
+ }
223
+ }
224
+
225
+ const serializeManifest = (manifest: LifecycleBootstrapManifest): string =>
226
+ `${JSON.stringify(manifest, null, 2)}\n`
227
+
228
+ export const writeLifecycleBootstrapManifest = (params: {
229
+ repoRoot: string
230
+ git?: ILifecycleGitService
231
+ }): LifecycleBootstrapManifestWriteResult => {
232
+ const git = params.git ?? new LifecycleGitService()
233
+ const repoRoot = git.resolveRepoRoot(params.repoRoot)
234
+ const path = join(repoRoot, BOOTSTRAP_MANIFEST_RELATIVE_PATH)
235
+ const manifest = buildLifecycleBootstrapManifest({ repoRoot, git })
236
+ const nextContents = serializeManifest(manifest)
237
+ const currentContents = existsSync(path) ? readFileSync(path, 'utf8') : ''
238
+ const changed = currentContents !== nextContents
239
+ if (changed) {
240
+ mkdirSync(dirname(path), { recursive: true })
241
+ writeFileSync(path, nextContents, 'utf8')
242
+ }
243
+ return {
244
+ path,
245
+ changed,
246
+ manifest,
247
+ }
248
+ }
@@ -77,7 +77,6 @@ import {
77
77
  type RemoteCiDiagnostics,
78
78
  } from './remoteCiDiagnostics';
79
79
  import { runPolicyReconcile } from './policyReconcile';
80
- import { runLifecycleAudit, type LifecycleAuditStage } from './audit';
81
80
  import { resolvePreWriteEnforcement, type PreWriteEnforcementResolution } from '../policy/preWriteEnforcement';
82
81
 
83
82
  type LifecycleCommand =
@@ -93,8 +92,7 @@ type LifecycleCommand =
93
92
  | 'sdd'
94
93
  | 'adapter'
95
94
  | 'analytics'
96
- | 'policy'
97
- | 'audit';
95
+ | 'policy';
98
96
 
99
97
  type SddCommand =
100
98
  | 'status'
@@ -180,8 +178,6 @@ export type ParsedArgs = {
180
178
  policyCommand?: PolicyCommand;
181
179
  policyStrict?: boolean;
182
180
  policyApply?: boolean;
183
- auditStage?: LifecycleAuditStage;
184
- auditEngine?: boolean;
185
181
  };
186
182
 
187
183
  const HELP_TEXT = `
@@ -192,7 +188,6 @@ Pumuki lifecycle commands:
192
188
  pumuki remove
193
189
  pumuki update [--latest|--spec=<package-spec>]
194
190
  pumuki doctor [--remote-checks] [--deep] [--parity] [--json]
195
- pumuki audit [--stage=PRE_COMMIT|PRE_PUSH|CI] [--engine] [--json]
196
191
  pumuki status [--json] [--remote-checks]
197
192
  pumuki watch [--stage=PRE_COMMIT|PRE_PUSH|CI] [--scope=workingTree|staged|repoAndStaged|repo] [--severity=critical|high|medium|low] [--interval-ms=<n>] [--notify-cooldown-ms=<n>] [--no-notify] [--once|--iterations=<n>] [--json]
198
193
  pumuki loop run --objective=<text> [--max-attempts=<n>] [--json]
@@ -238,8 +233,7 @@ const isLifecycleCommand = (value: string): value is LifecycleCommand =>
238
233
  value === 'sdd' ||
239
234
  value === 'adapter' ||
240
235
  value === 'analytics' ||
241
- value === 'policy' ||
242
- value === 'audit';
236
+ value === 'policy';
243
237
 
244
238
  const parseAdapterAgent = (value?: string): AdapterAgent => {
245
239
  const normalized = (value ?? '').trim();
@@ -269,16 +263,6 @@ const parseSddStage = (value?: string): SddStage => {
269
263
  throw new Error(`Unsupported SDD stage "${value}". Use PRE_WRITE, PRE_COMMIT, PRE_PUSH or CI.`);
270
264
  };
271
265
 
272
- const parseAuditStage = (value?: string): LifecycleAuditStage => {
273
- const stage = parseSddStage(value);
274
- if (stage === 'PRE_WRITE') {
275
- throw new Error(
276
- 'PRE_WRITE is not supported for "pumuki audit". Use PRE_COMMIT, PRE_PUSH or CI (aliases GREEN, REFACTOR, CLOSE).'
277
- );
278
- }
279
- return stage;
280
- };
281
-
282
266
  const parseSddEvidencePath = (value: string): string => {
283
267
  const normalized = value.trim();
284
268
  if (normalized.length === 0) {
@@ -644,8 +628,6 @@ export const parseLifecycleCliArgs = (argv: ReadonlyArray<string>): ParsedArgs =
644
628
  let analyticsSinceDays: ParsedArgs['analyticsSinceDays'];
645
629
  let analyticsJsonOutputPath: ParsedArgs['analyticsJsonOutputPath'];
646
630
  let analyticsMarkdownOutputPath: ParsedArgs['analyticsMarkdownOutputPath'];
647
- let auditStage: LifecycleAuditStage | undefined;
648
- let auditEngine = false;
649
631
 
650
632
  if (commandRaw === 'watch') {
651
633
  for (const arg of argv.slice(1)) {
@@ -829,31 +811,6 @@ export const parseLifecycleCliArgs = (argv: ReadonlyArray<string>): ParsedArgs =
829
811
  };
830
812
  }
831
813
 
832
- if (commandRaw === 'audit') {
833
- for (const arg of argv.slice(1)) {
834
- if (arg === '--json') {
835
- json = true;
836
- continue;
837
- }
838
- if (arg === '--engine') {
839
- auditEngine = true;
840
- continue;
841
- }
842
- if (arg.startsWith('--stage=')) {
843
- auditStage = parseAuditStage(arg.slice('--stage='.length));
844
- continue;
845
- }
846
- throw new Error(`Unsupported argument "${arg}".\n\n${HELP_TEXT}`);
847
- }
848
- return {
849
- command: commandRaw,
850
- purgeArtifacts: false,
851
- json,
852
- auditStage: auditStage ?? 'PRE_COMMIT',
853
- ...(auditEngine ? { auditEngine: true } : {}),
854
- };
855
- }
856
-
857
814
  if (commandRaw === 'loop') {
858
815
  const subcommandRaw = argv[1] ?? '';
859
816
  if (
@@ -1662,8 +1619,8 @@ export type PreWriteOpenSpecBootstrapTrace = {
1662
1619
  details?: string;
1663
1620
  };
1664
1621
 
1665
- export const PRE_WRITE_ENABLE_ADVISORY_COMMAND =
1666
- 'PUMUKI_EXPERIMENTAL_PRE_WRITE=advisory npx --yes --package pumuki@latest pumuki sdd validate --stage=PRE_WRITE --json';
1622
+ export const PRE_WRITE_ENABLE_STRICT_COMMAND =
1623
+ 'PUMUKI_EXPERIMENTAL_PRE_WRITE=strict npx --yes --package pumuki@latest pumuki sdd validate --stage=PRE_WRITE --json';
1667
1624
  export const buildSddExperimentalEnableAdvisoryCommand = (stage: SddStage): string =>
1668
1625
  `PUMUKI_EXPERIMENTAL_SDD=advisory npx --yes --package pumuki@latest pumuki sdd validate --stage=${stage} --json`;
1669
1626
  const buildAnalyticsExperimentalEnableCommand = (action: AnalyticsHotspotsCommand): string =>
@@ -1757,6 +1714,7 @@ const buildSaasIngestionExperimentalDisabledEnvelope = (
1757
1714
  export const buildPreWriteExperimentalDisabledResult = (params: {
1758
1715
  stage: SddStage;
1759
1716
  status: SddEvaluateResult['status'];
1717
+ source: 'env' | 'legacy-env' | 'default';
1760
1718
  }): SddEvaluateResult => ({
1761
1719
  stage: params.stage,
1762
1720
  status: params.status,
@@ -1764,14 +1722,16 @@ export const buildPreWriteExperimentalDisabledResult = (params: {
1764
1722
  allowed: true,
1765
1723
  code: 'PRE_WRITE_EXPERIMENTAL_DISABLED',
1766
1724
  message:
1767
- 'PRE_WRITE pertenece al namespace experimental y está desactivado por defecto. Actívalo explícitamente con PUMUKI_EXPERIMENTAL_PRE_WRITE=advisory o strict si necesitas este flujo.',
1725
+ 'PRE_WRITE está desactivado explícitamente. Reactívalo con PUMUKI_EXPERIMENTAL_PRE_WRITE=strict si necesitas recuperar el gate previo a escritura.',
1768
1726
  details: {
1769
1727
  experimental: true,
1770
- default_off: true,
1728
+ default_off: false,
1729
+ disabled_explicitly: true,
1730
+ disabled_source: params.source,
1771
1731
  layer: 'experimental',
1772
1732
  activation_env: 'PUMUKI_EXPERIMENTAL_PRE_WRITE',
1773
1733
  legacy_activation_env: 'PUMUKI_PREWRITE_ENFORCEMENT',
1774
- activation_command: PRE_WRITE_ENABLE_ADVISORY_COMMAND,
1734
+ activation_command: PRE_WRITE_ENABLE_STRICT_COMMAND,
1775
1735
  },
1776
1736
  },
1777
1737
  });
@@ -2153,6 +2113,10 @@ export const runLifecycleCli = async (
2153
2113
  repo_root: installResult.repoRoot,
2154
2114
  version: installResult.version,
2155
2115
  hooks_changed: installResult.changedHooks,
2116
+ bootstrap_manifest: {
2117
+ path: installResult.bootstrapManifest.path,
2118
+ changed: installResult.bootstrapManifest.changed,
2119
+ },
2156
2120
  openspec: installResult.openSpecBootstrap
2157
2121
  ? {
2158
2122
  installed: installResult.openSpecBootstrap.packageInstalled,
@@ -2165,6 +2129,10 @@ export const runLifecycleCli = async (
2165
2129
  mcp: {
2166
2130
  agent: adapterResult.agent,
2167
2131
  changed_files: adapterResult.changedFiles,
2132
+ bootstrap_manifest: {
2133
+ path: adapterResult.bootstrapManifest.path,
2134
+ changed: adapterResult.bootstrapManifest.changed,
2135
+ },
2168
2136
  adapter_health: adapterCheck
2169
2137
  ? {
2170
2138
  status: adapterCheck.status,
@@ -2191,6 +2159,9 @@ export const runLifecycleCli = async (
2191
2159
  writeInfo(
2192
2160
  `[pumuki] bootstrap install: hooks changed=${installResult.changedHooks.join(', ') || 'none'}`
2193
2161
  );
2162
+ writeInfo(
2163
+ `[pumuki] bootstrap manifest: path=${installResult.bootstrapManifest.path} changed=${installResult.bootstrapManifest.changed ? 'yes' : 'no'}`
2164
+ );
2194
2165
  if (installResult.openSpecBootstrap) {
2195
2166
  writeInfo(
2196
2167
  `[pumuki] bootstrap openspec: installed=${installResult.openSpecBootstrap.packageInstalled ? 'yes' : 'no'} project=${installResult.openSpecBootstrap.projectInitialized ? 'yes' : 'no'} actions=${installResult.openSpecBootstrap.actions.join(', ') || 'none'}`
@@ -2231,6 +2202,9 @@ export const runLifecycleCli = async (
2231
2202
  writeInfo(
2232
2203
  `[pumuki] installed ${result.version} at ${result.repoRoot} (hooks changed: ${result.changedHooks.join(', ') || 'none'})`
2233
2204
  );
2205
+ writeInfo(
2206
+ `[pumuki] bootstrap manifest: path=${result.bootstrapManifest.path} changed=${result.bootstrapManifest.changed ? 'yes' : 'no'}`
2207
+ );
2234
2208
  if (result.openSpecBootstrap) {
2235
2209
  writeInfo(
2236
2210
  `[pumuki] openspec bootstrap: installed=${result.openSpecBootstrap.packageInstalled ? 'yes' : 'no'} project=${result.openSpecBootstrap.projectInitialized ? 'yes' : 'no'} actions=${result.openSpecBootstrap.actions.join(', ') || 'none'}`
@@ -2246,6 +2220,9 @@ export const runLifecycleCli = async (
2246
2220
  writeInfo(
2247
2221
  `[pumuki] mcp wiring: agent=${adapterResult.agent} changed=${adapterResult.changedFiles.length}`
2248
2222
  );
2223
+ writeInfo(
2224
+ `[pumuki] mcp manifest: path=${adapterResult.bootstrapManifest.path} changed=${adapterResult.bootstrapManifest.changed ? 'yes' : 'no'}`
2225
+ );
2249
2226
  if (adapterResult.changedFiles.length > 0) {
2250
2227
  writeInfo(`[pumuki] mcp files: ${adapterResult.changedFiles.join(', ')}`);
2251
2228
  }
@@ -2302,24 +2279,6 @@ export const runLifecycleCli = async (
2302
2279
  );
2303
2280
  return 0;
2304
2281
  }
2305
- case 'audit': {
2306
- const result = await runLifecycleAudit({
2307
- stage: parsed.auditStage ?? 'PRE_COMMIT',
2308
- auditMode: parsed.auditEngine === true ? 'engine' : 'gate',
2309
- });
2310
- if (parsed.json) {
2311
- writeInfo(JSON.stringify(result, null, 2));
2312
- } else {
2313
- writeInfo(
2314
- `[pumuki] audit: repo=${result.repo_root} stage=${result.stage} mode=${result.audit_mode} exit=${result.gate_exit_code}`
2315
- );
2316
- writeInfo(
2317
- `[pumuki] audit: files_scanned=${result.files_scanned ?? 'n/a'} untracked_matching_extensions=${result.untracked_matching_extensions_count} outcome=${result.snapshot_outcome ?? 'n/a'}`
2318
- );
2319
- writeInfo(`[pumuki] audit: hint=${result.policy_reconcile_hint}`);
2320
- }
2321
- return result.gate_exit_code;
2322
- }
2323
2282
  case 'doctor': {
2324
2283
  const report = runLifecycleDoctor({
2325
2284
  deep: parsed.doctorDeep === true,
@@ -2772,6 +2731,9 @@ export const runLifecycleCli = async (
2772
2731
  `[pumuki] adapter files: ${result.changedFiles.join(', ')}`
2773
2732
  );
2774
2733
  }
2734
+ writeInfo(
2735
+ `[pumuki] adapter manifest: path=${result.bootstrapManifest.path} changed=${result.bootstrapManifest.changed ? 'yes' : 'no'}`
2736
+ );
2775
2737
  }
2776
2738
  return 0;
2777
2739
  }
@@ -36,7 +36,7 @@ import {
36
36
  buildPreWriteExperimentalDisabledResult,
37
37
  buildSddExperimentalEnableAdvisoryCommand,
38
38
  runRawPreWriteAiGateCheck,
39
- PRE_WRITE_ENABLE_ADVISORY_COMMAND,
39
+ PRE_WRITE_ENABLE_STRICT_COMMAND,
40
40
  } from './cli';
41
41
 
42
42
  export const runSddCommand = async (parsed: ParsedArgs, activeDependencies: LifecycleCliDependencies): Promise<number> => {
@@ -84,6 +84,7 @@ export const runSddCommand = async (parsed: ParsedArgs, activeDependencies: Life
84
84
  const disabledResult = buildPreWriteExperimentalDisabledResult({
85
85
  stage: requestedStage,
86
86
  status: readSddStatus(process.cwd()),
87
+ source: preWriteEnforcement.source,
87
88
  });
88
89
  if (parsed.json) {
89
90
  writeInfo(
@@ -106,7 +107,7 @@ export const runSddCommand = async (parsed: ParsedArgs, activeDependencies: Life
106
107
  },
107
108
  next_action: {
108
109
  reason: 'PRE_WRITE_EXPERIMENTAL_DISABLED',
109
- command: PRE_WRITE_ENABLE_ADVISORY_COMMAND,
110
+ command: PRE_WRITE_ENABLE_STRICT_COMMAND,
110
111
  },
111
112
  },
112
113
  null,
@@ -127,7 +128,7 @@ export const runSddCommand = async (parsed: ParsedArgs, activeDependencies: Life
127
128
  `[pumuki][sdd] pre-write enforcement: mode=${preWriteEnforcement.mode} source=${preWriteEnforcement.source} blocking=no`
128
129
  );
129
130
  writeInfo(
130
- `[pumuki][sdd] next action (PRE_WRITE_EXPERIMENTAL_DISABLED): ${PRE_WRITE_ENABLE_ADVISORY_COMMAND}`
131
+ `[pumuki][sdd] next action (PRE_WRITE_EXPERIMENTAL_DISABLED): ${PRE_WRITE_ENABLE_STRICT_COMMAND}`
131
132
  );
132
133
  }
133
134
  return 0;
@@ -113,7 +113,6 @@ export type LifecycleDoctorReport = {
113
113
  experimentalFeatures: LifecycleExperimentalFeaturesSnapshot;
114
114
  governanceObservation: GovernanceObservationSnapshot;
115
115
  governanceNextAction: GovernanceNextActionSummary;
116
- policy_signature_remediation?: string;
117
116
  issues: ReadonlyArray<DoctorIssue>;
118
117
  deep?: DoctorDeepReport;
119
118
  parity_profile?: DoctorParityProfile;
@@ -811,15 +810,6 @@ const compareDoctorParityProfile = (params: {
811
810
  };
812
811
  };
813
812
 
814
- const buildPolicySignatureRemediation = (
815
- policyValidation: LifecyclePolicyValidationSnapshot
816
- ): string | undefined => {
817
- const mismatch = Object.values(policyValidation.stages).some(
818
- (stage) => stage.validationCode === 'POLICY_AS_CODE_SIGNATURE_MISMATCH'
819
- );
820
- return mismatch ? 'pumuki policy reconcile --apply' : undefined;
821
- };
822
-
823
813
  export const runLifecycleDoctor = (params?: {
824
814
  cwd?: string;
825
815
  git?: ILifecycleGitService;
@@ -879,8 +869,6 @@ export const runLifecycleDoctor = (params?: {
879
869
  ? compareDoctorParityProfile({ repoRoot, actual: parity_profile })
880
870
  : undefined;
881
871
 
882
- const policySignatureRemediation = buildPolicySignatureRemediation(policyValidation);
883
-
884
872
  return {
885
873
  repoRoot,
886
874
  packageVersion: version.effective,
@@ -892,11 +880,8 @@ export const runLifecycleDoctor = (params?: {
892
880
  hooksDirectoryResolution: hooksDirectory.source,
893
881
  policyValidation,
894
882
  experimentalFeatures,
895
- governanceObservation,
896
883
  governanceNextAction,
897
- ...(policySignatureRemediation
898
- ? { policy_signature_remediation: policySignatureRemediation }
899
- : {}),
884
+ governanceObservation,
900
885
  issues,
901
886
  deep,
902
887
  parity_profile,
@@ -914,11 +899,11 @@ export const doctorHasGovernanceAttention = (report: LifecycleDoctorReport): boo
914
899
  doctorGovernanceNeedsAttention(report.governanceObservation);
915
900
 
916
901
  export const doctorCommandShouldWarnHuman = (report: LifecycleDoctorReport): boolean =>
917
- report.issues.length > 0 ||
918
- report.deep?.checks.some((check) => check.status !== 'pass') === true ||
919
- doctorHasGovernanceAttention(report);
902
+ report.issues.length > 0
903
+ || report.deep?.checks.some((check) => check.status !== 'pass') === true
904
+ || doctorHasGovernanceAttention(report);
920
905
 
921
906
  export const doctorCommandShouldFailExit = (report: LifecycleDoctorReport): boolean =>
922
- doctorHasBlockingIssues(report) ||
923
- doctorHasParityMismatch(report) ||
924
- doctorGovernanceIsBlocking(report.governanceObservation);
907
+ doctorHasBlockingIssues(report)
908
+ || doctorHasParityMismatch(report)
909
+ || doctorGovernanceIsBlocking(report.governanceObservation);