@mailwoman/registry 4.10.0 → 4.11.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.
package/README.md ADDED
@@ -0,0 +1,92 @@
1
+ # @mailwoman/registry
2
+
3
+ **Geocode-first record-matching application** — the high-level entry point that
4
+ runs the full block → score → cluster pipeline over ingested records and returns
5
+ canonical entities ready for export.
6
+
7
+ This is the clinic-funding use case Mailwoman was built for, standing on a
8
+ calibrated, label-free matcher.
9
+
10
+ ```ts
11
+ import { resolveEntities, ingestRows, toGeoJSON } from "@mailwoman/registry";
12
+
13
+ // 1. Ingest — CSV/array → normalized SourceRecords
14
+ const records = ingestRows(rows, {
15
+ mapping: { name: "Provider Name", address: "Street Address", city: "City", ... },
16
+ });
17
+
18
+ // 2. Resolve — block → score → cluster with geo-first defaults
19
+ const entities = resolveEntities(records, {
20
+ geocodeAddress: async (row) => ({ lat: 30.2672, lon: -97.7431 }),
21
+ });
22
+
23
+ // 3. Export — GeoJSON for QGIS
24
+ const fc = toGeoJSON(entities);
25
+ // → FeatureCollection with Point features + entity properties
26
+ ```
27
+
28
+ ## The full pipeline
29
+
30
+ ```
31
+ CSV / SQLite → ingestRows → SourceRecord[] → resolveEntities → ResolvedEntity[]
32
+
33
+ toGeoJSON()
34
+
35
+ GeoJSON → QGIS
36
+ ```
37
+
38
+ ## API
39
+
40
+ ```ts
41
+ // Ingest — parse CSV / map columns → normalized records
42
+ import { ingestRows, parseCsv, inferMapping } from "@mailwoman/registry"
43
+ // {
44
+ // ingestRows(rows, opts): SourceRecord[]
45
+ // parseCsv(csvText): string[][]
46
+ // inferMapping(headers): ColumnMapping
47
+ // }
48
+
49
+ // Resolve — run the full matcher pipeline
50
+ import { resolveEntities } from "@mailwoman/registry"
51
+ // resolveEntities(records, config): ResolvedEntity[]
52
+ // Config: { geocodeAddress?, scorer?, blockingKeys?, threshold?, discriminators? }
53
+
54
+ // Export — GeoJSON, MapLibre HTML, reconciliation reports
55
+ import { toGeoJSON, toMapHTML, reconcile } from "@mailwoman/registry"
56
+
57
+ // Learned scorer — pre-trained GBT for single-dataset dedup
58
+ import { dedupGbtEnUs } from "@mailwoman/registry"
59
+ ```
60
+
61
+ ## Default configuration
62
+
63
+ `resolveEntities` ships with sensible defaults:
64
+
65
+ - **Blocking keys:** geo-cell (H3) + canonical address + phone + email
66
+ - **Scoring model:** Fellegi-Sunter with label-free EM, term frequency adjustment
67
+ - **Learned scorer:** optional GBT for single-dataset dedup (opt-in via `scorer`)
68
+ - **Threshold:** 0.5 (configurable precision/recall knob)
69
+
70
+ ## CLI
71
+
72
+ The `mailwoman` CLI exposes `registry` as a command:
73
+
74
+ ```bash
75
+ # Multi-source entity resolution
76
+ mailwoman registry --sources config.json --out entities.geojson
77
+
78
+ # Cross-dataset reconciliation
79
+ mailwoman registry --sources tx-nppes.json --reconcile tx-fcc.json
80
+ ```
81
+
82
+ ## Related
83
+
84
+ - [`@mailwoman/match`](../match) — the low-level block/score/cluster primitives
85
+ - [`@mailwoman/record`](../record) — `SourceRecord` schema and normalizers
86
+ - [`@mailwoman/address-id`](../address-id) — exact-match join key
87
+ - [Geocode-First Record Matching](https://mailwoman.sister.software/articles/concepts/geocode-first-record-matching/)
88
+ - [Dedup Entity Truth](https://mailwoman.sister.software/articles/concepts/dedup-entity-truth/)
89
+
90
+ ## License
91
+
92
+ [AGPL-3.0-only](https://www.gnu.org/licenses/agpl-3.0.html)
package/out/index.d.ts CHANGED
@@ -14,7 +14,9 @@ export * from "./address-key.js";
14
14
  export * from "./geojson.js";
15
15
  export * from "./ingest.js";
16
16
  export * from "./learned-scorer.js";
17
+ export * from "./map-html.js";
17
18
  export * from "./models/dedup-gbt-en-us.js";
19
+ export * from "./reconcile.js";
18
20
  export * from "./resolve.js";
19
21
  export * from "./types.js";
20
22
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,cAAc,kBAAkB,CAAA;AAChC,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAC3B,cAAc,qBAAqB,CAAA;AACnC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,cAAc,kBAAkB,CAAA;AAChC,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAC3B,cAAc,qBAAqB,CAAA;AACnC,cAAc,eAAe,CAAA;AAC7B,cAAc,6BAA6B,CAAA;AAC3C,cAAc,gBAAgB,CAAA;AAC9B,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA"}
package/out/index.js CHANGED
@@ -14,7 +14,9 @@ export * from "./address-key.js";
14
14
  export * from "./geojson.js";
15
15
  export * from "./ingest.js";
16
16
  export * from "./learned-scorer.js";
17
+ export * from "./map-html.js";
17
18
  export * from "./models/dedup-gbt-en-us.js";
19
+ export * from "./reconcile.js";
18
20
  export * from "./resolve.js";
19
21
  export * from "./types.js";
20
22
  //# sourceMappingURL=index.js.map
package/out/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,cAAc,kBAAkB,CAAA;AAChC,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAC3B,cAAc,qBAAqB,CAAA;AACnC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,cAAc,kBAAkB,CAAA;AAChC,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAC3B,cAAc,qBAAqB,CAAA;AACnC,cAAc,eAAe,CAAA;AAC7B,cAAc,6BAA6B,CAAA;AAC3C,cAAc,gBAAgB,CAAA;AAC9B,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA"}
package/out/ingest.d.ts CHANGED
@@ -88,6 +88,18 @@ export declare function inferMapping(header: readonly string[]): ColumnMapping;
88
88
  export interface IngestOptions {
89
89
  /** The geocoding seam. Without it, records carry name/org but no resolved address. */
90
90
  geocodeAddress?: GeocodeAddress;
91
+ /**
92
+ * Separator for joining a multi-column ADDRESS mapping (name/org always join with a space).
93
+ * Default `" "`. Pass `", "` to give the parser delimited input (`"214 Main St, Austin, TX
94
+ * 78701"`) instead of a concatenated run (`"214 Main St Austin TX 78701"`) — the latter strips
95
+ * the parser's segmentation boundaries and is partly OOD (it also breaks all-caps
96
+ * case-normalization; #694). **Default `", "` (#694 flip, validated).** Comma-join is the correct
97
+ * shape for an address built from separate columns, and #700 measured it at +15% cross-dataset
98
+ * rooftop (579→667) with no comma-less crater. The dedup GBT was trained on the old space-joined
99
+ * coords, so this flip is paired with a GBT re-validation (#694). Pass `" "` to restore the
100
+ * legacy space-join for a byte-stable A/B.
101
+ */
102
+ addressSeparator?: string;
91
103
  }
92
104
  /** Parse a CSV string (with a header row) into row objects keyed by column name. */
93
105
  export declare function parseCsv(text: string): Record<string, string>[];
@@ -1 +1 @@
1
- {"version":3,"file":"ingest.d.ts","sourceRoot":"","sources":["../ingest.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ingest.d.ts","sourceRoot":"","sources":["../ingest.ts"],"names":[],"mappings":"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"}
package/out/ingest.js CHANGED
@@ -124,14 +124,14 @@ export function parseCsv(text) {
124
124
  return parseCsvSync(text, { columns: true, skip_empty_lines: true, trim: true, relax_column_count: true });
125
125
  }
126
126
  /** Join the named column(s) of a row into a single trimmed string, or undefined if empty. */
127
- function pick(row, columns) {
127
+ function pick(row, columns, separator = " ") {
128
128
  if (!columns)
129
129
  return undefined;
130
130
  const list = Array.isArray(columns) ? columns : [columns];
131
131
  const value = list
132
132
  .map((c) => row[c]?.trim())
133
133
  .filter(Boolean)
134
- .join(" ")
134
+ .join(separator)
135
135
  .trim();
136
136
  return value || undefined;
137
137
  }
@@ -147,7 +147,7 @@ export async function ingestRows(rows, mapping, opts = {}) {
147
147
  const id = (mapping.id ? row[mapping.id]?.trim() : "") || String(index);
148
148
  const nameValue = pick(row, mapping.name);
149
149
  const orgValue = pick(row, mapping.organization);
150
- const addressValue = pick(row, mapping.address);
150
+ const addressValue = pick(row, mapping.address, opts.addressSeparator ?? ", ");
151
151
  let attributes;
152
152
  if (mapping.attributes) {
153
153
  for (const [key, columns] of Object.entries(mapping.attributes)) {
package/out/ingest.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ingest.js","sourceRoot":"","sources":["../ingest.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ingest.js","sourceRoot":"","sources":["../ingest.ts"],"names":[],"mappings":"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"}
@@ -33,8 +33,10 @@ export interface LearnedFeatureConfig {
33
33
  * FS weight wholesale and owns its own feature vector.)
34
34
  */
35
35
  comparisons: Comparison<SourceRecord>[];
36
- /** Address-frequency table for the crowdedness feature (a crowded shared address is weak
37
- identity). */
36
+ /**
37
+ * Address-frequency table for the crowdedness feature (a crowded shared address is weak
38
+ * identity).
39
+ */
38
40
  addressFrequency: TermFrequencyTable;
39
41
  }
40
42
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"learned-scorer.d.ts","sourceRoot":"","sources":["../learned-scorer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAoB,KAAK,UAAU,EAAE,KAAK,GAAG,EAAY,KAAK,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AACjH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C,4DAA4D;AAC5D,MAAM,WAAW,oBAAoB;IACpC;;;;;OAKG;IACH,WAAW,EAAE,UAAU,CAAC,YAAY,CAAC,EAAE,CAAA;IACvC;aACY;IACZ,gBAAgB,EAAE,kBAAkB,CAAA;CACpC;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,oBAAoB,GAAG,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,YAAY,KAAK,MAAM,EAAE,CAkClH;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC9B,MAAM,EAAE,oBAAoB,GAAG;IAAE,KAAK,EAAE,GAAG,CAAA;CAAE,GAC3C,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,YAAY,KAAK,MAAM,CAI9C"}
1
+ {"version":3,"file":"learned-scorer.d.ts","sourceRoot":"","sources":["../learned-scorer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAoB,KAAK,UAAU,EAAE,KAAK,GAAG,EAAY,KAAK,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AACjH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C,4DAA4D;AAC5D,MAAM,WAAW,oBAAoB;IACpC;;;;;OAKG;IACH,WAAW,EAAE,UAAU,CAAC,YAAY,CAAC,EAAE,CAAA;IACvC;;;OAGG;IACH,gBAAgB,EAAE,kBAAkB,CAAA;CACpC;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,oBAAoB,GAAG,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,YAAY,KAAK,MAAM,EAAE,CAkClH;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC9B,MAAM,EAAE,oBAAoB,GAAG;IAAE,KAAK,EAAE,GAAG,CAAA;CAAE,GAC3C,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,YAAY,KAAK,MAAM,CAI9C"}
@@ -1 +1 @@
1
- {"version":3,"file":"learned-scorer.js","sourceRoot":"","sources":["../learned-scorer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"learned-scorer.js","sourceRoot":"","sources":["../learned-scorer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * Render resolved entities as a standalone map page — the visual complement to {@link toGeoJSON}'s
7
+ * QGIS/analyst export. `toMapHTML(featureCollection)` returns one HTML file you open in a
8
+ * browser; no server, no build.
9
+ *
10
+ * It renders on the HOUSE stack: MapLibre GL + a Protomaps basemap (`@protomaps/basemaps` generates
11
+ * the `layers()` for a named flavor) over the `basemap-v4` vector tiles served from R2 at
12
+ * `tiles.sister.software` — the same basemap the demo uses. Each entity is a circle sized by
13
+ * records-merged and colored by cross-dataset-link status (≥2 sources stand out), or
14
+ * categorically by `bucket` when the reconciliation output carries one. Pure: GeoJSON in, HTML
15
+ * string out (the Protomaps layer specs are generated at this point and inlined). The generated
16
+ * page fetches MapLibre, the basemap tiles, and glyphs/sprite over the network when opened (the
17
+ * house infra).
18
+ *
19
+ * SERVE IT OVER LOCALHOST, don't open it as a file. The house tile server (`tiles.sister.software`)
20
+ * CORS-restricts to localhost + the docs domains, so a `file://` page shows the (accurate)
21
+ * markers on a blank basemap. `npx serve` / `python3 -m http.server` in the output directory is
22
+ * enough; the page also surfaces a hint banner when it detects it's running from `file://`.
23
+ *
24
+ * Neutral entity-resolution view: it shows what resolved to what and how confidently (cohesion).
25
+ * Bucket labels render verbatim from the data, never editorialized.
26
+ */
27
+ import type { GeoJsonFeatureCollection } from "./types.js";
28
+ /** Protomaps stock flavors (shipped by `@protomaps/basemaps`). */
29
+ export type MapFlavor = "light" | "dark" | "white" | "grayscale" | "black";
30
+ export interface MapHTMLOptions {
31
+ /** Document `<title>` + on-map heading. Default: "Mailwoman — resolved entities". */
32
+ title?: string;
33
+ /** Protomaps basemap flavor. Default: "light" (data points read cleanly over it). */
34
+ flavor?: MapFlavor;
35
+ /**
36
+ * How to color the markers:
37
+ *
38
+ * - `"auto"` (default) — by `bucket` if any feature carries one (reconciliation output), else by
39
+ * cross-dataset-link status.
40
+ * - `"sources"` — always by cross-dataset-link status (≥2 sources stand out).
41
+ * - `"bucket"` — always by the `bucket` property.
42
+ */
43
+ colorBy?: "auto" | "sources" | "bucket";
44
+ }
45
+ /**
46
+ * Render `geojson` (a {@link toGeoJSON} / reconciliation FeatureCollection) as a complete,
47
+ * standalone HTML document. Entities without a coordinate are already absent from those
48
+ * collections; an empty collection renders a friendly empty state rather than a broken map.
49
+ */
50
+ export declare function toMapHTML(geojson: GeoJsonFeatureCollection, options?: MapHTMLOptions): string;
51
+ //# sourceMappingURL=map-html.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"map-html.d.ts","sourceRoot":"","sources":["../map-html.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAGH,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAA;AAuB1D,kEAAkE;AAClE,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,GAAG,WAAW,GAAG,OAAO,CAAA;AAE1E,MAAM,WAAW,cAAc;IAC9B,qFAAqF;IACrF,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,qFAAqF;IACrF,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAA;CACvC;AAyBD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,wBAAwB,EAAE,OAAO,GAAE,cAAmB,GAAG,MAAM,CAmMjG"}
@@ -0,0 +1,262 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * Render resolved entities as a standalone map page — the visual complement to {@link toGeoJSON}'s
7
+ * QGIS/analyst export. `toMapHTML(featureCollection)` returns one HTML file you open in a
8
+ * browser; no server, no build.
9
+ *
10
+ * It renders on the HOUSE stack: MapLibre GL + a Protomaps basemap (`@protomaps/basemaps` generates
11
+ * the `layers()` for a named flavor) over the `basemap-v4` vector tiles served from R2 at
12
+ * `tiles.sister.software` — the same basemap the demo uses. Each entity is a circle sized by
13
+ * records-merged and colored by cross-dataset-link status (≥2 sources stand out), or
14
+ * categorically by `bucket` when the reconciliation output carries one. Pure: GeoJSON in, HTML
15
+ * string out (the Protomaps layer specs are generated at this point and inlined). The generated
16
+ * page fetches MapLibre, the basemap tiles, and glyphs/sprite over the network when opened (the
17
+ * house infra).
18
+ *
19
+ * SERVE IT OVER LOCALHOST, don't open it as a file. The house tile server (`tiles.sister.software`)
20
+ * CORS-restricts to localhost + the docs domains, so a `file://` page shows the (accurate)
21
+ * markers on a blank basemap. `npx serve` / `python3 -m http.server` in the output directory is
22
+ * enough; the page also surfaces a hint banner when it detects it's running from `file://`.
23
+ *
24
+ * Neutral entity-resolution view: it shows what resolved to what and how confidently (cohesion).
25
+ * Bucket labels render verbatim from the data, never editorialized.
26
+ */
27
+ import { layers, namedFlavor } from "@protomaps/basemaps";
28
+ /** MapLibre GL release the page pins (CDN + SRI). Matches the workspace's `maplibre-gl` major. */
29
+ const MAPLIBRE_VERSION = "5.24.0";
30
+ const MAPLIBRE_JS_SRI = "sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp";
31
+ const MAPLIBRE_CSS_SRI = "sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK";
32
+ /**
33
+ * The house Protomaps basemap: `basemap-v4` PMTiles (tile-worker → R2 at `tiles.sister.software`,
34
+ * which sends CORS for localhost + the docs domains).
35
+ *
36
+ * Glyphs + sprite come from the UPSTREAM Protomaps assets (GitHub Pages, `ACAO: *`), not the house
37
+ * mirror at `public.sister.software` — that bucket sends no CORS headers, so the mirror can't be
38
+ * fetched cross-origin (`cartographer/base/composition.ts` flags the same: "Currently upstream
39
+ * URLs; we mirror these … but no public route fronts that bucket yet"). The upstream assets target
40
+ * the v4 schema, matching the `basemap-v4` tiles. Swap to the house mirror once it has a
41
+ * CORS-enabled route.
42
+ */
43
+ const BASEMAP_SOURCE_ID = "basemap-v4";
44
+ const BASEMAP_TILEJSON_URL = "https://tiles.sister.software/basemap-v4.json";
45
+ const GLYPHS_URL = "https://protomaps.github.io/basemaps-assets/fonts/{fontstack}/{range}.pbf";
46
+ const SPRITE_URL = "https://protomaps.github.io/basemaps-assets/sprites/v4/light";
47
+ /** Categorical palette (reused for buckets; cycles if there are more buckets than entries). */
48
+ const PALETTE = ["#2f9e44", "#f08c00", "#1971c2", "#e8590c", "#9c36b5", "#0c8599", "#e03131", "#5c940d"];
49
+ const SINGLE_COLOR = "#3388ff"; // single-source entity
50
+ const CROSS_COLOR = "#e8590c"; // cross-dataset link (≥2 sources)
51
+ /**
52
+ * Escape a value for safe inlining inside a `<script>` as JSON. `JSON.stringify` alone isn't enough
53
+ * — a record value containing `</script>` would close the block early; escaping `<`/`>`/`&` to
54
+ * `\uXXXX` keeps the JSON valid and makes a breakout impossible.
55
+ */
56
+ function safeJsonForScript(value) {
57
+ return JSON.stringify(value).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026");
58
+ }
59
+ /** Escape text for the HTML document body (title/heading), not the inlined script. */
60
+ function escapeHtml(text) {
61
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
62
+ }
63
+ function sourceCount(props) {
64
+ return Array.isArray(props["sources"]) ? props["sources"].length : 0;
65
+ }
66
+ /**
67
+ * Render `geojson` (a {@link toGeoJSON} / reconciliation FeatureCollection) as a complete,
68
+ * standalone HTML document. Entities without a coordinate are already absent from those
69
+ * collections; an empty collection renders a friendly empty state rather than a broken map.
70
+ */
71
+ export function toMapHTML(geojson, options = {}) {
72
+ const title = options.title ?? "Mailwoman — resolved entities";
73
+ const flavorName = options.flavor ?? "light";
74
+ const colorBy = options.colorBy ?? "auto";
75
+ const hasBuckets = geojson.features.some((f) => f.properties?.["bucket"] != null);
76
+ const mode = colorBy === "auto" ? (hasBuckets ? "bucket" : "sources") : colorBy;
77
+ // Assign a color to each distinct bucket value, in first-seen order.
78
+ const bucketColors = {};
79
+ if (mode === "bucket") {
80
+ let i = 0;
81
+ for (const f of geojson.features) {
82
+ const b = f.properties?.["bucket"] != null ? String(f.properties["bucket"]) : "—";
83
+ if (!(b in bucketColors))
84
+ bucketColors[b] = PALETTE[i++ % PALETTE.length];
85
+ }
86
+ }
87
+ const colorFor = (props) => {
88
+ if (mode === "bucket") {
89
+ const b = props["bucket"] != null ? String(props["bucket"]) : "—";
90
+ return bucketColors[b] ?? SINGLE_COLOR;
91
+ }
92
+ return sourceCount(props) >= 2 ? CROSS_COLOR : SINGLE_COLOR;
93
+ };
94
+ // Precompute the per-feature color (`_color`) so the circle layer is a simple `["get","_color"]`,
95
+ // and the bounding box, both at generate time. The data is copied (not mutated in place).
96
+ let minLng = Infinity;
97
+ let minLat = Infinity;
98
+ let maxLng = -Infinity;
99
+ let maxLat = -Infinity;
100
+ const features = geojson.features.map((f) => {
101
+ const [lng, lat] = f.geometry.coordinates;
102
+ if (lng < minLng)
103
+ minLng = lng;
104
+ if (lat < minLat)
105
+ minLat = lat;
106
+ if (lng > maxLng)
107
+ maxLng = lng;
108
+ if (lat > maxLat)
109
+ maxLat = lat;
110
+ return { ...f, properties: { ...f.properties, _color: colorFor(f.properties) } };
111
+ });
112
+ const bbox = features.length ? [[minLng, minLat], [maxLng, maxLat]] : null;
113
+ // The full MapLibre style: the Protomaps basemap layers (generated here) over the house basemap-v4
114
+ // vector source, plus our inlined entities source + a circle layer keyed off the precomputed color.
115
+ const style = {
116
+ version: 8,
117
+ glyphs: GLYPHS_URL,
118
+ sprite: SPRITE_URL,
119
+ sources: {
120
+ [BASEMAP_SOURCE_ID]: { type: "vector", url: BASEMAP_TILEJSON_URL },
121
+ entities: { type: "geojson", data: { type: "FeatureCollection", features } },
122
+ },
123
+ layers: [
124
+ ...layers(BASEMAP_SOURCE_ID, namedFlavor(flavorName), { lang: "en" }),
125
+ {
126
+ id: "mw-entities",
127
+ type: "circle",
128
+ source: "entities",
129
+ paint: {
130
+ "circle-radius": [
131
+ "interpolate",
132
+ ["linear"],
133
+ ["coalesce", ["get", "recordCount"], 1],
134
+ 1,
135
+ 5,
136
+ 5,
137
+ 9,
138
+ 25,
139
+ 15,
140
+ 100,
141
+ 22,
142
+ ],
143
+ "circle-color": ["get", "_color"],
144
+ "circle-stroke-color": "#ffffff",
145
+ "circle-stroke-width": 1.2,
146
+ "circle-opacity": 0.9,
147
+ },
148
+ },
149
+ ],
150
+ };
151
+ // Legend rows, built here so arbitrary bucket sets render without client-side guessing.
152
+ const legendRows = mode === "bucket"
153
+ ? Object.entries(bucketColors)
154
+ .map(([b, c]) => `<div><i style="background:${c}"></i>${escapeHtml(b)}</div>`)
155
+ .join("")
156
+ : `<div><i style="background:${CROSS_COLOR}"></i>cross-dataset link (&ge;2 sources)</div>` +
157
+ `<div><i style="background:${SINGLE_COLOR}"></i>single-source entity</div>` +
158
+ `<div class="muted" style="margin-top:4px">marker size = records merged</div>`;
159
+ const crossLinks = geojson.features.filter((f) => sourceCount(f.properties) >= 2).length;
160
+ const summary = `${geojson.features.length} entities` + (mode === "sources" ? ` &middot; ${crossLinks} cross-dataset links` : "");
161
+ // The client script avoids template literals and `${` so it survives this outer template verbatim.
162
+ return `<!doctype html>
163
+ <html lang="en">
164
+ <head>
165
+ <meta charset="utf-8" />
166
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
167
+ <title>${escapeHtml(title)}</title>
168
+ <link
169
+ rel="stylesheet"
170
+ href="https://unpkg.com/maplibre-gl@${MAPLIBRE_VERSION}/dist/maplibre-gl.css"
171
+ integrity="${MAPLIBRE_CSS_SRI}"
172
+ crossorigin="anonymous" />
173
+ <style>
174
+ html, body { margin: 0; height: 100%; font-family: system-ui, -apple-system, "Segoe UI", sans-serif; }
175
+ #map { position: absolute; inset: 0; }
176
+ .mw-panel {
177
+ position: absolute; z-index: 1; background: rgba(255,255,255,0.94); padding: 10px 12px;
178
+ border-radius: 8px; box-shadow: 0 1px 6px rgba(0,0,0,0.3); font-size: 12px; line-height: 1.5; color: #1a1a1a;
179
+ }
180
+ .mw-title { top: 10px; left: 10px; max-width: 60%; }
181
+ .mw-title h1 { font-size: 14px; margin: 0 0 4px; }
182
+ .mw-legend { bottom: 22px; right: 10px; }
183
+ .mw-legend i { display: inline-block; width: 12px; height: 12px; margin-right: 6px; border-radius: 50%; vertical-align: -1px; }
184
+ .muted { color: #666; }
185
+ .mw-popup { font-size: 12px; line-height: 1.5; max-width: 260px; }
186
+ .mw-popup .nm { font-weight: 600; font-size: 13px; }
187
+ .mw-popup dt { color: #666; display: inline; }
188
+ .mw-popup .link { color: ${CROSS_COLOR}; font-weight: 600; }
189
+ .mw-empty { position: absolute; inset: 0; display: grid; place-items: center; z-index: 1; }
190
+ .mw-warn { bottom: 10px; left: 10px; max-width: 52%; background: rgba(255,243,205,0.97); }
191
+ .mw-warn code { background: rgba(0,0,0,0.06); padding: 0 3px; border-radius: 3px; }
192
+ </style>
193
+ </head>
194
+ <body>
195
+ <div id="map"></div>
196
+ <div class="mw-panel mw-title"><h1>${escapeHtml(title)}</h1><div class="muted">${summary}</div></div>
197
+ ${features.length ? `<div class="mw-panel mw-legend">${legendRows}</div>` : `<div class="mw-empty"><div class="mw-panel"><h1>${escapeHtml(title)}</h1><div class="muted">No geocoded entities to display.</div></div></div>`}
198
+ <script
199
+ src="https://unpkg.com/maplibre-gl@${MAPLIBRE_VERSION}/dist/maplibre-gl.js"
200
+ integrity="${MAPLIBRE_JS_SRI}"
201
+ crossorigin="anonymous"></script>
202
+ <script>
203
+ "use strict";
204
+ var STYLE = ${safeJsonForScript(style)};
205
+ var BBOX = ${safeJsonForScript(bbox)};
206
+
207
+ function esc(v) {
208
+ if (v === null || v === undefined) return "";
209
+ return String(v).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
210
+ }
211
+
212
+ function popupHtml(p) {
213
+ var rows = [];
214
+ var heading = p.name || p.organization || p.entityId || "entity";
215
+ rows.push('<div class="nm">' + esc(heading) + '</div>');
216
+ if (p.organization && p.organization !== p.name) rows.push('<div>' + esc(p.organization) + '</div>');
217
+ if (p.address) rows.push('<div class="muted">' + esc(p.address) + '</div>');
218
+ rows.push('<hr style="border:none;border-top:1px solid #eee;margin:6px 0" />');
219
+ rows.push('<div><dt>records merged:</dt> ' + esc(p.recordCount) + '</div>');
220
+ var srcs = Array.isArray(p.sources) ? p.sources : [];
221
+ if (srcs.length) {
222
+ var cls = srcs.length >= 2 ? ' class="link"' : '';
223
+ rows.push('<div><dt>sources:</dt> <span' + cls + '>' + esc(srcs.join(", ")) + '</span>'
224
+ + (srcs.length >= 2 ? ' (cross-dataset link)' : '') + '</div>');
225
+ }
226
+ if (p.bucket !== null && p.bucket !== undefined) rows.push('<div><dt>bucket:</dt> ' + esc(p.bucket) + '</div>');
227
+ if (p.cohesion !== null && p.cohesion !== undefined) rows.push('<div><dt>cohesion:</dt> ' + esc(p.cohesion) + ' bits</div>');
228
+ if (p.geocodeTier) rows.push('<div><dt>geocode tier:</dt> ' + esc(p.geocodeTier) + '</div>');
229
+ return '<div class="mw-popup">' + rows.join("") + '</div>';
230
+ }
231
+
232
+ var map = new maplibregl.Map({ container: "map", style: STYLE, center: [-98, 39], zoom: 3, attributionControl: { compact: true } });
233
+ map.addControl(new maplibregl.NavigationControl({ showCompass: false }), "top-right");
234
+
235
+ map.on("load", function () {
236
+ if (BBOX) map.fitBounds(BBOX, { padding: 56, maxZoom: 15, duration: 0 });
237
+ });
238
+ map.on("click", "mw-entities", function (e) {
239
+ var f = e.features && e.features[0];
240
+ if (!f) return;
241
+ new maplibregl.Popup({ closeButton: true }).setLngLat(e.lngLat).setHTML(popupHtml(f.properties || {})).addTo(map);
242
+ });
243
+ map.on("mouseenter", "mw-entities", function () { map.getCanvas().style.cursor = "pointer"; });
244
+ map.on("mouseleave", "mw-entities", function () { map.getCanvas().style.cursor = ""; });
245
+
246
+ // The house basemap tiles are CORS-restricted to localhost + the docs domain, so a page opened
247
+ // straight off disk (file://) shows the markers on a blank basemap. Make that explicit rather than
248
+ // silent — the entity positions are correct regardless of whether the basemap paints.
249
+ if (location.protocol === "file:") {
250
+ var warn = document.createElement("div");
251
+ warn.className = "mw-panel mw-warn";
252
+ warn.innerHTML = "⚠ Basemap tiles are blocked from <code>file://</code>. Serve this over "
253
+ + "<code>http://localhost</code> (e.g. <code>npx serve</code> or <code>python3 -m http.server</code>) "
254
+ + "to see the map background — the markers are accurate either way.";
255
+ document.body.appendChild(warn);
256
+ }
257
+ </script>
258
+ </body>
259
+ </html>
260
+ `;
261
+ }
262
+ //# sourceMappingURL=map-html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"map-html.js","sourceRoot":"","sources":["../map-html.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * Coverage reconciliation (#621) — the library home for what `scripts/record-matcher/
7
+ * coverage-reconciliation.ts` does inline, so the CLI (`registry --reconcile`) and any consumer
8
+ * can reuse it.
9
+ *
10
+ * Given entities already resolved ACROSS sources (#618), classify each by which KIND of source its
11
+ * records come from. You tag each source label as either an **eligibility** source (it denotes
12
+ * membership in some base set — e.g. registered providers/facilities) or a **funding/enrollment**
13
+ * source (it denotes participation in a program). Three buckets fall out per entity:
14
+ *
15
+ * - **enrolled** — resolves to BOTH an eligibility and a funding record.
16
+ * - **eligible, not enrolled** — an eligibility record with NO funding record resolving to it (the
17
+ * ANTI-JOIN).
18
+ * - **funded, not in the eligibility set** — a funding record with no eligibility record resolving to
19
+ * it.
20
+ *
21
+ * This is strictly a **set-membership reconciliation, never a determination.** We produce the
22
+ * reconciled join and surface the candidate set; what a gap MEANS — and whether it is real, a
23
+ * sampling artifact, or actionable — is entirely the data consumer's call. Nothing here is an
24
+ * allegation. {@link reconciliationReport} bakes that caveat in by construction.
25
+ *
26
+ * Pairs with {@link toMapHTML}: {@link reconciliationGeoJSON} tags each feature with its `bucket`,
27
+ * which the map auto-detects and colors categorically.
28
+ */
29
+ import type { GeoJsonFeatureCollection, ResolvedEntity } from "./types.js";
30
+ /** The three reconciliation buckets an entity can fall into. */
31
+ export type ReconciliationBucket = "enrolled" | "eligible-not-enrolled" | "funded-not-eligible";
32
+ /** Which source labels denote eligibility vs funding/enrollment. */
33
+ export interface ReconcileConfig {
34
+ /** Source labels denoting membership in the base/eligibility set. */
35
+ eligibilitySources: readonly string[];
36
+ /** Source labels denoting enrollment / funding / program participation. */
37
+ fundingSources: readonly string[];
38
+ }
39
+ /** One entity, classified. */
40
+ export interface ReconciledEntity {
41
+ entity: ResolvedEntity;
42
+ /** Distinct provenance labels the entity's records span, sorted. */
43
+ sources: string[];
44
+ bucket: ReconciliationBucket;
45
+ }
46
+ export interface ReconciliationResult {
47
+ /** Entities that carry at least one eligibility- or funding-tagged source, each bucketed. */
48
+ reconciled: ReconciledEntity[];
49
+ counts: Record<ReconciliationBucket, number>;
50
+ }
51
+ /**
52
+ * Bucket an entity from the source labels its records span. Returns `null` when the entity carries
53
+ * NO eligibility- or funding-tagged source (it is outside this reconciliation — e.g. a source the
54
+ * caller didn't assign a role) so callers can exclude it rather than silently miscount it.
55
+ */
56
+ export declare function bucketOf(sources: Iterable<string>, config: ReconcileConfig): ReconciliationBucket | null;
57
+ /**
58
+ * Classify resolved entities into reconciliation buckets. Entities with no eligibility- or
59
+ * funding-tagged source are excluded (see {@link bucketOf}).
60
+ */
61
+ export declare function reconcileCoverage(entities: readonly ResolvedEntity[], config: ReconcileConfig): ReconciliationResult;
62
+ /**
63
+ * GeoJSON of every located reconciled entity, each feature tagged with its `bucket` + `sources` —
64
+ * the shape {@link toMapHTML} colors categorically by bucket. Entities without a coordinate are
65
+ * skipped.
66
+ */
67
+ export declare function reconciliationGeoJSON(result: ReconciliationResult): GeoJsonFeatureCollection;
68
+ export interface ReconciliationReportOptions {
69
+ /** H1 title. Default: "Coverage reconciliation — eligibility ↔ enrollment". */
70
+ title?: string;
71
+ /** An italic scope paragraph under the title (what the sources are, how they were scoped). */
72
+ scopeNote?: string;
73
+ /** A paragraph about the scorer choice (e.g. why the FS spine, not the dedup GBT). */
74
+ scorerNote?: string;
75
+ /** A paragraph about sampling/capping, woven into the caveat. */
76
+ sampleNote?: string;
77
+ /** How many "eligible, not enrolled" rows to spot-check. Default 15. */
78
+ spotCheckLimit?: number;
79
+ }
80
+ /**
81
+ * A markdown reconciliation report: the bucket counts, the enrolled-rate floor, an anti-join
82
+ * spot-check, and — always, by construction — the neutral caveat. The deliverable is the anti-join
83
+ * SET, not a rate, and never an allegation.
84
+ */
85
+ export declare function reconciliationReport(result: ReconciliationResult, options?: ReconciliationReportOptions): string;
86
+ //# sourceMappingURL=reconcile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconcile.d.ts","sourceRoot":"","sources":["../reconcile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,wBAAwB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAE1E,gEAAgE;AAChE,MAAM,MAAM,oBAAoB,GAAG,UAAU,GAAG,uBAAuB,GAAG,qBAAqB,CAAA;AAE/F,oEAAoE;AACpE,MAAM,WAAW,eAAe;IAC/B,qEAAqE;IACrE,kBAAkB,EAAE,SAAS,MAAM,EAAE,CAAA;IACrC,2EAA2E;IAC3E,cAAc,EAAE,SAAS,MAAM,EAAE,CAAA;CACjC;AAED,8BAA8B;AAC9B,MAAM,WAAW,gBAAgB;IAChC,MAAM,EAAE,cAAc,CAAA;IACtB,oEAAoE;IACpE,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,MAAM,EAAE,oBAAoB,CAAA;CAC5B;AAED,MAAM,WAAW,oBAAoB;IACpC,6FAA6F;IAC7F,UAAU,EAAE,gBAAgB,EAAE,CAAA;IAC9B,MAAM,EAAE,MAAM,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAA;CAC5C;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,eAAe,GAAG,oBAAoB,GAAG,IAAI,CAaxG;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,SAAS,cAAc,EAAE,EAAE,MAAM,EAAE,eAAe,GAAG,oBAAoB,CAepH;AASD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,oBAAoB,GAAG,wBAAwB,CAoB5F;AAED,MAAM,WAAW,2BAA2B;IAC3C,+EAA+E;IAC/E,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,8FAA8F;IAC9F,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,sFAAsF;IACtF,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,wEAAwE;IACxE,cAAc,CAAC,EAAE,MAAM,CAAA;CACvB;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,oBAAoB,EAAE,OAAO,GAAE,2BAAgC,GAAG,MAAM,CA0DpH"}
@@ -0,0 +1,161 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * Coverage reconciliation (#621) — the library home for what `scripts/record-matcher/
7
+ * coverage-reconciliation.ts` does inline, so the CLI (`registry --reconcile`) and any consumer
8
+ * can reuse it.
9
+ *
10
+ * Given entities already resolved ACROSS sources (#618), classify each by which KIND of source its
11
+ * records come from. You tag each source label as either an **eligibility** source (it denotes
12
+ * membership in some base set — e.g. registered providers/facilities) or a **funding/enrollment**
13
+ * source (it denotes participation in a program). Three buckets fall out per entity:
14
+ *
15
+ * - **enrolled** — resolves to BOTH an eligibility and a funding record.
16
+ * - **eligible, not enrolled** — an eligibility record with NO funding record resolving to it (the
17
+ * ANTI-JOIN).
18
+ * - **funded, not in the eligibility set** — a funding record with no eligibility record resolving to
19
+ * it.
20
+ *
21
+ * This is strictly a **set-membership reconciliation, never a determination.** We produce the
22
+ * reconciled join and surface the candidate set; what a gap MEANS — and whether it is real, a
23
+ * sampling artifact, or actionable — is entirely the data consumer's call. Nothing here is an
24
+ * allegation. {@link reconciliationReport} bakes that caveat in by construction.
25
+ *
26
+ * Pairs with {@link toMapHTML}: {@link reconciliationGeoJSON} tags each feature with its `bucket`,
27
+ * which the map auto-detects and colors categorically.
28
+ */
29
+ /**
30
+ * Bucket an entity from the source labels its records span. Returns `null` when the entity carries
31
+ * NO eligibility- or funding-tagged source (it is outside this reconciliation — e.g. a source the
32
+ * caller didn't assign a role) so callers can exclude it rather than silently miscount it.
33
+ */
34
+ export function bucketOf(sources, config) {
35
+ const elig = new Set(config.eligibilitySources);
36
+ const fund = new Set(config.fundingSources);
37
+ let hasEligibility = false;
38
+ let hasFunding = false;
39
+ for (const s of sources) {
40
+ if (elig.has(s))
41
+ hasEligibility = true;
42
+ if (fund.has(s))
43
+ hasFunding = true;
44
+ }
45
+ if (hasEligibility && hasFunding)
46
+ return "enrolled";
47
+ if (hasEligibility)
48
+ return "eligible-not-enrolled";
49
+ if (hasFunding)
50
+ return "funded-not-eligible";
51
+ return null;
52
+ }
53
+ /**
54
+ * Classify resolved entities into reconciliation buckets. Entities with no eligibility- or
55
+ * funding-tagged source are excluded (see {@link bucketOf}).
56
+ */
57
+ export function reconcileCoverage(entities, config) {
58
+ const reconciled = [];
59
+ const counts = {
60
+ enrolled: 0,
61
+ "eligible-not-enrolled": 0,
62
+ "funded-not-eligible": 0,
63
+ };
64
+ for (const entity of entities) {
65
+ const sources = [...new Set(entity.records.map((r) => r.source).filter((s) => !!s))].sort();
66
+ const bucket = bucketOf(sources, config);
67
+ if (!bucket)
68
+ continue;
69
+ reconciled.push({ entity, sources, bucket });
70
+ counts[bucket]++;
71
+ }
72
+ return { reconciled, counts };
73
+ }
74
+ /** A display name for a reconciled entity's representative record. */
75
+ function repName(entity) {
76
+ const rep = entity.representative;
77
+ const person = [rep.name?.given, rep.name?.family].filter(Boolean).join(" ");
78
+ return rep.organization?.canonical ?? (person || rep.id);
79
+ }
80
+ /**
81
+ * GeoJSON of every located reconciled entity, each feature tagged with its `bucket` + `sources` —
82
+ * the shape {@link toMapHTML} colors categorically by bucket. Entities without a coordinate are
83
+ * skipped.
84
+ */
85
+ export function reconciliationGeoJSON(result) {
86
+ return {
87
+ type: "FeatureCollection",
88
+ features: result.reconciled
89
+ .filter((c) => c.entity.coordinate)
90
+ .map((c) => ({
91
+ type: "Feature",
92
+ geometry: {
93
+ type: "Point",
94
+ coordinates: [c.entity.coordinate.longitude, c.entity.coordinate.latitude],
95
+ },
96
+ properties: {
97
+ entityId: c.entity.id,
98
+ bucket: c.bucket,
99
+ sources: c.sources,
100
+ name: repName(c.entity),
101
+ recordCount: c.entity.records.length,
102
+ },
103
+ })),
104
+ };
105
+ }
106
+ /**
107
+ * A markdown reconciliation report: the bucket counts, the enrolled-rate floor, an anti-join
108
+ * spot-check, and — always, by construction — the neutral caveat. The deliverable is the anti-join
109
+ * SET, not a rate, and never an allegation.
110
+ */
111
+ export function reconciliationReport(result, options = {}) {
112
+ const { counts, reconciled } = result;
113
+ const title = options.title ?? "Coverage reconciliation — eligibility ↔ enrollment";
114
+ const spotCheckLimit = options.spotCheckLimit ?? 15;
115
+ const eligibleTotal = counts.enrolled + counts["eligible-not-enrolled"];
116
+ const enrolledRate = eligibleTotal > 0 ? (100 * counts.enrolled) / eligibleTotal : 0;
117
+ const lines = [];
118
+ lines.push(`# ${title}`);
119
+ lines.push("");
120
+ if (options.scopeNote) {
121
+ lines.push(`_${options.scopeNote}_`);
122
+ lines.push("");
123
+ }
124
+ if (options.scorerNote) {
125
+ lines.push(options.scorerNote);
126
+ lines.push("");
127
+ }
128
+ lines.push(`## The reconciliation`);
129
+ lines.push("");
130
+ lines.push(`| bucket | entities | meaning |`);
131
+ lines.push(`|---|---:|---|`);
132
+ lines.push(`| **enrolled** | ${counts.enrolled} | resolves to an eligibility record AND a funding record |`);
133
+ lines.push(`| **eligible, not enrolled** | ${counts["eligible-not-enrolled"]} | eligibility record, no funding record resolved (the **anti-join**) |`);
134
+ lines.push(`| **funded, not in eligibility set** | ${counts["funded-not-eligible"]} | funding record, no eligibility record resolved |`);
135
+ lines.push("");
136
+ lines.push(`Of the ${eligibleTotal} entities with an eligibility record, ${enrolledRate.toFixed(1)}% also resolve to a ` +
137
+ `funding record — a **floor**, not a coverage rate (imperfect resolution + any sampling only ever miss ` +
138
+ `links, never invent them). The deliverable is the anti-join SET, not this percentage.`);
139
+ lines.push("");
140
+ lines.push(`## Anti-join spot-check — first ${spotCheckLimit} "eligible, not enrolled"`);
141
+ lines.push("");
142
+ lines.push(`| entity | sources | name | coordinate |`);
143
+ lines.push(`|---|---|---|---|`);
144
+ for (const c of reconciled.filter((x) => x.bucket === "eligible-not-enrolled").slice(0, spotCheckLimit)) {
145
+ const coord = c.entity.coordinate
146
+ ? `${c.entity.coordinate.latitude.toFixed(4)}, ${c.entity.coordinate.longitude.toFixed(4)}`
147
+ : "—";
148
+ lines.push(`| ${c.entity.id} | ${c.sources.join(", ")} | ${repName(c.entity)} | ${coord} |`);
149
+ }
150
+ lines.push("");
151
+ lines.push(`## The caveat that matters`);
152
+ lines.push("");
153
+ const sample = options.sampleNote ? `${options.sampleNote} ` : "";
154
+ lines.push(`${sample}This is a **set-membership reconciliation, not a determination**. A missing funding record can mean ` +
155
+ `the entity didn't apply, applied under a name we didn't resolve, is ineligible, or any number of things. We ` +
156
+ `produce the reconciled join and surface the candidate set; **what a gap means, and whether to act on it, is ` +
157
+ `entirely the data consumer's call.** Nothing here is an allegation.`);
158
+ lines.push("");
159
+ return lines.join("\n");
160
+ }
161
+ //# sourceMappingURL=reconcile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconcile.js","sourceRoot":"","sources":["../reconcile.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mailwoman/registry",
3
- "version": "4.10.0",
3
+ "version": "4.11.0",
4
4
  "description": "The geocode-first record-matching application: resolve messy contact/organization records into canonical, geocoded entities (block → score → cluster) and export them as GeoJSON for spatial analysis. The clinic-funding use case mailwoman was built for.",
5
5
  "license": "AGPL-3.0-only",
6
6
  "repository": {
@@ -17,9 +17,10 @@
17
17
  "./ingest": "./out/ingest.js"
18
18
  },
19
19
  "dependencies": {
20
- "@mailwoman/address-id": "4.10.0",
21
- "@mailwoman/match": "4.10.0",
22
- "@mailwoman/record": "4.10.0",
20
+ "@mailwoman/address-id": "4.11.0",
21
+ "@mailwoman/match": "4.11.0",
22
+ "@mailwoman/record": "4.11.0",
23
+ "@protomaps/basemaps": "^5.7.2",
23
24
  "csv-parse": "^5.6.0",
24
25
  "spliterator": "^2.0.0"
25
26
  },