@codyswann/lisa 3.64.2 → 3.64.3
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/dist/cli/sync-cmd.d.ts +2 -1
- package/dist/cli/sync-cmd.d.ts.map +1 -1
- package/dist/cli/sync-cmd.js +14 -1
- package/dist/cli/sync-cmd.js.map +1 -1
- package/dist/configs/vitest/scratch-setup.d.ts.map +1 -1
- package/dist/configs/vitest/scratch-setup.js +47 -2
- package/dist/configs/vitest/scratch-setup.js.map +1 -1
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +9 -1
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/sync/artifact-sync.d.ts +50 -0
- package/dist/sync/artifact-sync.d.ts.map +1 -0
- package/dist/sync/artifact-sync.js +142 -0
- package/dist/sync/artifact-sync.js.map +1 -0
- package/dist/sync/config-sync.d.ts +2 -16
- package/dist/sync/config-sync.d.ts.map +1 -1
- package/dist/sync/config-sync.js +12 -59
- package/dist/sync/config-sync.js.map +1 -1
- package/dist/sync/stryker-thresholds-ownership.d.ts +111 -0
- package/dist/sync/stryker-thresholds-ownership.d.ts.map +1 -0
- package/dist/sync/stryker-thresholds-ownership.js +227 -0
- package/dist/sync/stryker-thresholds-ownership.js.map +1 -0
- package/package.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/typescript/create-only/stryker.conf.json +1 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { type JsonObject, type JsonValue } from "./json-path.js";
|
|
2
|
+
import type { SyncedSetting } from "./registry.js";
|
|
3
|
+
import { MutationFloorDivergenceError } from "./stryker-thresholds-ownership.js";
|
|
4
|
+
/** What sync did (or would do, in dry-run) for one setting. */
|
|
5
|
+
export type SyncActionKind = "absorbed-artifact" | "populated-default" | "filled-missing" | "default-evolved" | "artifact-synced" | "divergence-honoured";
|
|
6
|
+
/** One reportable sync action. */
|
|
7
|
+
export interface SyncAction {
|
|
8
|
+
/** Config key the action applies to */
|
|
9
|
+
readonly key: string;
|
|
10
|
+
/** What happened */
|
|
11
|
+
readonly kind: SyncActionKind;
|
|
12
|
+
/** Human-readable detail (e.g. which artifact file was involved) */
|
|
13
|
+
readonly detail: string;
|
|
14
|
+
}
|
|
15
|
+
/** Read-only project inputs consumed by the sync planner. */
|
|
16
|
+
export interface SyncReadDependencies {
|
|
17
|
+
readonly readJson: (relativePath: string) => Promise<unknown | null>;
|
|
18
|
+
readonly pathExists: (relativePath: string) => Promise<boolean>;
|
|
19
|
+
}
|
|
20
|
+
/** Accumulated state threaded through the sync pass. */
|
|
21
|
+
export interface SyncState {
|
|
22
|
+
readonly committed: JsonObject;
|
|
23
|
+
readonly actions: readonly SyncAction[];
|
|
24
|
+
readonly artifactWrites: ReadonlyMap<string, JsonObject>;
|
|
25
|
+
/**
|
|
26
|
+
* Refusal recorded when the config-owned mutation floor and the floor on
|
|
27
|
+
* disk disagree. Held rather than thrown at the point of discovery so the
|
|
28
|
+
* pass still completes and nothing at all is persisted.
|
|
29
|
+
*/
|
|
30
|
+
readonly divergence: MutationFloorDivergenceError | undefined;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Apply an entry's optional executable value contract.
|
|
34
|
+
* @param entry - Registry entry owning the value
|
|
35
|
+
* @param value - Untrusted effective value
|
|
36
|
+
* @returns Validated or unchanged JSON value
|
|
37
|
+
*/
|
|
38
|
+
export declare function validateEntryValue(entry: SyncedSetting, value: JsonValue): JsonValue;
|
|
39
|
+
/**
|
|
40
|
+
* Queue artifact writes for one entry (sync direction: config wins). Only
|
|
41
|
+
* files that already exist on disk are written — sync never scaffolds
|
|
42
|
+
* artifacts into stacks that do not use them.
|
|
43
|
+
* @param state - Current sync state
|
|
44
|
+
* @param entry - Registry entry
|
|
45
|
+
* @param local - Local config overlay
|
|
46
|
+
* @param reads - Project input readers
|
|
47
|
+
* @returns Updated state
|
|
48
|
+
*/
|
|
49
|
+
export declare function syncArtifacts(state: SyncState, entry: SyncedSetting, local: JsonObject, reads: SyncReadDependencies): Promise<SyncState>;
|
|
50
|
+
//# sourceMappingURL=artifact-sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifact-sync.d.ts","sourceRoot":"","sources":["../../src/sync/artifact-sync.ts"],"names":[],"mappings":"AAUA,OAAO,EAKL,KAAK,UAAU,EACf,KAAK,SAAS,EACf,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAmB,aAAa,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,EACL,4BAA4B,EAI7B,MAAM,mCAAmC,CAAC;AAE3C,+DAA+D;AAC/D,MAAM,MAAM,cAAc,GACtB,mBAAmB,GACnB,mBAAmB,GACnB,gBAAgB,GAChB,iBAAiB,GACjB,iBAAiB,GACjB,qBAAqB,CAAC;AAE1B,kCAAkC;AAClC,MAAM,WAAW,UAAU;IACzB,uCAAuC;IACvC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,oBAAoB;IACpB,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,oEAAoE;IACpE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,6DAA6D;AAC7D,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,QAAQ,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IACrE,QAAQ,CAAC,UAAU,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACjE;AAED,wDAAwD;AACxD,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,SAAS,UAAU,EAAE,CAAC;IACxC,QAAQ,CAAC,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACzD;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,4BAA4B,GAAG,SAAS,CAAC;CAC/D;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,aAAa,EACpB,KAAK,EAAE,SAAS,GACf,SAAS,CAEX;AAED;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,SAAS,EAChB,KAAK,EAAE,aAAa,EACpB,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,oBAAoB,GAC1B,OAAO,CAAC,SAAS,CAAC,CAqBpB"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The sync direction of the config-sync pass — config wins, written into every
|
|
3
|
+
* artifact file that already exists — plus the state and action vocabulary the
|
|
4
|
+
* pass threads through it.
|
|
5
|
+
*
|
|
6
|
+
* Split out of `config-sync.ts` so the populate direction and the write
|
|
7
|
+
* direction each stay readable on their own.
|
|
8
|
+
* @module sync/artifact-sync
|
|
9
|
+
*/
|
|
10
|
+
import { deepMerge } from "../utils/index.js";
|
|
11
|
+
import { getAtPath, isJsonObject, jsonEquals, setAtPath, } from "./json-path.js";
|
|
12
|
+
import { MutationFloorDivergenceError, describeHonouredMutationFloorDivergence, isMutationFloorBinding, readMutationFloorDeclaration, } from "./stryker-thresholds-ownership.js";
|
|
13
|
+
/**
|
|
14
|
+
* Apply an entry's optional executable value contract.
|
|
15
|
+
* @param entry - Registry entry owning the value
|
|
16
|
+
* @param value - Untrusted effective value
|
|
17
|
+
* @returns Validated or unchanged JSON value
|
|
18
|
+
*/
|
|
19
|
+
export function validateEntryValue(entry, value) {
|
|
20
|
+
return entry.validate?.(value) ?? value;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Queue artifact writes for one entry (sync direction: config wins). Only
|
|
24
|
+
* files that already exist on disk are written — sync never scaffolds
|
|
25
|
+
* artifacts into stacks that do not use them.
|
|
26
|
+
* @param state - Current sync state
|
|
27
|
+
* @param entry - Registry entry
|
|
28
|
+
* @param local - Local config overlay
|
|
29
|
+
* @param reads - Project input readers
|
|
30
|
+
* @returns Updated state
|
|
31
|
+
*/
|
|
32
|
+
export async function syncArtifacts(state, entry, local, reads) {
|
|
33
|
+
const bindings = entry.artifacts ?? [];
|
|
34
|
+
if (bindings.length === 0) {
|
|
35
|
+
return state;
|
|
36
|
+
}
|
|
37
|
+
const effective = getAtPath(deepMerge(state.committed, local), entry.key);
|
|
38
|
+
if (effective === undefined) {
|
|
39
|
+
return state;
|
|
40
|
+
}
|
|
41
|
+
const validatedEffective = validateEntryValue(entry, effective);
|
|
42
|
+
return bindings.reduce(async (statePromise, binding) => syncOneArtifact(await statePromise, entry, binding, validatedEffective, reads), Promise.resolve(state));
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Queue — or refuse — the write for one artifact binding.
|
|
46
|
+
*
|
|
47
|
+
* The refusal arm is narrow on purpose. Five of the six sync artifacts are
|
|
48
|
+
* written wholesale (`pointer: ""`), so a hand-edit to one is obviously wrong
|
|
49
|
+
* and overwriting it is the point. `stryker.conf.json` is the lone partial
|
|
50
|
+
* binding — the one file where editing its other keys is correct — and it is
|
|
51
|
+
* the one that drifted: config declared a mutation floor of 60 while 32 was
|
|
52
|
+
* enforced and ~58 was measured, so a silent write would have reddened the
|
|
53
|
+
* mutation gate on code that had not changed (CodySwannGT/lisa#2968).
|
|
54
|
+
* @param current - Current sync state
|
|
55
|
+
* @param entry - Registry entry being synced
|
|
56
|
+
* @param binding - Artifact binding to write
|
|
57
|
+
* @param validatedEffective - Effective config value for the entry
|
|
58
|
+
* @param reads - Project input readers
|
|
59
|
+
* @returns Updated state
|
|
60
|
+
*/
|
|
61
|
+
async function syncOneArtifact(current, entry, binding, validatedEffective, reads) {
|
|
62
|
+
const pending = current.artifactWrites.get(binding.file);
|
|
63
|
+
const parsed = pending ?? (await reads.readJson(binding.file)) ?? undefined;
|
|
64
|
+
if (parsed === undefined && !(await reads.pathExists(binding.file))) {
|
|
65
|
+
return current;
|
|
66
|
+
}
|
|
67
|
+
const fileObject = isJsonObject(parsed) ? parsed : {};
|
|
68
|
+
const onDisk = getAtPath(fileObject, binding.pointer);
|
|
69
|
+
if (jsonEquals(onDisk, validatedEffective)) {
|
|
70
|
+
return current;
|
|
71
|
+
}
|
|
72
|
+
if (onDisk !== undefined &&
|
|
73
|
+
isMutationFloorBinding(entry.key, binding.file, binding.pointer)) {
|
|
74
|
+
return mutationFloorDisagreement(current, entry, fileObject, onDisk, validatedEffective);
|
|
75
|
+
}
|
|
76
|
+
return queueWrite(current, entry, binding, fileObject, validatedEffective);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Resolve the two mutation floors disagreeing: honour a recorded divergence,
|
|
80
|
+
* refuse an unrecorded one.
|
|
81
|
+
*
|
|
82
|
+
* Blocking the deliberate case too would make this guard the thing that gets
|
|
83
|
+
* deleted the first time it stands between someone and routine work, so a
|
|
84
|
+
* `_thresholdsDivergence` block that still records BOTH live numbers and a
|
|
85
|
+
* reason lets the pass continue — loudly. Anything else is a managed value
|
|
86
|
+
* someone edited with nothing saying why, which is the landmine.
|
|
87
|
+
* @param current - Current sync state
|
|
88
|
+
* @param entry - Registry entry being synced
|
|
89
|
+
* @param fileObject - Parsed artifact content
|
|
90
|
+
* @param onDisk - Floor the artifact currently enforces
|
|
91
|
+
* @param validatedEffective - Floor the config declares
|
|
92
|
+
* @returns Updated state
|
|
93
|
+
*/
|
|
94
|
+
function mutationFloorDisagreement(current, entry, fileObject, onDisk, validatedEffective) {
|
|
95
|
+
const declaration = readMutationFloorDeclaration(fileObject, onDisk, validatedEffective);
|
|
96
|
+
if (declaration === undefined) {
|
|
97
|
+
return {
|
|
98
|
+
...current,
|
|
99
|
+
divergence: current.divergence ??
|
|
100
|
+
new MutationFloorDivergenceError(validatedEffective, onDisk),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
...current,
|
|
105
|
+
actions: [
|
|
106
|
+
...current.actions,
|
|
107
|
+
{
|
|
108
|
+
key: entry.key,
|
|
109
|
+
kind: "divergence-honoured",
|
|
110
|
+
detail: describeHonouredMutationFloorDivergence(declaration),
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Record one artifact write and its reportable action.
|
|
117
|
+
* @param current - Current sync state
|
|
118
|
+
* @param entry - Registry entry being synced
|
|
119
|
+
* @param binding - Artifact binding to write
|
|
120
|
+
* @param fileObject - Parsed artifact content
|
|
121
|
+
* @param validatedEffective - Effective config value for the entry
|
|
122
|
+
* @returns Updated state
|
|
123
|
+
*/
|
|
124
|
+
function queueWrite(current, entry, binding, fileObject, validatedEffective) {
|
|
125
|
+
const updated = setAtPath(fileObject, binding.pointer, validatedEffective);
|
|
126
|
+
return {
|
|
127
|
+
...current,
|
|
128
|
+
artifactWrites: new Map([
|
|
129
|
+
...current.artifactWrites,
|
|
130
|
+
[binding.file, updated],
|
|
131
|
+
]),
|
|
132
|
+
actions: [
|
|
133
|
+
...current.actions,
|
|
134
|
+
{
|
|
135
|
+
key: entry.key,
|
|
136
|
+
kind: "artifact-synced",
|
|
137
|
+
detail: `${binding.file} updated from config`,
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=artifact-sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifact-sync.js","sourceRoot":"","sources":["../../src/sync/artifact-sync.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,14 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
export type
|
|
3
|
-
/** One reportable sync action. */
|
|
4
|
-
export interface SyncAction {
|
|
5
|
-
/** Config key the action applies to */
|
|
6
|
-
readonly key: string;
|
|
7
|
-
/** What happened */
|
|
8
|
-
readonly kind: SyncActionKind;
|
|
9
|
-
/** Human-readable detail (e.g. which artifact file was involved) */
|
|
10
|
-
readonly detail: string;
|
|
11
|
-
}
|
|
1
|
+
import { type SyncAction, type SyncActionKind, type SyncReadDependencies } from "./artifact-sync.js";
|
|
2
|
+
export type { SyncAction, SyncActionKind, SyncReadDependencies };
|
|
12
3
|
/** Result of one sync run. */
|
|
13
4
|
export interface SyncReport {
|
|
14
5
|
/** Actions taken (or planned, when dryRun) — excludes unchanged entries */
|
|
@@ -28,11 +19,6 @@ export interface SyncOptions {
|
|
|
28
19
|
/** Optional confined readers used by side-effect-free callers. */
|
|
29
20
|
readonly reads?: SyncReadDependencies;
|
|
30
21
|
}
|
|
31
|
-
/** Read-only project inputs consumed by the sync planner. */
|
|
32
|
-
export interface SyncReadDependencies {
|
|
33
|
-
readonly readJson: (relativePath: string) => Promise<unknown | null>;
|
|
34
|
-
readonly pathExists: (relativePath: string) => Promise<boolean>;
|
|
35
|
-
}
|
|
36
22
|
/**
|
|
37
23
|
* Run one populate-and-sync pass over a project.
|
|
38
24
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-sync.d.ts","sourceRoot":"","sources":["../../src/sync/config-sync.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"config-sync.d.ts","sourceRoot":"","sources":["../../src/sync/config-sync.ts"],"names":[],"mappings":"AAiCA,OAAO,EAGL,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,oBAAoB,EAE1B,MAAM,oBAAoB,CAAC;AAK5B,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,oBAAoB,EAAE,CAAC;AAEjE,8BAA8B;AAC9B,MAAM,WAAW,UAAU;IACzB,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,EAAE,SAAS,UAAU,EAAE,CAAC;IACxC,6DAA6D;IAC7D,QAAQ,CAAC,eAAe,EAAE,SAAS;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACxE,wDAAwD;IACxD,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC1B;AAED,yCAAyC;AACzC,MAAM,WAAW,WAAW;IAC1B,sCAAsC;IACtC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,kEAAkE;IAClE,QAAQ,CAAC,KAAK,CAAC,EAAE,oBAAoB,CAAC;CACvC;AAsMD;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,UAAU,CAAC,CAoDrB"}
|
package/dist/sync/config-sync.js
CHANGED
|
@@ -19,6 +19,7 @@ import * as path from "node:path";
|
|
|
19
19
|
import { deepMerge, readJsonOrNull, writeJson } from "../utils/index.js";
|
|
20
20
|
import { fillMissing, getAtPath, isJsonObject, jsonEquals, setAtPath, } from "./json-path.js";
|
|
21
21
|
import { REQUIRED_KEYS, SYNC_REGISTRY, } from "./registry.js";
|
|
22
|
+
import { syncArtifacts, validateEntryValue, } from "./artifact-sync.js";
|
|
22
23
|
import { aliasCompatibleDefault, hasLegacyAlias } from "./legacy-aliases.js";
|
|
23
24
|
import { applyLegacyMonitorThresholdMigration } from "./legacy-monitor-thresholds.js";
|
|
24
25
|
import { recordedPopulation, recordPopulation } from "./sync-population.js";
|
|
@@ -150,65 +151,6 @@ function populateEntry(state, entry, merged, local, artifactValue) {
|
|
|
150
151
|
}
|
|
151
152
|
return stateAfterMigration;
|
|
152
153
|
}
|
|
153
|
-
/**
|
|
154
|
-
* Queue artifact writes for one entry (sync direction: config wins). Only
|
|
155
|
-
* files that already exist on disk are written — sync never scaffolds
|
|
156
|
-
* artifacts into stacks that do not use them.
|
|
157
|
-
* @param state - Current sync state
|
|
158
|
-
* @param entry - Registry entry
|
|
159
|
-
* @param local - Local config overlay
|
|
160
|
-
* @param reads - Project input readers
|
|
161
|
-
* @returns Updated state
|
|
162
|
-
*/
|
|
163
|
-
async function syncArtifacts(state, entry, local, reads) {
|
|
164
|
-
const bindings = entry.artifacts ?? [];
|
|
165
|
-
if (bindings.length === 0) {
|
|
166
|
-
return state;
|
|
167
|
-
}
|
|
168
|
-
const effective = getAtPath(deepMerge(state.committed, local), entry.key);
|
|
169
|
-
if (effective === undefined) {
|
|
170
|
-
return state;
|
|
171
|
-
}
|
|
172
|
-
const validatedEffective = validateEntryValue(entry, effective);
|
|
173
|
-
return bindings.reduce(async (statePromise, binding) => {
|
|
174
|
-
const current = await statePromise;
|
|
175
|
-
const pending = current.artifactWrites.get(binding.file);
|
|
176
|
-
const parsed = pending ?? (await reads.readJson(binding.file)) ?? undefined;
|
|
177
|
-
if (parsed === undefined && !(await reads.pathExists(binding.file))) {
|
|
178
|
-
return current;
|
|
179
|
-
}
|
|
180
|
-
const fileObject = isJsonObject(parsed) ? parsed : {};
|
|
181
|
-
if (jsonEquals(getAtPath(fileObject, binding.pointer), validatedEffective)) {
|
|
182
|
-
return current;
|
|
183
|
-
}
|
|
184
|
-
const updated = setAtPath(fileObject, binding.pointer, validatedEffective);
|
|
185
|
-
const writes = new Map([
|
|
186
|
-
...current.artifactWrites,
|
|
187
|
-
[binding.file, updated],
|
|
188
|
-
]);
|
|
189
|
-
return {
|
|
190
|
-
...current,
|
|
191
|
-
artifactWrites: writes,
|
|
192
|
-
actions: [
|
|
193
|
-
...current.actions,
|
|
194
|
-
{
|
|
195
|
-
key: entry.key,
|
|
196
|
-
kind: "artifact-synced",
|
|
197
|
-
detail: `${binding.file} updated from config`,
|
|
198
|
-
},
|
|
199
|
-
],
|
|
200
|
-
};
|
|
201
|
-
}, Promise.resolve(state));
|
|
202
|
-
}
|
|
203
|
-
/**
|
|
204
|
-
* Apply an entry's optional executable value contract.
|
|
205
|
-
* @param entry - Registry entry owning the value
|
|
206
|
-
* @param value - Untrusted effective value
|
|
207
|
-
* @returns Validated or unchanged JSON value
|
|
208
|
-
*/
|
|
209
|
-
function validateEntryValue(entry, value) {
|
|
210
|
-
return entry.validate?.(value) ?? value;
|
|
211
|
-
}
|
|
212
154
|
/**
|
|
213
155
|
* Report required keys that are missing from the merged config.
|
|
214
156
|
* @param merged - Merged config view
|
|
@@ -238,6 +180,7 @@ export async function runConfigSync(destDir, options = {}) {
|
|
|
238
180
|
committed,
|
|
239
181
|
actions: [],
|
|
240
182
|
artifactWrites: new Map(),
|
|
183
|
+
divergence: undefined,
|
|
241
184
|
};
|
|
242
185
|
const finalState = await SYNC_REGISTRY.reduce(async (statePromise, entry) => {
|
|
243
186
|
const state = await statePromise;
|
|
@@ -249,6 +192,16 @@ export async function runConfigSync(destDir, options = {}) {
|
|
|
249
192
|
const populated = populateEntry(state, entry, merged, local, artifactValue);
|
|
250
193
|
return syncArtifacts(populated, entry, local, reads);
|
|
251
194
|
}, Promise.resolve(initial));
|
|
195
|
+
// Fail the whole pass, dry run included: a dry run that reported success
|
|
196
|
+
// while the two mutation floors disagree would be the same silence this
|
|
197
|
+
// guard exists to remove.
|
|
198
|
+
// Fail the whole pass, dry run included: a dry run that reported success
|
|
199
|
+
// while the two mutation floors disagree would be the same silence this
|
|
200
|
+
// guard exists to remove. A divergence the file itself records is handled
|
|
201
|
+
// in artifact-sync.ts and never reaches here.
|
|
202
|
+
if (finalState.divergence !== undefined) {
|
|
203
|
+
throw finalState.divergence;
|
|
204
|
+
}
|
|
252
205
|
const mergedFinal = deepMerge(finalState.committed, local);
|
|
253
206
|
if (options.dryRun !== true) {
|
|
254
207
|
await persistState(destDir, committed, finalState);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-sync.js","sourceRoot":"","sources":["../../src/sync/config-sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EACL,WAAW,EACX,SAAS,EACT,YAAY,EACZ,UAAU,EACV,SAAS,GAGV,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,aAAa,EACb,aAAa,GAEd,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,sBAAsB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EAAE,oCAAoC,EAAE,MAAM,gCAAgC,CAAC;AACtF,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"config-sync.js","sourceRoot":"","sources":["../../src/sync/config-sync.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ownership contract for the one mutation-score floor that lives in two files.
|
|
3
|
+
*
|
|
4
|
+
* `.lisa.config.json` (`quality.mutation.strykerThresholds`) owns the value;
|
|
5
|
+
* `stryker.conf.json` (`thresholds`) is where Stryker reads it. Every other
|
|
6
|
+
* sync artifact is written wholesale (`pointer: ""`), so a hand-edit to one of
|
|
7
|
+
* those is obviously wrong and gets overwritten. `stryker.conf.json` is the
|
|
8
|
+
* only partial binding in the registry: editing 17 of its 18 keys is correct
|
|
9
|
+
* and editing the 18th is silently wrong — which is exactly the file that
|
|
10
|
+
* drifted (CodySwannGT/lisa#2968).
|
|
11
|
+
*
|
|
12
|
+
* This module is deliberately named after that single instance rather than
|
|
13
|
+
* generalised. A framework for a one-instance problem is a bigger mistake than
|
|
14
|
+
* the instance; if a second partial binding is ever added, the registry test
|
|
15
|
+
* that pins "exactly one partial binding" fails and forces the decision then.
|
|
16
|
+
*
|
|
17
|
+
* Two controls live here:
|
|
18
|
+
*
|
|
19
|
+
* 1. {@link describeMutationFloorDivergence} — the message the config sync
|
|
20
|
+
* fails with when the two floors disagree with nothing recording why. Sync
|
|
21
|
+
* refuses in BOTH directions: writing the config value can raise the floor
|
|
22
|
+
* above the score the codebase actually measures (reddening the mutation
|
|
23
|
+
* gate on unchanged code), and absorbing the file value would silently
|
|
24
|
+
* lower a declared floor.
|
|
25
|
+
*
|
|
26
|
+
* A divergence someone deliberately chose is a different case, and blocking
|
|
27
|
+
* it would make the guard the thing that gets deleted. When
|
|
28
|
+
* `_thresholdsDivergence` records BOTH live numbers and a reason, sync
|
|
29
|
+
* proceeds, leaves the enforced floor alone, and reports that it is
|
|
30
|
+
* honouring a recorded divergence — visible, not blocking. A declaration
|
|
31
|
+
* that has gone stale against either live value is no longer a record of a
|
|
32
|
+
* decision, so it stops exempting anything.
|
|
33
|
+
* 2. {@link checkMutationFloorOwnership} — the executable form of the
|
|
34
|
+
* ownership statement carried in `stryker.conf.json` itself, so that a
|
|
35
|
+
* hand-edit of `thresholds` fails a test instead of relying on a comment
|
|
36
|
+
* being read.
|
|
37
|
+
* @module sync/stryker-thresholds-ownership
|
|
38
|
+
*/
|
|
39
|
+
import { type JsonValue } from "./json-path.js";
|
|
40
|
+
/** Config key that owns the mutation-score floor. */
|
|
41
|
+
export declare const MUTATION_FLOOR_CONFIG_KEY = "quality.mutation.strykerThresholds";
|
|
42
|
+
/** Config file that owns the mutation-score floor. */
|
|
43
|
+
export declare const MUTATION_FLOOR_CONFIG_FILE = ".lisa.config.json";
|
|
44
|
+
/** Artifact file that mirrors the mutation-score floor. */
|
|
45
|
+
export declare const MUTATION_FLOOR_ARTIFACT_FILE = "stryker.conf.json";
|
|
46
|
+
/** Dot path of the mirrored floor inside the artifact file. */
|
|
47
|
+
export declare const MUTATION_FLOOR_ARTIFACT_POINTER = "thresholds";
|
|
48
|
+
/** Artifact key stating, in the file itself, who owns `thresholds`. */
|
|
49
|
+
export declare const MUTATION_FLOOR_OWNER_FIELD = "_thresholdsOwner";
|
|
50
|
+
/** Artifact key declaring a deliberate, temporary, fully-recorded divergence. */
|
|
51
|
+
export declare const MUTATION_FLOOR_DIVERGENCE_FIELD = "_thresholdsDivergence";
|
|
52
|
+
/**
|
|
53
|
+
* Whether a registry binding is the single config-owned mutation floor.
|
|
54
|
+
* @param key - Registry entry key being synced
|
|
55
|
+
* @param file - Artifact file the binding writes
|
|
56
|
+
* @param pointer - Dot path inside that file
|
|
57
|
+
* @returns True for the one guarded binding
|
|
58
|
+
*/
|
|
59
|
+
export declare function isMutationFloorBinding(key: string, file: string, pointer: string): boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Build the operator-readable divergence diagnosis, naming both values and the
|
|
62
|
+
* file each came from.
|
|
63
|
+
* @param configValue - Floor declared in the config
|
|
64
|
+
* @param artifactValue - Floor enforced by the artifact
|
|
65
|
+
* @returns Multi-line failure message
|
|
66
|
+
*/
|
|
67
|
+
export declare function describeMutationFloorDivergence(configValue: unknown, artifactValue: unknown): string;
|
|
68
|
+
/** Config sync refused to write because the two mutation floors disagree. */
|
|
69
|
+
export declare class MutationFloorDivergenceError extends Error {
|
|
70
|
+
/** Floor declared in the config. */
|
|
71
|
+
readonly configValue: unknown;
|
|
72
|
+
/** Floor enforced by the artifact on disk. */
|
|
73
|
+
readonly artifactValue: unknown;
|
|
74
|
+
/**
|
|
75
|
+
* Build the refusal carrying both floors.
|
|
76
|
+
* @param configValue - Floor declared in the config
|
|
77
|
+
* @param artifactValue - Floor enforced by the artifact
|
|
78
|
+
*/
|
|
79
|
+
constructor(configValue: unknown, artifactValue: unknown);
|
|
80
|
+
}
|
|
81
|
+
/** A deliberate, fully-recorded divergence between the two floors. */
|
|
82
|
+
export interface MutationFloorDeclaration {
|
|
83
|
+
/** Why the two floors are allowed to differ, and what resolves it. */
|
|
84
|
+
readonly reason: string;
|
|
85
|
+
/** Floor this file enforces. */
|
|
86
|
+
readonly enforced: JsonValue;
|
|
87
|
+
/** Floor the owning config key declares. */
|
|
88
|
+
readonly declared: JsonValue;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Read `_thresholdsDivergence` when it still records the live numbers.
|
|
92
|
+
* @param strykerConf - Parsed `stryker.conf.json`
|
|
93
|
+
* @param onDisk - Floor this file currently enforces
|
|
94
|
+
* @param configValue - Floor the owning config key currently declares
|
|
95
|
+
* @returns The declaration, or undefined when absent, incomplete, or stale
|
|
96
|
+
*/
|
|
97
|
+
export declare function readMutationFloorDeclaration(strykerConf: unknown, onDisk: unknown, configValue: unknown): MutationFloorDeclaration | undefined;
|
|
98
|
+
/**
|
|
99
|
+
* Build the line sync prints when it honours a recorded divergence.
|
|
100
|
+
* @param declaration - The current, matching divergence declaration
|
|
101
|
+
* @returns Operator-readable single-line detail
|
|
102
|
+
*/
|
|
103
|
+
export declare function describeHonouredMutationFloorDivergence(declaration: MutationFloorDeclaration): string;
|
|
104
|
+
/**
|
|
105
|
+
* Check the ownership statement carried by `stryker.conf.json` itself.
|
|
106
|
+
* @param strykerConf - Parsed `stryker.conf.json`
|
|
107
|
+
* @param configValue - Floor declared in the owning config key
|
|
108
|
+
* @returns One problem string per violation; empty when the file is in step
|
|
109
|
+
*/
|
|
110
|
+
export declare function checkMutationFloorOwnership(strykerConf: unknown, configValue: unknown): readonly string[];
|
|
111
|
+
//# sourceMappingURL=stryker-thresholds-ownership.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stryker-thresholds-ownership.d.ts","sourceRoot":"","sources":["../../src/sync/stryker-thresholds-ownership.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,gBAAgB,CAAC;AAExB,qDAAqD;AACrD,eAAO,MAAM,yBAAyB,uCAAuC,CAAC;AAE9E,sDAAsD;AACtD,eAAO,MAAM,0BAA0B,sBAAsB,CAAC;AAE9D,2DAA2D;AAC3D,eAAO,MAAM,4BAA4B,sBAAsB,CAAC;AAEhE,+DAA+D;AAC/D,eAAO,MAAM,+BAA+B,eAAe,CAAC;AAE5D,uEAAuE;AACvE,eAAO,MAAM,0BAA0B,qBAAqB,CAAC;AAE7D,iFAAiF;AACjF,eAAO,MAAM,+BAA+B,0BAA0B,CAAC;AAEvE;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GACd,OAAO,CAMT;AAWD;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAC7C,WAAW,EAAE,OAAO,EACpB,aAAa,EAAE,OAAO,GACrB,MAAM,CAeR;AAED,6EAA6E;AAC7E,qBAAa,4BAA6B,SAAQ,KAAK;IACrD,oCAAoC;IACpC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAE9B,8CAA8C;IAC9C,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAEhC;;;;OAIG;gBACS,WAAW,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO;CAMzD;AAED,sEAAsE;AACtE,MAAM,WAAW,wBAAwB;IACvC,sEAAsE;IACtE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,gCAAgC;IAChC,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B,4CAA4C;IAC5C,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;CAC9B;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAC1C,WAAW,EAAE,OAAO,EACpB,MAAM,EAAE,OAAO,EACf,WAAW,EAAE,OAAO,GACnB,wBAAwB,GAAG,SAAS,CActC;AAED;;;;GAIG;AACH,wBAAgB,uCAAuC,CACrD,WAAW,EAAE,wBAAwB,GACpC,MAAM,CAMR;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACzC,WAAW,EAAE,OAAO,EACpB,WAAW,EAAE,OAAO,GACnB,SAAS,MAAM,EAAE,CAWnB"}
|