@goodbones/campaigns 0.1.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 (102) hide show
  1. package/LICENSE +21 -0
  2. package/build/dts/core/campaign-state.d.ts +38 -0
  3. package/build/dts/core/campaign-state.d.ts.map +1 -0
  4. package/build/dts/core/campaigns.d.ts +196 -0
  5. package/build/dts/core/campaigns.d.ts.map +1 -0
  6. package/build/dts/core/ledger.d.ts +173 -0
  7. package/build/dts/core/ledger.d.ts.map +1 -0
  8. package/build/dts/core/phases.d.ts +26 -0
  9. package/build/dts/core/phases.d.ts.map +1 -0
  10. package/build/dts/core/sectors.d.ts +52 -0
  11. package/build/dts/core/sectors.d.ts.map +1 -0
  12. package/build/dts/domain/config.d.ts +985 -0
  13. package/build/dts/domain/config.d.ts.map +1 -0
  14. package/build/dts/domain/report.d.ts +16 -0
  15. package/build/dts/domain/report.d.ts.map +1 -0
  16. package/build/dts/host/campaigns.d.ts +195 -0
  17. package/build/dts/host/campaigns.d.ts.map +1 -0
  18. package/build/dts/host/diff.d.ts +31 -0
  19. package/build/dts/host/diff.d.ts.map +1 -0
  20. package/build/dts/index.d.ts +17 -0
  21. package/build/dts/index.d.ts.map +1 -0
  22. package/build/dts/infrastructure/campaign-functions.d.ts +7 -0
  23. package/build/dts/infrastructure/campaign-functions.d.ts.map +1 -0
  24. package/build/dts/infrastructure/report-source-fake.d.ts +4 -0
  25. package/build/dts/infrastructure/report-source-fake.d.ts.map +1 -0
  26. package/build/dts/infrastructure/report-source-live.d.ts +3 -0
  27. package/build/dts/infrastructure/report-source-live.d.ts.map +1 -0
  28. package/build/dts/load/decode.d.ts +7 -0
  29. package/build/dts/load/decode.d.ts.map +1 -0
  30. package/build/dts/load/extension.d.ts +25 -0
  31. package/build/dts/load/extension.d.ts.map +1 -0
  32. package/build/dts/manifest/lower.d.ts +14 -0
  33. package/build/dts/manifest/lower.d.ts.map +1 -0
  34. package/build/dts/manifest/spec.d.ts +515 -0
  35. package/build/dts/manifest/spec.d.ts.map +1 -0
  36. package/build/dts/ports/campaign-predicate.d.ts +23 -0
  37. package/build/dts/ports/campaign-predicate.d.ts.map +1 -0
  38. package/build/dts/ports/report-source.d.ts +14 -0
  39. package/build/dts/ports/report-source.d.ts.map +1 -0
  40. package/build/dts/testing.d.ts +2 -0
  41. package/build/dts/testing.d.ts.map +1 -0
  42. package/build/esm/core/campaign-state.js +140 -0
  43. package/build/esm/core/campaign-state.js.map +1 -0
  44. package/build/esm/core/campaigns.js +954 -0
  45. package/build/esm/core/campaigns.js.map +1 -0
  46. package/build/esm/core/ledger.js +519 -0
  47. package/build/esm/core/ledger.js.map +1 -0
  48. package/build/esm/core/phases.js +87 -0
  49. package/build/esm/core/phases.js.map +1 -0
  50. package/build/esm/core/sectors.js +313 -0
  51. package/build/esm/core/sectors.js.map +1 -0
  52. package/build/esm/domain/config.js +245 -0
  53. package/build/esm/domain/config.js.map +1 -0
  54. package/build/esm/domain/report.js +168 -0
  55. package/build/esm/domain/report.js.map +1 -0
  56. package/build/esm/host/campaigns.js +1159 -0
  57. package/build/esm/host/campaigns.js.map +1 -0
  58. package/build/esm/host/diff.js +156 -0
  59. package/build/esm/host/diff.js.map +1 -0
  60. package/build/esm/index.js +27 -0
  61. package/build/esm/index.js.map +1 -0
  62. package/build/esm/infrastructure/campaign-functions.js +67 -0
  63. package/build/esm/infrastructure/campaign-functions.js.map +1 -0
  64. package/build/esm/infrastructure/report-source-fake.js +6 -0
  65. package/build/esm/infrastructure/report-source-fake.js.map +1 -0
  66. package/build/esm/infrastructure/report-source-live.js +165 -0
  67. package/build/esm/infrastructure/report-source-live.js.map +1 -0
  68. package/build/esm/load/decode.js +60 -0
  69. package/build/esm/load/decode.js.map +1 -0
  70. package/build/esm/load/extension.js +326 -0
  71. package/build/esm/load/extension.js.map +1 -0
  72. package/build/esm/manifest/lower.js +488 -0
  73. package/build/esm/manifest/lower.js.map +1 -0
  74. package/build/esm/manifest/spec.js +296 -0
  75. package/build/esm/manifest/spec.js.map +1 -0
  76. package/build/esm/ports/campaign-predicate.js +2 -0
  77. package/build/esm/ports/campaign-predicate.js.map +1 -0
  78. package/build/esm/ports/report-source.js +7 -0
  79. package/build/esm/ports/report-source.js.map +1 -0
  80. package/build/esm/testing.js +5 -0
  81. package/build/esm/testing.js.map +1 -0
  82. package/package.json +65 -0
  83. package/src/core/campaign-state.ts +227 -0
  84. package/src/core/campaigns.ts +1319 -0
  85. package/src/core/ledger.ts +675 -0
  86. package/src/core/phases.ts +138 -0
  87. package/src/core/sectors.ts +399 -0
  88. package/src/domain/config.ts +314 -0
  89. package/src/domain/report.ts +203 -0
  90. package/src/host/campaigns.ts +1664 -0
  91. package/src/host/diff.ts +203 -0
  92. package/src/index.ts +256 -0
  93. package/src/infrastructure/campaign-functions.ts +99 -0
  94. package/src/infrastructure/report-source-fake.ts +10 -0
  95. package/src/infrastructure/report-source-live.ts +193 -0
  96. package/src/load/decode.ts +81 -0
  97. package/src/load/extension.ts +466 -0
  98. package/src/manifest/lower.ts +615 -0
  99. package/src/manifest/spec.ts +364 -0
  100. package/src/ports/campaign-predicate.ts +29 -0
  101. package/src/ports/report-source.ts +35 -0
  102. package/src/testing.ts +4 -0
@@ -0,0 +1,296 @@
1
+ import { DeclarationKind, ImportProbeTarget, SurfaceConvention } from "@goodbones/core";
2
+ import * as Schema from "effect/Schema";
3
+ import { Holdout, ProbeDiagnostic, ReportFormat } from "../domain/config.js";
4
+ // The authoring surface of the campaigns family: the `campaigns` map and the
5
+ // `ledger` path, as a reader writes them. @goodbones/core decodes everything
6
+ // else in the manifest and hands these two keys here, so the core codec never
7
+ // learns a word of this vocabulary. `lower.ts` turns what this decodes into
8
+ // the rules the evaluators compile.
9
+ //
10
+ // `Globs` is repeated here rather than imported: it is two lines, and a codec
11
+ // primitive shared across a package boundary would make every glob in the
12
+ // manifest a thing two packages have to agree about.
13
+ const Globs = Schema.Union([Schema.String, Schema.Array(Schema.String)]);
14
+ // A campaign: a migration the repository is running, tracked as an object —
15
+ // a detector, a rationale, a guide, an owner, a definition of done, and a
16
+ // ledger of every place the pattern still occurs. Where a rule says what may
17
+ // never happen, a campaign names what the code is moving away from.
18
+ //
19
+ // The detector is a predicate algebra: `all`, `any` and `not` over leaf
20
+ // terms, the same three words ast-grep uses. Leaf terms reuse the other
21
+ // families' vocabularies where one exists.
22
+ // A regular expression over the whole repo-relative path, matched as the
23
+ // `structure` naming rules are: with `subject` and `convention`, the capture
24
+ // group named holds the name being judged, and the term holds when the name
25
+ // has the convention's shape.
26
+ const PathTerm = Schema.Struct({
27
+ file: Globs,
28
+ fileNot: Schema.optionalKey(Globs),
29
+ subject: Schema.optionalKey(Schema.Finite),
30
+ convention: Schema.optionalKey(SurfaceConvention),
31
+ });
32
+ // Holds when some import of the file resolves to the target — a path glob,
33
+ // `{ external: <package> }` or `{ builtin: <module> }` — and, with `symbols`,
34
+ // pulls one of those names across it.
35
+ const ImportsTerm = Schema.Struct({
36
+ resolves: ImportProbeTarget,
37
+ symbols: Schema.optionalKey(Schema.Array(Schema.String)),
38
+ });
39
+ // Holds for an export site the selectors admit — the `surface` selectors.
40
+ const ExportsTerm = Schema.Struct({
41
+ name: Schema.optionalKey(Globs),
42
+ kinds: Schema.optionalKey(Schema.Array(Schema.Literals(["named", "default", "namespace"]))),
43
+ declares: Schema.optionalKey(Schema.Array(DeclarationKind)),
44
+ reexport: Schema.optionalKey(Schema.Boolean),
45
+ });
46
+ // Holds for a member site the selectors admit — the `members` selectors.
47
+ const MembersTerm = Schema.Struct({
48
+ subject: Schema.Literals(["members", "calls"]),
49
+ name: Schema.optionalKey(Globs),
50
+ in: Schema.optionalKey(Globs),
51
+ declares: Schema.optionalKey(Schema.Array(DeclarationKind)),
52
+ });
53
+ // A regular expression over the file's text, multiline.
54
+ const ContentTerm = Schema.Struct({ regex: Schema.String });
55
+ // How a metavariable is narrowed: by the text it captured, or by what the
56
+ // identifier at its root is bound to — the module it resolves to and the
57
+ // name it was imported as (`member`), which is how `class $N extends $BASE`
58
+ // says "a React component" rather than "any class with a base".
59
+ const CaptureNarrowing = Schema.Struct({
60
+ regex: Schema.optionalKey(Schema.String),
61
+ binding: Schema.optionalKey(Schema.Struct({
62
+ resolves: ImportProbeTarget,
63
+ member: Schema.optionalKey(Schema.Array(Schema.String)),
64
+ })),
65
+ });
66
+ // An ast-grep rule object — `pattern`, `kind`, `regex`, `has`, `inside`,
67
+ // `precedes`, `follows`, `nthChild`, `all`, `any`, `not` — with `where`
68
+ // beside it. The rule is the engine's to validate; the manifest only knows
69
+ // which keys are a rule's and which is the narrowing. `kind` names are the
70
+ // engine's node kinds, which today are tree-sitter's.
71
+ const SyntaxTerm = Schema.Struct({
72
+ pattern: Schema.optionalKey(Schema.Unknown),
73
+ kind: Schema.optionalKey(Schema.Unknown),
74
+ regex: Schema.optionalKey(Schema.Unknown),
75
+ nthChild: Schema.optionalKey(Schema.Unknown),
76
+ inside: Schema.optionalKey(Schema.Unknown),
77
+ has: Schema.optionalKey(Schema.Unknown),
78
+ precedes: Schema.optionalKey(Schema.Unknown),
79
+ follows: Schema.optionalKey(Schema.Unknown),
80
+ all: Schema.optionalKey(Schema.Unknown),
81
+ any: Schema.optionalKey(Schema.Unknown),
82
+ not: Schema.optionalKey(Schema.Unknown),
83
+ where: Schema.optionalKey(Schema.Record(Schema.String, CaptureNarrowing)),
84
+ });
85
+ // One program to run or file to read, or several: a tool that takes one
86
+ // project at a time is run once per project, and the outputs are read as
87
+ // one report — a diagnostic two of them print is one diagnostic.
88
+ const ReportSources = Schema.Union([
89
+ Schema.String,
90
+ Schema.Array(Schema.String).check(Schema.isMinLength(1)),
91
+ ]);
92
+ // A finding of another program, read from the output of `command` (run
93
+ // from the repository root, once per `check`) or from `file` (written by
94
+ // an earlier step) in one of the known formats — `tsc`, `eslint --format
95
+ // json`, `oxlint --format json` — or by a `regex` with named groups. Holds
96
+ // for each diagnostic on the file whose code the term speaks to.
97
+ const ReportTerm = Schema.Struct({
98
+ command: Schema.optionalKey(ReportSources),
99
+ file: Schema.optionalKey(ReportSources),
100
+ format: ReportFormat,
101
+ pattern: Schema.optionalKey(Schema.String),
102
+ codes: Schema.optionalKey(Schema.Array(Schema.String)),
103
+ codesNot: Schema.optionalKey(Schema.Array(Schema.String)),
104
+ }).check(
105
+ // Refused at decode, where the issue names a line, rather than in the
106
+ // lowering: a term that names both sources or neither, and a `regex`
107
+ // term with nothing to match lines against.
108
+ Schema.makeFilter((term) => {
109
+ const issues = [];
110
+ if ((term.command === undefined) === (term.file === undefined)) {
111
+ issues.push("a report term names exactly one of `command` (a program to run) and `file` (a report already written)");
112
+ }
113
+ if (term.format === "regex" && term.pattern === undefined) {
114
+ issues.push({
115
+ path: ["pattern"],
116
+ issue: "a `regex` report term needs a `pattern` with named groups `file` and `line`",
117
+ });
118
+ }
119
+ return issues;
120
+ }));
121
+ // Each object carries exactly one term key, so a misspelled one is a decode
122
+ // error that names the line rather than a term quietly dropped.
123
+ const DetectorRef = Schema.suspend(() => DetectorSpec);
124
+ const DetectorSpec = Schema.Union([
125
+ Schema.Struct({ all: Schema.Array(DetectorRef) }),
126
+ Schema.Struct({ any: Schema.Array(DetectorRef) }),
127
+ Schema.Struct({ not: DetectorRef }),
128
+ Schema.Struct({ path: PathTerm }),
129
+ Schema.Struct({ imports: ImportsTerm }),
130
+ Schema.Struct({ exports: ExportsTerm }),
131
+ Schema.Struct({ members: MembersTerm }),
132
+ Schema.Struct({ requires: Schema.Array(Schema.String) }),
133
+ Schema.Struct({ content: ContentTerm }),
134
+ Schema.Struct({ syntax: SyntaxTerm }),
135
+ Schema.Struct({ report: ReportTerm }),
136
+ Schema.Struct({ fn: Schema.String }),
137
+ ]);
138
+ // A source an objective is proven against at load. `path` alone proves a
139
+ // path-shaped detector; `source` is parsed, `edges` answers the `imports`
140
+ // term and a binding narrowing in place of the live resolver, `files`
141
+ // answers `requires` in place of the file system, and `report` answers a
142
+ // `report` term in place of running anything — one-based positions, as a
143
+ // tool prints them.
144
+ const CampaignProbe = Schema.Struct({
145
+ path: Schema.String,
146
+ source: Schema.optionalKey(Schema.String),
147
+ edges: Schema.optionalKey(Schema.Record(Schema.String, ImportProbeTarget)),
148
+ files: Schema.optionalKey(Schema.Array(Schema.String)),
149
+ report: Schema.optionalKey(Schema.Array(ProbeDiagnostic)),
150
+ });
151
+ const CampaignProbes = Schema.Struct({
152
+ fires: Schema.Array(CampaignProbe),
153
+ ignores: Schema.optionalKey(Schema.Array(CampaignProbe)),
154
+ });
155
+ // `30d`, `12h`: how long a campaign may go without progress before the
156
+ // conformance report calls it stalled.
157
+ const Duration = Schema.String.check(Schema.isPattern(/^\d+[dh]$/));
158
+ const KebabId = Schema.String.check(Schema.isPattern(/^[a-z0-9]+(?:-[a-z0-9]+)*$/));
159
+ // A term quantified over a sector's files: `has` a detector at least one
160
+ // file (or declaration, or match) in the sector satisfies — a presence;
161
+ // `oneRoot` every file under the root the perimeter was found at; `oneHost`
162
+ // every file under the host named.
163
+ const SectorTermSpec = Schema.Union([
164
+ Schema.Struct({ has: DetectorRef }),
165
+ Schema.Struct({ oneRoot: Schema.Literal(true) }),
166
+ Schema.Struct({ oneHost: Globs }),
167
+ ]);
168
+ // An objective: a detector with a ledger that only shrinks on its own. The
169
+ // `holdout` says what one ledger entry is; `match` is a per-file detector
170
+ // and `sector` a term over the sector's files, exactly one of them.
171
+ // `until` names the phase at which it stops counting.
172
+ const Objective = Schema.Struct({
173
+ // What a reader at a holdout does about it — the message every hit
174
+ // carries; falls back to the campaign's.
175
+ how: Schema.optionalKey(Schema.String),
176
+ why: Schema.optionalKey(Schema.String),
177
+ holdout: Holdout,
178
+ match: Schema.optionalKey(DetectorRef),
179
+ sector: Schema.optionalKey(SectorTermSpec),
180
+ until: Schema.optionalKey(KebabId),
181
+ probes: Schema.optionalKey(CampaignProbes),
182
+ }).check(Schema.makeFilter((objective) => {
183
+ const issues = [];
184
+ if ((objective.match === undefined) === (objective.sector === undefined)) {
185
+ issues.push("an objective names exactly one of `match` (a detector over each file) and `sector` (a term over the sector's files)");
186
+ }
187
+ if (objective.sector !== undefined && objective.holdout !== "sector") {
188
+ issues.push({
189
+ path: ["holdout"],
190
+ issue: "a `sector` objective's holdout is the sector: write `holdout: sector`",
191
+ });
192
+ }
193
+ if (objective.match !== undefined && objective.holdout === "sector") {
194
+ issues.push({
195
+ path: ["holdout"],
196
+ issue: "a `match` objective's holdout is `file`, `declaration` or `match`",
197
+ });
198
+ }
199
+ if (objective.match !== undefined && (objective.probes?.fires.length ?? 0) === 0) {
200
+ issues.push({
201
+ path: ["probes"],
202
+ issue: "a `match` objective carries `probes.fires`: at least one source it must report, as every rule proves it can fire",
203
+ });
204
+ }
205
+ return issues;
206
+ }));
207
+ // How a sector is recognized. `file`: one per file, keyed by the path without
208
+ // its extension. `nx`: the workspace's projects. `{ glob }`: one per match.
209
+ // `{ marker }`: a file that names the sector — an exported `sector` object
210
+ // with `name` and the globs it `owns`; without one, the folder. `{ match }`:
211
+ // one per detector match, keyed by its anchor, and proven by probes at least
212
+ // one of which is a sector in its end shape.
213
+ const PerimeterSpec = Schema.Union([
214
+ Schema.Literals(["file", "nx"]),
215
+ Schema.Struct({ glob: Globs }),
216
+ Schema.Struct({
217
+ marker: Globs,
218
+ probes: Schema.optionalKey(Schema.Struct({ fires: Schema.optionalKey(Globs), ignores: Schema.optionalKey(Globs) })),
219
+ }),
220
+ Schema.Struct({
221
+ match: DetectorRef,
222
+ holdout: Schema.optionalKey(Schema.Literals(["declaration", "match"])),
223
+ probes: CampaignProbes,
224
+ }),
225
+ ]);
226
+ const OnTouchSpec = Schema.Literals(["advise", "ratchet", "paydown"]);
227
+ // A receipt for a change to a defined phase: a reason, dated.
228
+ const PhaseConcessionSpec = Schema.Struct({
229
+ reason: Schema.String,
230
+ at: Schema.String,
231
+ by: Schema.optionalKey(Schema.String),
232
+ });
233
+ // A sector-relative node tree: one root, `~/`, standing for the sector,
234
+ // shaped like the manifest's own tree with one allow entry of its own —
235
+ // `{ sector, via }`, another sector through its port. Carried as written
236
+ // and decoded as a tree once the lowering has rebased it for a sector, so
237
+ // the node codec itself never learns the extra entry.
238
+ const EndStateSpec = Schema.Record(Schema.String, Schema.Unknown);
239
+ // A phase: a named, ordered group of objectives. Defined when it names one
240
+ // (or is `attested`, or carries an `endState`); open when it has only an
241
+ // intent, and then it is last.
242
+ const Phase = Schema.Struct({
243
+ id: KebabId,
244
+ intent: Schema.optionalKey(Schema.String),
245
+ objectives: Schema.optionalKey(Schema.Array(KebabId)),
246
+ attested: Schema.optionalKey(Schema.Boolean),
247
+ onTouch: Schema.optionalKey(OnTouchSpec),
248
+ endState: Schema.optionalKey(EndStateSpec),
249
+ concessions: Schema.optionalKey(Schema.Array(PhaseConcessionSpec)),
250
+ });
251
+ // The scope, as globs or with the extensions the campaign widens the walk
252
+ // to beyond the packs' own.
253
+ const ScopeSpec = Schema.Union([
254
+ Globs,
255
+ Schema.Struct({ path: Globs, extensions: Schema.optionalKey(Schema.Array(Schema.String)) }),
256
+ ]);
257
+ // A campaign: one multi-step refactor with an end. Everything above
258
+ // `objectives` is optional, so a one-objective campaign is the minimal form.
259
+ const Campaign = Schema.Struct({
260
+ title: Schema.optionalKey(Schema.String),
261
+ why: Schema.optionalKey(Schema.String),
262
+ // The message a holdout carries when its objective states no `how`.
263
+ how: Schema.optionalKey(Schema.String),
264
+ owner: Schema.optionalKey(Schema.String),
265
+ // Which files the campaign sees. Alias-aware globs, as the graph rules
266
+ // take; a campaign's reach is its scope, so it joins no coverage row.
267
+ // Defaults to every file.
268
+ scope: Schema.optionalKey(ScopeSpec),
269
+ // What the unclaimed remainder of the scope counts as; absent, all of it.
270
+ legacy: Schema.optionalKey(Globs),
271
+ perimeter: Schema.optionalKey(PerimeterSpec),
272
+ onTouch: Schema.optionalKey(OnTouchSpec),
273
+ phases: Schema.optionalKey(Schema.Array(Phase)),
274
+ // Sugar for the last phase's end state.
275
+ endState: Schema.optionalKey(EndStateSpec),
276
+ objectives: Schema.Record(KebabId, Objective),
277
+ staleAfter: Schema.optionalKey(Duration),
278
+ onComplete: Schema.optionalKey(Schema.Literals(["keep", "remove"])),
279
+ });
280
+ // The slice of the manifest this family owns: the `campaigns` map, and the
281
+ // directory its ledgers are written under. @goodbones/core splits these two
282
+ // keys off the expanded manifest and hands them here; everything the core
283
+ // decodes, it decodes without them.
284
+ export const CampaignsManifest = Schema.Struct({
285
+ campaigns: Schema.optionalKey(Schema.Record(KebabId, Campaign)),
286
+ // Where the ledgers are written: `<ledger>/<campaign>/<objective>.json`,
287
+ // relative to the manifest. Defaults to `.architecture-campaigns`.
288
+ ledger: Schema.optionalKey(Schema.String),
289
+ });
290
+ export const DEFAULT_LEDGER_DIR = ".architecture-campaigns";
291
+ // `30d` -> milliseconds. The schema has already refused any other shape.
292
+ export const durationMs = (duration) => {
293
+ const amount = Number(duration.slice(0, -1));
294
+ return amount * (duration.endsWith("h") ? 3_600_000 : 86_400_000);
295
+ };
296
+ //# sourceMappingURL=spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec.js","sourceRoot":"","sources":["../../../src/manifest/spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=campaign-predicate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"campaign-predicate.js","sourceRoot":"","sources":["../../../src/ports/campaign-predicate.ts"],"names":[],"mappings":""}
@@ -0,0 +1,7 @@
1
+ // The commands or files a spec names, as a list.
2
+ export const reportSourcesOf = (spec) => {
3
+ const named = spec.command ?? spec.file ?? [];
4
+ return typeof named === "string" ? [named] : named;
5
+ };
6
+ export const NO_REPORTS = { diagnosticsOf: () => [] };
7
+ //# sourceMappingURL=report-source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report-source.js","sourceRoot":"","sources":["../../../src/ports/report-source.ts"],"names":[],"mappings":"AAaA,iDAAiD;AACjD,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,IAAgB,EAAyB,EAAE;IACzE,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC;IAC9C,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;AACrD,CAAC,CAAC;AAiBF,MAAM,CAAC,MAAM,UAAU,GAAiB,EAAE,aAAa,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC"}
@@ -0,0 +1,5 @@
1
+ // The fake for this family's one port, for tests that state what another
2
+ // tool reported and never run it. The fakes for the core's ports are under
3
+ // `@goodbones/core/testing`.
4
+ export { makeReportSourceFake } from "./infrastructure/report-source-fake.js";
5
+ //# sourceMappingURL=testing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.js","sourceRoot":"","sources":["../../src/testing.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,2EAA2E;AAC3E,6BAA6B;AAC7B,OAAO,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@goodbones/campaigns",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "license": "MIT",
6
+ "description": "The campaigns family for @goodbones/core: a refactor tracked as objectives over sectors through phases, with a ledger that only shrinks on its own and a diff-scoped nudge.",
7
+ "author": "zacharyweidenbach",
8
+ "keywords": [
9
+ "architecture",
10
+ "refactoring",
11
+ "migration",
12
+ "campaigns",
13
+ "lint",
14
+ "monorepo"
15
+ ],
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/dataquail/goodbones.git",
22
+ "directory": "packages/campaigns"
23
+ },
24
+ "homepage": "https://dataquail.github.io/goodbones",
25
+ "bugs": {
26
+ "url": "https://github.com/dataquail/goodbones/issues"
27
+ },
28
+ "exports": {
29
+ ".": {
30
+ "types": "./build/dts/index.d.ts",
31
+ "default": "./build/esm/index.js"
32
+ },
33
+ "./testing": {
34
+ "types": "./build/dts/testing.d.ts",
35
+ "default": "./build/esm/testing.js"
36
+ },
37
+ "./package.json": "./package.json"
38
+ },
39
+ "types": "./build/dts/index.d.ts",
40
+ "files": [
41
+ "build/dts",
42
+ "build/esm",
43
+ "src",
44
+ "!src/**/*.test.ts"
45
+ ],
46
+ "dependencies": {
47
+ "effect": "4.0.0-beta.94",
48
+ "@goodbones/core": "0.1.0-beta.11"
49
+ },
50
+ "devDependencies": {
51
+ "@types/node": "^25.9.4",
52
+ "ajv": "^8.20.0",
53
+ "yaml": "^2.9.0"
54
+ },
55
+ "nx": {
56
+ "includedScripts": []
57
+ },
58
+ "scripts": {
59
+ "build": "tsc -b tsconfig.build.json",
60
+ "check": "tsc -b tsconfig.json",
61
+ "clean": "rm -rf build .tsbuildinfo",
62
+ "test": "vitest run",
63
+ "coverage": "vitest run --coverage"
64
+ }
65
+ }
@@ -0,0 +1,227 @@
1
+ import type { Violation } from "@goodbones/core";
2
+
3
+ import type { PhaseRule } from "../domain/config.js";
4
+ import {
5
+ type CampaignHit,
6
+ type CampaignInput,
7
+ candidatesOf,
8
+ type CompiledCampaign,
9
+ type CompiledObjective,
10
+ evaluateObjectives,
11
+ perFileObjectivesOf,
12
+ } from "./campaigns.js";
13
+ import type { SectorRecord } from "./ledger.js";
14
+ import { positionOf } from "./ledger.js";
15
+ import {
16
+ derivePhase,
17
+ LEGACY_PHASE,
18
+ objectivesInWindow,
19
+ type Residue,
20
+ type SectorPosition,
21
+ } from "./phases.js";
22
+ import {
23
+ discoverSectors,
24
+ entryOf,
25
+ LEGACY_SECTOR,
26
+ rootOf,
27
+ type Sector,
28
+ SECTOR_HOLDOUT,
29
+ type SectorDiscovery,
30
+ type SectorIndex,
31
+ sectorNamed,
32
+ } from "./sectors.js";
33
+
34
+ // One campaign over the files in its scope: its sectors discovered, every
35
+ // objective evaluated — per file, or over a sector's files — every hit
36
+ // placed in its sector and keyed relative to the sector's root, and each
37
+ // sector's phase derived from what fired. The host supplies the file
38
+ // reading; nothing here touches one.
39
+
40
+ export type ObjectiveHit = CampaignHit & {
41
+ readonly sector: string;
42
+ // The ledger entry: relative to the sector's root.
43
+ readonly entry: string;
44
+ };
45
+
46
+ export type SectorState = {
47
+ readonly name: string;
48
+ readonly sector: Sector;
49
+ // The derived phase, as an index; `rule.phases.length` when done.
50
+ readonly phase: number;
51
+ readonly position: SectorPosition;
52
+ readonly inWindow: ReadonlyArray<CompiledObjective>;
53
+ // Every objective's hits in the sector, in window or not.
54
+ readonly counts: Residue;
55
+ // One dimension per objective in window.
56
+ readonly residue: Residue;
57
+ };
58
+
59
+ export type CampaignEvaluation = {
60
+ readonly rule: CompiledCampaign;
61
+ readonly index: SectorIndex;
62
+ // Every hit, whether or not its objective is in window for its sector.
63
+ readonly hits: ReadonlyArray<ObjectiveHit>;
64
+ readonly sectors: ReadonlyMap<string, SectorState>;
65
+ };
66
+
67
+ export type CampaignEvaluationInput = {
68
+ // The files in the campaign's scope.
69
+ readonly files: ReadonlyArray<string>;
70
+ readonly inputOf: (file: string) => CampaignInput;
71
+ readonly readText: (file: string) => string | null;
72
+ readonly globToRegExp: SectorDiscovery["globToRegExp"];
73
+ readonly projects?: SectorDiscovery["projects"];
74
+ readonly recordOf: (sector: string) => SectorRecord | undefined;
75
+ // For an `endState` objective: the violations of one family against the
76
+ // tree lowered for this sector. Absent, an end state has no residue —
77
+ // the plugin's position, which never evaluates one.
78
+ readonly endStateOf?:
79
+ ((sector: Sector, phase: PhaseRule, family: string) => ReadonlyArray<Violation>) | undefined;
80
+ };
81
+
82
+ const sectorHit = (objective: CompiledObjective, sector: Sector): ObjectiveHit => ({
83
+ violation: {
84
+ kind: "campaign",
85
+ ruleName: objective.name,
86
+ message: objective.message,
87
+ file: sector.files[0] ?? sector.roots[0] ?? "",
88
+ subject: null,
89
+ },
90
+ campaign: objective.campaign,
91
+ objective: objective.id,
92
+ sector: sector.name,
93
+ entry: SECTOR_HOLDOUT,
94
+ });
95
+
96
+ const under = (root: string, file: string): boolean =>
97
+ root === "" || file === root || file.startsWith(`${root}/`);
98
+
99
+ export const evaluateCampaign = (
100
+ rule: CompiledCampaign,
101
+ input: CampaignEvaluationInput,
102
+ ): CampaignEvaluation => {
103
+ const inputs = new Map<string, CampaignInput>();
104
+ const inputOf = (file: string): CampaignInput => {
105
+ const cached = inputs.get(file);
106
+ if (cached !== undefined) return cached;
107
+ const made = input.inputOf(file);
108
+ inputs.set(file, made);
109
+ return made;
110
+ };
111
+
112
+ const perimeter = rule.perimeter;
113
+ const index = discoverSectors(rule, {
114
+ files: input.files,
115
+ readText: input.readText,
116
+ globToRegExp: input.globToRegExp,
117
+ projects: input.projects,
118
+ perimeterMatches:
119
+ perimeter?.kind === "match"
120
+ ? (file) =>
121
+ candidatesOf(perimeter.detect, perimeter.unit, inputOf(file)).map(
122
+ (one) => one.key.split("#")[0] ?? one.key,
123
+ )
124
+ : undefined,
125
+ });
126
+
127
+ const hits: Array<ObjectiveHit> = [];
128
+ const perFile = perFileObjectivesOf(rule);
129
+ if (perFile.length > 0) {
130
+ for (const file of input.files) {
131
+ if (index.sectorOf(file) === null) continue;
132
+ for (const hit of evaluateObjectives(perFile, inputOf(file))) {
133
+ const sector = index.sectorOfHit(file, hit.violation.subject);
134
+ if (sector === null) continue;
135
+ const own = sectorNamed(index, sector);
136
+ const root = own === null ? "" : rootOf(own, file);
137
+ hits.push({ ...hit, sector, entry: entryOf(hit.violation, root) });
138
+ }
139
+ }
140
+ }
141
+
142
+ const names = [...index.sectors.keys(), ...(index.legacy.length > 0 ? [LEGACY_SECTOR] : [])];
143
+ for (const name of names) {
144
+ const sector = sectorNamed(index, name);
145
+ if (sector === null) continue;
146
+ for (const objective of rule.objectives) {
147
+ const term = objective.sector;
148
+ if (term !== null) {
149
+ let holds: boolean;
150
+ switch (term.kind) {
151
+ case "has":
152
+ holds = sector.files.some(
153
+ (file) => candidatesOf(term.detect, "file", inputOf(file)).length > 0,
154
+ );
155
+ break;
156
+ case "oneRoot": {
157
+ const [root] = sector.roots;
158
+ holds = root !== undefined && sector.files.every((file) => under(root, file));
159
+ break;
160
+ }
161
+ case "oneHost":
162
+ holds = sector.files.every((file) => term.hosts.some((host) => host.test(file)));
163
+ break;
164
+ }
165
+ if (!holds) hits.push(sectorHit(objective, sector));
166
+ continue;
167
+ }
168
+ if (objective.endState !== null && input.endStateOf !== undefined) {
169
+ if (name === LEGACY_SECTOR || sector.roots.length !== 1) continue;
170
+ const phase = rule.phases.find((one) => one.id === objective.endState?.phase);
171
+ if (phase === undefined) continue;
172
+ const root = sector.roots[0] ?? "";
173
+ for (const violation of input.endStateOf(sector, phase, objective.endState.family)) {
174
+ hits.push({
175
+ violation: { ...violation, kind: "campaign", ruleName: objective.name },
176
+ campaign: rule.id,
177
+ objective: objective.id,
178
+ sector: name,
179
+ entry: entryOf(violation, root),
180
+ });
181
+ }
182
+ }
183
+ }
184
+ }
185
+
186
+ const sectors = new Map<string, SectorState>();
187
+ for (const name of names) {
188
+ const sector = sectorNamed(index, name);
189
+ if (sector === null) continue;
190
+ const counts: Record<string, number> = {};
191
+ for (const objective of rule.objectives) counts[objective.id] = 0;
192
+ for (const hit of hits) {
193
+ if (hit.sector === name) counts[hit.objective] = (counts[hit.objective] ?? 0) + 1;
194
+ }
195
+ const position = positionOf(rule, input.recordOf(name));
196
+ const phase =
197
+ name === LEGACY_SECTOR
198
+ ? Math.min(LEGACY_PHASE, rule.phases.length)
199
+ : derivePhase(rule, (id) => counts[id] ?? 0, position);
200
+ const inWindow = objectivesInWindow(rule, phase, position);
201
+ const residue: Record<string, number> = {};
202
+ for (const objective of inWindow) residue[objective.id] = counts[objective.id] ?? 0;
203
+ sectors.set(name, { name, sector, phase, position, inWindow, counts, residue });
204
+ }
205
+
206
+ return { rule, index, hits, sectors };
207
+ };
208
+
209
+ // The hits that count: those whose objective is in window for their sector.
210
+ export const hitsInWindow = (evaluation: CampaignEvaluation): ReadonlyArray<ObjectiveHit> =>
211
+ evaluation.hits.filter(
212
+ (hit) =>
213
+ evaluation.sectors.get(hit.sector)?.inWindow.some((one) => one.id === hit.objective) ?? false,
214
+ );
215
+
216
+ // The residue toward the sector's next phase: the current phase's
217
+ // objectives that still have holdouts — what must reach zero to leave it.
218
+ export const towardNextOf = (rule: CompiledCampaign, state: SectorState): Residue => {
219
+ const phase = rule.phases[state.phase];
220
+ if (phase === undefined) return {};
221
+ const toward: Record<string, number> = {};
222
+ for (const id of phase.objectives) {
223
+ const count = state.counts[id] ?? 0;
224
+ if (count > 0) toward[id] = count;
225
+ }
226
+ return toward;
227
+ };