@org-design-studio/core 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.
package/docs/API.md ADDED
@@ -0,0 +1,202 @@
1
+ # `@org-design-studio/core` — API reference
2
+
3
+ The org-design analytics engine packaged as an embeddable TypeScript library: spans & layers
4
+ diagnostics, data validation and import mapping, a scenario engine with transition-cost-netted
5
+ economics, deterministic insight generation, and a headless org-chart layout. No React, no DOM,
6
+ no storage — the only runtime dependency is `d3-hierarchy`.
7
+
8
+ - Entry point: a single curated barrel (`src/lib/core.ts` in this repo; `@org-design-studio/core` when published).
9
+ - Build: `npm run build:core` (tsup → `dist-core/` with ESM + CJS + `.d.ts`).
10
+ - Publish manifest template: `packages/core-package.json`.
11
+
12
+ ## Quickstart
13
+
14
+ ```bash
15
+ npm install @org-design-studio/core
16
+ ```
17
+
18
+ ```ts
19
+ import {
20
+ generateDemoData,
21
+ computeMetrics,
22
+ generateInsightsFromEmployees,
23
+ DEFAULT_ASSUMPTIONS,
24
+ } from "@org-design-studio/core";
25
+
26
+ const employees = generateDemoData(); // or your own Employee[]
27
+ const metrics = computeMetrics(employees, DEFAULT_ASSUMPTIONS);
28
+ const insights = generateInsightsFromEmployees(employees, DEFAULT_ASSUMPTIONS);
29
+
30
+ console.log(metrics.headcount, metrics.maxLayers, metrics.totalOpportunity);
31
+ console.log(insights[0].title, insights[0].recommendation);
32
+ ```
33
+
34
+ ## Methodology guarantees
35
+
36
+ These hold for every function in the public surface and are covered by the test suite:
37
+
38
+ 1. **Level-aware spans.** Narrow/wide classification uses per-level target bands
39
+ (`Assumptions.targetSpansByLevel`), not one global threshold. A frontline manager with 6
40
+ reports and an executive with 6 reports are judged against different bands; flat thresholds
41
+ (`narrowSpanThreshold`/`wideSpanThreshold`) apply only to unleveled roles.
42
+ 2. **Disjoint levers.** The savings opportunities in `OrgMetrics` (delayering, span optimization,
43
+ duplication) are attributed to disjoint employee sets, so `totalOpportunity` never
44
+ double-counts a person.
45
+ 3. **Transition-cost-netted scenarios.** `evaluateScenario` reports run-rate saving separately
46
+ from year-1 saving; year-1 is pro-rated by effective month (`year1CapturePct`) and netted
47
+ against severance/backfill/change cost (`computeTransitionCost`).
48
+ 4. **Deterministic.** Same inputs → same outputs. No randomness, network, or clock reads in the
49
+ analytics path; `generateDemoData()` returns the same ~300-person org every call.
50
+
51
+ ## Modules
52
+
53
+ ### Data model & assumptions (`types`)
54
+
55
+ | Export | Notes |
56
+ | --- | --- |
57
+ | `Employee` | The canonical input row. Required identity fields plus `level`, `fte`, `salary`, `bonus`, `total_cost`, `status`, org dimensions (`function`, `business_unit`, `location`, …). |
58
+ | `Assumptions` / `DEFAULT_ASSUMPTIONS` | All methodology knobs: span bands per level, target layers, saving percentages, location cost multipliers, transition assumptions, scoring weights. Start from `DEFAULT_ASSUMPTIONS` and spread overrides. |
59
+ | `OrgMetrics`, `OrgHealthScore`, `LayerStat`, `SpanBucket`, `DuplicateRoleGroup` | Output shapes of the analytics module. |
60
+ | `Scenario`, `ScenarioChange`, `ScenarioEvaluation`, `ChangeRisk`, `ScenarioScore` | Scenario engine shapes. `ScenarioChange` is a discriminated union: `move`, `remove_role`, `add_role`, `merge_teams`, `delayer`, `location_shift`, `cost_change`. |
61
+ | `Insight`, `InsightCategory` | Insight output shape (title, narrative, recommendation, impact $, confidence, ease, rank). |
62
+ | `DataQualityReport`, `DataQualityIssue`, `IssueSeverity` | Validation output shapes. |
63
+ | `REQUIRED_FIELDS`, `OPTIONAL_FIELDS`, `FieldMapping` | Canonical field lists for import mapping. |
64
+ | `MONTH_NAMES`, `DEFAULT_TITLE_NOISE_WORDS` | Methodology constants. |
65
+
66
+ ### Hierarchy (`hierarchy`)
67
+
68
+ - `buildOrgTree(employees): OrgTree` — builds the reporting tree once; computes layer, span,
69
+ downstream headcount/cost per node, and surfaces `orphans` (manager id not found) and
70
+ `circular` (reporting cycles) instead of throwing. Every other module consumes this.
71
+ - `subtreeIds(tree, id): string[]` — an employee plus everyone below them.
72
+ - `deepestChains(tree, topN?)` — the longest reporting chains (for layer diagnostics).
73
+ - `wouldCreateCycle(tree, employeeId, newManagerId): boolean` — guard for re-parenting UIs.
74
+
75
+ ### Analytics (`analytics`)
76
+
77
+ - `computeMetrics(employees, assumptions): OrgMetrics` — the headline call. Headcount/cost
78
+ rollups by function/BU/location/grade/layer, span buckets, layer stats, manager-of-one and
79
+ pure-supervisory-layer detection, AI-augmentation exposure, duplicate roles, and the three
80
+ disjoint savings opportunities plus `orgHealth`.
81
+ - `computeOrgHealth(inputs, assumptions): OrgHealthScore` — explainable 0–100 composite
82
+ (layer health, span health, manager ratio, management cost, duplication), each component
83
+ scored and weighted with a plain-language explanation. Already included in
84
+ `computeMetrics().orgHealth`; exported separately for custom pipelines.
85
+ - `spanBandForLevel(level, assumptions)` / `classifySpan(span, level, assumptions)` — the
86
+ level-aware span methodology, exposed directly so hosts can render their own span flags.
87
+ - `findDuplicateRoles(employees, assumptions)` / `normalizeTitle(title, noiseWords?)` —
88
+ duplication analysis with seniority-noise-stripped title normalization.
89
+ - `classifyAiAugmentation(employee, assumptions)` — explicit role-family mapping with a
90
+ conservative title-keyword fallback; augmentation potential, not an elimination judgement.
91
+
92
+ ### Validation & import (`validation`)
93
+
94
+ - `validateEmployees(employees): DataQualityReport` — 0–100 quality score with banded verdict
95
+ and per-issue employee id lists (missing managers, cycles, duplicate ids, cost outliers, …).
96
+ - `autoMapColumns(columns, fields)` — fuzzy-maps a host system's column headers
97
+ ("Employee ID", "Mgr", "Job Title", …) onto the canonical `Employee` fields.
98
+ - `rowsToEmployees(rows, mapping)` — coerces untyped records into typed `Employee[]` using a
99
+ `FieldMapping` (numbers parsed, blanks → null manager, sensible defaults).
100
+
101
+ ### Scenario engine (`scenario`)
102
+
103
+ - `applyChanges(baseline, changes): ApplyResult` — pure function from a baseline plus a list of
104
+ `ScenarioChange`s to a future-state `Employee[]` (with `warnings` for changes that no longer
105
+ apply). Never mutates the baseline.
106
+ - `evaluateScenario(baseline, scenario, baselineMetrics, assumptions): ScenarioEvaluation` —
107
+ full economics: cost/headcount/layer deltas, change risk, weighted score, `runRateSaving`,
108
+ `transitionCost`, and `netYear1Saving = runRateSaving × year1CapturePct − transitionCost`.
109
+ - `computeTransitionCost(baseline, after, assumptions)` — severance by level band with country
110
+ multipliers, backfill percentage, and per-move one-time change cost.
111
+ - `year1CapturePct(assumptions)` — share of run-rate captured in year 1 given the effective month.
112
+ - `computeChangeRisk(...)` / `scoreScenario(...)` — the risk and scoring sub-steps, exported for
113
+ custom dashboards.
114
+ - `diffEmployees(baseline, scenario)` — moved/removed/added/changed sets between two states.
115
+ - `suggestConsolidations(employees, assumptions)` — machine-generated candidate actions
116
+ (`SuggestedAction[]`) with rationale and indicative saving.
117
+
118
+ ### Insights (`insights`)
119
+
120
+ - `generateInsightsFromEmployees(employees, assumptions, scenarioEvaluations?)` — convenience:
121
+ metrics + insights in one call.
122
+ - `generateInsights(ctx: InsightContext)` — same, when you already have `OrgMetrics`.
123
+ - `rankInsights(insights)` — deterministic re-ranking by impact, reach, confidence, and ease;
124
+ sets `rank` starting at 1.
125
+
126
+ ### Org-chart layout (`org-layout`)
127
+
128
+ Headless tidy-tree layout over `d3-hierarchy` — returns coordinates only, you render.
129
+
130
+ - `layoutOrg(tree, rootIds, isExpanded, opts): CanvasLayout` — positioned nodes and edges plus
131
+ bounds. `FULL_NODE` / `COMPACT_NODE` are ready-made `LayoutOptions`.
132
+ - `elbowPath(x1, y1, x2, y2): string` — SVG path string for an org-chart elbow connector.
133
+ - `fitToBounds(bounds, w, h, …): Viewport` / `zoomAround(vp, x, y, factor, …): Viewport` —
134
+ viewport math for pan/zoom canvases.
135
+ - `visibleNodes(nodes, vp, w, h, opts, overscan?)` — viewport culling for large orgs.
136
+
137
+ ### Demo data (`demo-data`)
138
+
139
+ - `generateDemoData(): Employee[]` — deterministic ~300-person company ("Meridian Global
140
+ Services") with intentional structural issues; useful for demos, tests, and golden files.
141
+
142
+ ### HRIS templates (`hris-templates`)
143
+
144
+ - `HRIS_TEMPLATES: HrisTemplate[]` — built-in column-name aliases and export instructions for
145
+ common HRIS exports (Workday, SuccessFactors, …) to seed `autoMapColumns` so a raw system
146
+ export maps onto the canonical model with little manual work.
147
+ - `getHrisTemplate(id)` — look up a template by its stable id (e.g. `"workday"`).
148
+
149
+ ## Embedding as an add-on module
150
+
151
+ The intended integration for an HR platform or consultancy toolchain — keep your own UI and
152
+ data store; call the engine on your data:
153
+
154
+ ```ts
155
+ import {
156
+ autoMapColumns,
157
+ rowsToEmployees,
158
+ validateEmployees,
159
+ computeMetrics,
160
+ generateInsightsFromEmployees,
161
+ DEFAULT_ASSUMPTIONS,
162
+ REQUIRED_FIELDS,
163
+ OPTIONAL_FIELDS,
164
+ type Assumptions,
165
+ } from "@org-design-studio/core";
166
+
167
+ // 1. Map whatever your HRIS export looks like onto the canonical model.
168
+ const columns = Object.keys(rawRows[0]);
169
+ const mapping = autoMapColumns(columns, [...REQUIRED_FIELDS, ...OPTIONAL_FIELDS]);
170
+ const employees = rowsToEmployees(rawRows, mapping);
171
+
172
+ // 2. Gate on data quality before showing analytics (render report.issues in your UI).
173
+ const report = validateEmployees(employees);
174
+ if (report.band === "Poor") showDataQualityScreen(report);
175
+
176
+ // 3. Tune the methodology to the client, then compute.
177
+ const assumptions: Assumptions = {
178
+ ...DEFAULT_ASSUMPTIONS,
179
+ targetMaxLayers: 6,
180
+ currency: "EUR",
181
+ currencySymbol: "€",
182
+ };
183
+ const metrics = computeMetrics(employees, assumptions);
184
+ const insights = generateInsightsFromEmployees(employees, assumptions);
185
+
186
+ // 4. Render metrics.layerStats, metrics.spanBuckets, metrics.orgHealth,
187
+ // and the ranked insights with your own components.
188
+ ```
189
+
190
+ Notes for embedders:
191
+
192
+ - Everything is synchronous and pure — safe in web workers, serverless functions, or the
193
+ browser. For very large orgs (10k+), run `computeMetrics` off the main thread.
194
+ - The engine never sees the network; data residency is whatever your host app does.
195
+ - Deliberately **not** exported: the app's zustand store and the file/share export module
196
+ (DOM-dependent). State management and persistence belong to the host.
197
+
198
+ ## Versioning
199
+
200
+ `0.x`: minors may adjust signatures or methodology constants (changelogged); patches are
201
+ additive/bug-fix only. From `1.0`: any breaking change to an exported symbol or shape is a
202
+ major; new exports and optional fields are minors.
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@org-design-studio/core",
3
+ "version": "0.1.0",
4
+ "description": "Org-design analytics engine: spans & layers diagnostics, level-aware span classification, scenario modelling with transition-cost-netted economics, and deterministic insight generation. Pure TypeScript, no UI dependencies.",
5
+ "license": "MIT",
6
+ "main": "./dist-core/core.js",
7
+ "module": "./dist-core/core.mjs",
8
+ "types": "./dist-core/core.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist-core/core.d.ts",
12
+ "import": "./dist-core/core.mjs",
13
+ "require": "./dist-core/core.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist-core",
18
+ "docs/API.md",
19
+ "README.md"
20
+ ],
21
+ "sideEffects": false,
22
+ "keywords": [
23
+ "org-design",
24
+ "spans-and-layers",
25
+ "organization",
26
+ "hr-analytics",
27
+ "workforce-planning",
28
+ "scenario-modelling"
29
+ ],
30
+ "dependencies": {
31
+ "d3-hierarchy": "^3.1.2"
32
+ },
33
+ "engines": {
34
+ "node": ">=18"
35
+ }
36
+ }