pumuki 6.3.113 → 6.3.115

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 (99) hide show
  1. package/CHANGELOG.md +52 -5
  2. package/README.md +4 -2
  3. package/VERSION +1 -1
  4. package/core/facts/detectors/typescript/index.test.ts +0 -229
  5. package/core/facts/detectors/typescript/index.ts +0 -278
  6. package/core/facts/extractHeuristicFacts.ts +0 -4
  7. package/core/rules/presets/heuristics/typescript.test.ts +1 -21
  8. package/core/rules/presets/heuristics/typescript.ts +0 -72
  9. package/docs/README.md +13 -9
  10. package/docs/codex-skills/backend-enterprise-rules.md +3 -3
  11. package/docs/operations/RELEASE_NOTES.md +41 -4
  12. package/docs/product/API_REFERENCE.md +1 -1
  13. package/docs/product/HOW_IT_WORKS.md +6 -0
  14. package/docs/product/INSTALLATION.md +1 -1
  15. package/docs/product/USAGE.md +42 -5
  16. package/docs/tracking/plan-curso-pumuki-stack-my-architecture.md +100 -44
  17. package/docs/validation/README.md +6 -3
  18. package/integrations/config/skillsDetectorRegistry.ts +0 -24
  19. package/integrations/config/skillsMarkdownRules.ts +0 -57
  20. package/integrations/evidence/buildEvidence.ts +0 -24
  21. package/integrations/evidence/repoState.ts +9 -7
  22. package/integrations/evidence/schema.ts +0 -18
  23. package/integrations/evidence/writeEvidence.ts +0 -24
  24. package/integrations/gate/evaluateAiGate.ts +8 -251
  25. package/integrations/gate/remediationCatalog.ts +0 -8
  26. package/integrations/git/GitService.ts +44 -5
  27. package/integrations/git/aiGateRepoPolicyFindings.ts +86 -17
  28. package/integrations/git/runPlatformGate.ts +1 -9
  29. package/integrations/git/runPlatformGateFacts.ts +19 -1
  30. package/integrations/git/runPlatformGateOutput.ts +41 -42
  31. package/integrations/lifecycle/adapter.templates.json +1 -0
  32. package/integrations/lifecycle/adapter.ts +0 -24
  33. package/integrations/lifecycle/audit.ts +101 -0
  34. package/integrations/lifecycle/cli.ts +120 -99
  35. package/integrations/lifecycle/cliSdd.ts +4 -26
  36. package/integrations/lifecycle/doctor.ts +40 -102
  37. package/integrations/lifecycle/index.ts +2 -0
  38. package/integrations/lifecycle/install.ts +0 -21
  39. package/integrations/lifecycle/packageInfo.ts +1 -118
  40. package/integrations/lifecycle/state.ts +1 -8
  41. package/integrations/lifecycle/status.ts +40 -59
  42. package/integrations/lifecycle/watch.ts +1 -1
  43. package/integrations/mcp/aiGateCheck.ts +10 -194
  44. package/integrations/mcp/autoExecuteAiStart.ts +116 -92
  45. package/integrations/mcp/enterpriseServer.ts +7 -23
  46. package/integrations/mcp/enterpriseStdioServer.cli.ts +4 -31
  47. package/integrations/mcp/preFlightCheck.ts +5 -67
  48. package/integrations/platform/detectPlatforms.ts +37 -0
  49. package/integrations/sdd/policy.ts +28 -20
  50. package/package.json +1 -1
  51. package/scripts/check-tracking-single-active.sh +1 -1
  52. package/scripts/consumer-menu-matrix-baseline-report-lib.ts +13 -38
  53. package/scripts/consumer-postinstall-resolve-args.cjs +44 -0
  54. package/scripts/consumer-postinstall.cjs +76 -21
  55. package/scripts/framework-menu-advanced-view-lib.ts +0 -49
  56. package/scripts/framework-menu-consumer-actions-lib.ts +28 -4
  57. package/scripts/framework-menu-consumer-preflight-hints.ts +5 -2
  58. package/scripts/framework-menu-consumer-preflight-render.ts +0 -10
  59. package/scripts/framework-menu-consumer-preflight-run.ts +0 -23
  60. package/scripts/framework-menu-consumer-preflight-types.ts +0 -12
  61. package/scripts/framework-menu-consumer-runtime-actions.ts +87 -17
  62. package/scripts/framework-menu-consumer-runtime-audit.ts +36 -2
  63. package/scripts/framework-menu-consumer-runtime-evidence-classic.ts +140 -0
  64. package/scripts/framework-menu-consumer-runtime-lib.ts +2 -38
  65. package/scripts/framework-menu-consumer-runtime-menu.ts +4 -31
  66. package/scripts/framework-menu-consumer-runtime-types.ts +3 -5
  67. package/scripts/framework-menu-evidence-summary-lib.ts +1 -0
  68. package/scripts/framework-menu-evidence-summary-read.ts +57 -5
  69. package/scripts/framework-menu-evidence-summary-severity.ts +3 -1
  70. package/scripts/framework-menu-evidence-summary-types.ts +7 -0
  71. package/scripts/framework-menu-gate-lib.ts +9 -0
  72. package/scripts/framework-menu-layout-data.ts +5 -0
  73. package/scripts/framework-menu-matrix-baseline-lib.ts +15 -14
  74. package/scripts/framework-menu-matrix-canary-lib.ts +22 -1
  75. package/scripts/framework-menu-matrix-evidence-lib.ts +1 -0
  76. package/scripts/framework-menu-matrix-evidence-types.ts +13 -1
  77. package/scripts/framework-menu-matrix-runner-lib.ts +35 -0
  78. package/scripts/framework-menu-system-notifications-cause.ts +0 -3
  79. package/scripts/framework-menu-system-notifications-macos-swift-source.ts +24 -204
  80. package/scripts/framework-menu-system-notifications-macos.ts +4 -0
  81. package/scripts/framework-menu-system-notifications-payloads-blocked.ts +1 -1
  82. package/scripts/framework-menu-system-notifications-text.ts +1 -7
  83. package/scripts/framework-menu.ts +3 -24
  84. package/scripts/package-install-smoke-consumer-git-repo-lib.ts +1 -10
  85. package/scripts/package-install-smoke-consumer-npm-lib.ts +9 -46
  86. package/scripts/pumuki-full-surface-smoke-lib.ts +37 -0
  87. package/scripts/pumuki-full-surface-smoke.ts +346 -0
  88. package/scripts/pumuki-smoke-installed-wrapper.cjs +31 -0
  89. package/integrations/evidence/trackingContract.ts +0 -17
  90. package/integrations/gate/governanceActionCatalog.ts +0 -275
  91. package/integrations/lifecycle/bootstrapManifest.ts +0 -248
  92. package/integrations/lifecycle/cliGovernanceConsole.ts +0 -69
  93. package/integrations/lifecycle/governanceNextAction.ts +0 -171
  94. package/integrations/lifecycle/governanceObservationSnapshot.ts +0 -369
  95. package/integrations/lifecycle/trackingState.ts +0 -403
  96. package/integrations/mcp/alignedPlatformGate.ts +0 -232
  97. package/integrations/mcp/readMcpPrePushStdin.ts +0 -7
  98. package/scripts/build-ruralgo-s1-evidence-pack.ts +0 -85
  99. package/scripts/ruralgo-s1-evidence-pack-lib.ts +0 -200
@@ -1,369 +0,0 @@
1
- import { existsSync, readdirSync } from 'node:fs';
2
- import { join } from 'node:path';
3
- import { readEvidenceResult } from '../evidence/readEvidence';
4
- import { readRepoTrackingState } from '../evidence/trackingContract';
5
- import type { RepoTrackingState } from '../evidence/schema';
6
- import { readSddStatus } from '../sdd';
7
- import type { SddStatusPayload } from '../sdd/types';
8
- import type { LifecycleExperimentalFeaturesSnapshot } from './experimentalFeaturesSnapshot';
9
- import type { ILifecycleGitService } from './gitService';
10
- import { LifecycleGitService } from './gitService';
11
- import type { LifecyclePolicyValidationSnapshot } from './policyValidationSnapshot';
12
- import { writeInfo } from './cliOutputs';
13
- import { formatTrackingActionableContext } from './trackingState';
14
-
15
- const DEFAULT_PROTECTED_BRANCHES = new Set(['main', 'master', 'develop', 'dev']);
16
-
17
- export type GovernanceEvidenceSummary = {
18
- path: string;
19
- readable: 'missing' | 'invalid' | 'valid';
20
- snapshot_stage?: string;
21
- snapshot_outcome?: 'PASS' | 'WARN' | 'BLOCK';
22
- matched_warn_count?: number;
23
- matched_blocking_count?: number;
24
- findings_count?: number;
25
- ai_gate_status?: 'ALLOWED' | 'BLOCKED';
26
- human_summary_preview: string[];
27
- };
28
-
29
- export type GovernanceContractSurface = {
30
- agents_md: boolean;
31
- skills_lock_json: boolean;
32
- skills_sources_json: boolean;
33
- vendor_skills_dir: boolean;
34
- pumuki_adapter_json: boolean;
35
- };
36
-
37
- export type GovernanceObservationSnapshot = {
38
- schema_version: '1';
39
- platform_bundles_effective?: ReadonlyArray<string>;
40
- pre_write_effective?: {
41
- mode: 'off' | 'advisory' | 'strict';
42
- source: string;
43
- blocking: boolean;
44
- strict_policy: boolean;
45
- };
46
- sdd: {
47
- experimental_raw: string | null;
48
- effective_mode: 'off' | 'advisory' | 'strict';
49
- experimental_source: string;
50
- };
51
- sdd_session: {
52
- active: boolean;
53
- valid: boolean;
54
- change_id: string | null;
55
- remaining_seconds: number | null;
56
- };
57
- policy_strict: {
58
- pre_write: boolean;
59
- pre_commit: boolean;
60
- pre_push: boolean;
61
- ci: boolean;
62
- };
63
- enterprise_warn_as_block_env: boolean;
64
- evidence: GovernanceEvidenceSummary;
65
- git: {
66
- current_branch: string | null;
67
- on_protected_branch_hint: boolean;
68
- };
69
- contract_surface: GovernanceContractSurface;
70
- tracking: RepoTrackingState;
71
- attention_codes: ReadonlyArray<string>;
72
- governance_effective: 'green' | 'attention' | 'blocked';
73
- agent_bootstrap_hints: ReadonlyArray<string>;
74
- };
75
-
76
- const truthyEnv = (value: string | undefined): boolean => {
77
- if (typeof value !== 'string') {
78
- return false;
79
- }
80
- const normalized = value.trim().toLowerCase();
81
- return normalized === '1' || normalized === 'true' || normalized === 'yes' || normalized === 'strict';
82
- };
83
-
84
- const readCurrentBranch = (git: ILifecycleGitService, repoRoot: string): string | null => {
85
- try {
86
- const branch = git.runGit(['rev-parse', '--abbrev-ref', 'HEAD'], repoRoot).trim();
87
- return branch.length > 0 ? branch : null;
88
- } catch {
89
- return null;
90
- }
91
- };
92
-
93
- const readSddStatusSafe = (repoRoot: string, git: ILifecycleGitService): SddStatusPayload => {
94
- try {
95
- return readSddStatus(repoRoot, git);
96
- } catch {
97
- return {
98
- repoRoot,
99
- openspec: {
100
- installed: false,
101
- projectInitialized: false,
102
- minimumVersion: '0.0.0',
103
- recommendedVersion: '0.0.0',
104
- compatible: false,
105
- },
106
- session: {
107
- repoRoot,
108
- active: false,
109
- valid: false,
110
- },
111
- };
112
- }
113
- };
114
-
115
- const buildContractSurface = (repoRoot: string): GovernanceContractSurface => ({
116
- agents_md: existsSync(join(repoRoot, 'AGENTS.md')),
117
- skills_lock_json: existsSync(join(repoRoot, 'skills.lock.json')),
118
- skills_sources_json: existsSync(join(repoRoot, 'skills.sources.json')),
119
- vendor_skills_dir: existsSync(join(repoRoot, 'vendor', 'skills')),
120
- pumuki_adapter_json: existsSync(join(repoRoot, '.pumuki', 'adapter.json')),
121
- });
122
-
123
- const PLATFORM_BUNDLE_ORDER = [
124
- 'android-enterprise-rules',
125
- 'backend-enterprise-rules',
126
- 'frontend-enterprise-rules',
127
- 'ios-enterprise-rules',
128
- ] as const;
129
-
130
- const PLATFORM_BUNDLE_LABELS: Record<(typeof PLATFORM_BUNDLE_ORDER)[number], string> = {
131
- 'android-enterprise-rules': 'android',
132
- 'backend-enterprise-rules': 'backend',
133
- 'frontend-enterprise-rules': 'frontend',
134
- 'ios-enterprise-rules': 'ios',
135
- };
136
-
137
- const resolvePlatformBundlesEffective = (repoRoot: string): ReadonlyArray<string> => {
138
- const vendorSkillsPath = join(repoRoot, 'vendor', 'skills');
139
- if (!existsSync(vendorSkillsPath)) {
140
- return [];
141
- }
142
- const present = new Set(
143
- readdirSync(vendorSkillsPath, { withFileTypes: true })
144
- .filter((entry) => entry.isDirectory())
145
- .map((entry) => entry.name)
146
- );
147
- return PLATFORM_BUNDLE_ORDER.filter((bundle) => present.has(bundle)).map(
148
- (bundle) => PLATFORM_BUNDLE_LABELS[bundle]
149
- );
150
- };
151
-
152
- const summarizeEvidence = (repoRoot: string): GovernanceEvidenceSummary => {
153
- const evidenceResult = readEvidenceResult(repoRoot);
154
- const path = evidenceResult.source_descriptor.path;
155
- if (evidenceResult.kind === 'missing') {
156
- return { path, readable: 'missing', human_summary_preview: [] };
157
- }
158
- if (evidenceResult.kind === 'invalid') {
159
- return {
160
- path,
161
- readable: 'invalid',
162
- human_summary_preview: [evidenceResult.detail ?? evidenceResult.reason],
163
- };
164
- }
165
-
166
- const snapshot = evidenceResult.evidence.snapshot;
167
- const hints = evidenceResult.evidence.operational_hints?.human_summary_lines ?? [];
168
- const breakdown = evidenceResult.evidence.operational_hints?.rule_execution_breakdown;
169
- return {
170
- path,
171
- readable: 'valid',
172
- snapshot_stage: snapshot.stage,
173
- snapshot_outcome: snapshot.outcome,
174
- matched_warn_count: breakdown?.matched_warn_count,
175
- matched_blocking_count: breakdown?.matched_blocking_count,
176
- findings_count: Array.isArray(snapshot.findings) ? snapshot.findings.length : 0,
177
- ai_gate_status: evidenceResult.evidence.ai_gate.status,
178
- human_summary_preview: hints.slice(0, 5),
179
- };
180
- };
181
-
182
- const buildHints = (
183
- surface: GovernanceContractSurface,
184
- branch: string | null,
185
- protectedBranchHint: boolean,
186
- tracking: RepoTrackingState
187
- ): string[] => {
188
- const hints: string[] = [];
189
- if (surface.agents_md) {
190
- hints.push('AGENTS.md presente: aplica el contrato del repo antes de dar governance en verde.');
191
- }
192
- if (!surface.skills_lock_json) {
193
- hints.push('Falta skills.lock.json: genera lock canónico de skills antes de cerrar la gobernanza.');
194
- }
195
- if (!surface.pumuki_adapter_json) {
196
- hints.push('Falta .pumuki/adapter.json: instala el adaptador si quieres wiring IDE/MCP explícito.');
197
- }
198
- if (protectedBranchHint && branch) {
199
- hints.push(`La rama "${branch}" cae en el set protegido por defecto: usa feature/* o refactor/*.`);
200
- }
201
- if (tracking.conflict) {
202
- hints.push('Tracking canónico en conflicto: AGENTS.md y los README del repo no apuntan al mismo MD.');
203
- }
204
- if (tracking.enforced && !tracking.canonical_present) {
205
- hints.push(`Falta el tracking canónico declarado (${tracking.canonical_path ?? 'sin resolver'}).`);
206
- }
207
- if (tracking.enforced && tracking.single_in_progress_valid === false) {
208
- const actionableContext = formatTrackingActionableContext(tracking);
209
- hints.push(
210
- `El tracking canónico debe dejar exactamente una 🚧 (actual=${tracking.in_progress_count ?? 'n/a'}${actionableContext ? `, ${actionableContext}` : ''}).`
211
- );
212
- }
213
- hints.push('SDD/OpenSpec: usa PUMUKI_EXPERIMENTAL_SDD=advisory|strict cuando el loop SDD esté activo.');
214
- hints.push('WARN-as-BLOCK: activa PUMUKI_ENTERPRISE_STRICT_WARN_AS_BLOCK=1 si el repo exige promoción dura.');
215
- return hints;
216
- };
217
-
218
- export const readGovernanceObservationSnapshot = (params: {
219
- repoRoot: string;
220
- experimentalFeatures: LifecycleExperimentalFeaturesSnapshot;
221
- policyValidation: LifecyclePolicyValidationSnapshot;
222
- git?: ILifecycleGitService;
223
- }): GovernanceObservationSnapshot => {
224
- const git = params.git ?? new LifecycleGitService();
225
- const { repoRoot, experimentalFeatures, policyValidation } = params;
226
- const rawSdd = process.env.PUMUKI_EXPERIMENTAL_SDD?.trim();
227
- const sddStatus = readSddStatusSafe(repoRoot, git);
228
- const evidence = summarizeEvidence(repoRoot);
229
- const branch = readCurrentBranch(git, repoRoot);
230
- const onProtected = typeof branch === 'string' && DEFAULT_PROTECTED_BRANCHES.has(branch.trim().toLowerCase());
231
- const surface = buildContractSurface(repoRoot);
232
- const tracking = readRepoTrackingState(repoRoot);
233
- const warnAsBlock = truthyEnv(process.env.PUMUKI_ENTERPRISE_STRICT_WARN_AS_BLOCK);
234
-
235
- const attention: string[] = [];
236
- if (evidence.readable === 'invalid') {
237
- attention.push('EVIDENCE_INVALID_OR_CHAIN');
238
- }
239
- if (evidence.readable === 'valid' && evidence.ai_gate_status === 'BLOCKED') {
240
- attention.push('AI_GATE_BLOCKED');
241
- }
242
- if (evidence.readable === 'valid' && evidence.snapshot_outcome === 'WARN') {
243
- attention.push('EVIDENCE_SNAPSHOT_WARN');
244
- }
245
- if (evidence.readable === 'valid' && evidence.snapshot_outcome === 'BLOCK') {
246
- attention.push('EVIDENCE_SNAPSHOT_BLOCK');
247
- }
248
- if (
249
- sddStatus.session.valid !== true &&
250
- (sddStatus.session.active === true || !!sddStatus.session.changeId || sddStatus.session.remainingSeconds === 0)
251
- ) {
252
- attention.push('SDD_SESSION_INVALID_OR_EXPIRED');
253
- }
254
- if (!policyValidation.stages.PRE_WRITE.strict) {
255
- attention.push('POLICY_PRE_WRITE_NOT_STRICT');
256
- }
257
- if (!policyValidation.stages.PRE_COMMIT.strict) {
258
- attention.push('POLICY_PRE_COMMIT_NOT_STRICT');
259
- }
260
- if (!policyValidation.stages.PRE_PUSH.strict) {
261
- attention.push('POLICY_PRE_PUSH_NOT_STRICT');
262
- }
263
- if (!policyValidation.stages.CI.strict) {
264
- attention.push('POLICY_CI_NOT_STRICT');
265
- }
266
- if (onProtected) {
267
- attention.push('GITFLOW_PROTECTED_BRANCH_CONTEXT');
268
- }
269
- if (tracking.conflict) {
270
- attention.push('TRACKING_CANONICAL_SOURCE_CONFLICT');
271
- }
272
- if (tracking.enforced && !tracking.canonical_present) {
273
- attention.push('TRACKING_CANONICAL_FILE_MISSING');
274
- }
275
- if (tracking.enforced && tracking.single_in_progress_valid === false) {
276
- attention.push('TRACKING_CANONICAL_IN_PROGRESS_INVALID');
277
- }
278
-
279
- let governanceEffective: GovernanceObservationSnapshot['governance_effective'] = 'green';
280
- if (
281
- evidence.readable === 'invalid'
282
- || (evidence.readable === 'valid' && evidence.ai_gate_status === 'BLOCKED')
283
- || (evidence.readable === 'valid' && evidence.snapshot_outcome === 'BLOCK')
284
- ) {
285
- governanceEffective = 'blocked';
286
- } else if (attention.length > 0) {
287
- governanceEffective = 'attention';
288
- }
289
-
290
- return {
291
- schema_version: '1',
292
- platform_bundles_effective: resolvePlatformBundlesEffective(repoRoot),
293
- pre_write_effective: {
294
- mode: experimentalFeatures.features.pre_write.mode,
295
- source: experimentalFeatures.features.pre_write.source,
296
- blocking: experimentalFeatures.features.pre_write.blocking,
297
- strict_policy: policyValidation.stages.PRE_WRITE.strict,
298
- },
299
- sdd: {
300
- experimental_raw: rawSdd && rawSdd.length > 0 ? rawSdd : null,
301
- effective_mode: experimentalFeatures.features.sdd.mode,
302
- experimental_source: experimentalFeatures.features.sdd.source,
303
- },
304
- sdd_session: {
305
- active: sddStatus.session.active,
306
- valid: sddStatus.session.valid,
307
- change_id: sddStatus.session.changeId ?? null,
308
- remaining_seconds:
309
- typeof sddStatus.session.remainingSeconds === 'number' ? sddStatus.session.remainingSeconds : null,
310
- },
311
- policy_strict: {
312
- pre_write: policyValidation.stages.PRE_WRITE.strict,
313
- pre_commit: policyValidation.stages.PRE_COMMIT.strict,
314
- pre_push: policyValidation.stages.PRE_PUSH.strict,
315
- ci: policyValidation.stages.CI.strict,
316
- },
317
- enterprise_warn_as_block_env: warnAsBlock,
318
- evidence,
319
- git: {
320
- current_branch: branch,
321
- on_protected_branch_hint: onProtected,
322
- },
323
- contract_surface: surface,
324
- tracking,
325
- attention_codes: attention,
326
- governance_effective: governanceEffective,
327
- agent_bootstrap_hints: buildHints(surface, branch, onProtected, tracking),
328
- };
329
- };
330
-
331
- export const buildGovernanceObservationSummaryLines = (
332
- snapshot: GovernanceObservationSnapshot
333
- ): string[] => {
334
- const lines = [
335
- `Contract: AGENTS=${snapshot.contract_surface.agents_md ? 'yes' : 'no'} skills.lock=${snapshot.contract_surface.skills_lock_json ? 'yes' : 'no'} skills.sources=${snapshot.contract_surface.skills_sources_json ? 'yes' : 'no'} vendor/skills=${snapshot.contract_surface.vendor_skills_dir ? 'yes' : 'no'} adapter=${snapshot.contract_surface.pumuki_adapter_json ? 'yes' : 'no'}`,
336
- `Governance: ${snapshot.governance_effective.toUpperCase()}`,
337
- `Platforms: ${snapshot.platform_bundles_effective?.join(', ') || 'none-detected'}`,
338
- `GitFlow: branch=${snapshot.git.current_branch ?? 'unknown'} protected_hint=${snapshot.git.on_protected_branch_hint ? 'yes' : 'no'}`,
339
- `SDD: env=${snapshot.sdd.experimental_raw ?? '(unset)'} effective=${snapshot.sdd.effective_mode} session_active=${snapshot.sdd_session.active} session_valid=${snapshot.sdd_session.valid} change=${snapshot.sdd_session.change_id ?? 'none'}`,
340
- `Evidence: readable=${snapshot.evidence.readable} stage=${snapshot.evidence.snapshot_stage ?? 'n/a'} outcome=${snapshot.evidence.snapshot_outcome ?? 'n/a'} ai_gate=${snapshot.evidence.ai_gate_status ?? 'n/a'} findings=${snapshot.evidence.findings_count ?? 'n/a'}`,
341
- `Pre-write: mode=${snapshot.pre_write_effective?.mode ?? 'unknown'} blocking=${snapshot.pre_write_effective?.blocking ? 'yes' : 'no'} strict_policy=${snapshot.pre_write_effective?.strict_policy ? 'yes' : 'no'} source=${snapshot.pre_write_effective?.source ?? 'unknown'}`,
342
- ];
343
- if (snapshot.attention_codes.length > 0) {
344
- lines.push(`Attention: ${snapshot.attention_codes.join(', ')}`);
345
- }
346
- if (snapshot.tracking.enforced && snapshot.tracking.single_in_progress_valid === false) {
347
- const actionableContext = formatTrackingActionableContext(snapshot.tracking);
348
- lines.push(
349
- `Tracking: canonical=${snapshot.tracking.canonical_path ?? 'unknown'} in_progress_count=${snapshot.tracking.in_progress_count ?? 'n/a'}${actionableContext ? ` ${actionableContext}` : ''}`
350
- );
351
- }
352
- return lines;
353
- };
354
-
355
- export const printGovernanceObservationHuman = (snapshot: GovernanceObservationSnapshot): void => {
356
- writeInfo('[pumuki] governance truth (S1 / governance console baseline):');
357
- for (const line of buildGovernanceObservationSummaryLines(snapshot)) {
358
- writeInfo(`[pumuki] ${line}`);
359
- }
360
- for (const hint of snapshot.evidence.human_summary_preview) {
361
- writeInfo(`[pumuki] evidence hint: ${hint}`);
362
- }
363
- };
364
-
365
- export const doctorGovernanceIsBlocking = (snapshot: GovernanceObservationSnapshot): boolean =>
366
- snapshot.governance_effective === 'blocked';
367
-
368
- export const doctorGovernanceNeedsAttention = (snapshot: GovernanceObservationSnapshot): boolean =>
369
- snapshot.governance_effective !== 'green';