@lumenflow/cli 5.5.0 → 5.7.14
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/README.md +41 -40
- package/dist/db-journal-recover.js +400 -0
- package/dist/db-journal-recover.js.map +1 -0
- package/dist/docs-sync.js +8 -3
- package/dist/docs-sync.js.map +1 -1
- package/dist/doctor.js +11 -0
- package/dist/doctor.js.map +1 -1
- package/dist/gate-defaults.js +37 -0
- package/dist/gate-defaults.js.map +1 -1
- package/dist/gates/monolithic-file-contention-guard.js +167 -0
- package/dist/gates/monolithic-file-contention-guard.js.map +1 -0
- package/dist/gates/prod-migration-drift.js +207 -0
- package/dist/gates/prod-migration-drift.js.map +1 -0
- package/dist/gates/test-over-deletion-guard.js +280 -0
- package/dist/gates/test-over-deletion-guard.js.map +1 -0
- package/dist/gates-runners.js +44 -3
- package/dist/gates-runners.js.map +1 -1
- package/dist/gates.js +3 -2
- package/dist/gates.js.map +1 -1
- package/dist/hooks/config-resolver.js +16 -1
- package/dist/hooks/config-resolver.js.map +1 -1
- package/dist/hooks/dirty-guard.js +43 -2
- package/dist/hooks/dirty-guard.js.map +1 -1
- package/dist/hooks/git-status-parser.js +22 -8
- package/dist/hooks/git-status-parser.js.map +1 -1
- package/dist/init-templates.js +241 -0
- package/dist/init-templates.js.map +1 -1
- package/dist/init.js +122 -16
- package/dist/init.js.map +1 -1
- package/dist/lumenflow-setup.js +144 -0
- package/dist/lumenflow-setup.js.map +1 -0
- package/dist/lumenflow-upgrade.js +43 -1
- package/dist/lumenflow-upgrade.js.map +1 -1
- package/dist/mem-create.js +10 -1
- package/dist/mem-create.js.map +1 -1
- package/dist/mem-signal.js +21 -4
- package/dist/mem-signal.js.map +1 -1
- package/dist/orchestrate-initiative.js +28 -3
- package/dist/orchestrate-initiative.js.map +1 -1
- package/dist/public-manifest.js +17 -7
- package/dist/public-manifest.js.map +1 -1
- package/dist/release.js +53 -18
- package/dist/release.js.map +1 -1
- package/dist/wu-done-gates.js +13 -9
- package/dist/wu-done-gates.js.map +1 -1
- package/dist/wu-done.js +14 -2
- package/dist/wu-done.js.map +1 -1
- package/dist/wu-edit-operations.js +74 -0
- package/dist/wu-edit-operations.js.map +1 -1
- package/dist/wu-edit-validators.js +58 -0
- package/dist/wu-edit-validators.js.map +1 -1
- package/dist/wu-edit.js +106 -4
- package/dist/wu-edit.js.map +1 -1
- package/dist/wu-prep.js +57 -9
- package/dist/wu-prep.js.map +1 -1
- package/dist/wu-recover.js +6 -0
- package/dist/wu-recover.js.map +1 -1
- package/dist/wu-release.js +120 -2
- package/dist/wu-release.js.map +1 -1
- package/dist/wu-sizing-validation.js +47 -17
- package/dist/wu-sizing-validation.js.map +1 -1
- package/dist/wu-status.js +33 -0
- package/dist/wu-status.js.map +1 -1
- package/package.json +13 -12
- package/packs/agent-runtime/package.json +1 -1
- package/packs/sidekick/package.json +1 -1
- package/packs/software-delivery/package.json +1 -1
- package/templates/core/AGENTS.md.template +67 -3
- package/templates/core/LUMENFLOW.md.template +196 -47
- package/dist/distribution-preflight.js +0 -230
- package/dist/distribution-preflight.js.map +0 -1
- package/packs/agent-runtime/agent-heartbeat.ts +0 -163
- package/packs/agent-runtime/auto-session-integration.ts +0 -888
- package/packs/agent-runtime/capability-factory.ts +0 -104
- package/packs/agent-runtime/constants.ts +0 -21
- package/packs/agent-runtime/delegation-registry-schema.ts +0 -220
- package/packs/agent-runtime/delegation-registry-store.ts +0 -269
- package/packs/agent-runtime/delegation-tree.ts +0 -328
- package/packs/agent-runtime/index.ts +0 -20
- package/packs/agent-runtime/manifest.ts +0 -348
- package/packs/agent-runtime/memory-coordination-contract.ts +0 -86
- package/packs/agent-runtime/orchestration.ts +0 -2027
- package/packs/agent-runtime/pack-registration.ts +0 -110
- package/packs/agent-runtime/policy-factory.ts +0 -165
- package/packs/agent-runtime/remote-controls/index.ts +0 -7
- package/packs/agent-runtime/remote-controls/operations.ts +0 -405
- package/packs/agent-runtime/remote-controls/port.ts +0 -48
- package/packs/agent-runtime/remote-controls/state-store.ts +0 -258
- package/packs/agent-runtime/remote-controls/types.ts +0 -105
- package/packs/agent-runtime/session-schema.ts +0 -467
- package/packs/agent-runtime/tool-impl/agent-turn-tools.ts +0 -793
- package/packs/agent-runtime/tool-impl/index.ts +0 -6
- package/packs/agent-runtime/tool-impl/provider-adapters.ts +0 -1245
- package/packs/agent-runtime/tool-impl/remote-controls.mock.ts +0 -256
- package/packs/agent-runtime/tool-impl/remote-controls.ts +0 -273
- package/packs/agent-runtime/tools/index.ts +0 -4
- package/packs/agent-runtime/tools/types.ts +0 -47
- package/packs/agent-runtime/turn-lifecycle-events.ts +0 -590
- package/packs/agent-runtime/types.ts +0 -128
- package/packs/agent-runtime/vitest.config.ts +0 -11
- package/packs/sidekick/channel-ingress.ts +0 -137
- package/packs/sidekick/constants.ts +0 -10
- package/packs/sidekick/index.ts +0 -8
- package/packs/sidekick/manifest-schema.ts +0 -49
- package/packs/sidekick/manifest.ts +0 -512
- package/packs/sidekick/pack-registration.ts +0 -110
- package/packs/sidekick/policy-factory.ts +0 -38
- package/packs/sidekick/sidekick-events.ts +0 -694
- package/packs/sidekick/src/adapters/cloud-queue.ts +0 -101
- package/packs/sidekick/src/adapters/control-plane-bridge.adapter.ts +0 -386
- package/packs/sidekick/src/adapters/filesystem-bridge.adapter.ts +0 -228
- package/packs/sidekick/src/domain/channel.types.ts +0 -64
- package/packs/sidekick/src/ports/channel-bridge.port.ts +0 -92
- package/packs/sidekick/src/routines/commit.ts +0 -74
- package/packs/sidekick/tool-impl/channel-tools.ts +0 -577
- package/packs/sidekick/tool-impl/channel-transports.ts +0 -75
- package/packs/sidekick/tool-impl/index.ts +0 -29
- package/packs/sidekick/tool-impl/memory-tools.ts +0 -290
- package/packs/sidekick/tool-impl/routine-commit.ts +0 -102
- package/packs/sidekick/tool-impl/routine-tools.ts +0 -440
- package/packs/sidekick/tool-impl/runtime-context.ts +0 -28
- package/packs/sidekick/tool-impl/shared.ts +0 -125
- package/packs/sidekick/tool-impl/storage.ts +0 -325
- package/packs/sidekick/tool-impl/system-tools.ts +0 -160
- package/packs/sidekick/tool-impl/task-tools.ts +0 -506
- package/packs/sidekick/tools/channel-tools.ts +0 -53
- package/packs/sidekick/tools/index.ts +0 -9
- package/packs/sidekick/tools/memory-tools.ts +0 -53
- package/packs/sidekick/tools/routine-tools.ts +0 -53
- package/packs/sidekick/tools/system-tools.ts +0 -47
- package/packs/sidekick/tools/task-tools.ts +0 -61
- package/packs/sidekick/tools/types.ts +0 -57
- package/packs/sidekick/vitest.config.ts +0 -11
- package/packs/software-delivery/constants.ts +0 -10
- package/packs/software-delivery/extensions.ts +0 -140
- package/packs/software-delivery/gate-policies.ts +0 -134
- package/packs/software-delivery/index.ts +0 -8
- package/packs/software-delivery/manifest-schema.ts +0 -268
- package/packs/software-delivery/manifest.ts +0 -657
- package/packs/software-delivery/pack-registration.ts +0 -113
- package/packs/software-delivery/src/commands/index.ts +0 -5
- package/packs/software-delivery/src/config/delivery-review-contract.ts +0 -256
- package/packs/software-delivery/src/config/env-accessors.ts +0 -66
- package/packs/software-delivery/src/config/index.ts +0 -8
- package/packs/software-delivery/src/config/normalize-config-keys.ts +0 -9
- package/packs/software-delivery/src/config/schemas/lumenflow-config-schema-types.ts +0 -460
- package/packs/software-delivery/src/config/workspace-reader.ts +0 -375
- package/packs/software-delivery/src/constants/backlog-patterns.ts +0 -31
- package/packs/software-delivery/src/constants/client-ids.ts +0 -19
- package/packs/software-delivery/src/constants/config-contract.ts +0 -7
- package/packs/software-delivery/src/constants/docs-layout-presets.ts +0 -50
- package/packs/software-delivery/src/constants/duration-constants.ts +0 -20
- package/packs/software-delivery/src/constants/gate-constants.ts +0 -32
- package/packs/software-delivery/src/constants/index.ts +0 -29
- package/packs/software-delivery/src/constants/lock-constants.ts +0 -35
- package/packs/software-delivery/src/constants/object-guards.ts +0 -12
- package/packs/software-delivery/src/constants/section-headings.ts +0 -107
- package/packs/software-delivery/src/constants/wu-cli-constants.ts +0 -500
- package/packs/software-delivery/src/constants/wu-domain-constants.ts +0 -466
- package/packs/software-delivery/src/constants/wu-git-constants.ts +0 -7
- package/packs/software-delivery/src/constants/wu-id-format.ts +0 -327
- package/packs/software-delivery/src/constants/wu-paths-constants.ts +0 -384
- package/packs/software-delivery/src/constants/wu-statuses.ts +0 -287
- package/packs/software-delivery/src/constants/wu-type-helpers.ts +0 -67
- package/packs/software-delivery/src/constants/wu-ui-constants.ts +0 -267
- package/packs/software-delivery/src/constants/wu-validation-constants.ts +0 -73
- package/packs/software-delivery/src/domain/index.ts +0 -5
- package/packs/software-delivery/src/domain/orchestration.constants.ts +0 -166
- package/packs/software-delivery/src/domain/orchestration.schemas.ts +0 -238
- package/packs/software-delivery/src/domain/orchestration.types.ts +0 -176
- package/packs/software-delivery/src/methodology/incremental-test.ts +0 -122
- package/packs/software-delivery/src/methodology/index.ts +0 -6
- package/packs/software-delivery/src/methodology/manual-test-validator.ts +0 -292
- package/packs/software-delivery/src/policy/coverage-gate.ts +0 -270
- package/packs/software-delivery/src/policy/gates-agent-mode.ts +0 -223
- package/packs/software-delivery/src/policy/gates-config-internal.ts +0 -121
- package/packs/software-delivery/src/policy/gates-config.ts +0 -300
- package/packs/software-delivery/src/policy/gates-coverage.ts +0 -356
- package/packs/software-delivery/src/policy/gates-presets.ts +0 -134
- package/packs/software-delivery/src/policy/gates-schemas.ts +0 -173
- package/packs/software-delivery/src/policy/index.ts +0 -22
- package/packs/software-delivery/src/policy/package-manager-resolver.ts +0 -319
- package/packs/software-delivery/src/policy/resolve-policy.ts +0 -601
- package/packs/software-delivery/src/ports/config.ports.ts +0 -90
- package/packs/software-delivery/src/ports/dashboard-renderer.port.ts +0 -125
- package/packs/software-delivery/src/ports/index.ts +0 -10
- package/packs/software-delivery/src/ports/sync-validator.ports.ts +0 -59
- package/packs/software-delivery/src/ports/wu-helpers.ports.ts +0 -168
- package/packs/software-delivery/src/ports/wu-state.ports.ts +0 -241
- package/packs/software-delivery/src/primitives/index.ts +0 -5
- package/packs/software-delivery/src/runtime/index.ts +0 -6
- package/packs/software-delivery/src/runtime/work-classifier.ts +0 -561
- package/packs/software-delivery/src/sandbox/index.ts +0 -10
- package/packs/software-delivery/src/sandbox/sandbox-allowlist.ts +0 -118
- package/packs/software-delivery/src/sandbox/sandbox-backend-linux.ts +0 -88
- package/packs/software-delivery/src/sandbox/sandbox-backend-macos.ts +0 -154
- package/packs/software-delivery/src/sandbox/sandbox-backend-windows.ts +0 -47
- package/packs/software-delivery/src/sandbox/sandbox-profile.ts +0 -153
- package/packs/software-delivery/src/schemas/index.ts +0 -5
- package/packs/software-delivery/src/state/date-utils.ts +0 -158
- package/packs/software-delivery/src/state/index.ts +0 -15
- package/packs/software-delivery/src/state/state-machine.ts +0 -119
- package/packs/software-delivery/src/state/wu-doc-types.ts +0 -51
- package/packs/software-delivery/src/state/wu-paths.ts +0 -381
- package/packs/software-delivery/src/state/wu-schema.ts +0 -1139
- package/packs/software-delivery/src/state/wu-state-schema.ts +0 -255
- package/packs/software-delivery/src/state/wu-yaml.ts +0 -338
- package/packs/software-delivery/tool-impl/agent-tools.ts +0 -263
- package/packs/software-delivery/tool-impl/delegation-tools.ts +0 -66
- package/packs/software-delivery/tool-impl/flow-metrics-tools.ts +0 -219
- package/packs/software-delivery/tool-impl/git-runner.ts +0 -113
- package/packs/software-delivery/tool-impl/git-tools.ts +0 -316
- package/packs/software-delivery/tool-impl/index.ts +0 -15
- package/packs/software-delivery/tool-impl/initiative-orchestration-tools.ts +0 -720
- package/packs/software-delivery/tool-impl/lane-lock.ts +0 -246
- package/packs/software-delivery/tool-impl/memory-tools.ts +0 -470
- package/packs/software-delivery/tool-impl/pending-runtime-tools.ts +0 -21
- package/packs/software-delivery/tool-impl/runtime-cli-adapter.ts +0 -329
- package/packs/software-delivery/tool-impl/runtime-native-tools.ts +0 -687
- package/packs/software-delivery/tool-impl/worker-loader.ts +0 -52
- package/packs/software-delivery/tool-impl/worktree-tools.ts +0 -46
- package/packs/software-delivery/tool-impl/wu-lifecycle-tools.ts +0 -807
- package/packs/software-delivery/tools/delegation-tools.ts +0 -23
- package/packs/software-delivery/tools/git-tools.ts +0 -55
- package/packs/software-delivery/tools/index.ts +0 -8
- package/packs/software-delivery/tools/lane-lock-tool.ts +0 -37
- package/packs/software-delivery/tools/types.ts +0 -71
- package/packs/software-delivery/tools/worktree-tools.ts +0 -49
- package/packs/software-delivery/vitest.config.ts +0 -11
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
// Copyright (c) 2026 Hellmai Ltd
|
|
2
|
+
// SPDX-License-Identifier: LicenseRef-LumenFlow-Proprietary
|
|
3
|
+
/**
|
|
4
|
+
* prod-migration-drift gate (WU-2944, INIT-069 Item 14)
|
|
5
|
+
*
|
|
6
|
+
* Detects when the production database is N+ migrations behind the canonical
|
|
7
|
+
* migrations/ directory on main. Driver: cloud INIT-105 item 14 — prod
|
|
8
|
+
* silently accumulated 7+ migrations of drift before any human noticed; no
|
|
9
|
+
* automated detection caught it.
|
|
10
|
+
*
|
|
11
|
+
* Contract (INIT-068 / WU-2926):
|
|
12
|
+
* - checkApplicability: 'applicable' iff repo has migrations/ AND
|
|
13
|
+
* `software_delivery.gates.overrides.prod-migration-drift.connection_string_env_var`
|
|
14
|
+
* is configured. Default behaviour for repos that have not opted in is
|
|
15
|
+
* 'not-applicable' (clean opt-out — no DB credentials required).
|
|
16
|
+
* - checkPreconditions: 'satisfied' iff the configured env var resolves AND
|
|
17
|
+
* the prod DB is reachable (probe). Otherwise 'missing'.
|
|
18
|
+
* - prereq_strategy: 'auto-skip' (advisory; non-prod / disconnected runs
|
|
19
|
+
* auto-skip rather than block).
|
|
20
|
+
* - skippable_default: true (per-repo override surface to make safety-critical).
|
|
21
|
+
*
|
|
22
|
+
* NOT registered into local `wu:prep` by default — most repos won't have
|
|
23
|
+
* PROD_DATABASE_URL configured locally. The gate auto-skips not-applicable
|
|
24
|
+
* until the override is set, so adding it to the registry is safe.
|
|
25
|
+
*
|
|
26
|
+
* Per-repo override surface:
|
|
27
|
+
* software_delivery.gates.overrides.prod-migration-drift.connection_string_env_var=PROD_DATABASE_URL
|
|
28
|
+
* software_delivery.gates.overrides.prod-migration-drift.threshold=5
|
|
29
|
+
* software_delivery.gates.overrides.prod-migration-drift.skippable=false # tighten
|
|
30
|
+
*
|
|
31
|
+
* @module gates/prod-migration-drift
|
|
32
|
+
*/
|
|
33
|
+
import { existsSync, readdirSync, statSync } from 'node:fs';
|
|
34
|
+
import path from 'node:path';
|
|
35
|
+
import { GATE_NAMES } from '@lumenflow/core/wu-constants';
|
|
36
|
+
import { getGatesSection } from '@lumenflow/packs-software-delivery/policy/gates-config-internal';
|
|
37
|
+
import { makeGateLogger } from '../gates-utils.js';
|
|
38
|
+
/** WU-2944: default drift threshold (failed when drift > threshold). */
|
|
39
|
+
export const DEFAULT_DRIFT_THRESHOLD = 5;
|
|
40
|
+
/** WU-2944: canonical override key for `pnpm config:set`. */
|
|
41
|
+
export const PROD_MIGRATION_DRIFT_OVERRIDE_KEY = 'software_delivery.gates.overrides.prod-migration-drift';
|
|
42
|
+
const MIGRATIONS_DIRNAME = 'migrations';
|
|
43
|
+
const SQL_FILE_PATTERN = /\.sql$/i;
|
|
44
|
+
/**
|
|
45
|
+
* Compute the set of migrations present on main but not applied to prod.
|
|
46
|
+
*
|
|
47
|
+
* Pure function: deterministic, no IO. The "drift" we surface is one-directional
|
|
48
|
+
* (main → prod). Migrations applied on prod that no longer exist on main are
|
|
49
|
+
* a separate concern (rollback / stale snapshot) and not flagged here.
|
|
50
|
+
*/
|
|
51
|
+
export function detectMigrationDrift(input) {
|
|
52
|
+
const applied = new Set(input.appliedMigrations);
|
|
53
|
+
const missingFromProd = input.mainMigrations.filter((m) => !applied.has(m));
|
|
54
|
+
const driftCount = missingFromProd.length;
|
|
55
|
+
return {
|
|
56
|
+
missingFromProd,
|
|
57
|
+
driftCount,
|
|
58
|
+
failed: driftCount > input.threshold,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Read the canonical migrations/ directory and return `.sql` filenames in
|
|
63
|
+
* lexicographic order. Returns `[]` when the directory does not exist (which
|
|
64
|
+
* is also the clean opt-out signal for `checkApplicability`).
|
|
65
|
+
*/
|
|
66
|
+
export function readMigrationsDir(cwd) {
|
|
67
|
+
const dir = path.join(cwd, MIGRATIONS_DIRNAME);
|
|
68
|
+
try {
|
|
69
|
+
if (!existsSync(dir))
|
|
70
|
+
return [];
|
|
71
|
+
if (!statSync(dir).isDirectory())
|
|
72
|
+
return [];
|
|
73
|
+
return readdirSync(dir)
|
|
74
|
+
.filter((f) => SQL_FILE_PATTERN.test(f))
|
|
75
|
+
.sort();
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Read `software_delivery.gates.overrides.prod-migration-drift` from
|
|
83
|
+
* workspace.yaml. Returns undefined when missing or malformed so callers
|
|
84
|
+
* fall through to "not-applicable" (clean opt-out).
|
|
85
|
+
*/
|
|
86
|
+
function readOverride(cwd) {
|
|
87
|
+
try {
|
|
88
|
+
const section = getGatesSection(cwd);
|
|
89
|
+
const raw = section?.overrides?.[GATE_NAMES.PROD_MIGRATION_DRIFT];
|
|
90
|
+
if (raw && typeof raw === 'object' && !Array.isArray(raw)) {
|
|
91
|
+
return raw;
|
|
92
|
+
}
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function resolveThreshold(override) {
|
|
100
|
+
const t = override?.threshold;
|
|
101
|
+
if (typeof t === 'number' && Number.isInteger(t) && t >= 0)
|
|
102
|
+
return t;
|
|
103
|
+
return DEFAULT_DRIFT_THRESHOLD;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Default probe: refuses to attempt a real DB connection from the gate
|
|
107
|
+
* runtime — the gate intentionally ships without a Postgres driver dependency
|
|
108
|
+
* to keep `@lumenflow/cli` slim. Real prod-drift detection happens in CI
|
|
109
|
+
* workflows (or via a project-supplied probe), not on every local `wu:prep`.
|
|
110
|
+
*
|
|
111
|
+
* In CI, the gate is invoked with a probe injected via
|
|
112
|
+
* `ctx.__prodDriftProbe` (see scheduled prod-drift workflow). When no probe
|
|
113
|
+
* is supplied, we report unreachable so the runtime cleanly auto-skips
|
|
114
|
+
* (`prereq_strategy: 'auto-skip'`) rather than fabricating a passing result.
|
|
115
|
+
*/
|
|
116
|
+
const defaultProbe = () => Promise.resolve({ reachable: false, applied: [] });
|
|
117
|
+
function readProbe(ctx) {
|
|
118
|
+
const ext = ctx;
|
|
119
|
+
return ext.__prodDriftProbe ?? defaultProbe;
|
|
120
|
+
}
|
|
121
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
122
|
+
// Gate definition (INIT-068 contract)
|
|
123
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
124
|
+
async function checkApplicability(ctx) {
|
|
125
|
+
const cwd = ctx.cwd ?? process.cwd();
|
|
126
|
+
const override = readOverride(cwd);
|
|
127
|
+
if (!override?.connection_string_env_var)
|
|
128
|
+
return 'not-applicable';
|
|
129
|
+
if (readMigrationsDir(cwd).length === 0)
|
|
130
|
+
return 'not-applicable';
|
|
131
|
+
return 'applicable';
|
|
132
|
+
}
|
|
133
|
+
async function checkPreconditions(ctx) {
|
|
134
|
+
const cwd = ctx.cwd ?? process.cwd();
|
|
135
|
+
const override = readOverride(cwd);
|
|
136
|
+
const envVar = override?.connection_string_env_var;
|
|
137
|
+
if (!envVar)
|
|
138
|
+
return 'missing';
|
|
139
|
+
const connectionString = process.env[envVar];
|
|
140
|
+
if (!connectionString || connectionString.length === 0)
|
|
141
|
+
return 'missing';
|
|
142
|
+
// Probe reachability. Default probe always reports unreachable so missing
|
|
143
|
+
// CI/probe injection auto-skips cleanly under `prereq_strategy: 'auto-skip'`.
|
|
144
|
+
const probe = readProbe(ctx);
|
|
145
|
+
try {
|
|
146
|
+
const result = await probe({ connectionString, cwd });
|
|
147
|
+
return result.reachable ? 'satisfied' : 'missing';
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
return 'missing';
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
async function run(ctx) {
|
|
154
|
+
const start = Date.now();
|
|
155
|
+
const cwd = ctx.cwd ?? process.cwd();
|
|
156
|
+
const logLine = makeGateLogger({
|
|
157
|
+
agentLog: ctx.agentLog ?? null,
|
|
158
|
+
useAgentMode: ctx.useAgentMode,
|
|
159
|
+
});
|
|
160
|
+
logLine('\n> prod-migration-drift check\n');
|
|
161
|
+
const override = readOverride(cwd);
|
|
162
|
+
const envVar = override?.connection_string_env_var;
|
|
163
|
+
if (!envVar) {
|
|
164
|
+
// checkApplicability should have prevented this; defensive auto-pass.
|
|
165
|
+
logLine('ℹ️ No prod-migration-drift override configured; skipping.');
|
|
166
|
+
return { ok: true, duration: Date.now() - start };
|
|
167
|
+
}
|
|
168
|
+
const connectionString = process.env[envVar];
|
|
169
|
+
if (!connectionString) {
|
|
170
|
+
logLine(`ℹ️ Env var ${envVar} not set; skipping (auto-skip).`);
|
|
171
|
+
return { ok: true, duration: Date.now() - start };
|
|
172
|
+
}
|
|
173
|
+
const mainMigrations = readMigrationsDir(cwd);
|
|
174
|
+
const probe = readProbe(ctx);
|
|
175
|
+
const probeResult = await probe({ connectionString, cwd });
|
|
176
|
+
if (!probeResult.reachable) {
|
|
177
|
+
logLine(`⚠️ Prod DB unreachable via ${envVar}; skipping (auto-skip).`);
|
|
178
|
+
return { ok: true, duration: Date.now() - start };
|
|
179
|
+
}
|
|
180
|
+
const threshold = resolveThreshold(override);
|
|
181
|
+
const drift = detectMigrationDrift({
|
|
182
|
+
mainMigrations,
|
|
183
|
+
appliedMigrations: probeResult.applied,
|
|
184
|
+
threshold,
|
|
185
|
+
});
|
|
186
|
+
if (!drift.failed) {
|
|
187
|
+
logLine(`prod-migration-drift check passed (drift=${drift.driftCount}, threshold=${threshold}).`);
|
|
188
|
+
return { ok: true, duration: Date.now() - start };
|
|
189
|
+
}
|
|
190
|
+
logLine(`❌ prod-migration-drift: prod is ${drift.driftCount} migration(s) behind main (threshold=${threshold}).`);
|
|
191
|
+
for (const m of drift.missingFromProd) {
|
|
192
|
+
logLine(` - missing from prod: ${m}`);
|
|
193
|
+
}
|
|
194
|
+
logLine('Hint: review the migration backlog before deploying. Apply migrations to prod incrementally, ' +
|
|
195
|
+
'or split deploys to land smaller migration sequences. To raise/lower the threshold per repo: ' +
|
|
196
|
+
`\`pnpm config:set ${PROD_MIGRATION_DRIFT_OVERRIDE_KEY}.threshold=<n>\`.`);
|
|
197
|
+
return { ok: false, duration: Date.now() - start };
|
|
198
|
+
}
|
|
199
|
+
export const prodMigrationDriftGate = {
|
|
200
|
+
name: GATE_NAMES.PROD_MIGRATION_DRIFT,
|
|
201
|
+
run,
|
|
202
|
+
skippable_default: true,
|
|
203
|
+
prereq_strategy: 'auto-skip',
|
|
204
|
+
checkApplicability,
|
|
205
|
+
checkPreconditions,
|
|
206
|
+
};
|
|
207
|
+
//# sourceMappingURL=prod-migration-drift.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prod-migration-drift.js","sourceRoot":"","sources":["../../src/gates/prod-migration-drift.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
// Copyright (c) 2026 Hellmai Ltd
|
|
2
|
+
// SPDX-License-Identifier: LicenseRef-LumenFlow-Proprietary
|
|
3
|
+
/**
|
|
4
|
+
* test-over-deletion-guard (WU-2943, INIT-069 Item 12)
|
|
5
|
+
*
|
|
6
|
+
* Heuristic gate riding the INIT-068 GateDefinition contract. When `wu:prep`
|
|
7
|
+
* runs, scan deleted `.test.ts` / `.test.tsx` files; parse top-level
|
|
8
|
+
* `describe()` / `describe.each()` string literals; flag any whose described
|
|
9
|
+
* symbols are NOT inside the current WU's `code_paths`.
|
|
10
|
+
*
|
|
11
|
+
* Driver: cloud INIT-105 item 12. WU-1502 dropped a substrate and incidentally
|
|
12
|
+
* over-deleted tests covering other concerns. Codex flagged it during review.
|
|
13
|
+
*
|
|
14
|
+
* Contract (INIT-068 / WU-2926):
|
|
15
|
+
* - checkApplicability: 'applicable' iff diff includes deleted .test.ts(x)
|
|
16
|
+
* - checkPreconditions: 'satisfied' (no env prereqs)
|
|
17
|
+
* - prereq_strategy: 'auto-skip'
|
|
18
|
+
* - skippable_default: true (advisory — agents can --skip-gate when intentional)
|
|
19
|
+
* - Per-repo configurable via software_delivery.gates.overrides.<gate_id>
|
|
20
|
+
*
|
|
21
|
+
* @module gates/test-over-deletion-guard
|
|
22
|
+
*/
|
|
23
|
+
import { existsSync } from 'node:fs';
|
|
24
|
+
import path from 'node:path';
|
|
25
|
+
import { GATE_NAMES } from '@lumenflow/core/wu-constants';
|
|
26
|
+
import { createGitForPath } from '@lumenflow/core/git-adapter';
|
|
27
|
+
import { getCurrentWU } from '@lumenflow/core/telemetry';
|
|
28
|
+
import { createWuPaths } from '@lumenflow/core/wu-paths';
|
|
29
|
+
import { readWURaw } from '@lumenflow/core/wu-yaml';
|
|
30
|
+
import { resolveIntegrationTargetRef } from '@lumenflow/core/integration-target';
|
|
31
|
+
import { makeGateLogger } from '../gates-utils.js';
|
|
32
|
+
const TEST_FILE_PATTERN = /\.test\.tsx?$/;
|
|
33
|
+
// Bounded literal regex: the inner class explicitly excludes the matching
|
|
34
|
+
// quote, the `[^)]{0,256}` chunk is bounded, and the outer engine is RE2-safe
|
|
35
|
+
// (no nested quantifiers over the same captured class). Suppresses
|
|
36
|
+
// security/detect-unsafe-regex which heuristically flags any class.
|
|
37
|
+
// eslint-disable-next-line security/detect-unsafe-regex
|
|
38
|
+
const DESCRIBE_LITERAL_RE = /\bdescribe(?:\.each\s*\([^)]{0,256}\))?\s*\(\s*(['"`])([^'"`]+)\1/g;
|
|
39
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
40
|
+
// Pure helpers (exported for tests)
|
|
41
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
42
|
+
/**
|
|
43
|
+
* Parse top-level describe()/describe.each() string literals from test source.
|
|
44
|
+
*
|
|
45
|
+
* Simple regex parser (no AST) per the WU-2943 spec: "via simple AST or regex".
|
|
46
|
+
* Captures the first string-literal argument of `describe(...)` and
|
|
47
|
+
* `describe.each(...)(...)`.
|
|
48
|
+
*/
|
|
49
|
+
export function parseDescribedSymbols(source) {
|
|
50
|
+
const out = new Set();
|
|
51
|
+
for (const match of source.matchAll(DESCRIBE_LITERAL_RE)) {
|
|
52
|
+
const literal = match[2];
|
|
53
|
+
if (typeof literal === 'string' && literal.length > 0) {
|
|
54
|
+
out.add(literal);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return [...out];
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* For each deleted test file, decide whether its described symbols are
|
|
61
|
+
* "outside" the current WU's code_paths.
|
|
62
|
+
*
|
|
63
|
+
* Heuristic match: a described symbol is considered IN scope if any
|
|
64
|
+
* code_paths entry's basename (without `.ts/.tsx`) equals the symbol, or if
|
|
65
|
+
* any code_paths entry's path normalised contains the symbol as a substring.
|
|
66
|
+
* Otherwise the symbol is flagged.
|
|
67
|
+
*
|
|
68
|
+
* WU-2989: `deletedSourcePaths` (paths of non-test source files removed in
|
|
69
|
+
* the same diff) also contribute scope tokens. This resolves the mutual
|
|
70
|
+
* unsatisfiability between preflight (requires every code_paths entry to
|
|
71
|
+
* exist on disk) and this gate (requires the described symbol's source to
|
|
72
|
+
* be findable from code_paths) when a WU intentionally deletes a source
|
|
73
|
+
* file alongside its co-located test file. The deletion diff itself is the
|
|
74
|
+
* symbol-provenance signal — no WU-spec schema change required.
|
|
75
|
+
*/
|
|
76
|
+
export function detectCrossScopeDeletions(input) {
|
|
77
|
+
const { deletedTestFiles, codePaths, deletedSourcePaths = [] } = input;
|
|
78
|
+
// Scope tokens come from BOTH live code_paths (post-delete) and source
|
|
79
|
+
// paths deleted in the same diff. Both flavours are basename- and
|
|
80
|
+
// substring-matched identically.
|
|
81
|
+
const tokens = buildScopeTokens([...codePaths, ...deletedSourcePaths]);
|
|
82
|
+
const findings = [];
|
|
83
|
+
for (const file of deletedTestFiles) {
|
|
84
|
+
const symbols = parseDescribedSymbols(file.content);
|
|
85
|
+
const unrecognised = symbols.filter((s) => !isSymbolInScope(s, tokens));
|
|
86
|
+
if (unrecognised.length > 0) {
|
|
87
|
+
findings.push({ testFile: file.path, unrecognisedSymbols: unrecognised });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return findings;
|
|
91
|
+
}
|
|
92
|
+
function buildScopeTokens(codePaths) {
|
|
93
|
+
const basenames = new Set();
|
|
94
|
+
const paths = [];
|
|
95
|
+
for (const p of codePaths) {
|
|
96
|
+
if (typeof p !== 'string' || p.length === 0)
|
|
97
|
+
continue;
|
|
98
|
+
const normalised = p.replace(/\\/g, '/').toLowerCase();
|
|
99
|
+
paths.push(normalised);
|
|
100
|
+
const base = path.basename(normalised).replace(/\.[^.]+$/, '');
|
|
101
|
+
if (base.length > 0)
|
|
102
|
+
basenames.add(base);
|
|
103
|
+
// Also kebab → camel/pascal token, so 'payment-gateway' matches 'PaymentGateway'.
|
|
104
|
+
const camel = base.replace(/[-_]([a-z])/g, (_, c) => c.toUpperCase());
|
|
105
|
+
if (camel.length > 0)
|
|
106
|
+
basenames.add(camel.toLowerCase());
|
|
107
|
+
}
|
|
108
|
+
return { basenames, paths };
|
|
109
|
+
}
|
|
110
|
+
function isSymbolInScope(symbol, tokens) {
|
|
111
|
+
const lower = symbol.toLowerCase();
|
|
112
|
+
if (tokens.basenames.has(lower))
|
|
113
|
+
return true;
|
|
114
|
+
for (const p of tokens.paths) {
|
|
115
|
+
if (p.includes(lower))
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
async function getDiffNameStatus(cwd, baseRef) {
|
|
121
|
+
try {
|
|
122
|
+
const git = createGitForPath(cwd);
|
|
123
|
+
const out = await git.raw(['diff', '--name-status', `${baseRef}...HEAD`]);
|
|
124
|
+
return out
|
|
125
|
+
.split('\n')
|
|
126
|
+
.map((line) => line.trim())
|
|
127
|
+
.filter(Boolean)
|
|
128
|
+
.map((line) => {
|
|
129
|
+
const [status, ...rest] = line.split(/\s+/);
|
|
130
|
+
return { status: status ?? '', path: rest.join(' ') };
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return [];
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
async function resolveMergeBase(cwd) {
|
|
138
|
+
// Prefer the configured tracking ref (origin/main or override). Fall back
|
|
139
|
+
// to local `main` when no remote exists (fresh clones, ephemeral test
|
|
140
|
+
// repos). Final fallback: empty repo with no main → null.
|
|
141
|
+
const candidates = [resolveIntegrationTargetRef(), 'main'];
|
|
142
|
+
const seen = new Set();
|
|
143
|
+
const git = createGitForPath(cwd);
|
|
144
|
+
for (const candidate of candidates) {
|
|
145
|
+
if (!candidate || seen.has(candidate))
|
|
146
|
+
continue;
|
|
147
|
+
seen.add(candidate);
|
|
148
|
+
try {
|
|
149
|
+
const base = await git.mergeBase('HEAD', candidate);
|
|
150
|
+
if (base)
|
|
151
|
+
return base;
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
// try next candidate
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
async function getDeletedDiffArtifacts(cwd) {
|
|
160
|
+
const mergeBase = await resolveMergeBase(cwd);
|
|
161
|
+
if (!mergeBase)
|
|
162
|
+
return { testFiles: [], sourcePaths: [] };
|
|
163
|
+
const entries = await getDiffNameStatus(cwd, mergeBase);
|
|
164
|
+
const deleted = entries.filter((e) => e.status.startsWith('D'));
|
|
165
|
+
const deletedTests = deleted.filter((e) => TEST_FILE_PATTERN.test(e.path));
|
|
166
|
+
// WU-2989: non-test deletions provide symbol provenance for the test-scope
|
|
167
|
+
// check. We don't need their content — only their paths/basenames.
|
|
168
|
+
const sourcePaths = deleted.filter((e) => !TEST_FILE_PATTERN.test(e.path)).map((e) => e.path);
|
|
169
|
+
const testFiles = [];
|
|
170
|
+
const git = createGitForPath(cwd);
|
|
171
|
+
for (const entry of deletedTests) {
|
|
172
|
+
// Read content from the merge-base (where the file still existed).
|
|
173
|
+
// showFileAtRef returns '' when the file is unreadable — treat that as
|
|
174
|
+
// a best-effort skip rather than a gate block.
|
|
175
|
+
const content = await git.showFileAtRef(mergeBase, entry.path);
|
|
176
|
+
if (content.length > 0) {
|
|
177
|
+
testFiles.push({ path: entry.path, content });
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return { testFiles, sourcePaths };
|
|
181
|
+
}
|
|
182
|
+
// Back-compat thin wrapper for legacy callers that only need the test files.
|
|
183
|
+
async function getDeletedTestFiles(cwd) {
|
|
184
|
+
return (await getDeletedDiffArtifacts(cwd)).testFiles;
|
|
185
|
+
}
|
|
186
|
+
async function loadCurrentWUCodePathsForCwd(cwd) {
|
|
187
|
+
// Mirror gates-utils.loadCurrentWUCodePaths but rooted at an arbitrary cwd
|
|
188
|
+
// (the gate runs against the worktree, not necessarily process.cwd()).
|
|
189
|
+
let wuId = getCurrentWU();
|
|
190
|
+
if (!wuId) {
|
|
191
|
+
try {
|
|
192
|
+
const branch = await createGitForPath(cwd).getCurrentBranch();
|
|
193
|
+
const m = branch.match(/wu-(\d+)/i);
|
|
194
|
+
if (m)
|
|
195
|
+
wuId = `WU-${m[1]}`;
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
// ignore
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (!wuId)
|
|
202
|
+
return [];
|
|
203
|
+
try {
|
|
204
|
+
const wuPaths = createWuPaths({ projectRoot: cwd });
|
|
205
|
+
const yamlPath = wuPaths.WU(wuId);
|
|
206
|
+
const abs = path.isAbsolute(yamlPath) ? yamlPath : path.join(cwd, yamlPath);
|
|
207
|
+
if (!existsSync(abs))
|
|
208
|
+
return [];
|
|
209
|
+
const doc = readWURaw(abs);
|
|
210
|
+
if (doc && Array.isArray(doc.code_paths)) {
|
|
211
|
+
return doc.code_paths.filter((p) => typeof p === 'string');
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
// ignore — return empty so gate auto-skips not-applicable on missing WU
|
|
216
|
+
}
|
|
217
|
+
return [];
|
|
218
|
+
}
|
|
219
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
220
|
+
// Gate definition (INIT-068 contract)
|
|
221
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
222
|
+
async function checkApplicability(ctx) {
|
|
223
|
+
const cwd = ctx.cwd ?? process.cwd();
|
|
224
|
+
const deleted = await getDeletedTestFiles(cwd);
|
|
225
|
+
return deleted.length > 0 ? 'applicable' : 'not-applicable';
|
|
226
|
+
}
|
|
227
|
+
async function checkPreconditions(_ctx) {
|
|
228
|
+
// No environmental prerequisites: this gate only consults the git diff +
|
|
229
|
+
// the current WU spec, both of which are repo-local.
|
|
230
|
+
return 'satisfied';
|
|
231
|
+
}
|
|
232
|
+
async function run(ctx) {
|
|
233
|
+
const start = Date.now();
|
|
234
|
+
const cwd = ctx.cwd ?? process.cwd();
|
|
235
|
+
const logLine = makeGateLogger({
|
|
236
|
+
agentLog: ctx.agentLog ?? null,
|
|
237
|
+
useAgentMode: ctx.useAgentMode,
|
|
238
|
+
});
|
|
239
|
+
logLine('\n> test-over-deletion check\n');
|
|
240
|
+
const { testFiles: deleted, sourcePaths: deletedSourcePaths } = await getDeletedDiffArtifacts(cwd);
|
|
241
|
+
if (deleted.length === 0) {
|
|
242
|
+
logLine('ℹ️ No deleted test files; nothing to evaluate.');
|
|
243
|
+
return { ok: true, duration: Date.now() - start };
|
|
244
|
+
}
|
|
245
|
+
const codePaths = await loadCurrentWUCodePathsForCwd(cwd);
|
|
246
|
+
if (codePaths.length === 0) {
|
|
247
|
+
// Without a WU spec we can't decide scope. Auto-pass (advisory) and warn.
|
|
248
|
+
logLine('⚠️ No WU code_paths resolvable; skipping describe()-symbol scope check.');
|
|
249
|
+
return { ok: true, duration: Date.now() - start };
|
|
250
|
+
}
|
|
251
|
+
// WU-2989: pass deleted source paths from the same diff so co-deleted
|
|
252
|
+
// source+test pairs satisfy this gate without --skip-gate.
|
|
253
|
+
const findings = detectCrossScopeDeletions({
|
|
254
|
+
deletedTestFiles: deleted,
|
|
255
|
+
codePaths,
|
|
256
|
+
deletedSourcePaths,
|
|
257
|
+
});
|
|
258
|
+
if (findings.length === 0) {
|
|
259
|
+
logLine('test-over-deletion check passed.');
|
|
260
|
+
return { ok: true, duration: Date.now() - start };
|
|
261
|
+
}
|
|
262
|
+
logLine("❌ test-over-deletion: deleted tests reference symbols outside this WU's code_paths.");
|
|
263
|
+
for (const f of findings) {
|
|
264
|
+
logLine(` - ${f.testFile}`);
|
|
265
|
+
for (const s of f.unrecognisedSymbols) {
|
|
266
|
+
logLine(` describe('${s}') — not found in code_paths`);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
logLine('Hint: confirm the deletion is intentional and in scope, or split the unrelated removal into its own WU.');
|
|
270
|
+
return { ok: false, duration: Date.now() - start };
|
|
271
|
+
}
|
|
272
|
+
export const testOverDeletionGate = {
|
|
273
|
+
name: GATE_NAMES.TEST_OVER_DELETION,
|
|
274
|
+
run,
|
|
275
|
+
skippable_default: true,
|
|
276
|
+
prereq_strategy: 'auto-skip',
|
|
277
|
+
checkApplicability,
|
|
278
|
+
checkPreconditions,
|
|
279
|
+
};
|
|
280
|
+
//# sourceMappingURL=test-over-deletion-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-over-deletion-guard.js","sourceRoot":"","sources":["../../src/gates/test-over-deletion-guard.ts"],"names":[],"mappings":"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"}
|
package/dist/gates-runners.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* @module gates-runners
|
|
10
10
|
*/
|
|
11
11
|
import { spawnSync } from 'node:child_process';
|
|
12
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync, writeSync } from 'node:fs';
|
|
12
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, writeSync, } from 'node:fs';
|
|
13
13
|
import { access } from 'node:fs/promises';
|
|
14
14
|
import { createRequire } from 'node:module';
|
|
15
15
|
import path from 'node:path';
|
|
@@ -19,7 +19,7 @@ import { createGitForPath } from '@lumenflow/core/git-adapter';
|
|
|
19
19
|
import { resolveGatesCommands, resolveTestRunner, getGatesSection, resolveScopedUnitTestExecutionPlan, formatScopedUnitTestFallbackMessage, } from '@lumenflow/packs-software-delivery/policy/gates-config';
|
|
20
20
|
import { CoChangeRuleConfigSchema, ConditionalCommandConfigSchema, } from '@lumenflow/core/config-schema';
|
|
21
21
|
import { validateBacklogSync } from '@lumenflow/core/validators/backlog-sync';
|
|
22
|
-
import { DELIVERY_REVIEW_ARTIFACT_DIR, DeliveryReviewResultSchema, calculateDeliveryReviewVerdict, createWuPaths, flattenDeliveryReviewAcceptanceCriteria, isCodePathCoveredByChangedFiles, isDeliveryReviewSkippedForType, isPathLikeTestEntry, readWU, resolveDeliveryReviewRuntimeConfig, validateClaimValidation, } from '@lumenflow/core';
|
|
22
|
+
import { DELIVERY_REVIEW_ARTIFACT_DIR, DeliveryReviewResultSchema, calculateDeliveryReviewVerdict, createWuPaths, flattenDeliveryReviewAcceptanceCriteria, isCodePathCoveredByChangedFiles, isDeliveryReviewSkippedForType, isPathLikeTestEntry, readWU, resolveDeliveryReviewRuntimeConfig, validateClaimValidation, DOCS_LAYOUT_PRESETS, } from '@lumenflow/core';
|
|
23
23
|
import { runSupabaseDocsLinter } from '@lumenflow/core/validators/supabase-docs-linter';
|
|
24
24
|
import { isIntegrationMainBranch, resolveIntegrationTargetRef, } from '@lumenflow/core/integration-target';
|
|
25
25
|
import { PACKAGES, PKG_MANAGER, ESLINT_FLAGS, ESLINT_COMMANDS, ESLINT_DEFAULTS, SCRIPTS, CACHE_STRATEGIES, DIRECTORIES, EXIT_CODES, FILE_SYSTEM, } from '@lumenflow/core/wu-constants';
|
|
@@ -49,6 +49,8 @@ const DEFAULT_MIGRATION_VERIFY_PATTERNS = [
|
|
|
49
49
|
'prisma/migrations/**',
|
|
50
50
|
'drizzle/migrations/**',
|
|
51
51
|
];
|
|
52
|
+
const WU_DB_IMPACTING_FIELD = 'db_impacting';
|
|
53
|
+
const WU_YAML_EXTENSION = '.yaml';
|
|
52
54
|
const DELIVERY_REVIEW_HIGH_RISK_PATH_PATTERN = /(auth|permission|secret|token|credential|billing|payment|migration|schema|policy|sandbox|security|hook)/i;
|
|
53
55
|
const DELIVERY_REVIEW_TEST_PATH_PATTERN = /(^|\/)(__tests__\/|.*\.(test|spec)\.[A-Za-z0-9]+$)/i;
|
|
54
56
|
function asStringArray(value) {
|
|
@@ -785,6 +787,38 @@ export function getMatchingCoChangeRulesForPaths(options) {
|
|
|
785
787
|
export function shouldRunMigrationVerifyForChanges(options) {
|
|
786
788
|
return hasPatternMatch(options.changedFiles, [...DEFAULT_MIGRATION_VERIFY_PATTERNS]);
|
|
787
789
|
}
|
|
790
|
+
async function isDbImpactingDisabledForCurrentWU(cwd) {
|
|
791
|
+
const wuId = (await detectCurrentWUForCwd(cwd)) ?? findSingleWuIdInWuDirectory(cwd);
|
|
792
|
+
if (!wuId) {
|
|
793
|
+
return false;
|
|
794
|
+
}
|
|
795
|
+
try {
|
|
796
|
+
const wuPaths = createWuPaths({ projectRoot: cwd });
|
|
797
|
+
const wuPath = path.join(cwd, wuPaths.WU(wuId));
|
|
798
|
+
const wu = readWU(wuPath, wuId);
|
|
799
|
+
return wu[WU_DB_IMPACTING_FIELD] === false;
|
|
800
|
+
}
|
|
801
|
+
catch {
|
|
802
|
+
return false;
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
function findSingleWuIdInWuDirectory(cwd) {
|
|
806
|
+
try {
|
|
807
|
+
const wuPaths = createWuPaths({ projectRoot: cwd });
|
|
808
|
+
const wuDir = path.join(cwd, wuPaths.WU_DIR());
|
|
809
|
+
if (!existsSync(wuDir)) {
|
|
810
|
+
return null;
|
|
811
|
+
}
|
|
812
|
+
const wuFiles = readdirSync(wuDir).filter((entry) => entry.endsWith(WU_YAML_EXTENSION));
|
|
813
|
+
if (wuFiles.length !== 1) {
|
|
814
|
+
return null;
|
|
815
|
+
}
|
|
816
|
+
return path.basename(wuFiles[0], WU_YAML_EXTENSION);
|
|
817
|
+
}
|
|
818
|
+
catch {
|
|
819
|
+
return null;
|
|
820
|
+
}
|
|
821
|
+
}
|
|
788
822
|
function resolveConditionalCommandGuidanceRefs(commands, projectRoot, logWarning) {
|
|
789
823
|
return commands.map((command) => {
|
|
790
824
|
if (!command.guidance_ref) {
|
|
@@ -928,6 +962,10 @@ export async function runMigrationVerifyGate({ agentLog, useAgentMode, cwd }) {
|
|
|
928
962
|
logLine('ℹ️ No changed files detected; skipping migration verification.');
|
|
929
963
|
return { ok: true, duration: Date.now() - start };
|
|
930
964
|
}
|
|
965
|
+
if (await isDbImpactingDisabledForCurrentWU(effectiveCwd)) {
|
|
966
|
+
logLine('ℹ️ WU declares db_impacting:false; skipping migration verification.');
|
|
967
|
+
return { ok: true, duration: Date.now() - start };
|
|
968
|
+
}
|
|
931
969
|
const conditionalCommands = getResolvedConditionalCommands(effectiveCwd, (msg) => logLine(`⚠️ ${msg}`));
|
|
932
970
|
const matchingConditionalCommands = getMatchingConditionalCommandsForPaths({
|
|
933
971
|
filePaths: changedFiles,
|
|
@@ -1360,6 +1398,9 @@ function readApplicabilityPathsOverride(gateId, cwd) {
|
|
|
1360
1398
|
*/
|
|
1361
1399
|
export async function checkMigrationVerifyApplicability(ctx) {
|
|
1362
1400
|
try {
|
|
1401
|
+
if (await isDbImpactingDisabledForCurrentWU(ctx.cwd ?? process.cwd())) {
|
|
1402
|
+
return 'not-applicable';
|
|
1403
|
+
}
|
|
1363
1404
|
const git = createGitForPath(ctx.cwd ?? process.cwd());
|
|
1364
1405
|
const changed = await getChangedFilesForIncremental({ git });
|
|
1365
1406
|
return shouldRunMigrationVerifyForChanges({ changedFiles: changed })
|
|
@@ -1395,7 +1436,7 @@ export function checkInvariantsPreconditions(ctx) {
|
|
|
1395
1436
|
'invariants.yaml',
|
|
1396
1437
|
'invariants.yml',
|
|
1397
1438
|
'.lumenflow/invariants.yaml',
|
|
1398
|
-
|
|
1439
|
+
`${DOCS_LAYOUT_PRESETS.arc42.operations}/_frameworks/lumenflow/invariants.yaml`,
|
|
1399
1440
|
];
|
|
1400
1441
|
for (const rel of candidates) {
|
|
1401
1442
|
if (existsSync(path.join(cwd, rel)))
|