pumuki 6.3.35 → 6.3.37
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/VERSION +1 -1
- package/docs/CONFIGURATION.md +2 -1
- package/docs/RELEASE_NOTES.md +34 -0
- package/docs/registro-maestro-de-seguimiento.md +2 -2
- package/docs/seguimiento-completo-validacion-ruralgo-03-03-2026.md +55 -4
- package/integrations/evidence/schema.ts +1 -1
- package/integrations/gate/stagePolicies.ts +17 -1
- package/integrations/lifecycle/cli.ts +114 -7
- package/integrations/lifecycle/doctor.ts +6 -0
- package/integrations/lifecycle/policyValidationSnapshot.ts +51 -0
- package/integrations/lifecycle/status.ts +6 -0
- package/integrations/sdd/index.ts +1 -1
- package/integrations/sdd/syncDocs.ts +63 -0
- package/integrations/telemetry/gateTelemetry.ts +2 -2
- package/package.json +1 -1
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
v6.3.
|
|
1
|
+
v6.3.37
|
package/docs/CONFIGURATION.md
CHANGED
|
@@ -191,6 +191,7 @@ Behavior:
|
|
|
191
191
|
- non dry-run: persists `learning.json` deterministically and reports digest/path in output.
|
|
192
192
|
- `rule_updates`: deterministic recommendations derived from evidence/gate signals (`missing`, `invalid`, `blocked`, `allowed`).
|
|
193
193
|
- dedicated command: `pumuki sdd learn --change=<id> [--stage=<stage>] [--task=<task>] [--dry-run] [--json]` generates/persists the same artifact without requiring `sync-docs`.
|
|
194
|
+
- orchestration command: `pumuki sdd auto-sync --change=<id> [--stage=<stage>] [--task=<task>] [--dry-run] [--json]` executes deterministic docs sync plus learning generation in one step.
|
|
194
195
|
|
|
195
196
|
## Gate telemetry export (optional)
|
|
196
197
|
|
|
@@ -228,7 +229,7 @@ Expected JSONL keys for enterprise audit ingestion:
|
|
|
228
229
|
- `schema=telemetry_event_v1` with `schema_version=1.0`
|
|
229
230
|
- `stage`, `gate_outcome`, `severity_counts`
|
|
230
231
|
- `policy.bundle`, `policy.hash`, `policy.version`, `policy.signature`, `policy.policy_source`
|
|
231
|
-
- `policy.validation_status`, `policy.validation_code` (when policy-as-code validation is available)
|
|
232
|
+
- `policy.validation_status`, `policy.validation_code` (when policy-as-code validation is available; status can be `valid|invalid|expired|unknown-source|unsigned`)
|
|
232
233
|
|
|
233
234
|
## Heuristic pilot flag
|
|
234
235
|
|
package/docs/RELEASE_NOTES.md
CHANGED
|
@@ -5,6 +5,40 @@ Detailed commit history remains available through Git history (`git log` / `git
|
|
|
5
5
|
|
|
6
6
|
## 2026-03 (enterprise hardening updates)
|
|
7
7
|
|
|
8
|
+
### 2026-03-04 (v6.3.37)
|
|
9
|
+
|
|
10
|
+
- Policy-as-code enterprise hardening shipped:
|
|
11
|
+
- strict mode now blocks unsigned runtime policy metadata with deterministic code `POLICY_AS_CODE_UNSIGNED`.
|
|
12
|
+
- lifecycle outputs now expose policy validation metadata in `status --json`, `doctor --json`, and `sdd validate --json`.
|
|
13
|
+
- telemetry/evidence contract now supports policy validation status `unsigned`.
|
|
14
|
+
- Traceability:
|
|
15
|
+
- implementation issue: `#606`
|
|
16
|
+
- implementation PR: `#608`
|
|
17
|
+
- tracking sync PR: `#609`
|
|
18
|
+
- Consumer quick verification:
|
|
19
|
+
- `npx --yes --package pumuki@latest pumuki status --json`
|
|
20
|
+
- `npx --yes --package pumuki@latest pumuki doctor --json`
|
|
21
|
+
- `PUMUKI_POLICY_STRICT=1 npx --yes --package pumuki@latest pumuki-pre-commit`
|
|
22
|
+
- expected signal:
|
|
23
|
+
- JSON includes `policyValidation.stages.*.validationCode`.
|
|
24
|
+
- strict mode blocks unsigned contracts with `POLICY_AS_CODE_UNSIGNED`.
|
|
25
|
+
|
|
26
|
+
### 2026-03-04 (v6.3.36)
|
|
27
|
+
|
|
28
|
+
- SDD orchestration hardening shipped:
|
|
29
|
+
- New enterprise command `pumuki sdd auto-sync` with `--change`, optional `--stage/--task`, `--dry-run`, and `--json`.
|
|
30
|
+
- `auto-sync` orchestrates deterministic docs sync plus learning generation in one step.
|
|
31
|
+
- Fail-safe behavior preserved through the existing transactional `sync-docs` path (no partial writes on conflict).
|
|
32
|
+
- Traceability:
|
|
33
|
+
- implementation issue: `#600`
|
|
34
|
+
- implementation PR: `#602`
|
|
35
|
+
- tracking sync PR: `#604`
|
|
36
|
+
- Consumer quick verification:
|
|
37
|
+
- `npx --yes --package pumuki@latest pumuki sdd auto-sync --change=rgo-quickstart-02 --stage=PRE_WRITE --task=P12.F2.T70 --dry-run --json`
|
|
38
|
+
- expected signal:
|
|
39
|
+
- `command=pumuki sdd auto-sync` available in CLI help.
|
|
40
|
+
- JSON payload includes `syncDocs.updated` and `learning.path`.
|
|
41
|
+
|
|
8
42
|
### 2026-03-04 (v6.3.35)
|
|
9
43
|
|
|
10
44
|
- SDD enterprise incremental hardening shipped:
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
## Estado actual
|
|
8
8
|
- Plan activo: `docs/seguimiento-completo-validacion-ruralgo-03-03-2026.md`
|
|
9
9
|
- Estado del plan: EN CURSO
|
|
10
|
-
- Última task cerrada (`✅`): `P12.F2.
|
|
11
|
-
- Task activa (`🚧`): `P12.F2.
|
|
10
|
+
- Última task cerrada (`✅`): `P12.F2.T72` (hardening `policy-as-code` completado: issue `#606` cerrada con PR `#608`).
|
|
11
|
+
- Task activa (`🚧`): `P12.F2.T73` (preparar/publicar release incremental con el hardening de `#606`).
|
|
12
12
|
|
|
13
13
|
## Historial resumido
|
|
14
14
|
- No se mantienen MDs históricos de seguimiento en este repositorio.
|
|
@@ -1942,11 +1942,62 @@ Criterio de salida F5:
|
|
|
1942
1942
|
- `npx --yes tsx@4.21.0 --test integrations/sdd/__tests__/syncDocs.test.ts integrations/lifecycle/__tests__/cli.test.ts` => `38 passed, 0 failed`.
|
|
1943
1943
|
- `npm run -s typecheck` => `exit 0`.
|
|
1944
1944
|
|
|
1945
|
-
-
|
|
1945
|
+
- ✅ `P12.F2.T69` Publicar release `6.3.35` con cierre SDD incremental.
|
|
1946
|
+
- cierre ejecutado:
|
|
1947
|
+
- versión incrementada a `6.3.35` en `package.json` y `package-lock.json`.
|
|
1948
|
+
- release notes actualizadas con entrada `2026-03-04 (v6.3.35)` en `docs/RELEASE_NOTES.md`.
|
|
1949
|
+
- publicación npm ejecutada con éxito (`npm publish --access public`).
|
|
1950
|
+
- propagación validada:
|
|
1951
|
+
- `npm view pumuki dist-tags --json` => `"latest": "6.3.35"`.
|
|
1952
|
+
- smoke `@latest` en carpeta temporal mostrando `pumuki sdd learn ...` en `--help`.
|
|
1953
|
+
- evidencia:
|
|
1954
|
+
- `npx --yes tsx@4.21.0 --test integrations/sdd/__tests__/syncDocs.test.ts integrations/lifecycle/__tests__/cli.test.ts` => `38 passed, 0 failed`.
|
|
1955
|
+
- `npm run -s typecheck` => `exit 0`.
|
|
1956
|
+
- `npm publish --access public` => `+ pumuki@6.3.35`.
|
|
1957
|
+
- `npm view pumuki@6.3.35 version` => `6.3.35`.
|
|
1958
|
+
|
|
1959
|
+
- ✅ `P12.F2.T70` Siguiente SDD pendiente enterprise: comando orquestador `pumuki sdd auto-sync` (`#600`).
|
|
1960
|
+
- cierre ejecutado:
|
|
1961
|
+
- nuevo runtime `runSddAutoSync` en capa SDD para orquestar `sync-docs` + `learning` en un único paso determinista.
|
|
1962
|
+
- CLI ampliada con:
|
|
1963
|
+
- `pumuki sdd auto-sync --change=<id> --stage=<stage> --task=<task-id> [--dry-run] [--json]`.
|
|
1964
|
+
- cobertura de regresión añadida en `syncDocs`/`index`/`cli` y documentación actualizada en `docs/CONFIGURATION.md`.
|
|
1965
|
+
- issue cerrada: `#600`.
|
|
1966
|
+
- PR mergeada: `#602` (`commit 2be34c5`).
|
|
1967
|
+
- evidencia:
|
|
1968
|
+
- `npx --yes tsx@4.21.0 --test integrations/sdd/__tests__/syncDocs.test.ts integrations/sdd/__tests__/index.test.ts integrations/lifecycle/__tests__/cli.test.ts` => `44 passed, 0 failed`.
|
|
1969
|
+
- `npm run -s typecheck` => `exit 0`.
|
|
1970
|
+
|
|
1971
|
+
- ✅ `P12.F2.T71` Publicar release `6.3.36` con `pumuki sdd auto-sync` (`#603`).
|
|
1972
|
+
- cierre ejecutado:
|
|
1973
|
+
- versionado a `6.3.36` en `package.json`, `package-lock.json` y `VERSION`.
|
|
1974
|
+
- release notes actualizadas con entrada `2026-03-04 (v6.3.36)` en `docs/RELEASE_NOTES.md`.
|
|
1975
|
+
- publicación npm ejecutada con éxito (`npm publish --access public`).
|
|
1976
|
+
- propagación validada:
|
|
1977
|
+
- `npm view pumuki dist-tags --json` => `"latest": "6.3.36"`.
|
|
1978
|
+
- smoke `@latest` con `--help` mostrando `pumuki sdd auto-sync ...`.
|
|
1979
|
+
- evidencia:
|
|
1980
|
+
- `npx --yes tsx@4.21.0 --test integrations/sdd/__tests__/syncDocs.test.ts integrations/sdd/__tests__/index.test.ts integrations/lifecycle/__tests__/cli.test.ts` => `44 passed, 0 failed`.
|
|
1981
|
+
- `npm run -s typecheck` => `exit 0`.
|
|
1982
|
+
- `npm publish --access public` => `+ pumuki@6.3.36`.
|
|
1983
|
+
- `npm view pumuki@6.3.36 version` => `6.3.36`.
|
|
1984
|
+
|
|
1985
|
+
- ✅ `P12.F2.T72` Hardening enterprise policy-as-code firmada/versionada (`#606`).
|
|
1986
|
+
- cierre ejecutado:
|
|
1987
|
+
- modo estricto bloquea política no firmada con código determinista `POLICY_AS_CODE_UNSIGNED`.
|
|
1988
|
+
- `status`, `doctor` y `sdd validate --json` exponen metadatos de validación de policy (`source/bundle/hash/version/signature/status/code/strict`).
|
|
1989
|
+
- telemetría/evidence alineadas con estado adicional `unsigned`.
|
|
1990
|
+
- cobertura de regresión añadida para strict unsigned + metadatos lifecycle.
|
|
1991
|
+
- issue cerrada: `#606`.
|
|
1992
|
+
- PR mergeada: `#608` (`commit 881eac8`).
|
|
1993
|
+
- evidencia:
|
|
1994
|
+
- `npx --yes tsx@4.21.0 --test integrations/gate/__tests__/stagePolicies.test.ts integrations/git/__tests__/runPlatformGate.test.ts integrations/lifecycle/__tests__/status.test.ts integrations/lifecycle/__tests__/doctor.test.ts integrations/lifecycle/__tests__/cli.test.ts` => `81 passed, 0 failed`.
|
|
1995
|
+
- `npm run -s typecheck` => `exit 0`.
|
|
1996
|
+
|
|
1997
|
+
- 🚧 `P12.F2.T73` Preparar y publicar release con el hardening de `#606`.
|
|
1946
1998
|
- salida esperada:
|
|
1947
|
-
-
|
|
1948
|
-
-
|
|
1949
|
-
- smoke mínimo con `npx --yes --package pumuki@latest pumuki --help`.
|
|
1999
|
+
- versionar release incremental y notas de publicación.
|
|
2000
|
+
- publicar en npm y verificar propagación `dist-tags`.
|
|
1950
2001
|
|
|
1951
2002
|
Criterio de salida F6:
|
|
1952
2003
|
- veredicto final trazable y cierre administrativo completo.
|
|
@@ -89,7 +89,7 @@ export type RulesetState = {
|
|
|
89
89
|
version?: string;
|
|
90
90
|
signature?: string;
|
|
91
91
|
source?: string;
|
|
92
|
-
validation_status?: 'valid' | 'invalid' | 'expired' | 'unknown-source';
|
|
92
|
+
validation_status?: 'valid' | 'invalid' | 'expired' | 'unknown-source' | 'unsigned';
|
|
93
93
|
validation_code?: string;
|
|
94
94
|
degraded_mode_enabled?: boolean;
|
|
95
95
|
degraded_mode_action?: 'allow' | 'block';
|
|
@@ -49,9 +49,10 @@ export type ResolvedStagePolicy = {
|
|
|
49
49
|
signature?: string;
|
|
50
50
|
policySource?: string;
|
|
51
51
|
validation?: {
|
|
52
|
-
status: 'valid' | 'invalid' | 'expired' | 'unknown-source';
|
|
52
|
+
status: 'valid' | 'invalid' | 'expired' | 'unknown-source' | 'unsigned';
|
|
53
53
|
code:
|
|
54
54
|
| 'POLICY_AS_CODE_VALID'
|
|
55
|
+
| 'POLICY_AS_CODE_UNSIGNED'
|
|
55
56
|
| 'POLICY_AS_CODE_CONTRACT_INVALID'
|
|
56
57
|
| 'POLICY_AS_CODE_CONTRACT_EXPIRED'
|
|
57
58
|
| 'POLICY_AS_CODE_SIGNATURE_MISMATCH'
|
|
@@ -307,6 +308,21 @@ const resolvePolicyAsCodeTraceMetadata = (params: {
|
|
|
307
308
|
const contractPath = join(params.repoRoot, POLICY_AS_CODE_CONTRACT_PATH);
|
|
308
309
|
|
|
309
310
|
if (!existsSync(contractPath)) {
|
|
311
|
+
if (strict) {
|
|
312
|
+
return {
|
|
313
|
+
version: computedVersion,
|
|
314
|
+
signature: computedSignature,
|
|
315
|
+
policySource: 'computed-local',
|
|
316
|
+
validation: {
|
|
317
|
+
status: 'unsigned',
|
|
318
|
+
code: 'POLICY_AS_CODE_UNSIGNED',
|
|
319
|
+
message:
|
|
320
|
+
'Policy-as-code contract is missing; runtime policy metadata is unsigned.',
|
|
321
|
+
strict,
|
|
322
|
+
},
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
|
|
310
326
|
return {
|
|
311
327
|
version: computedVersion,
|
|
312
328
|
signature: computedSignature,
|
|
@@ -10,6 +10,10 @@ import {
|
|
|
10
10
|
import { runLifecycleInstall } from './install';
|
|
11
11
|
import { runLifecycleRemove } from './remove';
|
|
12
12
|
import { readLifecycleStatus } from './status';
|
|
13
|
+
import {
|
|
14
|
+
readLifecyclePolicyValidationSnapshot,
|
|
15
|
+
type LifecyclePolicyValidationSnapshot,
|
|
16
|
+
} from './policyValidationSnapshot';
|
|
13
17
|
import { runLifecycleUninstall } from './uninstall';
|
|
14
18
|
import { runLifecycleUpdate } from './update';
|
|
15
19
|
import { runOpenSpecBootstrap } from './openSpecBootstrap';
|
|
@@ -27,6 +31,7 @@ import {
|
|
|
27
31
|
openSddSession,
|
|
28
32
|
readSddStatus,
|
|
29
33
|
refreshSddSession,
|
|
34
|
+
runSddAutoSync,
|
|
30
35
|
runSddLearn,
|
|
31
36
|
runSddSyncDocs,
|
|
32
37
|
type SddStage,
|
|
@@ -63,7 +68,7 @@ type LifecycleCommand =
|
|
|
63
68
|
| 'adapter'
|
|
64
69
|
| 'analytics';
|
|
65
70
|
|
|
66
|
-
type SddCommand = 'status' | 'validate' | 'session' | 'sync-docs' | 'learn';
|
|
71
|
+
type SddCommand = 'status' | 'validate' | 'session' | 'sync-docs' | 'learn' | 'auto-sync';
|
|
67
72
|
type LoopCommand = 'run' | 'status' | 'stop' | 'resume' | 'list' | 'export';
|
|
68
73
|
type AnalyticsCommand = 'hotspots';
|
|
69
74
|
type AnalyticsHotspotsCommand = 'report' | 'diagnose';
|
|
@@ -95,6 +100,10 @@ type ParsedArgs = {
|
|
|
95
100
|
sddLearnChange?: string;
|
|
96
101
|
sddLearnStage?: SddStage;
|
|
97
102
|
sddLearnTask?: string;
|
|
103
|
+
sddAutoSyncDryRun?: boolean;
|
|
104
|
+
sddAutoSyncChange?: string;
|
|
105
|
+
sddAutoSyncStage?: SddStage;
|
|
106
|
+
sddAutoSyncTask?: string;
|
|
98
107
|
adapterCommand?: 'install';
|
|
99
108
|
adapterAgent?: AdapterAgent;
|
|
100
109
|
adapterDryRun?: boolean;
|
|
@@ -130,6 +139,7 @@ Pumuki lifecycle commands:
|
|
|
130
139
|
pumuki sdd session --close [--json]
|
|
131
140
|
pumuki sdd sync-docs [--change=<change-id>] [--stage=PRE_WRITE|PRE_COMMIT|PRE_PUSH|CI] [--task=<task-id>] [--dry-run] [--json]
|
|
132
141
|
pumuki sdd learn --change=<change-id> [--stage=PRE_WRITE|PRE_COMMIT|PRE_PUSH|CI] [--task=<task-id>] [--dry-run] [--json]
|
|
142
|
+
pumuki sdd auto-sync --change=<change-id> [--stage=PRE_WRITE|PRE_COMMIT|PRE_PUSH|CI] [--task=<task-id>] [--dry-run] [--json]
|
|
133
143
|
`.trim();
|
|
134
144
|
|
|
135
145
|
const LOOP_RUN_POLICY: GatePolicy = {
|
|
@@ -436,6 +446,10 @@ export const parseLifecycleCliArgs = (argv: ReadonlyArray<string>): ParsedArgs =
|
|
|
436
446
|
let sddLearnChange: ParsedArgs['sddLearnChange'];
|
|
437
447
|
let sddLearnStage: ParsedArgs['sddLearnStage'];
|
|
438
448
|
let sddLearnTask: ParsedArgs['sddLearnTask'];
|
|
449
|
+
let sddAutoSyncDryRun = false;
|
|
450
|
+
let sddAutoSyncChange: ParsedArgs['sddAutoSyncChange'];
|
|
451
|
+
let sddAutoSyncStage: ParsedArgs['sddAutoSyncStage'];
|
|
452
|
+
let sddAutoSyncTask: ParsedArgs['sddAutoSyncTask'];
|
|
439
453
|
let adapterCommand: ParsedArgs['adapterCommand'];
|
|
440
454
|
let adapterAgent: ParsedArgs['adapterAgent'];
|
|
441
455
|
let adapterDryRun = false;
|
|
@@ -615,7 +629,8 @@ export const parseLifecycleCliArgs = (argv: ReadonlyArray<string>): ParsedArgs =
|
|
|
615
629
|
subcommandRaw !== 'validate' &&
|
|
616
630
|
subcommandRaw !== 'session' &&
|
|
617
631
|
subcommandRaw !== 'sync-docs' &&
|
|
618
|
-
subcommandRaw !== 'learn'
|
|
632
|
+
subcommandRaw !== 'learn' &&
|
|
633
|
+
subcommandRaw !== 'auto-sync'
|
|
619
634
|
) {
|
|
620
635
|
throw new Error(`Unsupported SDD subcommand "${subcommandRaw}".\n\n${HELP_TEXT}`);
|
|
621
636
|
}
|
|
@@ -635,7 +650,11 @@ export const parseLifecycleCliArgs = (argv: ReadonlyArray<string>): ParsedArgs =
|
|
|
635
650
|
sddLearnDryRun = true;
|
|
636
651
|
continue;
|
|
637
652
|
}
|
|
638
|
-
|
|
653
|
+
if (sddCommand === 'auto-sync') {
|
|
654
|
+
sddAutoSyncDryRun = true;
|
|
655
|
+
continue;
|
|
656
|
+
}
|
|
657
|
+
throw new Error(`--dry-run is only supported with "pumuki sdd sync-docs", "pumuki sdd learn" or "pumuki sdd auto-sync".\n\n${HELP_TEXT}`);
|
|
639
658
|
}
|
|
640
659
|
if (arg.startsWith('--stage=')) {
|
|
641
660
|
if (sddCommand === 'validate') {
|
|
@@ -650,7 +669,11 @@ export const parseLifecycleCliArgs = (argv: ReadonlyArray<string>): ParsedArgs =
|
|
|
650
669
|
sddLearnStage = parseSddStage(arg.slice('--stage='.length));
|
|
651
670
|
continue;
|
|
652
671
|
}
|
|
653
|
-
|
|
672
|
+
if (sddCommand === 'auto-sync') {
|
|
673
|
+
sddAutoSyncStage = parseSddStage(arg.slice('--stage='.length));
|
|
674
|
+
continue;
|
|
675
|
+
}
|
|
676
|
+
throw new Error(`--stage is only supported with "pumuki sdd validate", "pumuki sdd sync-docs", "pumuki sdd learn" or "pumuki sdd auto-sync".\n\n${HELP_TEXT}`);
|
|
654
677
|
}
|
|
655
678
|
if (arg === '--open') {
|
|
656
679
|
if (sddCommand !== 'session') {
|
|
@@ -694,7 +717,15 @@ export const parseLifecycleCliArgs = (argv: ReadonlyArray<string>): ParsedArgs =
|
|
|
694
717
|
sddLearnChange = changeValue;
|
|
695
718
|
continue;
|
|
696
719
|
}
|
|
697
|
-
|
|
720
|
+
if (sddCommand === 'auto-sync') {
|
|
721
|
+
const changeValue = arg.slice('--change='.length).trim();
|
|
722
|
+
if (changeValue.length === 0) {
|
|
723
|
+
throw new Error(`Invalid --change value "${arg}".`);
|
|
724
|
+
}
|
|
725
|
+
sddAutoSyncChange = changeValue;
|
|
726
|
+
continue;
|
|
727
|
+
}
|
|
728
|
+
throw new Error(`--change is only supported with "pumuki sdd session", "pumuki sdd sync-docs", "pumuki sdd learn" or "pumuki sdd auto-sync".\n\n${HELP_TEXT}`);
|
|
698
729
|
}
|
|
699
730
|
if (arg.startsWith('--task=')) {
|
|
700
731
|
if (sddCommand === 'sync-docs') {
|
|
@@ -713,7 +744,15 @@ export const parseLifecycleCliArgs = (argv: ReadonlyArray<string>): ParsedArgs =
|
|
|
713
744
|
sddLearnTask = taskValue;
|
|
714
745
|
continue;
|
|
715
746
|
}
|
|
716
|
-
|
|
747
|
+
if (sddCommand === 'auto-sync') {
|
|
748
|
+
const taskValue = arg.slice('--task='.length).trim();
|
|
749
|
+
if (taskValue.length === 0) {
|
|
750
|
+
throw new Error(`Invalid --task value "${arg}".`);
|
|
751
|
+
}
|
|
752
|
+
sddAutoSyncTask = taskValue;
|
|
753
|
+
continue;
|
|
754
|
+
}
|
|
755
|
+
throw new Error(`--task is only supported with "pumuki sdd sync-docs", "pumuki sdd learn" or "pumuki sdd auto-sync".\n\n${HELP_TEXT}`);
|
|
717
756
|
}
|
|
718
757
|
if (arg.startsWith('--ttl-minutes=')) {
|
|
719
758
|
if (sddCommand !== 'session') {
|
|
@@ -783,6 +822,26 @@ export const parseLifecycleCliArgs = (argv: ReadonlyArray<string>): ParsedArgs =
|
|
|
783
822
|
...(sddLearnTask ? { sddLearnTask } : {}),
|
|
784
823
|
};
|
|
785
824
|
}
|
|
825
|
+
if (sddCommand === 'auto-sync') {
|
|
826
|
+
if (sddSessionAction || sddChangeId || typeof sddTtlMinutes === 'number') {
|
|
827
|
+
throw new Error(
|
|
828
|
+
`"pumuki sdd auto-sync" only supports --change=<change-id> [--stage=PRE_WRITE|PRE_COMMIT|PRE_PUSH|CI] [--task=<task-id>] [--dry-run] [--json].\n\n${HELP_TEXT}`
|
|
829
|
+
);
|
|
830
|
+
}
|
|
831
|
+
if (!sddAutoSyncChange || sddAutoSyncChange.length === 0) {
|
|
832
|
+
throw new Error(`Missing --change=<change-id> for "pumuki sdd auto-sync".\n\n${HELP_TEXT}`);
|
|
833
|
+
}
|
|
834
|
+
return {
|
|
835
|
+
command: commandRaw,
|
|
836
|
+
purgeArtifacts: false,
|
|
837
|
+
json,
|
|
838
|
+
sddCommand,
|
|
839
|
+
sddAutoSyncDryRun,
|
|
840
|
+
sddAutoSyncChange,
|
|
841
|
+
...(sddAutoSyncStage ? { sddAutoSyncStage } : {}),
|
|
842
|
+
...(sddAutoSyncTask ? { sddAutoSyncTask } : {}),
|
|
843
|
+
};
|
|
844
|
+
}
|
|
786
845
|
|
|
787
846
|
if (!sddSessionAction) {
|
|
788
847
|
throw new Error(
|
|
@@ -922,6 +981,11 @@ const printDoctorReport = (
|
|
|
922
981
|
writeInfo(
|
|
923
982
|
`[pumuki] hook pre-push: ${report.hookStatus['pre-push'].managedBlockPresent ? 'managed' : 'missing'}`
|
|
924
983
|
);
|
|
984
|
+
writeInfo(
|
|
985
|
+
`[pumuki] policy-as-code: PRE_COMMIT=${report.policyValidation.stages.PRE_COMMIT.validationCode ?? 'n/a'} strict=${report.policyValidation.stages.PRE_COMMIT.strict ? 'yes' : 'no'} ` +
|
|
986
|
+
`PRE_PUSH=${report.policyValidation.stages.PRE_PUSH.validationCode ?? 'n/a'} strict=${report.policyValidation.stages.PRE_PUSH.strict ? 'yes' : 'no'} ` +
|
|
987
|
+
`CI=${report.policyValidation.stages.CI.validationCode ?? 'n/a'} strict=${report.policyValidation.stages.CI.strict ? 'yes' : 'no'}`
|
|
988
|
+
);
|
|
925
989
|
|
|
926
990
|
for (const issue of report.issues) {
|
|
927
991
|
writeInfo(`[pumuki] ${issue.severity.toUpperCase()}: ${issue.message}`);
|
|
@@ -961,6 +1025,7 @@ const PRE_WRITE_INSTALL_REMEDIATION_COMMAND =
|
|
|
961
1025
|
type PreWriteValidationEnvelope = {
|
|
962
1026
|
sdd: ReturnType<typeof evaluateSddPolicy>;
|
|
963
1027
|
ai_gate: ReturnType<typeof evaluateAiGate>;
|
|
1028
|
+
policy_validation: LifecyclePolicyValidationSnapshot;
|
|
964
1029
|
automation: PreWriteAutomationTrace;
|
|
965
1030
|
bootstrap: {
|
|
966
1031
|
enabled: boolean;
|
|
@@ -1174,12 +1239,14 @@ const resolveAiGateViolationLocation = (code: string) => {
|
|
|
1174
1239
|
const buildPreWriteValidationEnvelope = (
|
|
1175
1240
|
result: ReturnType<typeof evaluateSddPolicy>,
|
|
1176
1241
|
aiGate: ReturnType<typeof evaluateAiGate>,
|
|
1242
|
+
policyValidation: LifecyclePolicyValidationSnapshot,
|
|
1177
1243
|
automation: PreWriteAutomationTrace,
|
|
1178
1244
|
bootstrap: PreWriteOpenSpecBootstrapTrace,
|
|
1179
1245
|
nextAction: PreWriteValidationEnvelope['next_action']
|
|
1180
1246
|
): PreWriteValidationEnvelope => ({
|
|
1181
1247
|
sdd: result,
|
|
1182
1248
|
ai_gate: aiGate,
|
|
1249
|
+
policy_validation: policyValidation,
|
|
1183
1250
|
automation: {
|
|
1184
1251
|
attempted: automation.attempted,
|
|
1185
1252
|
actions: [...automation.actions],
|
|
@@ -1343,6 +1410,11 @@ export const runLifecycleCli = async (
|
|
|
1343
1410
|
writeInfo(
|
|
1344
1411
|
`[pumuki] tracked node_modules paths: ${status.trackedNodeModulesCount}`
|
|
1345
1412
|
);
|
|
1413
|
+
writeInfo(
|
|
1414
|
+
`[pumuki] policy-as-code: PRE_COMMIT=${status.policyValidation.stages.PRE_COMMIT.validationCode ?? 'n/a'} strict=${status.policyValidation.stages.PRE_COMMIT.strict ? 'yes' : 'no'} ` +
|
|
1415
|
+
`PRE_PUSH=${status.policyValidation.stages.PRE_PUSH.validationCode ?? 'n/a'} strict=${status.policyValidation.stages.PRE_PUSH.strict ? 'yes' : 'no'} ` +
|
|
1416
|
+
`CI=${status.policyValidation.stages.CI.validationCode ?? 'n/a'} strict=${status.policyValidation.stages.CI.strict ? 'yes' : 'no'}`
|
|
1417
|
+
);
|
|
1346
1418
|
if (remoteCiDiagnostics) {
|
|
1347
1419
|
printRemoteCiDiagnostics(remoteCiDiagnostics);
|
|
1348
1420
|
}
|
|
@@ -1601,6 +1673,7 @@ export const runLifecycleCli = async (
|
|
|
1601
1673
|
let result = evaluateSddPolicy({
|
|
1602
1674
|
stage: parsed.sddStage ?? 'PRE_COMMIT',
|
|
1603
1675
|
});
|
|
1676
|
+
const policyValidation = readLifecyclePolicyValidationSnapshot(process.cwd());
|
|
1604
1677
|
const preWriteAutoBootstrapEnabled = process.env.PUMUKI_PREWRITE_AUTO_BOOTSTRAP !== '0';
|
|
1605
1678
|
const preWriteBootstrapTrace: PreWriteOpenSpecBootstrapTrace = {
|
|
1606
1679
|
enabled: preWriteAutoBootstrapEnabled,
|
|
@@ -1672,11 +1745,15 @@ export const runLifecycleCli = async (
|
|
|
1672
1745
|
? buildPreWriteValidationEnvelope(
|
|
1673
1746
|
result,
|
|
1674
1747
|
aiGate,
|
|
1748
|
+
policyValidation,
|
|
1675
1749
|
automationTrace,
|
|
1676
1750
|
preWriteBootstrapTrace,
|
|
1677
1751
|
nextAction
|
|
1678
1752
|
)
|
|
1679
|
-
:
|
|
1753
|
+
: {
|
|
1754
|
+
...result,
|
|
1755
|
+
policy_validation: policyValidation,
|
|
1756
|
+
},
|
|
1680
1757
|
null,
|
|
1681
1758
|
2
|
|
1682
1759
|
)
|
|
@@ -1685,6 +1762,11 @@ export const runLifecycleCli = async (
|
|
|
1685
1762
|
writeInfo(
|
|
1686
1763
|
`[pumuki][sdd] stage=${result.stage} allowed=${result.decision.allowed ? 'yes' : 'no'} code=${result.decision.code}`
|
|
1687
1764
|
);
|
|
1765
|
+
writeInfo(
|
|
1766
|
+
`[pumuki][sdd] policy-as-code: PRE_COMMIT=${policyValidation.stages.PRE_COMMIT.validationCode ?? 'n/a'} strict=${policyValidation.stages.PRE_COMMIT.strict ? 'yes' : 'no'} ` +
|
|
1767
|
+
`PRE_PUSH=${policyValidation.stages.PRE_PUSH.validationCode ?? 'n/a'} strict=${policyValidation.stages.PRE_PUSH.strict ? 'yes' : 'no'} ` +
|
|
1768
|
+
`CI=${policyValidation.stages.CI.validationCode ?? 'n/a'} strict=${policyValidation.stages.CI.strict ? 'yes' : 'no'}`
|
|
1769
|
+
);
|
|
1688
1770
|
writeInfo(
|
|
1689
1771
|
withOptionalLocation(
|
|
1690
1772
|
`[pumuki][sdd] ${result.decision.message}`,
|
|
@@ -1824,6 +1906,31 @@ export const runLifecycleCli = async (
|
|
|
1824
1906
|
}
|
|
1825
1907
|
return 0;
|
|
1826
1908
|
}
|
|
1909
|
+
if (parsed.sddCommand === 'auto-sync') {
|
|
1910
|
+
const autoSyncResult = runSddAutoSync({
|
|
1911
|
+
repoRoot: process.cwd(),
|
|
1912
|
+
dryRun: parsed.sddAutoSyncDryRun === true,
|
|
1913
|
+
change: parsed.sddAutoSyncChange,
|
|
1914
|
+
stage: parsed.sddAutoSyncStage,
|
|
1915
|
+
task: parsed.sddAutoSyncTask,
|
|
1916
|
+
});
|
|
1917
|
+
if (parsed.json) {
|
|
1918
|
+
writeInfo(JSON.stringify(autoSyncResult, null, 2));
|
|
1919
|
+
} else {
|
|
1920
|
+
writeInfo(
|
|
1921
|
+
`[pumuki][sdd] auto-sync dry_run=${autoSyncResult.dryRun ? 'yes' : 'no'} change=${autoSyncResult.context.change} stage=${autoSyncResult.context.stage ?? 'none'} task=${autoSyncResult.context.task ?? 'none'} updated=${autoSyncResult.syncDocs.updated ? 'yes' : 'no'} files=${autoSyncResult.syncDocs.files.length} learning_written=${autoSyncResult.learning.written ? 'yes' : 'no'}`
|
|
1922
|
+
);
|
|
1923
|
+
writeInfo(
|
|
1924
|
+
`[pumuki][sdd] learning_path=${autoSyncResult.learning.path} digest=${autoSyncResult.learning.digest}`
|
|
1925
|
+
);
|
|
1926
|
+
for (const file of autoSyncResult.syncDocs.files) {
|
|
1927
|
+
writeInfo(
|
|
1928
|
+
`[pumuki][sdd] file=${file.path} updated=${file.updated ? 'yes' : 'no'} before=${file.beforeDigest} after=${file.afterDigest}`
|
|
1929
|
+
);
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
return 0;
|
|
1933
|
+
}
|
|
1827
1934
|
return 0;
|
|
1828
1935
|
}
|
|
1829
1936
|
case 'adapter': {
|
|
@@ -5,6 +5,10 @@ import { resolvePolicyForStage } from '../gate/stagePolicies';
|
|
|
5
5
|
import { getPumukiHooksStatus } from './hookManager';
|
|
6
6
|
import { LifecycleGitService, type ILifecycleGitService } from './gitService';
|
|
7
7
|
import { getCurrentPumukiVersion } from './packageInfo';
|
|
8
|
+
import {
|
|
9
|
+
readLifecyclePolicyValidationSnapshot,
|
|
10
|
+
type LifecyclePolicyValidationSnapshot,
|
|
11
|
+
} from './policyValidationSnapshot';
|
|
8
12
|
import { readLifecycleState, type LifecycleState } from './state';
|
|
9
13
|
import {
|
|
10
14
|
detectOpenSpecInstallation,
|
|
@@ -71,6 +75,7 @@ export type LifecycleDoctorReport = {
|
|
|
71
75
|
lifecycleState: LifecycleState;
|
|
72
76
|
trackedNodeModulesPaths: ReadonlyArray<string>;
|
|
73
77
|
hookStatus: ReturnType<typeof getPumukiHooksStatus>;
|
|
78
|
+
policyValidation: LifecyclePolicyValidationSnapshot;
|
|
74
79
|
issues: ReadonlyArray<DoctorIssue>;
|
|
75
80
|
deep?: DoctorDeepReport;
|
|
76
81
|
};
|
|
@@ -653,6 +658,7 @@ export const runLifecycleDoctor = (params?: {
|
|
|
653
658
|
lifecycleState,
|
|
654
659
|
trackedNodeModulesPaths,
|
|
655
660
|
hookStatus,
|
|
661
|
+
policyValidation: readLifecyclePolicyValidationSnapshot(repoRoot),
|
|
656
662
|
issues,
|
|
657
663
|
deep,
|
|
658
664
|
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { SkillsStage } from '../config/skillsLock';
|
|
2
|
+
import {
|
|
3
|
+
resolvePolicyForStage,
|
|
4
|
+
type ResolvedStagePolicy,
|
|
5
|
+
} from '../gate/stagePolicies';
|
|
6
|
+
|
|
7
|
+
export type LifecyclePolicyValidationStageSnapshot = {
|
|
8
|
+
source: ResolvedStagePolicy['trace']['source'];
|
|
9
|
+
bundle: string;
|
|
10
|
+
hash: string;
|
|
11
|
+
version: string | null;
|
|
12
|
+
signature: string | null;
|
|
13
|
+
policySource: string | null;
|
|
14
|
+
validationStatus: NonNullable<ResolvedStagePolicy['trace']['validation']>['status'] | null;
|
|
15
|
+
validationCode: NonNullable<ResolvedStagePolicy['trace']['validation']>['code'] | null;
|
|
16
|
+
strict: boolean;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type LifecyclePolicyValidationSnapshot = {
|
|
20
|
+
stages: Record<SkillsStage, LifecyclePolicyValidationStageSnapshot>;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const POLICY_STAGES: ReadonlyArray<SkillsStage> = ['PRE_COMMIT', 'PRE_PUSH', 'CI'];
|
|
24
|
+
|
|
25
|
+
const toStageSnapshot = (
|
|
26
|
+
resolved: ResolvedStagePolicy
|
|
27
|
+
): LifecyclePolicyValidationStageSnapshot => {
|
|
28
|
+
return {
|
|
29
|
+
source: resolved.trace.source,
|
|
30
|
+
bundle: resolved.trace.bundle,
|
|
31
|
+
hash: resolved.trace.hash,
|
|
32
|
+
version: resolved.trace.version ?? null,
|
|
33
|
+
signature: resolved.trace.signature ?? null,
|
|
34
|
+
policySource: resolved.trace.policySource ?? null,
|
|
35
|
+
validationStatus: resolved.trace.validation?.status ?? null,
|
|
36
|
+
validationCode: resolved.trace.validation?.code ?? null,
|
|
37
|
+
strict: resolved.trace.validation?.strict ?? false,
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const readLifecyclePolicyValidationSnapshot = (
|
|
42
|
+
repoRoot: string
|
|
43
|
+
): LifecyclePolicyValidationSnapshot => {
|
|
44
|
+
const resolvedByStage = Object.fromEntries(
|
|
45
|
+
POLICY_STAGES.map((stage) => [stage, toStageSnapshot(resolvePolicyForStage(stage, repoRoot))])
|
|
46
|
+
) as Record<SkillsStage, LifecyclePolicyValidationStageSnapshot>;
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
stages: resolvedByStage,
|
|
50
|
+
};
|
|
51
|
+
};
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { getPumukiHooksStatus } from './hookManager';
|
|
2
2
|
import { LifecycleGitService, type ILifecycleGitService } from './gitService';
|
|
3
3
|
import { getCurrentPumukiVersion } from './packageInfo';
|
|
4
|
+
import {
|
|
5
|
+
readLifecyclePolicyValidationSnapshot,
|
|
6
|
+
type LifecyclePolicyValidationSnapshot,
|
|
7
|
+
} from './policyValidationSnapshot';
|
|
4
8
|
import { readLifecycleState, type LifecycleState } from './state';
|
|
5
9
|
|
|
6
10
|
export type LifecycleStatus = {
|
|
@@ -9,6 +13,7 @@ export type LifecycleStatus = {
|
|
|
9
13
|
lifecycleState: LifecycleState;
|
|
10
14
|
hookStatus: ReturnType<typeof getPumukiHooksStatus>;
|
|
11
15
|
trackedNodeModulesCount: number;
|
|
16
|
+
policyValidation: LifecyclePolicyValidationSnapshot;
|
|
12
17
|
};
|
|
13
18
|
|
|
14
19
|
export const readLifecycleStatus = (params?: {
|
|
@@ -26,5 +31,6 @@ export const readLifecycleStatus = (params?: {
|
|
|
26
31
|
lifecycleState: readLifecycleState(git, repoRoot),
|
|
27
32
|
hookStatus: getPumukiHooksStatus(repoRoot),
|
|
28
33
|
trackedNodeModulesCount,
|
|
34
|
+
policyValidation: readLifecyclePolicyValidationSnapshot(repoRoot),
|
|
29
35
|
};
|
|
30
36
|
};
|
|
@@ -9,4 +9,4 @@ export type {
|
|
|
9
9
|
} from './types';
|
|
10
10
|
export { evaluateSddPolicy, readSddStatus } from './policy';
|
|
11
11
|
export { closeSddSession, openSddSession, readSddSession, refreshSddSession } from './sessionStore';
|
|
12
|
-
export { runSddLearn, runSddSyncDocs } from './syncDocs';
|
|
12
|
+
export { runSddAutoSync, runSddLearn, runSddSyncDocs } from './syncDocs';
|
|
@@ -85,6 +85,22 @@ export type SddLearnResult = {
|
|
|
85
85
|
learning: NonNullable<SddSyncDocsResult['learning']>;
|
|
86
86
|
};
|
|
87
87
|
|
|
88
|
+
export type SddAutoSyncResult = {
|
|
89
|
+
command: 'pumuki sdd auto-sync';
|
|
90
|
+
dryRun: boolean;
|
|
91
|
+
repoRoot: string;
|
|
92
|
+
context: {
|
|
93
|
+
change: string;
|
|
94
|
+
stage: SddStage | null;
|
|
95
|
+
task: string | null;
|
|
96
|
+
};
|
|
97
|
+
syncDocs: {
|
|
98
|
+
updated: boolean;
|
|
99
|
+
files: ReadonlyArray<SddSyncDocsFileResult>;
|
|
100
|
+
};
|
|
101
|
+
learning: NonNullable<SddSyncDocsResult['learning']>;
|
|
102
|
+
};
|
|
103
|
+
|
|
88
104
|
const normalizeSectionBody = (value: string): string => value.trim().replace(/\r\n/g, '\n');
|
|
89
105
|
|
|
90
106
|
const computeDigest = (value: string): string =>
|
|
@@ -433,3 +449,50 @@ export const runSddLearn = (params?: {
|
|
|
433
449
|
learning: result.learning,
|
|
434
450
|
};
|
|
435
451
|
};
|
|
452
|
+
|
|
453
|
+
export const runSddAutoSync = (params?: {
|
|
454
|
+
repoRoot?: string;
|
|
455
|
+
dryRun?: boolean;
|
|
456
|
+
change?: string;
|
|
457
|
+
stage?: SddStage;
|
|
458
|
+
task?: string;
|
|
459
|
+
now?: () => Date;
|
|
460
|
+
evidenceReader?: (repoRoot: string) => EvidenceReadResult;
|
|
461
|
+
targets?: ReadonlyArray<SddSyncDocsTarget>;
|
|
462
|
+
}): SddAutoSyncResult => {
|
|
463
|
+
const change = params?.change?.trim();
|
|
464
|
+
if (!change) {
|
|
465
|
+
throw new Error('[pumuki][sdd] auto-sync requires --change=<change-id>.');
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
const syncResult = runSddSyncDocs({
|
|
469
|
+
repoRoot: params?.repoRoot,
|
|
470
|
+
dryRun: params?.dryRun,
|
|
471
|
+
change,
|
|
472
|
+
stage: params?.stage,
|
|
473
|
+
task: params?.task,
|
|
474
|
+
now: params?.now,
|
|
475
|
+
evidenceReader: params?.evidenceReader,
|
|
476
|
+
targets: params?.targets,
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
if (!syncResult.learning) {
|
|
480
|
+
throw new Error('[pumuki][sdd] auto-sync could not generate learning artifact.');
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
return {
|
|
484
|
+
command: 'pumuki sdd auto-sync',
|
|
485
|
+
dryRun: syncResult.dryRun,
|
|
486
|
+
repoRoot: syncResult.repoRoot,
|
|
487
|
+
context: {
|
|
488
|
+
change,
|
|
489
|
+
stage: syncResult.context.stage,
|
|
490
|
+
task: syncResult.context.task,
|
|
491
|
+
},
|
|
492
|
+
syncDocs: {
|
|
493
|
+
updated: syncResult.updated,
|
|
494
|
+
files: syncResult.files,
|
|
495
|
+
},
|
|
496
|
+
learning: syncResult.learning,
|
|
497
|
+
};
|
|
498
|
+
};
|
|
@@ -25,7 +25,7 @@ type PolicyTrace = ResolvedStagePolicy['trace'] & {
|
|
|
25
25
|
signature?: string;
|
|
26
26
|
policySource?: string;
|
|
27
27
|
validation?: {
|
|
28
|
-
status: 'valid' | 'invalid' | 'expired' | 'unknown-source';
|
|
28
|
+
status: 'valid' | 'invalid' | 'expired' | 'unknown-source' | 'unsigned';
|
|
29
29
|
code: string;
|
|
30
30
|
};
|
|
31
31
|
degraded?: {
|
|
@@ -189,7 +189,7 @@ export type GateTelemetryEventV1 = {
|
|
|
189
189
|
version?: string;
|
|
190
190
|
signature?: string;
|
|
191
191
|
policy_source?: string;
|
|
192
|
-
validation_status?: 'valid' | 'invalid' | 'expired' | 'unknown-source';
|
|
192
|
+
validation_status?: 'valid' | 'invalid' | 'expired' | 'unknown-source' | 'unsigned';
|
|
193
193
|
validation_code?: string;
|
|
194
194
|
degraded_mode_enabled?: boolean;
|
|
195
195
|
degraded_mode_action?: 'allow' | 'block';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pumuki",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.37",
|
|
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": {
|