@codyswann/lisa 4.15.1 → 4.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/all/copy-overwrite/scripts/lisa-hooks/parity-safety-net.sh +62 -9
  2. package/dist/cli/doctor-override-floor-conflicts.d.ts +15 -0
  3. package/dist/cli/doctor-override-floor-conflicts.d.ts.map +1 -0
  4. package/dist/cli/doctor-override-floor-conflicts.js +225 -0
  5. package/dist/cli/doctor-override-floor-conflicts.js.map +1 -0
  6. package/dist/cli/doctor.d.ts.map +1 -1
  7. package/dist/cli/doctor.js +8 -0
  8. package/dist/cli/doctor.js.map +1 -1
  9. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  10. package/dist/core/lisa-owned-hash-ledger.js +2 -0
  11. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  12. package/dist/core/override-floors.d.ts +146 -0
  13. package/dist/core/override-floors.d.ts.map +1 -0
  14. package/dist/core/override-floors.js +200 -0
  15. package/dist/core/override-floors.js.map +1 -0
  16. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  17. package/dist/core/upstream-evidence-manifest.js +8 -2
  18. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  19. package/dist/opencode/plugin-templates/parity-safety-net.sh +62 -9
  20. package/dist/strategies/package-lisa.d.ts +59 -0
  21. package/dist/strategies/package-lisa.d.ts.map +1 -1
  22. package/dist/strategies/package-lisa.js +112 -130
  23. package/dist/strategies/package-lisa.js.map +1 -1
  24. package/package.json +1 -1
  25. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  26. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  27. package/plugins/lisa/hooks/parity-safety-net.sh +62 -9
  28. package/plugins/lisa-agy/hooks/parity-safety-net.sh +62 -9
  29. package/plugins/lisa-agy/plugin.json +1 -1
  30. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  31. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  32. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  33. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  34. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  35. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  36. package/plugins/lisa-copilot/hooks/parity-safety-net.sh +62 -9
  37. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  38. package/plugins/lisa-cursor/hooks/parity-safety-net.sh +62 -9
  39. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  40. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  41. package/plugins/lisa-expo-agy/plugin.json +1 -1
  42. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  43. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  44. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  45. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  46. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  47. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  48. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  49. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  50. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  51. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  52. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  53. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  55. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  56. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  57. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  58. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  59. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  60. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  61. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  62. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  63. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  64. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  65. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  66. package/plugins/lisa-rails-agy/plugin.json +1 -1
  67. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  68. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  69. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  70. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  71. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  72. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  73. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  74. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  75. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  76. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  77. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  78. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  79. package/plugins/src/base/hooks/parity-safety-net.sh +62 -9
@@ -0,0 +1,146 @@
1
+ /** package.json sections whose keys are treated as direct dependencies. */
2
+ export declare const DIRECT_DEPENDENCY_SECTIONS: readonly ["dependencies", "devDependencies", "optionalDependencies", "peerDependencies"];
3
+ /** package.json sections that carry npm-style version overrides. */
4
+ export declare const OVERRIDE_SECTIONS: readonly ["overrides", "resolutions"];
5
+ /** One of the two npm override sections. */
6
+ export type OverrideSection = (typeof OVERRIDE_SECTIONS)[number];
7
+ /**
8
+ * What rewriting one literal override to `"$name"` would do to its floor.
9
+ *
10
+ * `unprovable` and `widening` are kept apart because they are different
11
+ * operator problems: the first is a range Lisa cannot parse on either side (a
12
+ * git spec, `workspace:*`, an alias), the second is an ordinary semver range
13
+ * that genuinely starts lower than the floor.
14
+ */
15
+ export type SelfReferenceVerdict = "safe" | "unprovable" | "widening";
16
+ /** One override whose `$name` rewrite would resolve below Lisa's floor. */
17
+ export interface SelfReferenceFloorConflict {
18
+ /** Override section carrying the entry. */
19
+ readonly section: OverrideSection;
20
+ /** Package name. */
21
+ readonly name: string;
22
+ /** Literal override range in the merged manifest — Lisa's floor. */
23
+ readonly floorRange: string;
24
+ /** Direct dependency range `$name` would resolve to. */
25
+ readonly directRange: string;
26
+ /** Why the rewrite is refused. */
27
+ readonly verdict: Exclude<SelfReferenceVerdict, "safe">;
28
+ /**
29
+ * A direct range that has been VERIFIED to satisfy the guard, or null when
30
+ * none could be derived. Never a guess.
31
+ */
32
+ readonly suggestedDirectRange: string | null;
33
+ }
34
+ /**
35
+ * What one pass over a merged manifest actually looked at, alongside what it
36
+ * found.
37
+ *
38
+ * The counts are not decoration. A check that inspected nothing and a check
39
+ * that inspected everything and found nothing produce the same empty conflict
40
+ * list, and only the counts tell them apart — so every caller reports them and
41
+ * refuses to call zero inspected a pass.
42
+ */
43
+ export interface OverrideFloorAudit {
44
+ /** Every top-level entry across `overrides` and `resolutions`. */
45
+ readonly overridesInspected: number;
46
+ /** Those the `$name` normalization guard actually judges. */
47
+ readonly rewritesJudged: number;
48
+ /** Those the guard would refuse. */
49
+ readonly conflicts: readonly SelfReferenceFloorConflict[];
50
+ /** Those the guard would perform, in the order they were read. */
51
+ readonly rewritable: readonly SelfReferenceCandidate[];
52
+ }
53
+ /** One override the `$name` normalization guard judges. */
54
+ export interface SelfReferenceCandidate {
55
+ /** Override section carrying the entry. */
56
+ readonly section: OverrideSection;
57
+ /** Package name. */
58
+ readonly name: string;
59
+ /** Literal override range in the merged manifest. */
60
+ readonly floorRange: string;
61
+ /** Direct dependency range `$name` would resolve to. */
62
+ readonly directRange: string;
63
+ }
64
+ /**
65
+ * Narrow an unknown value to a plain object record, returning {} otherwise.
66
+ * @param value - Candidate value
67
+ * @returns The value as a record, or an empty record when it is not a plain object
68
+ */
69
+ export declare function asRecord(value: unknown): Record<string, unknown>;
70
+ /**
71
+ * Collect the names of every direct dependency declared across a package.json's
72
+ * dependency sections.
73
+ * @param pkg - Merged package.json object
74
+ * @returns Set of direct dependency names
75
+ */
76
+ export declare function collectDirectDependencyNames(pkg: Record<string, unknown>): Set<string>;
77
+ /**
78
+ * Collect direct dependency ranges by package name.
79
+ * @param pkg - Merged package.json object
80
+ * @returns Package name to string range map
81
+ */
82
+ export declare function collectDirectDependencyRanges(pkg: Record<string, unknown>): ReadonlyMap<string, string>;
83
+ /**
84
+ * Decide whether an override/resolution entry is a candidate for npm `$name`
85
+ * normalization at all.
86
+ * @param directDeps - Direct dependency names in the merged package
87
+ * @param name - Override/resolution key
88
+ * @param value - Override/resolution value
89
+ * @returns True when the entry is a literal direct-dependency collision
90
+ */
91
+ export declare function needsSelfReferenceRewrite(directDeps: ReadonlySet<string>, name: string, value: unknown): boolean;
92
+ /**
93
+ * The one predicate behind both the apply's refusal and doctor's report.
94
+ * @remarks
95
+ * Deliberately plain `semver.subset`, with no ceiling normalization. The
96
+ * normalizing variant used by the host-pin branch (`rangeContains` in the
97
+ * package-lisa strategy) can only turn a refusal into an acceptance, and this
98
+ * branch is the one that decides whether a security floor may be resolved
99
+ * DOWNWARDS — so it keeps the stricter reading. Any range that reaches a
100
+ * `widening` verdict here still has a followable way out, because
101
+ * {@link suggestSatisfyingDirectRange} is verified against this same function.
102
+ * @param floorRange - The literal override range being replaced
103
+ * @param directRange - The direct dependency range `$name` would resolve to
104
+ * @returns Whether the rewrite is safe, unprovable, or widening
105
+ */
106
+ export declare function classifySelfReferenceRewrite(floorRange: string, directRange: string): SelfReferenceVerdict;
107
+ /**
108
+ * Derive a direct dependency range that satisfies the floor, and PROVE it does
109
+ * before handing it to an operator.
110
+ * @remarks
111
+ * The natural suggestion is a caret at the floor's minimum version, because
112
+ * that is what a person writes and what the one already-fixed instance of this
113
+ * defect was fixed with. It is not always right: measured, `^1.18.0` is not a
114
+ * subset of a bounded floor `>=1.18.0 <1.19.0`, and `^0.0.0` is not a subset of
115
+ * `*`. So the caret is a CANDIDATE, tested against
116
+ * {@link classifySelfReferenceRewrite} and discarded when it fails, falling
117
+ * back to the floor range verbatim — which is a subset of itself for any range
118
+ * semver can parse.
119
+ *
120
+ * A floor semver cannot parse yields null. Printing an untested range would be
121
+ * the #3191 failure exactly: advice that sends the operator back into the same
122
+ * refusal.
123
+ * @param floorRange - The literal override range acting as the floor
124
+ * @returns A range verified to satisfy the guard, or null when none exists
125
+ */
126
+ export declare function suggestSatisfyingDirectRange(floorRange: string): string | null;
127
+ /**
128
+ * Judge every `$name`-rewrite candidate in a merged manifest, collecting ALL
129
+ * conflicts rather than stopping at the first.
130
+ * @remarks
131
+ * The apply throws on the first conflict, which is right for an apply — it is
132
+ * about to write a file and must not. A report has the opposite obligation: an
133
+ * operator who fixes the one package named and re-runs, only to be handed the
134
+ * next one, is being drip-fed a list Lisa already had in full.
135
+ * @param pkg - Merged package.json, as the apply would persist it
136
+ * @returns What was inspected and every conflict found
137
+ */
138
+ export declare function auditSelfReferenceRewrites(pkg: Record<string, unknown>): OverrideFloorAudit;
139
+ /**
140
+ * The remedy sentence, shared by the apply's refusal and doctor's report so an
141
+ * operator is never told two different things about the same conflict.
142
+ * @param conflict - The conflict to describe
143
+ * @returns One operator-readable sentence naming the exact raise to make
144
+ */
145
+ export declare function describeSelfReferenceRemedy(conflict: SelfReferenceFloorConflict): string;
146
+ //# sourceMappingURL=override-floors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"override-floors.d.ts","sourceRoot":"","sources":["../../src/core/override-floors.ts"],"names":[],"mappings":"AAgCA,2EAA2E;AAC3E,eAAO,MAAM,0BAA0B,0FAK7B,CAAC;AAEX,oEAAoE;AACpE,eAAO,MAAM,iBAAiB,uCAAwC,CAAC;AAEvE,4CAA4C;AAC5C,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjE;;;;;;;GAOG;AACH,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,YAAY,GAAG,UAAU,CAAC;AAEtE,2EAA2E;AAC3E,MAAM,WAAW,0BAA0B;IACzC,2CAA2C;IAC3C,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,oBAAoB;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,oEAAoE;IACpE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,wDAAwD;IACxD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,kCAAkC;IAClC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC;IACxD;;;OAGG;IACH,QAAQ,CAAC,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9C;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,kBAAkB;IACjC,kEAAkE;IAClE,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,6DAA6D;IAC7D,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,oCAAoC;IACpC,QAAQ,CAAC,SAAS,EAAE,SAAS,0BAA0B,EAAE,CAAC;IAC1D,kEAAkE;IAClE,QAAQ,CAAC,UAAU,EAAE,SAAS,sBAAsB,EAAE,CAAC;CACxD;AAED,2DAA2D;AAC3D,MAAM,WAAW,sBAAsB;IACrC,2CAA2C;IAC3C,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,oBAAoB;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,qDAAqD;IACrD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,wDAAwD;IACxD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAKhE;AAED;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAC1C,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC3B,GAAG,CAAC,MAAM,CAAC,CAMb;AAED;;;;GAIG;AACH,wBAAgB,6BAA6B,CAC3C,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC3B,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAQ7B;AAED;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CACvC,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,EAC/B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,OAAO,GACb,OAAO,CAOT;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,4BAA4B,CAC1C,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,GAClB,oBAAoB,CAKtB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,4BAA4B,CAC1C,UAAU,EAAE,MAAM,GACjB,MAAM,GAAG,IAAI,CAUf;AAeD;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC3B,kBAAkB,CA4CpB;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,0BAA0B,GACnC,MAAM,CAcR"}
@@ -0,0 +1,200 @@
1
+ /**
2
+ * @file Shared vocabulary for npm `$name` override self-references and the
3
+ * security floors they can silently resolve downwards.
4
+ * @description
5
+ * An `overrides`/`resolutions` entry written `"$name"` means "whatever the
6
+ * direct dependency says". Lisa's templates force floors into those same two
7
+ * sections, so when a floored package is ALSO a direct dependency, the apply
8
+ * normalizes the forced literal to `$name` — and if the host's direct range
9
+ * starts below the floor, that rewrite would readmit exactly the versions the
10
+ * floor exists to exclude. `lisa apply` refuses rather than perform it.
11
+ *
12
+ * The refusal is correct. What it was missing is a way to be seen coming, and a
13
+ * remedy the operator can actually follow. Both halves are here, in ONE place,
14
+ * because they must agree by construction:
15
+ *
16
+ * - {@link classifySelfReferenceRewrite} is the single predicate. The apply's
17
+ * guard and `lisa doctor` both call it, so doctor can never report clean on a
18
+ * manifest the apply is about to refuse, nor cry wolf on one it accepts.
19
+ * - {@link suggestSatisfyingDirectRange} runs its own suggestion back through
20
+ * that same predicate before returning it. Advice that has not been tested
21
+ * against the guard is how CodySwannGT/lisa#3191 happened — an operator who
22
+ * did what the refusal told them was refused a second time, which reads as
23
+ * the guard being broken rather than the range being wrong. A suggestion that
24
+ * does not verify is withheld, not printed hopefully.
25
+ *
26
+ * Nothing here rewrites a host range or lowers a floor. Raising a dependency is
27
+ * a version decision with its own blast radius; this module names the raise and
28
+ * the operator makes it.
29
+ * @module core/override-floors
30
+ */
31
+ import semver from "semver";
32
+ /** package.json sections whose keys are treated as direct dependencies. */
33
+ export const DIRECT_DEPENDENCY_SECTIONS = [
34
+ "dependencies",
35
+ "devDependencies",
36
+ "optionalDependencies",
37
+ "peerDependencies",
38
+ ];
39
+ /** package.json sections that carry npm-style version overrides. */
40
+ export const OVERRIDE_SECTIONS = ["overrides", "resolutions"];
41
+ /**
42
+ * Narrow an unknown value to a plain object record, returning {} otherwise.
43
+ * @param value - Candidate value
44
+ * @returns The value as a record, or an empty record when it is not a plain object
45
+ */
46
+ export function asRecord(value) {
47
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
48
+ return value;
49
+ }
50
+ return {};
51
+ }
52
+ /**
53
+ * Collect the names of every direct dependency declared across a package.json's
54
+ * dependency sections.
55
+ * @param pkg - Merged package.json object
56
+ * @returns Set of direct dependency names
57
+ */
58
+ export function collectDirectDependencyNames(pkg) {
59
+ return new Set(DIRECT_DEPENDENCY_SECTIONS.flatMap(section => Object.keys(asRecord(pkg[section]))));
60
+ }
61
+ /**
62
+ * Collect direct dependency ranges by package name.
63
+ * @param pkg - Merged package.json object
64
+ * @returns Package name to string range map
65
+ */
66
+ export function collectDirectDependencyRanges(pkg) {
67
+ return new Map(DIRECT_DEPENDENCY_SECTIONS.flatMap(section => Object.entries(asRecord(pkg[section])).flatMap(([name, value]) => typeof value === "string" ? [[name, value]] : [])));
68
+ }
69
+ /**
70
+ * Decide whether an override/resolution entry is a candidate for npm `$name`
71
+ * normalization at all.
72
+ * @param directDeps - Direct dependency names in the merged package
73
+ * @param name - Override/resolution key
74
+ * @param value - Override/resolution value
75
+ * @returns True when the entry is a literal direct-dependency collision
76
+ */
77
+ export function needsSelfReferenceRewrite(directDeps, name, value) {
78
+ return (directDeps.has(name) &&
79
+ typeof value === "string" &&
80
+ value.length > 0 &&
81
+ !value.startsWith("$"));
82
+ }
83
+ /**
84
+ * The one predicate behind both the apply's refusal and doctor's report.
85
+ * @remarks
86
+ * Deliberately plain `semver.subset`, with no ceiling normalization. The
87
+ * normalizing variant used by the host-pin branch (`rangeContains` in the
88
+ * package-lisa strategy) can only turn a refusal into an acceptance, and this
89
+ * branch is the one that decides whether a security floor may be resolved
90
+ * DOWNWARDS — so it keeps the stricter reading. Any range that reaches a
91
+ * `widening` verdict here still has a followable way out, because
92
+ * {@link suggestSatisfyingDirectRange} is verified against this same function.
93
+ * @param floorRange - The literal override range being replaced
94
+ * @param directRange - The direct dependency range `$name` would resolve to
95
+ * @returns Whether the rewrite is safe, unprovable, or widening
96
+ */
97
+ export function classifySelfReferenceRewrite(floorRange, directRange) {
98
+ if (!semver.validRange(floorRange) || !semver.validRange(directRange)) {
99
+ return "unprovable";
100
+ }
101
+ return semver.subset(directRange, floorRange) ? "safe" : "widening";
102
+ }
103
+ /**
104
+ * Derive a direct dependency range that satisfies the floor, and PROVE it does
105
+ * before handing it to an operator.
106
+ * @remarks
107
+ * The natural suggestion is a caret at the floor's minimum version, because
108
+ * that is what a person writes and what the one already-fixed instance of this
109
+ * defect was fixed with. It is not always right: measured, `^1.18.0` is not a
110
+ * subset of a bounded floor `>=1.18.0 <1.19.0`, and `^0.0.0` is not a subset of
111
+ * `*`. So the caret is a CANDIDATE, tested against
112
+ * {@link classifySelfReferenceRewrite} and discarded when it fails, falling
113
+ * back to the floor range verbatim — which is a subset of itself for any range
114
+ * semver can parse.
115
+ *
116
+ * A floor semver cannot parse yields null. Printing an untested range would be
117
+ * the #3191 failure exactly: advice that sends the operator back into the same
118
+ * refusal.
119
+ * @param floorRange - The literal override range acting as the floor
120
+ * @returns A range verified to satisfy the guard, or null when none exists
121
+ */
122
+ export function suggestSatisfyingDirectRange(floorRange) {
123
+ const minimum = rangeMinimum(floorRange);
124
+ const candidates = minimum === null ? [floorRange] : [`^${minimum}`, floorRange];
125
+ return (candidates.find(candidate => classifySelfReferenceRewrite(floorRange, candidate) === "safe") ?? null);
126
+ }
127
+ /**
128
+ * Lowest version a range admits, without throwing on an unparseable range.
129
+ * @param range - An npm version range
130
+ * @returns The minimum version, or null when the range is not comparable
131
+ */
132
+ function rangeMinimum(range) {
133
+ try {
134
+ return semver.minVersion(range)?.version ?? null;
135
+ }
136
+ catch {
137
+ return null;
138
+ }
139
+ }
140
+ /**
141
+ * Judge every `$name`-rewrite candidate in a merged manifest, collecting ALL
142
+ * conflicts rather than stopping at the first.
143
+ * @remarks
144
+ * The apply throws on the first conflict, which is right for an apply — it is
145
+ * about to write a file and must not. A report has the opposite obligation: an
146
+ * operator who fixes the one package named and re-runs, only to be handed the
147
+ * next one, is being drip-fed a list Lisa already had in full.
148
+ * @param pkg - Merged package.json, as the apply would persist it
149
+ * @returns What was inspected and every conflict found
150
+ */
151
+ export function auditSelfReferenceRewrites(pkg) {
152
+ const directDeps = collectDirectDependencyNames(pkg);
153
+ const directRanges = collectDirectDependencyRanges(pkg);
154
+ const entries = OVERRIDE_SECTIONS.flatMap(section => Object.entries(asRecord(pkg[section])).map(([name, value]) => [section, name, value]));
155
+ const judged = entries.flatMap(([section, name, value]) => {
156
+ if (!needsSelfReferenceRewrite(directDeps, name, value)) {
157
+ return [];
158
+ }
159
+ const directRange = directRanges.get(name);
160
+ return directRange === undefined
161
+ ? []
162
+ : [{ section, name, floorRange: value, directRange }];
163
+ });
164
+ const verdicts = judged.map(candidate => ({
165
+ candidate,
166
+ verdict: classifySelfReferenceRewrite(candidate.floorRange, candidate.directRange),
167
+ }));
168
+ return {
169
+ overridesInspected: entries.length,
170
+ rewritesJudged: judged.length,
171
+ conflicts: verdicts.flatMap(({ candidate, verdict }) => verdict === "safe"
172
+ ? []
173
+ : [
174
+ {
175
+ ...candidate,
176
+ verdict,
177
+ suggestedDirectRange: suggestSatisfyingDirectRange(candidate.floorRange),
178
+ },
179
+ ]),
180
+ rewritable: verdicts.flatMap(({ candidate, verdict }) => verdict === "safe" ? [candidate] : []),
181
+ };
182
+ }
183
+ /**
184
+ * The remedy sentence, shared by the apply's refusal and doctor's report so an
185
+ * operator is never told two different things about the same conflict.
186
+ * @param conflict - The conflict to describe
187
+ * @returns One operator-readable sentence naming the exact raise to make
188
+ */
189
+ export function describeSelfReferenceRemedy(conflict) {
190
+ if (conflict.suggestedDirectRange === null) {
191
+ return (`Lisa could not derive a direct range that satisfies the floor ` +
192
+ `${JSON.stringify(conflict.floorRange)}, so it is not guessing one. ` +
193
+ `Reconcile ${conflict.name} by hand, or raise Lisa's template pin.`);
194
+ }
195
+ return (`Raise the direct dependency ${conflict.name} from ` +
196
+ `${JSON.stringify(conflict.directRange)} to ` +
197
+ `${JSON.stringify(conflict.suggestedDirectRange)}, which Lisa has ` +
198
+ `verified satisfies the floor ${JSON.stringify(conflict.floorRange)}.`);
199
+ }
200
+ //# sourceMappingURL=override-floors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"override-floors.js","sourceRoot":"","sources":["../../src/core/override-floors.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"upstream-evidence-manifest.d.ts","sourceRoot":"","sources":["../../src/core/upstream-evidence-manifest.ts"],"names":[],"mappings":"AACA,iFAAiF;AACjF,eAAO,MAAM,0BAA0B,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAo6EpE,CAAC;AAEL,4EAA4E;AAC5E,eAAO,MAAM,yBAAyB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAusPjE,CAAC;AAEL,sFAAsF;AACtF,eAAO,MAAM,uBAAuB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAk6I/D,CAAC"}
1
+ {"version":3,"file":"upstream-evidence-manifest.d.ts","sourceRoot":"","sources":["../../src/core/upstream-evidence-manifest.ts"],"names":[],"mappings":"AACA,iFAAiF;AACjF,eAAO,MAAM,0BAA0B,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAo6EpE,CAAC;AAEL,4EAA4E;AAC5E,eAAO,MAAM,yBAAyB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CA6sPjE,CAAC;AAEL,sFAAsF;AACtF,eAAO,MAAM,uBAAuB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAk6I/D,CAAC"}
@@ -21,7 +21,7 @@ export const UPSTREAM_EVIDENCE_MANIFEST = Object.freeze({
21
21
  "all/copy-overwrite/scripts/lisa-hooks/block-managed-file-edits.sh": "d3ef2562b718156912f0877f8a0668026f5966fcca43eb9e064aa57a4e9a4976",
22
22
  "all/copy-overwrite/scripts/lisa-hooks/block-no-verify.sh": "cdf59a4423ced0c1744a9742cd20a3e59eb728972be0657acde7c4658528fbfc",
23
23
  "all/copy-overwrite/scripts/lisa-hooks/block-shell-json-parsing.sh": "234cb82cc9033bd04940a553d4ddc95b078e585e41777d6529d887676edc40b1",
24
- "all/copy-overwrite/scripts/lisa-hooks/parity-safety-net.sh": "fb487a2d54b05a8d3268ce46e64b044a68896827941eb9e6e25b975e76762e13",
24
+ "all/copy-overwrite/scripts/lisa-hooks/parity-safety-net.sh": "a211f1a06aecddb841851463da302f694ab0eafff6bab75ee944b0a58aac431d",
25
25
  "all/copy-overwrite/scripts/lisa-hooks/sonar-secrets.sh": "f21cdcb54b353714a92c2d1e25fd6b76318e8e8d7cb90cdb016f174923c532ef",
26
26
  "all/copy-overwrite/scripts/lisa-lint-staged-preflight.mjs": "ce4bc224a102e3ac2bc29d8f2038eefd619129d4639c25954c93f05011f7f977",
27
27
  "all/copy-overwrite/scripts/lisa-postinstall.mjs": "d22fe47acf9a68828ca28b19b88e269bf535e77bc38952dec9f7a703269f1c44",
@@ -365,7 +365,7 @@ export const UPSTREAM_EVIDENCE_MANIFEST = Object.freeze({
365
365
  "plugins/src/base/hooks/install-pkgs.sh": "e9a13faba849a277410dde911ab102ee6f88ef915b33571bb1d4eef904172db5",
366
366
  "plugins/src/base/hooks/parity-safety-net-heredoc.py": "f21b1232ac82bd0e42d2ba43c68f4cc4546334fc885ba31747881f427097ef78",
367
367
  "plugins/src/base/hooks/parity-safety-net.agy.sh": "ce9bd2b4566ad9147e4ace56434cffbbe87edb4ccbb57549ab3fd9f4c671ced4",
368
- "plugins/src/base/hooks/parity-safety-net.sh": "67a600105e3d96c0bfedcfc576fe667c4698679236a6c8253fcd8ff2eaf9e649",
368
+ "plugins/src/base/hooks/parity-safety-net.sh": "c0b820f7b9a1992f11e8ac79b717a228bb3abb055dc72a8f2b11a489c7a6b68b",
369
369
  "plugins/src/base/hooks/secrets-preflight.sh": "cdc638627e5769770aefb061ea0ddfacc1888a8eab3a5fade6c43129a2d3ff6e",
370
370
  "plugins/src/base/hooks/setup-jira-cli.sh": "384bc8d935a2ed5788d7185979b3e74c75c17cd4bc5c230d7d8f086ab2703443",
371
371
  "plugins/src/base/hooks/shell-write-nudge.sh": "69839af423f8792b1e52c71097316c3264425553031627c0a2f9409a9d5becd0",
@@ -7197,6 +7197,7 @@ export const UPSTREAM_SURFACE_MANIFEST = Object.freeze({
7197
7197
  "src/cli/doctor-lisa-owned-universal.ts": true,
7198
7198
  "src/cli/doctor-merge-drivers.ts": true,
7199
7199
  "src/cli/doctor-monitor-thresholds.ts": true,
7200
+ "src/cli/doctor-override-floor-conflicts.ts": true,
7200
7201
  "src/cli/doctor-readiness-action-pins.ts": true,
7201
7202
  "src/cli/doctor-readiness-audit-allowlist.ts": true,
7202
7203
  "src/cli/doctor-readiness-blockers.ts": true,
@@ -7442,6 +7443,7 @@ export const UPSTREAM_SURFACE_MANIFEST = Object.freeze({
7442
7443
  "src/core/lisa-skill-sources.ts": true,
7443
7444
  "src/core/lisa.ts": true,
7444
7445
  "src/core/merge-driver-registration.ts": true,
7446
+ "src/core/override-floors.ts": true,
7445
7447
  "src/core/project-config-kane.ts": true,
7446
7448
  "src/core/project-config.ts": true,
7447
7449
  "src/core/self-apply.ts": true,
@@ -7851,6 +7853,7 @@ export const UPSTREAM_SURFACE_MANIFEST = Object.freeze({
7851
7853
  "tests/unit/cli/doctor-lisa-owned-artifacts.test.ts": true,
7852
7854
  "tests/unit/cli/doctor-merge-drivers.test.ts": true,
7853
7855
  "tests/unit/cli/doctor-monitor-thresholds.test.ts": true,
7856
+ "tests/unit/cli/doctor-override-floor-conflicts.test.ts": true,
7854
7857
  "tests/unit/cli/doctor-readiness-blockers.test.ts": true,
7855
7858
  "tests/unit/cli/doctor-readiness-capabilities.test.ts": true,
7856
7859
  "tests/unit/cli/doctor-readiness-concurrent-write.test.ts": true,
@@ -8124,6 +8127,7 @@ export const UPSTREAM_SURFACE_MANIFEST = Object.freeze({
8124
8127
  "tests/unit/core/lisa-plugin-selection.test.ts": true,
8125
8128
  "tests/unit/core/lisa-skill-sources.test.ts": true,
8126
8129
  "tests/unit/core/no-downstream-project-names.test.ts": true,
8130
+ "tests/unit/core/override-floors.test.ts": true,
8127
8131
  "tests/unit/core/plugin-sync-marker.test.ts": true,
8128
8132
  "tests/unit/core/project-config-harness-migration.test.ts": true,
8129
8133
  "tests/unit/core/project-config-kane.test.ts": true,
@@ -8213,6 +8217,7 @@ export const UPSTREAM_SURFACE_MANIFEST = Object.freeze({
8213
8217
  "tests/unit/hooks/parity-safety-net-heredoc-literal.test.ts": true,
8214
8218
  "tests/unit/hooks/parity-safety-net-heredoc.test.ts": true,
8215
8219
  "tests/unit/hooks/parity-safety-net-line-continuation.test.ts": true,
8220
+ "tests/unit/hooks/parity-safety-net-prose-remedy.test.ts": true,
8216
8221
  "tests/unit/hooks/parity-safety-net-scan-failure.test.ts": true,
8217
8222
  "tests/unit/hooks/parity-safety-net.test.ts": true,
8218
8223
  "tests/unit/hooks/post-checkout.test.ts": true,
@@ -8670,6 +8675,7 @@ export const UPSTREAM_SURFACE_MANIFEST = Object.freeze({
8670
8675
  "tests/unit/strategies/multi-repo-container-decomposition.test.ts": true,
8671
8676
  "tests/unit/strategies/not-established-section-contract.test.ts": true,
8672
8677
  "tests/unit/strategies/package-lisa-integration-script-layout.test.ts": true,
8678
+ "tests/unit/strategies/package-lisa-override-floor-remedy.test.ts": true,
8673
8679
  "tests/unit/strategies/package-lisa-script-composition.test.ts": true,
8674
8680
  "tests/unit/strategies/package-lisa-script-report.test.ts": true,
8675
8681
  "tests/unit/strategies/package-lisa-version-pin.test.ts": true,