@untangle-code/config 0.0.0-stage → 0.1.1

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 (86) hide show
  1. package/LICENSE +661 -0
  2. package/NOTICE +90 -0
  3. package/README.md +262 -2
  4. package/THIRD_PARTY_LICENSES.md +29 -0
  5. package/dist/chunker-budget.d.ts +58 -0
  6. package/dist/chunker-budget.d.ts.map +1 -0
  7. package/dist/chunker-budget.js +11 -0
  8. package/dist/chunker-budget.js.map +1 -0
  9. package/dist/defaults.d.ts +50 -0
  10. package/dist/defaults.d.ts.map +1 -0
  11. package/dist/defaults.js +179 -0
  12. package/dist/defaults.js.map +1 -0
  13. package/dist/discovery.d.ts +40 -0
  14. package/dist/discovery.d.ts.map +1 -0
  15. package/dist/discovery.js +76 -0
  16. package/dist/discovery.js.map +1 -0
  17. package/dist/env.d.ts +37 -0
  18. package/dist/env.d.ts.map +1 -0
  19. package/dist/env.js +100 -0
  20. package/dist/env.js.map +1 -0
  21. package/dist/hash.d.ts +37 -0
  22. package/dist/hash.d.ts.map +1 -0
  23. package/dist/hash.js +52 -0
  24. package/dist/hash.js.map +1 -0
  25. package/dist/index.d.ts +22 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +37 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/load.d.ts +61 -0
  30. package/dist/load.d.ts.map +1 -0
  31. package/dist/load.js +264 -0
  32. package/dist/load.js.map +1 -0
  33. package/dist/merge.d.ts +32 -0
  34. package/dist/merge.d.ts.map +1 -0
  35. package/dist/merge.js +65 -0
  36. package/dist/merge.js.map +1 -0
  37. package/dist/migrations.d.ts +26 -0
  38. package/dist/migrations.d.ts.map +1 -0
  39. package/dist/migrations.js +40 -0
  40. package/dist/migrations.js.map +1 -0
  41. package/dist/parse.d.ts +7 -0
  42. package/dist/parse.d.ts.map +1 -0
  43. package/dist/parse.js +14 -0
  44. package/dist/parse.js.map +1 -0
  45. package/dist/paths.d.ts +14 -0
  46. package/dist/paths.d.ts.map +1 -0
  47. package/dist/paths.js +37 -0
  48. package/dist/paths.js.map +1 -0
  49. package/dist/schema.d.ts +5 -0
  50. package/dist/schema.d.ts.map +1 -0
  51. package/dist/schema.js +19 -0
  52. package/dist/schema.js.map +1 -0
  53. package/dist/secrets.d.ts +33 -0
  54. package/dist/secrets.d.ts.map +1 -0
  55. package/dist/secrets.js +69 -0
  56. package/dist/secrets.js.map +1 -0
  57. package/dist/types.d.ts +323 -0
  58. package/dist/types.d.ts.map +1 -0
  59. package/dist/types.js +20 -0
  60. package/dist/types.js.map +1 -0
  61. package/dist/user-config.d.ts +13 -0
  62. package/dist/user-config.d.ts.map +1 -0
  63. package/dist/user-config.js +34 -0
  64. package/dist/user-config.js.map +1 -0
  65. package/dist/validate.d.ts +36 -0
  66. package/dist/validate.d.ts.map +1 -0
  67. package/dist/validate.js +337 -0
  68. package/dist/validate.js.map +1 -0
  69. package/package.json +65 -3
  70. package/schemas/config-v1.json +375 -0
  71. package/src/chunker-budget.ts +63 -0
  72. package/src/defaults.ts +207 -0
  73. package/src/discovery.ts +95 -0
  74. package/src/env.ts +141 -0
  75. package/src/hash.ts +71 -0
  76. package/src/index.ts +51 -0
  77. package/src/load.ts +346 -0
  78. package/src/merge.ts +69 -0
  79. package/src/migrations.ts +64 -0
  80. package/src/parse.ts +18 -0
  81. package/src/paths.ts +42 -0
  82. package/src/schema.ts +23 -0
  83. package/src/secrets.ts +78 -0
  84. package/src/types.ts +366 -0
  85. package/src/user-config.ts +48 -0
  86. package/src/validate.ts +430 -0
@@ -0,0 +1,375 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://untangle.dev/schema/config-v1.json",
4
+ "title": "Untangle Configuration v1",
5
+ "description": "JSON Schema for .untangle.json / .untangle.local.json (spec.md §4.2-4.8, product.md §11, portfolio.md §14.4). This schema is hand-authored for editor tooling (autocompletion, structural hints); it deliberately does NOT encode the 'unknown top-level field warns instead of failing' rule from spec.md §4.8 -- that rule is enforced by @untangle-code/config's own validator, not by schema validation, so additionalProperties:false here is advisory for tooling only.",
6
+ "type": "object",
7
+ "required": ["schemaVersion"],
8
+ "properties": {
9
+ "$schema": { "type": "string" },
10
+ "schemaVersion": { "const": 1 },
11
+ "workspaceId": { "type": "string", "minLength": 1 },
12
+ "projectId": { "type": "string", "minLength": 1 },
13
+ "repositories": {
14
+ "type": "array",
15
+ "items": { "$ref": "#/$defs/repositoryConfig" }
16
+ },
17
+ "storage": { "$ref": "#/$defs/storageConfig" },
18
+ "qdrant": { "$ref": "#/$defs/qdrantConfig" },
19
+ "embeddings": { "$ref": "#/$defs/embeddingConfig" },
20
+ "index": { "$ref": "#/$defs/indexConfig" },
21
+ "search": { "$ref": "#/$defs/searchConfig" },
22
+ "graph": { "$ref": "#/$defs/graphConfig" },
23
+ "complexity": { "$ref": "#/$defs/complexityConfig" },
24
+ "changes": { "$ref": "#/$defs/changeConfig" },
25
+ "qualityGate": { "$ref": "#/$defs/qualityGateConfig" },
26
+ "contextArtifacts": {
27
+ "type": "array",
28
+ "items": { "$ref": "#/$defs/contextArtifactConfig" }
29
+ },
30
+ "telemetry": { "$ref": "#/$defs/telemetryConfig" },
31
+ "portfolio": { "$ref": "#/$defs/portfolioConfig" }
32
+ },
33
+ "additionalProperties": false,
34
+ "$defs": {
35
+ "repositoryConfig": {
36
+ "type": "object",
37
+ "description": "spec.md §4.3",
38
+ "required": ["id", "path"],
39
+ "properties": {
40
+ "id": { "type": "string", "minLength": 1 },
41
+ "path": { "type": "string", "minLength": 1 },
42
+ "label": { "type": "string" },
43
+ "ref": { "type": "string" },
44
+ "include": { "type": "array", "items": { "type": "string" } },
45
+ "exclude": { "type": "array", "items": { "type": "string" } },
46
+ "packageAliases": { "type": "object", "additionalProperties": { "type": "string" } },
47
+ "metadata": { "type": "object", "additionalProperties": { "type": "string" } }
48
+ },
49
+ "additionalProperties": false
50
+ },
51
+ "storageConfig": {
52
+ "type": "object",
53
+ "description": "Search backend. Defaults to embedded LanceDB unless a legacy qdrant section is explicitly configured. An explicit backend overrides that compatibility default.",
54
+ "properties": {
55
+ "backend": { "enum": ["lancedb", "qdrant"], "default": "lancedb" },
56
+ "path": {
57
+ "type": "string",
58
+ "minLength": 1,
59
+ "description": "Local LanceDB directory, relative to the repository root or absolute; URI schemes and network paths are unsupported.",
60
+ "default": ".untangle/index/lancedb",
61
+ "anyOf": [
62
+ { "pattern": "^[A-Za-z]:[\\\\/][^\\u0000]*$" },
63
+ { "pattern": "^(?!\\s*$)(?![A-Za-z][A-Za-z0-9+.-]*:)(?![\\\\/]{2})[^\\u0000]+$" }
64
+ ]
65
+ }
66
+ },
67
+ "additionalProperties": false
68
+ },
69
+ "qdrantConfig": {
70
+ "type": "object",
71
+ "description": "spec.md §4.4",
72
+ "required": ["mode"],
73
+ "properties": {
74
+ "mode": { "enum": ["managed", "external"] },
75
+ "url": { "type": "string" },
76
+ "apiKeyEnv": {
77
+ "type": "string",
78
+ "description": "Name of an environment variable; never the key itself (architecture.md §23.2)."
79
+ },
80
+ "collectionPrefix": { "type": "string" },
81
+ "requestTimeoutMs": { "type": "integer", "minimum": 1 },
82
+ "maxRetries": { "type": "integer", "minimum": 0 },
83
+ "upsertMaxPoints": { "type": "integer", "minimum": 1 },
84
+ "upsertBudgetBytes": { "type": "integer", "minimum": 1 }
85
+ },
86
+ "additionalProperties": false
87
+ },
88
+ "embeddingConfig": {
89
+ "type": "object",
90
+ "description": "spec.md §4.5",
91
+ "required": [
92
+ "provider",
93
+ "mode",
94
+ "model",
95
+ "dimensions",
96
+ "contextLength",
97
+ "queryPrefix",
98
+ "documentPrefix",
99
+ "includePath",
100
+ "batchSize",
101
+ "maxConnections",
102
+ "requestTimeoutMs"
103
+ ],
104
+ "properties": {
105
+ "provider": { "const": "ollama" },
106
+ "mode": { "enum": ["auto", "managed", "external"] },
107
+ "url": { "type": "string" },
108
+ "apiKeyEnv": {
109
+ "type": "string",
110
+ "description": "Name of an environment variable; never the key itself (architecture.md §23.2)."
111
+ },
112
+ "model": { "type": "string", "minLength": 1 },
113
+ "dimensions": { "type": "integer", "minimum": 1 },
114
+ "contextLength": { "type": "integer", "minimum": 1 },
115
+ "queryPrefix": { "type": "string" },
116
+ "documentPrefix": { "type": "string" },
117
+ "includePath": { "type": "boolean" },
118
+ "batchSize": { "type": "integer", "minimum": 1 },
119
+ "maxConnections": { "type": "integer", "minimum": 1 },
120
+ "requestTimeoutMs": { "type": "integer", "minimum": 1 }
121
+ },
122
+ "additionalProperties": false
123
+ },
124
+ "indexConfig": {
125
+ "type": "object",
126
+ "description": "spec.md §4.6",
127
+ "properties": {
128
+ "watch": { "type": "boolean" },
129
+ "reconcileIntervalMinutes": { "type": "number", "minimum": 0 },
130
+ "maxFileSizeMb": { "type": "number", "minimum": 0 },
131
+ "maxGraphFileSizeMb": { "type": "number", "minimum": 0 },
132
+ "scanConcurrency": { "type": "integer", "minimum": 1 },
133
+ "embeddingConcurrency": { "type": "integer", "minimum": 1 },
134
+ "filesPerCheckpoint": { "type": "integer", "minimum": 1 },
135
+ "targetChunkTokens": { "type": "integer", "minimum": 1 },
136
+ "maxChunkTokens": { "type": "integer", "minimum": 1 },
137
+ "fallbackOverlapTokens": { "type": "integer", "minimum": 0 },
138
+ "includeDotFiles": { "type": "boolean" },
139
+ "indexExtensionless": { "type": "boolean" },
140
+ "extraExtensions": { "type": "array", "items": { "type": "string" } },
141
+ "extensionLanguageMap": { "type": "object", "additionalProperties": { "type": "string" } }
142
+ },
143
+ "additionalProperties": false
144
+ },
145
+ "searchConfig": {
146
+ "type": "object",
147
+ "description": "Untangle decision: spec.md §13 names no SearchConfig shape.",
148
+ "properties": {
149
+ "defaultLimit": { "type": "integer", "minimum": 1 },
150
+ "defaultCandidateLimit": { "type": "integer", "minimum": 1 },
151
+ "maxCandidateLimit": { "type": "integer", "minimum": 1 },
152
+ "diversifyByDefault": { "type": "boolean" },
153
+ "minScoreDefault": { "type": "number" },
154
+ "relevancePolicies": {
155
+ "type": "object",
156
+ "description": "Calibrated absence decisions keyed by the exact embedding profile id and additionally bound to one workspace and collection.",
157
+ "propertyNames": { "type": "string", "minLength": 1 },
158
+ "additionalProperties": { "$ref": "#/$defs/searchRelevancePolicyBinding" }
159
+ },
160
+ "boostWeights": {
161
+ "type": "object",
162
+ "properties": {
163
+ "exactSymbol": { "type": "number" },
164
+ "exactPath": { "type": "number" },
165
+ "repoPriority": { "type": "number" },
166
+ "graphProximity": { "type": "number" },
167
+ "recency": { "type": "number" },
168
+ "duplicatePenalty": { "type": "number" }
169
+ },
170
+ "additionalProperties": false
171
+ }
172
+ },
173
+ "additionalProperties": false
174
+ },
175
+ "searchRelevancePolicyBinding": {
176
+ "type": "object",
177
+ "required": ["workspaceId", "collection", "policy"],
178
+ "properties": {
179
+ "workspaceId": { "type": "string", "minLength": 1 },
180
+ "collection": { "type": "string", "minLength": 1 },
181
+ "policy": { "$ref": "#/$defs/searchRelevancePolicy" }
182
+ },
183
+ "additionalProperties": false
184
+ },
185
+ "searchRelevancePolicy": {
186
+ "type": "object",
187
+ "required": [
188
+ "calibrationId",
189
+ "minimumDenseScore",
190
+ "minimumDenseSpread",
191
+ "denseReferenceRank",
192
+ "minimumCandidateIdfCoverage"
193
+ ],
194
+ "properties": {
195
+ "calibrationId": { "type": "string", "minLength": 1 },
196
+ "minimumDenseScore": { "type": "number", "minimum": -1, "maximum": 1 },
197
+ "minimumDenseSpread": { "type": "number", "minimum": 0, "maximum": 2 },
198
+ "denseReferenceRank": { "type": "integer", "minimum": 2 },
199
+ "minimumCandidateIdfCoverage": { "type": "number", "minimum": 0, "maximum": 1 }
200
+ },
201
+ "additionalProperties": false
202
+ },
203
+ "graphConfig": {
204
+ "type": "object",
205
+ "description": "Untangle decision, values anchored on architecture.md §17.4 traversal limits.",
206
+ "properties": {
207
+ "enabled": { "type": "boolean" },
208
+ "maxTraversalDepth": { "type": "integer", "minimum": 1 },
209
+ "maxTraversalNodes": { "type": "integer", "minimum": 1 },
210
+ "maxTraversalEdges": { "type": "integer", "minimum": 1 },
211
+ "maxTraversalWallTimeMs": { "type": "integer", "minimum": 1 },
212
+ "includeDynamicImports": { "type": "boolean" }
213
+ },
214
+ "additionalProperties": false
215
+ },
216
+ "complexityConfig": {
217
+ "type": "object",
218
+ "description": "Untangle decision; product.md §11 example anchors 'enabled'/'cognitiveAlgorithm'.",
219
+ "properties": {
220
+ "enabled": { "type": "boolean" },
221
+ "cognitiveAlgorithm": { "type": "string" },
222
+ "cyclomaticVariant": { "enum": ["classic", "extended", "both"] },
223
+ "maintainabilityIndexEnabled": { "type": "boolean" }
224
+ },
225
+ "additionalProperties": false
226
+ },
227
+ "changeConfig": {
228
+ "type": "object",
229
+ "description": "Untangle decision; weight defaults from spec.md §17.6.",
230
+ "properties": {
231
+ "enabled": { "type": "boolean" },
232
+ "riskWeights": {
233
+ "type": "object",
234
+ "properties": {
235
+ "churn": { "type": "number" },
236
+ "positiveCognitiveDelta": { "type": "number" },
237
+ "positiveCyclomaticDelta": { "type": "number" },
238
+ "blastRadius": { "type": "number" },
239
+ "graphCentrality": { "type": "number" },
240
+ "semanticChange": { "type": "number" },
241
+ "testDistance": { "type": "number" }
242
+ },
243
+ "additionalProperties": false
244
+ },
245
+ "symbolMatching": {
246
+ "type": "object",
247
+ "properties": {
248
+ "tokenSimilarityMin": { "type": "number", "minimum": 0, "maximum": 1 },
249
+ "enableEmbeddingSimilarity": { "type": "boolean" }
250
+ },
251
+ "additionalProperties": false
252
+ }
253
+ },
254
+ "additionalProperties": false
255
+ },
256
+ "qualityGateConfig": {
257
+ "type": "object",
258
+ "description": "spec.md §4.7",
259
+ "required": ["mode", "newCyclesAllowed", "minimumParseQuality", "ignoreExistingViolations"],
260
+ "properties": {
261
+ "mode": { "enum": ["off", "warn", "fail"] },
262
+ "newCyclesAllowed": { "type": "integer", "minimum": 0 },
263
+ "cognitivePerSymbolMax": { "type": "number" },
264
+ "cognitiveDeltaPerSymbolMax": { "type": "number" },
265
+ "cyclomaticPerSymbolMax": { "type": "number" },
266
+ "cyclomaticDeltaPerSymbolMax": { "type": "number" },
267
+ "maxNestingPerSymbolMax": { "type": "number" },
268
+ "maintainabilityIndexMin": { "type": "number" },
269
+ "riskScoreMax": { "type": "number" },
270
+ "minimumParseQuality": { "enum": ["failed", "partial", "complete"] },
271
+ "ignoreExistingViolations": { "type": "boolean" },
272
+ "include": { "type": "array", "items": { "type": "string" } },
273
+ "exclude": { "type": "array", "items": { "type": "string" } }
274
+ },
275
+ "additionalProperties": false
276
+ },
277
+ "contextArtifactConfig": {
278
+ "type": "object",
279
+ "description": "spec.md §19.1-19.2",
280
+ "required": ["name", "path", "description"],
281
+ "properties": {
282
+ "name": { "type": "string", "minLength": 1 },
283
+ "path": { "type": "string", "minLength": 1 },
284
+ "description": { "type": "string" },
285
+ "type": {
286
+ "enum": [
287
+ "auto",
288
+ "sql",
289
+ "openapi",
290
+ "asyncapi",
291
+ "protobuf",
292
+ "markdown",
293
+ "terraform",
294
+ "kubernetes",
295
+ "text"
296
+ ]
297
+ },
298
+ "include": { "type": "array", "items": { "type": "string" } },
299
+ "exclude": { "type": "array", "items": { "type": "string" } }
300
+ },
301
+ "additionalProperties": false
302
+ },
303
+ "telemetryConfig": {
304
+ "type": "object",
305
+ "description": "spec.md §24: telemetry MUST default to off.",
306
+ "properties": {
307
+ "enabled": { "type": "boolean" },
308
+ "logLevel": { "enum": ["error", "warn", "info", "debug", "trace"] },
309
+ "metricsExport": { "enum": ["none", "prometheus"] }
310
+ },
311
+ "additionalProperties": false
312
+ },
313
+ "portfolioConfig": {
314
+ "type": "object",
315
+ "description": "portfolio.md §14.4 (normative shape)",
316
+ "properties": {
317
+ "enabled": { "type": "boolean" },
318
+ "includeTransitive": { "type": "boolean" },
319
+ "conventionMinRepos": { "type": "integer", "minimum": 0 },
320
+ "conventions": {
321
+ "type": "object",
322
+ "properties": {
323
+ "pinned": { "type": "array", "items": { "type": "string" } },
324
+ "suppressed": { "type": "array", "items": { "type": "string" } }
325
+ },
326
+ "additionalProperties": false
327
+ },
328
+ "radar": {
329
+ "type": "object",
330
+ "properties": {
331
+ "adoptShareMin": { "type": "number", "minimum": 0, "maximum": 1 },
332
+ "maintainShareMin": { "type": "number", "minimum": 0, "maximum": 1 },
333
+ "retireAfterDays": { "type": "integer", "minimum": 0 }
334
+ },
335
+ "additionalProperties": false
336
+ },
337
+ "blueprint": {
338
+ "type": "object",
339
+ "properties": {
340
+ "weights": {
341
+ "type": "object",
342
+ "description": "MUST sum to 1.0 (±0.001) -- portfolio.md §14.4.",
343
+ "properties": {
344
+ "adoption": { "type": "number" },
345
+ "recency": { "type": "number" },
346
+ "health": { "type": "number" },
347
+ "versionConsistency": { "type": "number" }
348
+ },
349
+ "additionalProperties": false
350
+ }
351
+ },
352
+ "additionalProperties": false
353
+ },
354
+ "enrichment": { "$ref": "#/$defs/portfolioEnrichmentConfig" }
355
+ },
356
+ "additionalProperties": false
357
+ },
358
+ "portfolioEnrichmentConfig": {
359
+ "type": "object",
360
+ "description": "portfolio.md §13. Default fully disabled.",
361
+ "required": ["enabled", "registries", "cacheTtlHours", "timeoutMs"],
362
+ "properties": {
363
+ "enabled": { "type": "boolean" },
364
+ "registries": {
365
+ "type": "array",
366
+ "items": { "enum": ["npm", "pypi", "crates", "maven", "nuget", "go", "composer"] }
367
+ },
368
+ "registryUrls": { "type": "object", "additionalProperties": { "type": "string" } },
369
+ "cacheTtlHours": { "type": "number", "minimum": 0 },
370
+ "timeoutMs": { "type": "integer", "minimum": 1 }
371
+ },
372
+ "additionalProperties": false
373
+ }
374
+ }
375
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Chunk-token budget wiring: `IndexConfig` (spec.md §4.6) →
3
+ * `@untangle-code/chunker`'s `ChunkerConfigInput.maxTokensPerChunk`.
4
+ *
5
+ * spec.md §4.6 defines TWO index-level chunk-size knobs —
6
+ * `targetChunkTokens` (a soft aim) and `maxChunkTokens` (a hard ceiling) —
7
+ * with their product defaults (900 / 1500) fixed exactly once, in this
8
+ * package's `defaults.ts` (`DEFAULT_INDEX_CONFIG`). That is the single
9
+ * source of truth this file wires onward.
10
+ *
11
+ * `@untangle-code/chunker` currently exposes exactly ONE budget knob,
12
+ * `ChunkerConfigInput.maxTokensPerChunk`, and treats it everywhere as a hard
13
+ * "must not exceed" ceiling (every comparison in chunker's `split.ts` and
14
+ * `line-fallback-strategy.ts` is `<=`). The correct mapping is therefore
15
+ * `maxChunkTokens → maxTokensPerChunk`. `targetChunkTokens` has no
16
+ * chunker-side counterpart today — the chunker does not currently support
17
+ * aiming below its hard ceiling — and is deliberately NOT passed here;
18
+ * smuggling it in as `maxTokensPerChunk` instead would silently turn a soft
19
+ * aim into the hard ceiling the chunker actually enforces, which is a
20
+ * different (smaller) budget than spec.md §4.6 configures.
21
+ *
22
+ * `@untangle-code/config` deliberately does NOT take a runtime dependency on
23
+ * `@untangle-code/chunker` for this: config sits below chunker in the
24
+ * dependency graph (chunker already depends on domain; depending on chunker
25
+ * from here would invert that layering for the sake of one field mapping),
26
+ * and chunker is maintained independently of this package. `deriveChunkerBudget`
27
+ * therefore returns a plain object structurally compatible with
28
+ * `ChunkerConfigInput` (verified in `chunker-budget.test.ts` against the
29
+ * fields spec.md and the chunker README actually document) rather than
30
+ * importing its type.
31
+ *
32
+ * Whatever code builds the indexer (constructing `ChunkerConfigInput` to
33
+ * pass to `@untangle-code/chunker`'s `resolveChunkerConfig`/`chunkFile`) MUST
34
+ * spread this function's result into that input — e.g.
35
+ * `resolveChunkerConfig({ ...deriveChunkerBudget(effectiveConfig.index), preparedText })`
36
+ * — rather than reading `index.maxChunkTokens` directly or (worse) relying
37
+ * on the chunker's own independent default. That is the one piece of wiring
38
+ * this fix cannot complete by itself: no indexer package exists in this
39
+ * repository yet to receive it (see this package's `package.json`
40
+ * `dependencies`, and `packages/`'s directory listing) — but every future
41
+ * caller now has exactly one function to call instead of a field name to
42
+ * remember, which is what keeps a changed config value from silently
43
+ * failing to reach the chunker.
44
+ */
45
+ import type { IndexConfig } from "./types.js";
46
+
47
+ /** Structurally assignable to `@untangle-code/chunker`'s `ChunkerConfigInput` — see file doc comment. */
48
+ export interface ChunkerBudgetInput {
49
+ maxTokensPerChunk: number;
50
+ }
51
+
52
+ /**
53
+ * Derives the chunker's token-budget input from the effective `IndexConfig`
54
+ * (spec.md §4.6). This is the ONLY function in the codebase permitted to
55
+ * decide which `IndexConfig` field feeds
56
+ * `ChunkerConfigInput.maxTokensPerChunk` — see file doc comment for why it
57
+ * is `maxChunkTokens`, not `targetChunkTokens`.
58
+ */
59
+ export function deriveChunkerBudget(
60
+ index: Pick<IndexConfig, "maxChunkTokens">,
61
+ ): ChunkerBudgetInput {
62
+ return { maxTokensPerChunk: index.maxChunkTokens };
63
+ }
@@ -0,0 +1,207 @@
1
+ /**
2
+ * Product defaults (spec.md §4.5 "Defaultprofil für MVP"; product.md §11).
3
+ *
4
+ * These are layer 6 (lowest priority) of spec.md §4.1's six-level layering.
5
+ * Every field an `EffectiveUntangleConfigV1` guarantees present is defaulted
6
+ * here.
7
+ */
8
+ import { COGNITIVE_ALGORITHM_ID } from "@untangle-code/domain";
9
+
10
+ import type {
11
+ ChangeConfig,
12
+ ComplexityConfig,
13
+ EmbeddingConfig,
14
+ GraphConfig,
15
+ IndexConfig,
16
+ PortfolioConfig,
17
+ QdrantConfig,
18
+ QualityGateConfig,
19
+ RawConfigLayer,
20
+ SearchConfig,
21
+ StorageConfig,
22
+ TelemetryConfig,
23
+ } from "./types.js";
24
+
25
+ /**
26
+ * Embedding profile fields that are NOT part of `EmbeddingConfig` (spec.md
27
+ * §4.5 lists exactly ten fields, none of them `normalization` or
28
+ * `preparedTextVersion`) but ARE required inputs to
29
+ * `buildEmbeddingProfileId` (spec.md §5.7). Untangle decision: default
30
+ * normalization to "none" (no vector post-processing) and mint an explicit
31
+ * prepared-text format version so a future change to how document text is
32
+ * assembled can roll `embedding_profile_id` forward deliberately.
33
+ */
34
+ export const DEFAULT_EMBEDDING_NORMALIZATION = "none" as const;
35
+ export const PREPARED_TEXT_VERSION = "untangle-prepared-text-v1" as const;
36
+
37
+ /** spec.md §4.5 "Defaultprofil für MVP" — Ollama, nomic-embed-text, 768 dims. */
38
+ export const DEFAULT_EMBEDDING_CONFIG: EmbeddingConfig = {
39
+ provider: "ollama",
40
+ mode: "auto",
41
+ model: "nomic-embed-text",
42
+ dimensions: 768,
43
+ contextLength: 2048,
44
+ queryPrefix: "search_query: ",
45
+ documentPrefix: "search_document: ",
46
+ includePath: true,
47
+ batchSize: 32,
48
+ maxConnections: 4,
49
+ requestTimeoutMs: 120000,
50
+ };
51
+
52
+ /**
53
+ * Qdrant defaults. spec.md §4.4 defines the shape but not default values;
54
+ * `managed` mode with no explicit `url` matches product.md §4.1's
55
+ * zero-config narrative (the CLI starts a managed container and fills `url`
56
+ * in from that at runtime — this package only supplies the mode default).
57
+ */
58
+ export const DEFAULT_QDRANT_CONFIG: QdrantConfig = {
59
+ mode: "managed",
60
+ collectionPrefix: "untangle_",
61
+ requestTimeoutMs: 30000,
62
+ maxRetries: 3,
63
+ upsertMaxPoints: 256,
64
+ upsertBudgetBytes: 4 * 1024 * 1024,
65
+ };
66
+
67
+ /** Embedded search for new projects; explicit legacy Qdrant layers retain their backend. */
68
+ export const DEFAULT_STORAGE_CONFIG: StorageConfig = {
69
+ backend: "lancedb",
70
+ path: ".untangle/index/lancedb",
71
+ };
72
+
73
+ /** spec.md §4.6; numeric defaults are an Untangle decision, chosen conservatively. */
74
+ export const DEFAULT_INDEX_CONFIG: IndexConfig = {
75
+ watch: true,
76
+ reconcileIntervalMinutes: 30,
77
+ maxFileSizeMb: 5,
78
+ maxGraphFileSizeMb: 20,
79
+ scanConcurrency: 8,
80
+ embeddingConcurrency: 4,
81
+ filesPerCheckpoint: 200,
82
+ targetChunkTokens: 900,
83
+ maxChunkTokens: 1500,
84
+ fallbackOverlapTokens: 100,
85
+ includeDotFiles: false,
86
+ indexExtensionless: false,
87
+ extraExtensions: [],
88
+ extensionLanguageMap: {},
89
+ };
90
+
91
+ /** Untangle decision (see `types.ts` `SearchConfig` doc comment). */
92
+ export const DEFAULT_SEARCH_CONFIG: SearchConfig = {
93
+ defaultLimit: 10,
94
+ defaultCandidateLimit: 50,
95
+ maxCandidateLimit: 200,
96
+ diversifyByDefault: false,
97
+ boostWeights: {
98
+ exactSymbol: 0.15,
99
+ exactPath: 0.1,
100
+ repoPriority: 0.05,
101
+ graphProximity: 0.1,
102
+ recency: 0.05,
103
+ duplicatePenalty: 0.2,
104
+ },
105
+ };
106
+
107
+ /** Untangle decision, values taken from architecture.md §17.4's per-query limits. */
108
+ export const DEFAULT_GRAPH_CONFIG: GraphConfig = {
109
+ enabled: true,
110
+ maxTraversalDepth: 20,
111
+ maxTraversalNodes: 2000,
112
+ maxTraversalEdges: 4000,
113
+ maxTraversalWallTimeMs: 5000,
114
+ includeDynamicImports: true,
115
+ };
116
+
117
+ /** Untangle decision (see `types.ts` `ComplexityConfig` doc comment). */
118
+ export const DEFAULT_COMPLEXITY_CONFIG: ComplexityConfig = {
119
+ enabled: true,
120
+ cognitiveAlgorithm: COGNITIVE_ALGORITHM_ID,
121
+ cyclomaticVariant: "both",
122
+ maintainabilityIndexEnabled: true,
123
+ };
124
+
125
+ /** spec.md §17.6 default risk score weights. */
126
+ export const DEFAULT_CHANGE_CONFIG: ChangeConfig = {
127
+ enabled: true,
128
+ riskWeights: {
129
+ churn: 0.25,
130
+ positiveCognitiveDelta: 0.2,
131
+ positiveCyclomaticDelta: 0.15,
132
+ blastRadius: 0.15,
133
+ graphCentrality: 0.1,
134
+ semanticChange: 0.1,
135
+ testDistance: 0.05,
136
+ },
137
+ symbolMatching: {
138
+ tokenSimilarityMin: 0.6,
139
+ enableEmbeddingSimilarity: true,
140
+ },
141
+ };
142
+
143
+ /** spec.md §4.7 shape; MVP defaults chosen deliberately permissive (warn, not fail). */
144
+ export const DEFAULT_QUALITY_GATE_CONFIG: QualityGateConfig = {
145
+ mode: "warn",
146
+ newCyclesAllowed: 0,
147
+ minimumParseQuality: "partial",
148
+ ignoreExistingViolations: true,
149
+ };
150
+
151
+ /** spec.md §24: "Telemetrie MUSS standardmäßig aus sein." */
152
+ export const DEFAULT_TELEMETRY_CONFIG: TelemetryConfig = {
153
+ enabled: false,
154
+ logLevel: "info",
155
+ metricsExport: "none",
156
+ };
157
+
158
+ /** portfolio.md §14.4 defaults, spelled out explicitly. */
159
+ export const DEFAULT_PORTFOLIO_CONFIG: PortfolioConfig = {
160
+ enabled: true,
161
+ includeTransitive: false,
162
+ conventionMinRepos: 2,
163
+ radar: {
164
+ adoptShareMin: 0.5,
165
+ maintainShareMin: 0.2,
166
+ retireAfterDays: 540,
167
+ },
168
+ blueprint: {
169
+ weights: {
170
+ adoption: 0.4,
171
+ recency: 0.2,
172
+ health: 0.25,
173
+ versionConsistency: 0.15,
174
+ },
175
+ },
176
+ enrichment: {
177
+ enabled: false,
178
+ registries: [],
179
+ cacheTtlHours: 168,
180
+ timeoutMs: 10000,
181
+ },
182
+ };
183
+
184
+ /** Current config schema version this package produces and validates against. */
185
+ export const CURRENT_SCHEMA_VERSION = 1 as const;
186
+
187
+ /**
188
+ * The complete product-default layer (spec.md §4.1 layer 6, lowest
189
+ * priority). Every optional sub-config `EffectiveUntangleConfigV1`
190
+ * guarantees present is defaulted here; `repositories` and
191
+ * `contextArtifacts` are deliberately absent (spec.md §4.3: no universal
192
+ * default repository set exists).
193
+ */
194
+ export const PRODUCT_DEFAULT_CONFIG: RawConfigLayer = {
195
+ schemaVersion: CURRENT_SCHEMA_VERSION,
196
+ storage: DEFAULT_STORAGE_CONFIG,
197
+ qdrant: DEFAULT_QDRANT_CONFIG,
198
+ embeddings: DEFAULT_EMBEDDING_CONFIG,
199
+ index: DEFAULT_INDEX_CONFIG,
200
+ search: DEFAULT_SEARCH_CONFIG,
201
+ graph: DEFAULT_GRAPH_CONFIG,
202
+ complexity: DEFAULT_COMPLEXITY_CONFIG,
203
+ changes: DEFAULT_CHANGE_CONFIG,
204
+ qualityGate: DEFAULT_QUALITY_GATE_CONFIG,
205
+ telemetry: DEFAULT_TELEMETRY_CONFIG,
206
+ portfolio: DEFAULT_PORTFOLIO_CONFIG,
207
+ };