haansi 0.1.45 → 0.1.46

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 (2) hide show
  1. package/dist/haansi.js +1152 -35
  2. package/package.json +1 -1
package/dist/haansi.js CHANGED
@@ -95,7 +95,7 @@ var require_package = __commonJS({
95
95
  "package.json"(exports2, module2) {
96
96
  module2.exports = {
97
97
  name: "haansi",
98
- version: "0.1.45",
98
+ version: "0.1.46",
99
99
  description: "Haansi CLI - Session collector and MCP server for Claude Code & OpenAI Codex",
100
100
  bin: {
101
101
  haansi: "./dist/haansi.js"
@@ -958,12 +958,24 @@ function normKey(key) {
958
958
  function isSensitiveKey(key) {
959
959
  return SENSITIVE_KEY_RE.test(normKey(key));
960
960
  }
961
+ function isUserContentKey(key) {
962
+ return USER_CONTENT_KEY_RE.test(normKey(key));
963
+ }
964
+ function redactUserContent(value) {
965
+ if (typeof value === "string") return `[REDACTED ${value.length} chars]`;
966
+ if (Array.isArray(value)) return `[REDACTED ${value.length} items]`;
967
+ if (value === null || value === void 0) return value;
968
+ if (typeof value === "object") return "[REDACTED]";
969
+ return value;
970
+ }
961
971
  function sanitizeMetadata(obj, depth = 0) {
962
972
  if (depth > 5) return obj;
963
973
  const result = {};
964
974
  for (const [k, v] of Object.entries(obj)) {
965
975
  if (isSensitiveKey(k)) {
966
976
  result[k] = "[REDACTED]";
977
+ } else if (isUserContentKey(k)) {
978
+ result[k] = redactUserContent(v);
967
979
  } else if (v !== null && typeof v === "object" && !Array.isArray(v) && !(v instanceof Date)) {
968
980
  result[k] = sanitizeMetadata(v, depth + 1);
969
981
  } else {
@@ -972,6 +984,17 @@ function sanitizeMetadata(obj, depth = 0) {
972
984
  }
973
985
  return result;
974
986
  }
987
+ function causeForFirstLine(metadata) {
988
+ const error2 = metadata?.error;
989
+ const raw = typeof error2 === "string" ? error2 : error2 && typeof error2 === "object" ? error2.message : void 0;
990
+ if (typeof raw !== "string") return "";
991
+ const text = raw.replace(/\s+/g, " ").trim();
992
+ if (!text) return "";
993
+ const name = error2 && typeof error2 === "object" ? error2.name : void 0;
994
+ const kind = typeof name === "string" && name && name !== "Error" ? `${name}: ` : "";
995
+ const words = text.length > FIRST_LINE_CAUSE_CHARS ? `${text.slice(0, FIRST_LINE_CAUSE_CHARS - 1)}\u2026` : text;
996
+ return ` \u2014 ${kind}${words}`;
997
+ }
975
998
  function normalizeErrorMetadata(error2, metadata) {
976
999
  if (!error2) return metadata;
977
1000
  if (error2 instanceof Error) {
@@ -998,7 +1021,7 @@ function createLogger(serviceName, projectId) {
998
1021
  projectId === null ? void 0 : projectId || process.env.GCP_PROJECT_ID
999
1022
  );
1000
1023
  }
1001
- var import_async_hooks, _cloudBackend, SENSITIVE_KEY_RE, traceStorage, StructuredLogger, TraceContext;
1024
+ var import_async_hooks, _cloudBackend, SENSITIVE_KEY_RE, USER_CONTENT_KEY_RE, FIRST_LINE_CAUSE_CHARS, traceStorage, StructuredLogger, TraceContext;
1002
1025
  var init_logger = __esm({
1003
1026
  "../../packages/@company/observability/src/logger.ts"() {
1004
1027
  "use strict";
@@ -1006,6 +1029,8 @@ var init_logger = __esm({
1006
1029
  init_esm_node();
1007
1030
  init_types();
1008
1031
  SENSITIVE_KEY_RE = /^(password|passwd|secret|token|apikey|authorization|auth|authtoken|authkey|credentials?|connectionstring|privatekey)$/;
1032
+ USER_CONTENT_KEY_RE = /^(question|questiontext|query|querytext|q|searchquery|searchtext|searchterm|searchterms|userquery|prompt|userprompt|usermessage|messagetext|messagecontent)$/;
1033
+ FIRST_LINE_CAUSE_CHARS = 300;
1009
1034
  traceStorage = new import_async_hooks.AsyncLocalStorage();
1010
1035
  StructuredLogger = class {
1011
1036
  serviceName;
@@ -1061,7 +1086,7 @@ var init_logger = __esm({
1061
1086
  const traceInfo = entry2.traceId ? ` [trace: ${entry2.traceId.slice(0, 8)}]` : "";
1062
1087
  const orgInfo = entry2.organizationId ? ` [org: ${entry2.organizationId.slice(0, 8)}]` : "";
1063
1088
  const prefix = `[${timestamp}] [${entry2.severity}] [${this.serviceName}]${traceInfo}${orgInfo}`;
1064
- const message = `${prefix} ${entry2.message}`;
1089
+ const message = `${prefix} ${entry2.message}${causeForFirstLine(entry2.metadata)}`;
1065
1090
  const metadata = entry2.metadata ? `
1066
1091
  ${JSON.stringify(entry2.metadata, null, 2)}` : "";
1067
1092
  switch (entry2.severity) {
@@ -1699,6 +1724,22 @@ var init_scaling_metrics = __esm({
1699
1724
  treatMissingData: "breaching",
1700
1725
  missingDataRationale: "The reconciler is the only publisher and runs on a schedule independent of every worker. Its silence means the reconciler itself is broken, and the backlog it was watching is unobserved rather than empty. Scaling out against an unknown backlog wastes money; scaling in loses the queue. Breaching keeps capacity up until a human looks."
1701
1726
  }),
1727
+ MINING_REPLAYS_IN_PROGRESS: spec({
1728
+ name: "haansi/mining/replays_in_progress",
1729
+ unit: "Count",
1730
+ description: "Orgs whose mining is held by a re-run (hard reset or re-mine): organization_mining_config.rerun_in_progress.",
1731
+ dimensions: ["environment"],
1732
+ treatMissingData: "breaching",
1733
+ missingDataRationale: "A re-run embeds and classifies every event through Presidio and the embedders, but it is not request-driven, so nothing else marks those services busy while it runs. Silence means the reconciler failed, not that no re-run is running; breaching keeps them awake."
1734
+ }),
1735
+ REPLAY_PENDING_EVENTS: spec({
1736
+ name: "haansi/replay/pending_events",
1737
+ unit: "Count",
1738
+ description: "source_events a RUNNING re-run (live hard-reset lease) has yet to replay, past its watermark, all orgs.",
1739
+ dimensions: ["environment"],
1740
+ treatMissingData: "breaching",
1741
+ missingDataRationale: "A re-run embeds every event inline through the batch embedders, but those rows are already normalized and never become claims, so neither backlog_rows nor claims_pending nor canonical_backlog_events can see the demand: one TEI task served a whole re-run at ~1 s/text (dev 2026-10-06). This is the only publisher of that demand; silence means it is unobserved, not finished. Breaching keeps the embedders up."
1742
+ }),
1702
1743
  CANONICAL_BACKLOG_OLDEST_SECONDS: spec({
1703
1744
  name: "haansi/ingestion/canonical_backlog_oldest_seconds",
1704
1745
  unit: "Seconds",
@@ -2130,11 +2171,19 @@ var init_detect = __esm({
2130
2171
  }
2131
2172
  });
2132
2173
 
2174
+ // ../../packages/@company/encryption/src/org-binding.ts
2175
+ var init_org_binding = __esm({
2176
+ "../../packages/@company/encryption/src/org-binding.ts"() {
2177
+ "use strict";
2178
+ }
2179
+ });
2180
+
2133
2181
  // ../../packages/@company/encryption/src/org-keys.ts
2134
2182
  var DEK_CACHE_TTL_MS, DEK_CACHE_MAX, HKDF_SALT;
2135
2183
  var init_org_keys = __esm({
2136
2184
  "../../packages/@company/encryption/src/org-keys.ts"() {
2137
2185
  "use strict";
2186
+ init_org_binding();
2138
2187
  DEK_CACHE_TTL_MS = Number(
2139
2188
  process.env.ORG_DEK_CACHE_TTL_MS ?? 10 * 6e4
2140
2189
  );
@@ -16363,6 +16412,98 @@ var init_redis_client = __esm({
16363
16412
  }
16364
16413
  });
16365
16414
 
16415
+ // ../../packages/@company/security/src/service-delegation.ts
16416
+ var ORG, SERVICE_INTELLIGENCE_RULES;
16417
+ var init_service_delegation = __esm({
16418
+ "../../packages/@company/security/src/service-delegation.ts"() {
16419
+ "use strict";
16420
+ ORG = "[0-9a-fA-F-]{36}";
16421
+ SERVICE_INTELLIGENCE_RULES = [
16422
+ {
16423
+ caller: "service-capture",
16424
+ method: "POST",
16425
+ path: /^\/ingest$/,
16426
+ action: "ingest"
16427
+ },
16428
+ ...["service-capture", "service-pipeline", "api"].map(
16429
+ (caller) => ({
16430
+ caller,
16431
+ method: "POST",
16432
+ path: /^\/replay$/,
16433
+ action: "replay"
16434
+ })
16435
+ ),
16436
+ {
16437
+ caller: "api",
16438
+ method: "GET",
16439
+ path: new RegExp(`^/replay/${ORG}$`),
16440
+ action: "replay.read"
16441
+ },
16442
+ { caller: "api", method: "GET", path: /^\/replays$/, action: "replay.read" },
16443
+ {
16444
+ caller: "api",
16445
+ method: "POST",
16446
+ path: /^\/quiesce$/,
16447
+ action: "mining.quiesce"
16448
+ },
16449
+ {
16450
+ caller: "api",
16451
+ method: "POST",
16452
+ path: /^\/resume$/,
16453
+ action: "mining.resume"
16454
+ },
16455
+ {
16456
+ caller: "service-pipeline",
16457
+ method: "POST",
16458
+ path: /^\/cron\/[a-z-]+$/,
16459
+ action: "cron"
16460
+ },
16461
+ {
16462
+ caller: "api",
16463
+ method: "POST",
16464
+ path: new RegExp(`^/processes/refresh-stats/${ORG}$`),
16465
+ action: "processes.refresh"
16466
+ },
16467
+ {
16468
+ caller: "api",
16469
+ method: "GET",
16470
+ path: new RegExp(`^/mining-config/${ORG}$`),
16471
+ action: "mining-config.read"
16472
+ },
16473
+ {
16474
+ caller: "api",
16475
+ method: "PATCH",
16476
+ path: new RegExp(`^/mining-config/${ORG}$`),
16477
+ action: "mining-config.write"
16478
+ },
16479
+ {
16480
+ caller: "api",
16481
+ method: "POST",
16482
+ path: /^\/graph\/connections$/,
16483
+ action: "graph.connections"
16484
+ },
16485
+ {
16486
+ caller: "api",
16487
+ method: "POST",
16488
+ path: /^\/on-integration$/,
16489
+ action: "integration.connected"
16490
+ },
16491
+ {
16492
+ caller: "api",
16493
+ method: "POST",
16494
+ path: /^\/debug\/classify-preview$/,
16495
+ action: "debug.classify"
16496
+ },
16497
+ {
16498
+ caller: "api",
16499
+ method: "GET",
16500
+ path: new RegExp(`^/diagnostics/${ORG}$`),
16501
+ action: "diagnostics.read"
16502
+ }
16503
+ ];
16504
+ }
16505
+ });
16506
+
16366
16507
  // ../../packages/@company/security/src/index.ts
16367
16508
  var init_src3 = __esm({
16368
16509
  "../../packages/@company/security/src/index.ts"() {
@@ -16372,6 +16513,7 @@ var init_src3 = __esm({
16372
16513
  init_rate_limiter();
16373
16514
  init_in_process_rate_limiter();
16374
16515
  init_redis_client();
16516
+ init_service_delegation();
16375
16517
  }
16376
16518
  });
16377
16519
 
@@ -16384,6 +16526,7 @@ var init_decrypt_rows = __esm({
16384
16526
  init_src2();
16385
16527
  init_detect();
16386
16528
  init_org_keys();
16529
+ init_org_binding();
16387
16530
  obs2 = createObservabilityService("decrypt-rows");
16388
16531
  }
16389
16532
  });
@@ -16416,6 +16559,14 @@ var init_encrypt_write = __esm({
16416
16559
  init_column_registry();
16417
16560
  init_jsonb_leaf_policy();
16418
16561
  init_hmac();
16562
+ init_org_binding();
16563
+ }
16564
+ });
16565
+
16566
+ // ../../packages/@company/encryption/src/write-refusal.ts
16567
+ var init_write_refusal = __esm({
16568
+ "../../packages/@company/encryption/src/write-refusal.ts"() {
16569
+ "use strict";
16419
16570
  }
16420
16571
  });
16421
16572
 
@@ -16434,22 +16585,43 @@ var init_exclusions = __esm({
16434
16585
  }
16435
16586
  });
16436
16587
 
16588
+ // ../../packages/@company/encryption/src/audit.ts
16589
+ var init_audit = __esm({
16590
+ "../../packages/@company/encryption/src/audit.ts"() {
16591
+ "use strict";
16592
+ init_detect();
16593
+ init_jsonb_leaf_policy();
16594
+ }
16595
+ });
16596
+
16597
+ // ../../packages/@company/encryption/src/rebind.ts
16598
+ var init_rebind = __esm({
16599
+ "../../packages/@company/encryption/src/rebind.ts"() {
16600
+ "use strict";
16601
+ init_detect();
16602
+ init_org_keys();
16603
+ }
16604
+ });
16605
+
16437
16606
  // ../../packages/@company/encryption/src/org-encryption-service.ts
16438
16607
  var init_org_encryption_service = __esm({
16439
16608
  "../../packages/@company/encryption/src/org-encryption-service.ts"() {
16440
16609
  "use strict";
16441
16610
  init_src3();
16442
16611
  init_org_keys();
16612
+ init_org_binding();
16443
16613
  }
16444
16614
  });
16445
16615
 
16446
16616
  // ../../packages/@company/encryption/src/install-org-key-warmer.ts
16447
- var WARM_TTL_MS;
16617
+ var import_node_async_hooks, WARM_TTL_MS, warmingInThisChain;
16448
16618
  var init_install_org_key_warmer = __esm({
16449
16619
  "../../packages/@company/encryption/src/install-org-key-warmer.ts"() {
16450
16620
  "use strict";
16621
+ import_node_async_hooks = require("node:async_hooks");
16451
16622
  init_org_keys();
16452
16623
  WARM_TTL_MS = Number(process.env.ORG_KEY_WARM_TTL_MS ?? 5 * 6e4);
16624
+ warmingInThisChain = new import_node_async_hooks.AsyncLocalStorage();
16453
16625
  }
16454
16626
  });
16455
16627
 
@@ -16461,6 +16633,14 @@ var init_provision_hook = __esm({
16461
16633
  }
16462
16634
  });
16463
16635
 
16636
+ // ../../packages/@company/encryption/src/org-data-key-wrapper.ts
16637
+ var init_org_data_key_wrapper = __esm({
16638
+ "../../packages/@company/encryption/src/org-data-key-wrapper.ts"() {
16639
+ "use strict";
16640
+ init_org_encryption_service();
16641
+ }
16642
+ });
16643
+
16464
16644
  // ../../packages/@company/encryption/src/index.ts
16465
16645
  var init_src4 = __esm({
16466
16646
  "../../packages/@company/encryption/src/index.ts"() {
@@ -16472,13 +16652,18 @@ var init_src4 = __esm({
16472
16652
  init_decrypt_rows();
16473
16653
  init_encrypt_write();
16474
16654
  init_write_plan();
16655
+ init_write_refusal();
16475
16656
  init_jsonb_leaf_policy();
16476
16657
  init_read_gate();
16477
16658
  init_exclusions();
16659
+ init_audit();
16478
16660
  init_org_keys();
16661
+ init_org_binding();
16662
+ init_rebind();
16479
16663
  init_org_encryption_service();
16480
16664
  init_install_org_key_warmer();
16481
16665
  init_provision_hook();
16666
+ init_org_data_key_wrapper();
16482
16667
  }
16483
16668
  });
16484
16669
 
@@ -21545,12 +21730,23 @@ var init_listener_connection = __esm({
21545
21730
  }
21546
21731
  });
21547
21732
 
21733
+ // ../../packages/@company/db/src/release-after-callback.ts
21734
+ var observability3;
21735
+ var init_release_after_callback = __esm({
21736
+ "../../packages/@company/db/src/release-after-callback.ts"() {
21737
+ "use strict";
21738
+ init_src2();
21739
+ observability3 = createObservabilityService("db-release-after-callback");
21740
+ }
21741
+ });
21742
+
21548
21743
  // ../../packages/@company/db/src/byod/routed-pool.ts
21549
21744
  var logger, ROUTED_TENANT_CONTEXT, ROUTED_TENANT_RAW_POOL, ADMITTED_TENANT_CONTEXT;
21550
21745
  var init_routed_pool = __esm({
21551
21746
  "../../packages/@company/db/src/byod/routed-pool.ts"() {
21552
21747
  "use strict";
21553
21748
  init_src2();
21749
+ init_release_after_callback();
21554
21750
  logger = createLogger("db-byod-routed-pool");
21555
21751
  ROUTED_TENANT_CONTEXT = Symbol.for("haansi.db.routedTenantContext");
21556
21752
  ROUTED_TENANT_RAW_POOL = Symbol.for("haansi.db.routedTenantRawPool");
@@ -21559,18 +21755,28 @@ var init_routed_pool = __esm({
21559
21755
  });
21560
21756
 
21561
21757
  // ../../packages/@company/db/src/org-context.ts
21562
- var observability3, DECRYPTING_CLIENT;
21758
+ var observability4, DECRYPTING_CLIENT;
21563
21759
  var init_org_context = __esm({
21564
21760
  "../../packages/@company/db/src/org-context.ts"() {
21565
21761
  "use strict";
21566
21762
  init_src4();
21567
21763
  init_src2();
21568
21764
  init_routed_pool();
21569
- observability3 = createObservabilityService("db-org-context");
21765
+ init_release_after_callback();
21766
+ observability4 = createObservabilityService("db-org-context");
21570
21767
  DECRYPTING_CLIENT = Symbol.for("haansi.db.decryptingClient");
21571
21768
  }
21572
21769
  });
21573
21770
 
21771
+ // ../../packages/@company/db/src/admin-pools.ts
21772
+ var init_admin_pools = __esm({
21773
+ "../../packages/@company/db/src/admin-pools.ts"() {
21774
+ "use strict";
21775
+ init_client();
21776
+ init_org_context();
21777
+ }
21778
+ });
21779
+
21574
21780
  // ../../packages/@company/types/src/timeline.ts
21575
21781
  var init_timeline = __esm({
21576
21782
  "../../packages/@company/types/src/timeline.ts"() {
@@ -21578,6 +21784,13 @@ var init_timeline = __esm({
21578
21784
  }
21579
21785
  });
21580
21786
 
21787
+ // ../../packages/@company/types/src/qna.ts
21788
+ var init_qna = __esm({
21789
+ "../../packages/@company/types/src/qna.ts"() {
21790
+ "use strict";
21791
+ }
21792
+ });
21793
+
21581
21794
  // ../../packages/@company/types/src/artifact.ts
21582
21795
  var init_artifact = __esm({
21583
21796
  "../../packages/@company/types/src/artifact.ts"() {
@@ -21706,10 +21919,10 @@ var init_taxonomy = __esm({
21706
21919
  category: "report",
21707
21920
  label: "Onboarding Guide",
21708
21921
  description: "Onboarding guide (or legacy onboarding packet) built from selected team knowledge. Served only after its sources are checked for the viewer; read and written only by the onboarding guide service.",
21709
- icon: "GraduationCap",
21922
+ icon: "Sprout",
21710
21923
  aliases: ["onboarding_packet", "onboarding-packet"],
21711
21924
  autoApproved: false,
21712
- producedBy: ["POST /api/v1/memory/reports/onboarding"],
21925
+ producedBy: ["POST /api/v1/memory/onboarding/guides/:id/generate"],
21713
21926
  // docs/product/plans/2026-09-24-onboarding-guides.md D11: no generic
21714
21927
  // reader or writer may touch a guide.
21715
21928
  reserved: true
@@ -21721,7 +21934,9 @@ var init_taxonomy = __esm({
21721
21934
  icon: "GitPullRequest",
21722
21935
  aliases: ["pr_context", "pr-context"],
21723
21936
  autoApproved: true,
21724
- producedBy: ["POST /api/v1/memory/reports/pr"]
21937
+ // PR generation is parked until workstream-backed context retrieval exists.
21938
+ // producedBy: ["POST /api/v1/memory/reports/pr"],
21939
+ producedBy: []
21725
21940
  },
21726
21941
  expert: {
21727
21942
  category: "expert",
@@ -21752,6 +21967,18 @@ var init_taxonomy = __esm({
21752
21967
  autoApproved: true,
21753
21968
  producedBy: ["POST /api/v1/memory/recall/messages/:id/feedback"]
21754
21969
  },
21970
+ skill: {
21971
+ category: "solution",
21972
+ label: "Skill",
21973
+ description: "Reusable, agent-loadable procedure in SKILL.md form, skillified from mined solutions or imported. One immutable revision per row; lifecycle and access live in the skills tables and are applied only by the skill service.",
21974
+ icon: "Wand2",
21975
+ aliases: ["skills", "playbook"],
21976
+ autoApproved: false,
21977
+ producedBy: [],
21978
+ // docs/product/plans/2026-10-10-memory-skills.md §3.1: no generic reader
21979
+ // or writer may touch a skill revision.
21980
+ reserved: true
21981
+ },
21755
21982
  digest_entry: {
21756
21983
  category: "digest",
21757
21984
  label: "Digest Entry",
@@ -21842,16 +22069,825 @@ var init_report_template = __esm({
21842
22069
  }
21843
22070
  });
21844
22071
 
22072
+ // ../../packages/@company/types/src/mining-config-fields.ts
22073
+ var MINING_FIELDS, MINING_PARAMETER_FIELDS, MINING_BUILTIN_DEFAULTS, MINING_FIELDS_BY_KEY;
22074
+ var init_mining_config_fields = __esm({
22075
+ "../../packages/@company/types/src/mining-config-fields.ts"() {
22076
+ "use strict";
22077
+ MINING_FIELDS = [
22078
+ {
22079
+ key: "matcher_mode",
22080
+ camel: "matcherMode",
22081
+ label: "Matcher mode",
22082
+ kind: "select",
22083
+ group: "matcher",
22084
+ options: ["heuristic", "classifier"],
22085
+ help: "Heuristic score vs the trained event-pair classifier. Classifier requires a version.",
22086
+ platformDefault: false
22087
+ },
22088
+ {
22089
+ key: "classifier_name",
22090
+ camel: "classifierName",
22091
+ label: "Classifier model",
22092
+ kind: "text",
22093
+ group: "classifier",
22094
+ help: "Model name served by ml-inference (default event-pair-classifier)."
22095
+ },
22096
+ {
22097
+ key: "pair_cap_graph",
22098
+ camel: "pairCapGraph",
22099
+ label: "Pair candidates \u2014 graph",
22100
+ kind: "number",
22101
+ group: "sampling",
22102
+ min: 0,
22103
+ max: 1e3,
22104
+ integer: true,
22105
+ help: "Candidate pairs sampled per feedback event from graph neighbours. Raising it produces more labels per event and a denser training set; lowering it keeps materialization cheap.",
22106
+ default: 4
22107
+ },
22108
+ {
22109
+ key: "pair_cap_temporal",
22110
+ camel: "pairCapTemporal",
22111
+ label: "Pair candidates \u2014 temporal",
22112
+ kind: "number",
22113
+ group: "sampling",
22114
+ min: 0,
22115
+ max: 1e3,
22116
+ integer: true,
22117
+ help: "Candidate pairs sampled per feedback event from temporal neighbours.",
22118
+ default: 4
22119
+ },
22120
+ {
22121
+ key: "pair_cap_object",
22122
+ camel: "pairCapObject",
22123
+ label: "Pair candidates \u2014 object",
22124
+ kind: "number",
22125
+ group: "sampling",
22126
+ min: 0,
22127
+ max: 1e3,
22128
+ integer: true,
22129
+ help: "Candidate pairs sampled per feedback event from shared-object neighbours.",
22130
+ default: 4
22131
+ },
22132
+ {
22133
+ key: "pair_cap_random",
22134
+ camel: "pairCapRandom",
22135
+ label: "Pair candidates \u2014 random",
22136
+ kind: "number",
22137
+ group: "sampling",
22138
+ min: 0,
22139
+ max: 1e3,
22140
+ integer: true,
22141
+ help: "Random candidate pairs per feedback event. These are the cheap negatives that keep the model from learning only from near-misses.",
22142
+ default: 2
22143
+ },
22144
+ {
22145
+ key: "test_component_size_guard",
22146
+ camel: "testComponentSizeGuard",
22147
+ label: "Holdout component size guard",
22148
+ kind: "number",
22149
+ group: "sampling",
22150
+ min: 1,
22151
+ help: "How much larger than its quota a connected component may be and still be taken for test or golden. Holdout allocation moves whole components, so without a ceiling one oversized component can swallow the training set. Raising it makes holdouts easier to fill on a corpus where labels have fused into few large components; lowering it protects train.",
22152
+ default: 5
22153
+ },
22154
+ {
22155
+ key: "min_new_labels",
22156
+ camel: "minNewLabels",
22157
+ label: "Retrain \u2014 min new labels (any split)",
22158
+ kind: "number",
22159
+ group: "sampling",
22160
+ min: 0,
22161
+ max: 1e6,
22162
+ integer: true,
22163
+ help: "Cheap pre-filter before the TRAIN-split floors are evaluated: this many new labels of any kind must have landed since the last completed run. The train-specific floors below are what actually decide whether a retrain is worth running.",
22164
+ default: 50
22165
+ },
22166
+ {
22167
+ key: "min_new_train_labels",
22168
+ camel: "minNewTrainLabels",
22169
+ label: "Retrain \u2014 min new train labels",
22170
+ kind: "number",
22171
+ group: "sampling",
22172
+ min: 0,
22173
+ max: 1e6,
22174
+ integer: true,
22175
+ help: "New TRAIN-split labels required before a retrain is queued. Deliberately separate from materialization, which drains feedback continuously \u2014 new eval/test rows change what a run is measured on, not what it can learn from, so they never trigger one. Mirrors the floor the trainer itself enforces, so the trigger does not enqueue runs the trainer will reject.",
22176
+ default: 400
22177
+ },
22178
+ {
22179
+ key: "min_new_train_positive",
22180
+ camel: "minNewTrainPositive",
22181
+ label: "Retrain \u2014 min new positives",
22182
+ kind: "number",
22183
+ group: "sampling",
22184
+ min: 0,
22185
+ max: 1e6,
22186
+ integer: true,
22187
+ help: "New TRAIN positives required before a retrain is queued.",
22188
+ default: 200
22189
+ },
22190
+ {
22191
+ key: "min_new_train_negative",
22192
+ camel: "minNewTrainNegative",
22193
+ label: "Retrain \u2014 min new negatives",
22194
+ kind: "number",
22195
+ group: "sampling",
22196
+ min: 0,
22197
+ max: 1e6,
22198
+ integer: true,
22199
+ help: "New TRAIN negatives required before a retrain is queued. Negatives are usually the scarce side, so this is the floor most likely to gate retraining.",
22200
+ default: 30
22201
+ },
22202
+ {
22203
+ key: "retrain_growth_fraction",
22204
+ camel: "retrainGrowthFraction",
22205
+ label: "Retrain \u2014 growth fraction",
22206
+ kind: "number",
22207
+ group: "sampling",
22208
+ min: 0,
22209
+ max: 1,
22210
+ help: "Extra new TRAIN data required, as a fraction of what the LAST COMPLETED run trained on. A flat floor has a mature org retraining forever for negligible gain: 100 new rows on top of 200 can move a model, on top of 20,000 they cannot. 0.25 means 'retrain once the training set has grown by a quarter', giving roughly logarithmic retrain frequency. 0 disables escalation and keeps the flat floors.",
22211
+ default: 0.25
22212
+ },
22213
+ {
22214
+ key: "max_new_train_labels",
22215
+ camel: "maxNewTrainLabels",
22216
+ label: "Retrain \u2014 max new labels (cap)",
22217
+ kind: "number",
22218
+ group: "sampling",
22219
+ min: 0,
22220
+ max: 1e6,
22221
+ integer: true,
22222
+ help: "Ceiling on the escalated requirement, as a budget on TOTAL new train labels. Without it a percentage of a large corpus stops being achievable \u2014 25% of 500k is 125,000 new human labels, which no org produces, so the org silently stops retraining. The per-class bars take shares of this budget apportioned by the corpus class mix, rather than each demanding the full cap. 0 removes the cap.",
22223
+ default: 2e3
22224
+ },
22225
+ {
22226
+ key: "train_lookback_days",
22227
+ camel: "trainLookbackDays",
22228
+ label: "Train lookback (days)",
22229
+ kind: "number",
22230
+ group: "sampling",
22231
+ min: 0,
22232
+ max: 1e5,
22233
+ integer: true,
22234
+ help: "How far back the TRAIN split may reach. Older labels age out of training, so the corpus stops growing without bound and the retrain growth bar plateaus instead of escalating past what an org can ever label. Applied to train ONLY: eval/test/golden keep every label, because the snapshot id is a hash of the evaluated set and ranking versions is only meaningful within a snapshot \u2014 a sliding holdout would score each version against different data. 0 disables the window. Note that shrinking this can push a split back under the per-class floors and stop training entirely, since rare classes thin out first.",
22235
+ default: 365
22236
+ },
22237
+ {
22238
+ key: "classifier_accept_min_f1",
22239
+ camel: "classifierAcceptMinF1",
22240
+ label: "Accept classifier \u2014 min best F1",
22241
+ kind: "number",
22242
+ group: "classifier",
22243
+ min: 0,
22244
+ max: 1,
22245
+ help: "A classifier must reach at least this best-F1 on the evaluated snapshot before it can be promoted and take over from the heuristic matcher. Measured at the model's OWN best threshold, not at 0.5, so an uncalibrated model is judged on its ranking rather than on where the cut happens to sit. This is an absolute floor \u2014 'better than the incumbent' is already enforced by the rank gate.",
22246
+ default: 0.75
22247
+ },
22248
+ {
22249
+ key: "classifier_accept_min_pr_auc",
22250
+ camel: "classifierAcceptMinPrAuc",
22251
+ label: "Accept classifier \u2014 min PR-AUC",
22252
+ kind: "number",
22253
+ group: "classifier",
22254
+ min: 0,
22255
+ max: 1,
22256
+ help: "Minimum average precision (PR-AUC) before a classifier can be promoted. Threshold-free, so it measures ranking quality independently of any operating point.",
22257
+ default: 0.8
22258
+ },
22259
+ {
22260
+ key: "global_classifier_versions",
22261
+ camel: "globalClassifierVersions",
22262
+ label: "Global classifier catalog",
22263
+ kind: "text",
22264
+ group: "classifier",
22265
+ help: "Comma-separated versions offered to orgs that have not trained their own model yet \u2014 the cold-start candidates evaluated against an org's labels once it has enough of them. Blank means every version in the model bucket is a candidate, including half-finished experiments, so setting this is how you curate. A version an org has already adopted is always kept even if it is not listed, so editing this cannot drop the model currently deciding; and a version an org archives is dropped for that org only, never globally."
22266
+ },
22267
+ {
22268
+ key: "classifier_version",
22269
+ camel: "classifierVersion",
22270
+ label: "Classifier version",
22271
+ kind: "select",
22272
+ group: "classifier",
22273
+ help: "Required when mode = classifier. Choices come from the model versions in S3.",
22274
+ platformDefault: false
22275
+ },
22276
+ {
22277
+ key: "classifier_precision_target",
22278
+ camel: "classifierPrecisionTarget",
22279
+ label: "Attach precision target",
22280
+ kind: "number",
22281
+ group: "classifier",
22282
+ min: 0,
22283
+ max: 1,
22284
+ help: "Minimum precision required to attach automatically. The score cutoff is DERIVED from this against each version's own test evaluation \u2014 the lowest cut that still meets the target, so recall is not given away. Set what error rate you accept (0.97 = at most three wrong links in a hundred); the number that implements it changes every retrain, because the same score means different things on different models. If a version cannot reach this precision at any cut, no attach threshold is installed and the previous one stands.",
22285
+ default: 0.97
22286
+ },
22287
+ {
22288
+ key: "classifier_review_precision_target",
22289
+ camel: "classifierReviewPrecisionTarget",
22290
+ label: "Review precision target",
22291
+ kind: "number",
22292
+ group: "classifier",
22293
+ min: 0,
22294
+ max: 1,
22295
+ help: "Minimum precision required to send a candidate to the LLM rather than reject it outright. Must be at or below the attach target \u2014 scores between the two cuts are the ambiguous middle the LLM adjudicates. A value above the attach target would make that band empty and silently remove the LLM safety net, so it is clamped down to the attach target.",
22296
+ default: 0.93
22297
+ },
22298
+ {
22299
+ key: "seed_aggregation",
22300
+ camel: "seedAggregation",
22301
+ label: "Seed discovery \u2014 score aggregation",
22302
+ kind: "select",
22303
+ group: "classifier",
22304
+ options: ["mean", "median", "max", "min", "trimmed_mean"],
22305
+ help: "How a candidate's per-seed pair scores collapse to one number when a curator asks for related events on /timelines. mean makes every seed vote, so a candidate has to resemble the seed set rather than any one seed; median is the outlier-tolerant version of that. max asks only 'is there a seed this candidate clearly belongs with?' \u2014 it keeps the score on the per-pair scale the attach cut was calibrated on, but one coincidental neighbour is enough to pull a candidate in. min demands every seed agree; trimmed_mean drops the weakest seed, then averages. Blank uses the built-in default (mean)."
22306
+ },
22307
+ {
22308
+ key: "seed_expansion_paths",
22309
+ camel: "seedExpansionPaths",
22310
+ label: "Seed discovery \u2014 candidate paths",
22311
+ kind: "select",
22312
+ group: "classifier",
22313
+ options: ["graph", "graph_objects", "graph_objects_semantic"],
22314
+ help: "Which paths find candidates when a curator asks for related events. graph walks activity_graph_edges only. The object and semantic paths mirror how the miner finds candidates, but the miner scores them against an established process; a curator-seeded set has no such history, so those paths mostly surface events that merely share a busy channel, a repo or a topic \u2014 they inflate the list without changing what gets approved. Widen only for an org whose graph is too thin to surface anything at all; the per-path counts in the discovery note tell you which one is empty. Blank uses the built-in default (graph)."
22315
+ },
22316
+ {
22317
+ key: "seed_llm_defer_top_n",
22318
+ camel: "seedLlmDeferTopN",
22319
+ label: "Seed discovery \u2014 LLM adjudication (top N)",
22320
+ kind: "number",
22321
+ group: "classifier",
22322
+ min: 0,
22323
+ integer: true,
22324
+ help: "How many ambiguous-band candidates (score between the review and attach cuts) are sent to the LLM per 'find related events' click. The band is exactly where the classifier says 'not confident either way', so something other than a number has to decide; the LLM sees the seed set once and rules on all N in a single call. The curator is waiting, so this is a latency and cost budget as much as a quality knob. 0 disables adjudication and leaves band candidates unapproved. Blank uses the built-in default (10). Requires the org to have a reasoning model configured."
22325
+ },
22326
+ {
22327
+ key: "classifier_max_second_hop_per_process",
22328
+ camel: "classifierMaxSecondHopPerProcess",
22329
+ label: "Max 2nd-hop / process",
22330
+ kind: "number",
22331
+ group: "classifier",
22332
+ min: 1,
22333
+ integer: true,
22334
+ help: "Cap on 2nd-hop neighbors scored per process. Default 8."
22335
+ },
22336
+ {
22337
+ key: "shadow_classifier_enabled",
22338
+ camel: "shadowClassifierEnabled",
22339
+ label: "Shadow candidate",
22340
+ kind: "boolean",
22341
+ group: "classifier",
22342
+ help: "Score a candidate model on live events without changing process assignment.",
22343
+ platformDefault: false
22344
+ },
22345
+ {
22346
+ key: "shadow_classifier_name",
22347
+ camel: "shadowClassifierName",
22348
+ label: "Shadow model",
22349
+ kind: "text",
22350
+ group: "classifier",
22351
+ help: "Candidate model name to score live in shadow mode."
22352
+ },
22353
+ {
22354
+ key: "shadow_classifier_version",
22355
+ camel: "shadowClassifierVersion",
22356
+ label: "Shadow version",
22357
+ kind: "select",
22358
+ group: "classifier",
22359
+ help: "Candidate classifier version to shadow-score before promotion.",
22360
+ platformDefault: false
22361
+ },
22362
+ {
22363
+ key: "shadow_classifier_sample_rate",
22364
+ camel: "shadowClassifierSampleRate",
22365
+ label: "Shadow sample rate",
22366
+ kind: "number",
22367
+ group: "classifier",
22368
+ min: 0,
22369
+ max: 1,
22370
+ help: "Fraction of live events to shadow-score with the candidate (0\u20131). Default 1."
22371
+ },
22372
+ {
22373
+ key: "semantic_negative_max_process_similarity",
22374
+ camel: "semanticNegativeMaxProcessSimilarity",
22375
+ label: "Negatives: max workstream similarity",
22376
+ kind: "number",
22377
+ group: "sampling",
22378
+ min: 0,
22379
+ max: 1,
22380
+ help: "Two workstreams must be at or below this cosine similarity before their members can be paired as negatives.",
22381
+ default: 0.35
22382
+ },
22383
+ {
22384
+ key: "semantic_negative_max_event_similarity",
22385
+ camel: "semanticNegativeMaxEventSimilarity",
22386
+ label: "Negatives: max event similarity",
22387
+ kind: "number",
22388
+ group: "sampling",
22389
+ min: 0,
22390
+ max: 1,
22391
+ help: "Two events must ALSO be at or below this cosine similarity to be labeled a negative. Distance at both levels is what keeps the label safe.",
22392
+ default: 0.35
22393
+ },
22394
+ {
22395
+ key: "semantic_negative_pairs_per_process_pair",
22396
+ camel: "semanticNegativePairsPerProcessPair",
22397
+ label: "Negatives: pairs per workstream pair",
22398
+ kind: "number",
22399
+ group: "sampling",
22400
+ min: 1,
22401
+ help: "Cap on negatives minted from one pair of workstreams per sweep.",
22402
+ default: 20
22403
+ },
22404
+ {
22405
+ key: "semantic_negative_max_process_pairs",
22406
+ camel: "semanticNegativeMaxProcessPairs",
22407
+ label: "Negatives: workstream pairs per sweep",
22408
+ kind: "number",
22409
+ group: "sampling",
22410
+ min: 1,
22411
+ help: "Cap on how many workstream pairs one sweep considers.",
22412
+ default: 40
22413
+ },
22414
+ {
22415
+ key: "enable_llm",
22416
+ camel: "enableLlm",
22417
+ label: "Enable LLM escalation",
22418
+ kind: "boolean",
22419
+ group: "escalation",
22420
+ help: "When on, ambiguous events escalate to the reasoning LLM (and it may create new processes).",
22421
+ platformDefault: false
22422
+ },
22423
+ {
22424
+ key: "heuristic_threshold_mode",
22425
+ camel: "heuristicThresholdMode",
22426
+ label: "Threshold mode",
22427
+ kind: "select",
22428
+ group: "thresholds",
22429
+ options: ["fixed", "calibrated"],
22430
+ help: "fixed: use the attach/review/reactivation values below. calibrated: mining waits until the org has enough events, then derives the three thresholds from percentiles of heuristic scores between random event pairs, and replays the org with them.",
22431
+ default: "fixed"
22432
+ },
22433
+ {
22434
+ key: "calibration_attach_percentile",
22435
+ camel: "calibrationAttachPercentile",
22436
+ label: "Attach percentile",
22437
+ kind: "number",
22438
+ group: "thresholds",
22439
+ minExclusive: 0,
22440
+ max: 1,
22441
+ help: "Attach threshold = this percentile of random-pair scores (0\u20131). Random pairs are mostly unrelated work, so 0.97 means about 3% of unrelated pairs would clear the bar.",
22442
+ default: 0.97,
22443
+ visibleWhen: { key: "heuristic_threshold_mode", in: ["calibrated"] }
22444
+ },
22445
+ {
22446
+ key: "calibration_review_percentile",
22447
+ camel: "calibrationReviewPercentile",
22448
+ label: "Review percentile",
22449
+ kind: "number",
22450
+ group: "thresholds",
22451
+ minExclusive: 0,
22452
+ max: 1,
22453
+ help: "Review (LLM) threshold = this percentile of random-pair scores (0\u20131). Must be below the attach percentile.",
22454
+ default: 0.9,
22455
+ visibleWhen: { key: "heuristic_threshold_mode", in: ["calibrated"] }
22456
+ },
22457
+ {
22458
+ key: "calibration_reactivation_percentile",
22459
+ camel: "calibrationReactivationPercentile",
22460
+ label: "Reactivation percentile",
22461
+ kind: "number",
22462
+ group: "thresholds",
22463
+ minExclusive: 0,
22464
+ max: 1,
22465
+ help: "Reactivation threshold = this percentile of random-pair scores (0\u20131). Must be at or above the attach percentile.",
22466
+ default: 0.99,
22467
+ visibleWhen: { key: "heuristic_threshold_mode", in: ["calibrated"] }
22468
+ },
22469
+ {
22470
+ key: "calibration_min_events",
22471
+ camel: "calibrationMinEvents",
22472
+ label: "Calibrate after (events)",
22473
+ kind: "number",
22474
+ group: "thresholds",
22475
+ min: 50,
22476
+ integer: true,
22477
+ help: "Process assignment waits until the org has this many non-noise events; then thresholds are calibrated and the org is replayed.",
22478
+ default: 1e3,
22479
+ visibleWhen: { key: "heuristic_threshold_mode", in: ["calibrated"] }
22480
+ },
22481
+ {
22482
+ key: "calibration_sample_pairs",
22483
+ camel: "calibrationSamplePairs",
22484
+ label: "Calibration sample pairs",
22485
+ kind: "number",
22486
+ group: "thresholds",
22487
+ min: 100,
22488
+ max: 2e4,
22489
+ integer: true,
22490
+ help: "How many random event pairs are scored. Tail percentiles need a large sample: at 2000 pairs the 99th percentile rests on ~20 scores.",
22491
+ default: 2e3,
22492
+ visibleWhen: { key: "heuristic_threshold_mode", in: ["calibrated"] }
22493
+ },
22494
+ {
22495
+ key: "attach_threshold",
22496
+ camel: "attachThreshold",
22497
+ label: "Attach threshold",
22498
+ kind: "number",
22499
+ group: "thresholds",
22500
+ min: 0,
22501
+ max: 1,
22502
+ help: "Heuristic score \u2265 this attaches the event (0\u20131). Overrides the global 0.55.",
22503
+ visibleWhen: { key: "heuristic_threshold_mode", in: ["fixed"] }
22504
+ },
22505
+ {
22506
+ key: "review_threshold",
22507
+ camel: "reviewThreshold",
22508
+ label: "Review threshold",
22509
+ kind: "number",
22510
+ group: "thresholds",
22511
+ min: 0,
22512
+ max: 1,
22513
+ help: "Score in [review, attach) defers to the LLM; below it starts a new context. Also the floor for name-based dedup (0\u20131). Overrides the global 0.4.",
22514
+ visibleWhen: { key: "heuristic_threshold_mode", in: ["fixed"] }
22515
+ },
22516
+ {
22517
+ key: "reactivation_threshold",
22518
+ camel: "reactivationThreshold",
22519
+ label: "Reactivation threshold",
22520
+ kind: "number",
22521
+ group: "thresholds",
22522
+ min: 0,
22523
+ max: 1,
22524
+ help: "Score \u2265 this reactivates a dormant context (0\u20131). Overrides the global 0.7.",
22525
+ visibleWhen: { key: "heuristic_threshold_mode", in: ["fixed"] }
22526
+ },
22527
+ {
22528
+ key: "max_attaches",
22529
+ camel: "maxAttaches",
22530
+ label: "Max attaches / event",
22531
+ kind: "number",
22532
+ group: "thresholds",
22533
+ min: 1,
22534
+ integer: true,
22535
+ help: "Max contexts one event attaches to. Overrides the global 5."
22536
+ },
22537
+ // ── Group B: heuristic scoring weights (null → global default) ────────────
22538
+ {
22539
+ key: "weight_entity",
22540
+ camel: "weightEntity",
22541
+ label: "Weight \xB7 entity",
22542
+ kind: "number",
22543
+ group: "weights",
22544
+ min: 0,
22545
+ max: 1,
22546
+ help: "Shared-object overlap weight (0\u20131). Global default 0.25. Weights should sum to ~1."
22547
+ },
22548
+ {
22549
+ key: "weight_author",
22550
+ camel: "weightAuthor",
22551
+ label: "Weight \xB7 author",
22552
+ kind: "number",
22553
+ group: "weights",
22554
+ min: 0,
22555
+ max: 1,
22556
+ help: "Same-author weight (0\u20131). Global default 0.15."
22557
+ },
22558
+ {
22559
+ key: "weight_temporal",
22560
+ camel: "weightTemporal",
22561
+ label: "Weight \xB7 temporal",
22562
+ kind: "number",
22563
+ group: "weights",
22564
+ min: 0,
22565
+ max: 1,
22566
+ help: "Time-proximity weight (0\u20131). Global default 0.10."
22567
+ },
22568
+ {
22569
+ key: "weight_semantic",
22570
+ camel: "weightSemantic",
22571
+ label: "Weight \xB7 semantic",
22572
+ kind: "number",
22573
+ group: "weights",
22574
+ min: 0,
22575
+ max: 1,
22576
+ help: "Embedding-similarity weight (0\u20131). Global default 0.35."
22577
+ },
22578
+ {
22579
+ key: "weight_graph",
22580
+ camel: "weightGraph",
22581
+ label: "Weight \xB7 graph",
22582
+ kind: "number",
22583
+ group: "weights",
22584
+ min: 0,
22585
+ max: 1,
22586
+ help: "Graph-walk signal weight (0\u20131). Global default 0.15."
22587
+ },
22588
+ {
22589
+ key: "idf_strength",
22590
+ camel: "idfStrength",
22591
+ label: "IDF strength",
22592
+ kind: "number",
22593
+ group: "weights",
22594
+ min: 0,
22595
+ max: 1,
22596
+ help: "Frequency-damping for the entity/author/graph terms (each signal weighted by 1/df^strength, df = object fan-in). 0 = off (legacy full weight); 0.5 = default; 1 = full inverse frequency. Blank = inherit global 0.5. Demotes super-node objects and a single dominant author so specific signals drive matches."
22597
+ },
22598
+ {
22599
+ key: "summary_embedding_decay",
22600
+ camel: "summaryEmbeddingDecay",
22601
+ label: "Summary embedding decay",
22602
+ kind: "number",
22603
+ group: "weights",
22604
+ min: 0.01,
22605
+ max: 1,
22606
+ help: "Recency weighting for a process's summary embedding: the newest member weighs 1, the one i events back weighs decay^i. 1 = plain mean; 0.95 = default (about 36% weight 20 events back). Blank = inherit global 0.95."
22607
+ },
22608
+ // ── Group B: object-graph edge caps + matcher budget ─────────────────────
22609
+ {
22610
+ key: "matcher_max_neighbors",
22611
+ camel: "matcherMaxNeighbors",
22612
+ label: "Matcher max neighbors",
22613
+ kind: "number",
22614
+ group: "graph",
22615
+ min: 1,
22616
+ integer: true,
22617
+ help: "Graph-walk neighbour budget per event. Global default 50. Raise when per-type caps thin a node's neighbourhood below this and starve candidate discovery."
22618
+ },
22619
+ {
22620
+ key: "matcher_min_edge_weight",
22621
+ camel: "matcherMinEdgeWeight",
22622
+ label: "Matcher min edge weight",
22623
+ kind: "number",
22624
+ group: "graph",
22625
+ min: 0,
22626
+ max: 1,
22627
+ help: "Weight floor for candidate edges (0\u20131). Global default 0.2."
22628
+ },
22629
+ {
22630
+ key: "late_event_window_hours",
22631
+ camel: "lateEventWindowHours",
22632
+ label: "Late-event window (hours)",
22633
+ kind: "number",
22634
+ group: "graph",
22635
+ min: 1,
22636
+ help: "A live event older than this (behind the org's newest processed event) is not scored inline; it waits for the windowed replay so it is scored in order. Global default 24."
22637
+ },
22638
+ {
22639
+ key: "max_edges_co_object",
22640
+ camel: "maxEdgesCoObject",
22641
+ label: "Cap \xB7 co_object edges",
22642
+ kind: "number",
22643
+ group: "graph",
22644
+ min: 1,
22645
+ integer: true,
22646
+ help: "Top-N co_object edges kept per event. Global default 5. Raising this is the durable fix for edge-cap-starved matching."
22647
+ },
22648
+ {
22649
+ key: "max_edges_time_neighbor",
22650
+ camel: "maxEdgesTimeNeighbor",
22651
+ label: "Cap \xB7 time_neighbor edges",
22652
+ kind: "number",
22653
+ group: "graph",
22654
+ min: 1,
22655
+ integer: true,
22656
+ help: "Top-N time_neighbor edges kept per event. Global default 5."
22657
+ },
22658
+ {
22659
+ key: "max_edges_same_semantic",
22660
+ camel: "maxEdgesSameSemantic",
22661
+ label: "Cap \xB7 same_semantic edges",
22662
+ kind: "number",
22663
+ group: "graph",
22664
+ min: 1,
22665
+ integer: true,
22666
+ help: "Top-N same_semantic edges kept per event. Global default 8."
22667
+ },
22668
+ {
22669
+ key: "max_edges_similar_authors",
22670
+ camel: "maxEdgesSimilarAuthors",
22671
+ label: "Cap \xB7 similar_authors edges",
22672
+ kind: "number",
22673
+ group: "graph",
22674
+ min: 1,
22675
+ integer: true,
22676
+ help: "Top-N similar_authors edges kept per event. Global default 8."
22677
+ },
22678
+ {
22679
+ key: "max_edges_pr_to_issue",
22680
+ camel: "maxEdgesPrToIssue",
22681
+ label: "Cap \xB7 pr_to_issue edges",
22682
+ kind: "number",
22683
+ group: "graph",
22684
+ min: 1,
22685
+ integer: true,
22686
+ help: "Top-N pr_to_issue edges kept per event. Global default 8."
22687
+ },
22688
+ // ─── Split allocation + training data shape (object-graph/070, /071) ──────
22689
+ // These existed as columns and behaviour before they existed as controls,
22690
+ // which meant the only way to configure a split was raw SQL — and an unset
22691
+ // quota silently meant "no holdout at all".
22692
+ {
22693
+ key: "golden_target_events",
22694
+ camel: "goldenTargetEvents",
22695
+ label: "Golden target (events)",
22696
+ kind: "number",
22697
+ group: "sampling",
22698
+ min: 1,
22699
+ integer: true,
22700
+ help: "How many EVENTS to hold out as golden. Blank derives ~5% of eligible events. Sized in events, not labels: labels are quadratic in events, so a label count overstates evaluation power. Holdout is bounded by component count \u2014 with few curated workstreams you are choosing which components, not how many events."
22701
+ },
22702
+ {
22703
+ key: "test_target_events",
22704
+ camel: "testTargetEvents",
22705
+ label: "Test target (events)",
22706
+ kind: "number",
22707
+ group: "sampling",
22708
+ min: 1,
22709
+ integer: true,
22710
+ help: "How many EVENTS to hold out as test. Blank derives ~15% of eligible events."
22711
+ },
22712
+ {
22713
+ key: "min_split_labels",
22714
+ camel: "minSplitLabels",
22715
+ label: "Min labels per split",
22716
+ kind: "number",
22717
+ group: "sampling",
22718
+ min: 0,
22719
+ max: 1e6,
22720
+ integer: true,
22721
+ help: "Floor each of train/eval/test must clear. Also the precondition for the golden fallback: once all three clear it, golden may take a component the size guard would otherwise refuse, so the split is never permanently empty.",
22722
+ default: 100
22723
+ },
22724
+ {
22725
+ key: "min_train_class_labels",
22726
+ camel: "minTrainClassLabels",
22727
+ label: "Min labels per class \u2014 train",
22728
+ kind: "number",
22729
+ group: "sampling",
22730
+ min: 0,
22731
+ max: 1e6,
22732
+ integer: true,
22733
+ help: "Floor EACH class must clear in the TRAIN split before fitting. This is a capacity question, not a statistical one: below roughly this many examples per class the model memorises rather than generalises. Train is also the split that can be topped up \u2014 fallback negatives are borrowed at extraction time and the set is then downsampled to balance \u2014 so it can carry a higher bar than the holdouts.",
22734
+ default: 200
22735
+ },
22736
+ {
22737
+ key: "min_eval_class_labels",
22738
+ camel: "minEvalClassLabels",
22739
+ label: "Min labels per class \u2014 eval / test",
22740
+ kind: "number",
22741
+ group: "sampling",
22742
+ min: 0,
22743
+ max: 1e6,
22744
+ integer: true,
22745
+ help: "Floor EACH class must clear in EACH of eval and test. A split can pass the total floor on volume alone and still be single-class \u2014 dev's test split held 142 labels, all positive, which any always-say-same model scores 100% on. This one is about whether a measurement means anything: a 95% interval on an observed 0.80 accuracy is roughly +/-0.14 at 30 per class and +/-0.08 at 100. Held-out splits cannot borrow negatives, so setting this above what your data can supply blocks training rather than improving it.",
22746
+ default: 10
22747
+ },
22748
+ {
22749
+ key: "label_conflict_policy",
22750
+ camel: "labelConflictPolicy",
22751
+ label: "Conflicting label policy",
22752
+ kind: "select",
22753
+ group: "sampling",
22754
+ options: ["keep_both", "replace", "withhold"],
22755
+ help: "What happens when new curation contradicts an existing human label on the same pair. keep_both (default) keeps both judgements, so training sees the ambiguity and the model is less confident there. replace supersedes the older one, for when later curation is the correction. withhold discards the new label and holds the event for review \u2014 the old behaviour, which silently dropped most removal labels."
22756
+ },
22757
+ {
22758
+ key: "train_negative_fallback_enabled",
22759
+ camel: "trainNegativeFallbackEnabled",
22760
+ label: "Borrow negatives for training",
22761
+ kind: "boolean",
22762
+ group: "sampling",
22763
+ help: "When train (or eval) has too few negatives, borrow them from sources the routing rules do not admit \u2014 typically LLM review removals. NEVER touches test or golden, and never borrows a label whose events sit in a held-out split. These are labels no human checked.",
22764
+ default: true
22765
+ },
22766
+ {
22767
+ key: "train_min_negative_share",
22768
+ camel: "trainMinNegativeShare",
22769
+ label: "Min negative share (train)",
22770
+ kind: "number",
22771
+ group: "sampling",
22772
+ min: 0,
22773
+ max: 1,
22774
+ help: "Below this share of negatives, the fallback engages.",
22775
+ default: 0.5
22776
+ },
22777
+ {
22778
+ key: "train_negative_fallback_max_ratio",
22779
+ camel: "trainNegativeFallbackMaxRatio",
22780
+ label: "Borrowed negatives cap (x positives)",
22781
+ kind: "number",
22782
+ group: "sampling",
22783
+ min: 0,
22784
+ help: "Ceiling on borrowed negatives as a multiple of the split's positives, so a large ineligible pool cannot swamp a small human-labelled one.",
22785
+ default: 1
22786
+ },
22787
+ {
22788
+ key: "train_balance_strategy",
22789
+ camel: "trainBalanceStrategy",
22790
+ label: "Training class balance",
22791
+ kind: "select",
22792
+ group: "sampling",
22793
+ options: ["none", "downsample_majority", "oversample_minority"],
22794
+ help: "Applied to the training split after extraction. Downsampling loses data but adds no duplicates; oversampling keeps all signal but repeats rows, which inflates confidence.",
22795
+ default: "downsample_majority"
22796
+ },
22797
+ {
22798
+ key: "min_golden_curators",
22799
+ camel: "minGoldenCurators",
22800
+ label: "Min curators for golden",
22801
+ kind: "number",
22802
+ group: "sampling",
22803
+ min: 1,
22804
+ integer: true,
22805
+ help: "Distinct human curators required on a workstream before its explicit labels are auto-promoted to golden.",
22806
+ default: 2
22807
+ },
22808
+ {
22809
+ key: "feedback_triage_max_contradictions",
22810
+ camel: "feedbackTriageMaxContradictions",
22811
+ label: "Feedback triage \u2014 max contradictions",
22812
+ kind: "number",
22813
+ group: "sampling",
22814
+ min: 1,
22815
+ integer: true,
22816
+ help: "A curator feedback event contradicted by more than this many other curators is held rather than turned into labels.",
22817
+ default: 1
22818
+ },
22819
+ {
22820
+ key: "feedback_triage_max_selection_size",
22821
+ camel: "feedbackTriageMaxSelectionSize",
22822
+ label: "Feedback triage \u2014 max selection",
22823
+ kind: "number",
22824
+ group: "sampling",
22825
+ min: 1,
22826
+ integer: true,
22827
+ help: "A feedback event selecting more activities than this is held (likely a bulk action, not a judgement).",
22828
+ default: 400
22829
+ },
22830
+ {
22831
+ key: "process_review_step_1",
22832
+ camel: "processReviewStep1",
22833
+ label: "Review at (step 1)",
22834
+ kind: "number",
22835
+ group: "review",
22836
+ min: 1,
22837
+ integer: true,
22838
+ help: "Members a process must reach before its first automated review (re-describe, rename, recategorize, drop unrelated members). Must be below step 2. Whether an org is reviewed at all is its mining_process_review feature flag.",
22839
+ default: 5
22840
+ },
22841
+ {
22842
+ key: "process_review_step_2",
22843
+ camel: "processReviewStep2",
22844
+ label: "Review again at (step 2)",
22845
+ kind: "number",
22846
+ group: "review",
22847
+ min: 2,
22848
+ integer: true,
22849
+ help: "Members at which the process is reviewed a second time, once more of it is known. Must be above step 1.",
22850
+ default: 10
22851
+ },
22852
+ {
22853
+ key: "notes",
22854
+ camel: "notes",
22855
+ label: "Notes",
22856
+ kind: "text",
22857
+ group: "meta",
22858
+ help: "Free-text admin note.",
22859
+ platformDefault: false
22860
+ }
22861
+ ];
22862
+ MINING_PARAMETER_FIELDS = MINING_FIELDS.filter(
22863
+ (field) => field.platformDefault !== false
22864
+ );
22865
+ MINING_BUILTIN_DEFAULTS = Object.freeze(
22866
+ Object.fromEntries(
22867
+ MINING_FIELDS.filter((f) => f.default !== void 0).map((f) => [
22868
+ f.key,
22869
+ f.default
22870
+ ])
22871
+ )
22872
+ );
22873
+ MINING_FIELDS_BY_KEY = new Map(
22874
+ MINING_FIELDS.map((f) => [f.key, f])
22875
+ );
22876
+ }
22877
+ });
22878
+
21845
22879
  // ../../packages/@company/types/src/index.ts
21846
22880
  var init_src5 = __esm({
21847
22881
  "../../packages/@company/types/src/index.ts"() {
21848
22882
  init_timeline();
22883
+ init_qna();
21849
22884
  init_artifact();
21850
22885
  init_taxonomy();
21851
22886
  init_embedding();
21852
22887
  init_repo_file_url();
21853
22888
  init_query_dialect();
21854
22889
  init_report_template();
22890
+ init_mining_config_fields();
21855
22891
  }
21856
22892
  });
21857
22893
 
@@ -21869,7 +22905,7 @@ var init_search_vector_write = __esm({
21869
22905
  });
21870
22906
 
21871
22907
  // ../../packages/@company/db/src/org-purge.ts
21872
- var observability4;
22908
+ var observability5;
21873
22909
  var init_org_purge = __esm({
21874
22910
  "../../packages/@company/db/src/org-purge.ts"() {
21875
22911
  "use strict";
@@ -21877,12 +22913,12 @@ var init_org_purge = __esm({
21877
22913
  init_src2();
21878
22914
  init_client();
21879
22915
  init_org_context();
21880
- observability4 = createObservabilityService("org-purge");
22916
+ observability5 = createObservabilityService("org-purge");
21881
22917
  }
21882
22918
  });
21883
22919
 
21884
22920
  // ../../packages/@company/db/src/org-orphan-scan.ts
21885
- var observability5;
22921
+ var observability6;
21886
22922
  var init_org_orphan_scan = __esm({
21887
22923
  "../../packages/@company/db/src/org-orphan-scan.ts"() {
21888
22924
  "use strict";
@@ -21890,7 +22926,7 @@ var init_org_orphan_scan = __esm({
21890
22926
  init_src2();
21891
22927
  init_org_context();
21892
22928
  init_org_purge();
21893
- observability5 = createObservabilityService("org-orphan-scan");
22929
+ observability6 = createObservabilityService("org-orphan-scan");
21894
22930
  }
21895
22931
  });
21896
22932
 
@@ -21938,12 +22974,13 @@ var init_expert_areas = __esm({
21938
22974
  });
21939
22975
 
21940
22976
  // ../../packages/@company/db/src/exchange-context.ts
21941
- var observability6;
22977
+ var observability7;
21942
22978
  var init_exchange_context = __esm({
21943
22979
  "../../packages/@company/db/src/exchange-context.ts"() {
21944
22980
  "use strict";
21945
22981
  init_src2();
21946
- observability6 = createObservabilityService("db-exchange-context");
22982
+ init_org_context();
22983
+ observability7 = createObservabilityService("db-exchange-context");
21947
22984
  }
21948
22985
  });
21949
22986
 
@@ -21972,14 +23009,30 @@ var init_data_access = __esm({
21972
23009
  }
21973
23010
  });
21974
23011
 
23012
+ // ../../packages/@company/db/src/byod/host-guard.ts
23013
+ var init_host_guard = __esm({
23014
+ "../../packages/@company/db/src/byod/host-guard.ts"() {
23015
+ "use strict";
23016
+ }
23017
+ });
23018
+
23019
+ // ../../packages/@company/db/src/byod/admission-policy.ts
23020
+ var init_admission_policy = __esm({
23021
+ "../../packages/@company/db/src/byod/admission-policy.ts"() {
23022
+ "use strict";
23023
+ }
23024
+ });
23025
+
21975
23026
  // ../../packages/@company/db/src/byod/pool-manager.ts
21976
- var observability7, IDLE_TIMEOUT_MS;
23027
+ var observability8, IDLE_TIMEOUT_MS;
21977
23028
  var init_pool_manager = __esm({
21978
23029
  "../../packages/@company/db/src/byod/pool-manager.ts"() {
21979
23030
  "use strict";
21980
23031
  init_esm();
21981
23032
  init_src2();
21982
- observability7 = createObservabilityService("byod-pool-manager");
23033
+ init_host_guard();
23034
+ init_admission_policy();
23035
+ observability8 = createObservabilityService("byod-pool-manager");
21983
23036
  IDLE_TIMEOUT_MS = 5 * 60 * 1e3;
21984
23037
  }
21985
23038
  });
@@ -22014,6 +23067,7 @@ var init_org_db_resolver = __esm({
22014
23067
  "../../packages/@company/db/src/byod/org-db-resolver.ts"() {
22015
23068
  "use strict";
22016
23069
  init_pool_manager();
23070
+ init_admission_policy();
22017
23071
  init_routed_pool();
22018
23072
  }
22019
23073
  });
@@ -22060,7 +23114,7 @@ function entry(database, dbName, table, orgScoping, opts = {}) {
22060
23114
  identity: {
22061
23115
  kind: "columns",
22062
23116
  columns: opts.mergePolicy.identityKey,
22063
- version: 1
23117
+ version: opts.identityVersion ?? 1
22064
23118
  }
22065
23119
  } : DEFAULT_RECONCILIATION)
22066
23120
  };
@@ -22069,15 +23123,27 @@ function standardIntegrationTables(database, dbName) {
22069
23123
  return [
22070
23124
  entry(database, dbName, "source_events", "direct", {
22071
23125
  scopingColumn: "organization_id",
23126
+ // v2: the identity gained source_account_id (A0).
23127
+ identityVersion: 2,
22072
23128
  // PK `source_event_id` is a generated uuid; identity is the natural key.
22073
23129
  // Two databases capturing the same event agree on the natural key and
22074
23130
  // disagree on the uuid, so a PK arbiter never fires and the insert
22075
23131
  // violates the natural key instead — the failure that made move-back
22076
- // impossible. This is also exactly the key the write path already treats
22077
- // as identity (`ON CONFLICT … DO NOTHING` in appendSourceEvent), so
22078
- // migration and ingestion now agree on what "the same event" means.
23132
+ // impossible.
23133
+ //
23134
+ // The natural key includes the ACCOUNT (source-events-standard-tables/
23135
+ // 008): two accounts of one platform can produce the same
23136
+ // source_event_key and those are distinct events. While a destination
23137
+ // still has the old (org, platform, key) unique index, the copier's
23138
+ // preflight refuses to fold such rows together — see
23139
+ // `assertMergeKeysSafe` in migration-utils.ts.
22079
23140
  mergePolicy: {
22080
- identityKey: ["organization_id", "source_platform", "source_event_key"],
23141
+ identityKey: [
23142
+ "organization_id",
23143
+ "source_platform",
23144
+ "source_account_id",
23145
+ "source_event_key"
23146
+ ],
22081
23147
  // Keep the destination's uuid on conflict. NOT because it is the more
22082
23148
  // referenced one — measured on dev, 6,124 of 7,717 referenced uuids
22083
23149
  // (79%) existed only on the BYOD side — but because rewriting a primary
@@ -22143,6 +23209,14 @@ var init_table_registry = __esm({
22143
23209
  entry("intelligence", INTELLIGENCE_DB, "qna_conversation_shares", "direct", {
22144
23210
  scopingColumn: "organization_id"
22145
23211
  }),
23212
+ // Charts saved with their answer (qna-charts/001); child of qna_messages.
23213
+ entry("intelligence", INTELLIGENCE_DB, "qna_charts", "direct", {
23214
+ scopingColumn: "organization_id"
23215
+ }),
23216
+ // Retry-safe Q&A writes + notification outbox (qna-charts/002).
23217
+ entry("intelligence", INTELLIGENCE_DB, "qna_write_operations", "direct", {
23218
+ scopingColumn: "organization_id"
23219
+ }),
22146
23220
  entry("intelligence", INTELLIGENCE_DB, "incidents", "direct", {
22147
23221
  scopingColumn: "organization_id",
22148
23222
  hasVectorColumn: true
@@ -22225,6 +23299,10 @@ var init_table_registry = __esm({
22225
23299
  entry("intelligence", INTELLIGENCE_DB, "expert_profile_facts", "direct", {
22226
23300
  scopingColumn: "organization_id"
22227
23301
  }),
23302
+ // Durable pending-decision queue for process membership (object-graph/113).
23303
+ entry("intelligence", INTELLIGENCE_DB, "mining_pending_decisions", "direct", {
23304
+ scopingColumn: "organization_id"
23305
+ }),
22228
23306
  // A member's "this wasn't me", awaiting the identities admin (object-graph/107).
22229
23307
  entry("intelligence", INTELLIGENCE_DB, "identity_disputes", "direct", {
22230
23308
  scopingColumn: "organization_id"
@@ -22255,6 +23333,10 @@ var init_table_registry = __esm({
22255
23333
  entry("intelligence", INTELLIGENCE_DB, "expert_area_merge_log", "direct", {
22256
23334
  scopingColumn: "organization_id"
22257
23335
  }),
23336
+ // Load-cycle gate for profile generation (object-graph/127).
23337
+ entry("intelligence", INTELLIGENCE_DB, "expert_load_cycle_state", "direct", {
23338
+ scopingColumn: "organization_id"
23339
+ }),
22258
23340
  entry("intelligence", INTELLIGENCE_DB, "expert_profile_curation", "direct", {
22259
23341
  scopingColumn: "organization_id"
22260
23342
  }),
@@ -22294,12 +23376,40 @@ var init_table_registry = __esm({
22294
23376
  "direct",
22295
23377
  { scopingColumn: "organization_id" }
22296
23378
  ),
23379
+ // Memory → Skills (migrations/skills/001): lifecycle, revisions,
23380
+ // publications, dependencies and temporary Skillify output. skills and
23381
+ // skill_revisions reference `skill` rows in knowledge_artifacts, so they
23382
+ // ride on the same database as the artifacts.
23383
+ entry("intelligence", INTELLIGENCE_DB, "skills", "direct", {
23384
+ scopingColumn: "organization_id"
23385
+ }),
23386
+ entry("intelligence", INTELLIGENCE_DB, "skill_revisions", "direct", {
23387
+ scopingColumn: "organization_id"
23388
+ }),
23389
+ entry("intelligence", INTELLIGENCE_DB, "skill_publications", "direct", {
23390
+ scopingColumn: "organization_id"
23391
+ }),
23392
+ entry("intelligence", INTELLIGENCE_DB, "skill_sources", "direct", {
23393
+ scopingColumn: "organization_id"
23394
+ }),
23395
+ entry("intelligence", INTELLIGENCE_DB, "skill_generation_outputs", "direct", {
23396
+ scopingColumn: "organization_id"
23397
+ }),
22297
23398
  entry("intelligence", INTELLIGENCE_DB, "activity_graph_edges", "direct", {
22298
23399
  scopingColumn: "organization_id"
22299
23400
  }),
22300
23401
  entry("intelligence", INTELLIGENCE_DB, "process_registry", "direct", {
22301
23402
  scopingColumn: "organization_id"
22302
23403
  }),
23404
+ // Append-only history of a process's scored state (object-graph/114). After
23405
+ // process_registry: composite FK to it.
23406
+ entry("intelligence", INTELLIGENCE_DB, "process_state_history", "direct", {
23407
+ scopingColumn: "organization_id"
23408
+ }),
23409
+ // Late live events awaiting a windowed replay (object-graph/115).
23410
+ entry("intelligence", INTELLIGENCE_DB, "mining_awaiting_replay", "direct", {
23411
+ scopingColumn: "organization_id"
23412
+ }),
22303
23413
  // Incident overlay + evidence signals + audit — the incident-specific
22304
23414
  // projection keyed by (organization_id, process_id) on top of
22305
23415
  // process_registry (category='incident_response'). Rides to BYOD with the
@@ -22413,19 +23523,12 @@ var init_table_registry = __esm({
22413
23523
  entry("intelligence", INTELLIGENCE_DB, "artifact_cluster_members", "direct", {
22414
23524
  scopingColumn: "organization_id"
22415
23525
  }),
22416
- // Graph-enrichment watermark + deadletter ride to BYOD with the object graph
22417
- // they walk. audit round 4 (#3).
22418
- entry("intelligence", INTELLIGENCE_DB, "graph_enrichment_state", "direct", {
22419
- scopingColumn: "organization_id"
22420
- }),
22421
23526
  entry(
22422
23527
  "intelligence",
22423
23528
  INTELLIGENCE_DB,
22424
- "graph_enrichment_deadletter",
23529
+ "artifact_cluster_workstreams",
22425
23530
  "direct",
22426
- {
22427
- scopingColumn: "organization_id"
22428
- }
23531
+ { scopingColumn: "organization_id" }
22429
23532
  )
22430
23533
  ];
22431
23534
  INTEGRATIONS_DB = "haansi_integrations_prod";
@@ -22452,6 +23555,7 @@ var init_schema_provisioner = __esm({
22452
23555
  path = __toESM(require("path"));
22453
23556
  init_src2();
22454
23557
  init_pool_manager();
23558
+ init_host_guard();
22455
23559
  init_table_registry();
22456
23560
  SCHEMAS_DIR = path.join(__dirname, "schemas");
22457
23561
  logger2 = createLogger("db-byod-schema-provisioner");
@@ -22459,7 +23563,7 @@ var init_schema_provisioner = __esm({
22459
23563
  });
22460
23564
 
22461
23565
  // ../../packages/@company/db/src/byod/host-resolver.ts
22462
- var observability8;
23566
+ var observability9;
22463
23567
  var init_host_resolver = __esm({
22464
23568
  "../../packages/@company/db/src/byod/host-resolver.ts"() {
22465
23569
  "use strict";
@@ -22467,7 +23571,9 @@ var init_host_resolver = __esm({
22467
23571
  init_src2();
22468
23572
  init_config_cache();
22469
23573
  init_pool_manager();
22470
- observability8 = createObservabilityService("byod-host-resolver");
23574
+ init_host_guard();
23575
+ init_admission_policy();
23576
+ observability9 = createObservabilityService("byod-host-resolver");
22471
23577
  }
22472
23578
  });
22473
23579
 
@@ -23395,6 +24501,7 @@ var init_migration_service = __esm({
23395
24501
  "use strict";
23396
24502
  init_esm();
23397
24503
  init_pool_manager();
24504
+ init_host_guard();
23398
24505
  init_org_context();
23399
24506
  init_client();
23400
24507
  init_src2();
@@ -23429,6 +24536,8 @@ var init_byod = __esm({
23429
24536
  "../../packages/@company/db/src/byod/index.ts"() {
23430
24537
  "use strict";
23431
24538
  init_pool_manager();
24539
+ init_host_guard();
24540
+ init_admission_policy();
23432
24541
  init_config_cache();
23433
24542
  init_control_plane_residency();
23434
24543
  init_integration_storage();
@@ -23463,6 +24572,7 @@ var init_src6 = __esm({
23463
24572
  init_client();
23464
24573
  init_listener_connection();
23465
24574
  init_org_context();
24575
+ init_admin_pools();
23466
24576
  init_search_vector_write();
23467
24577
  init_org_purge();
23468
24578
  init_org_orphan_scan();
@@ -80049,10 +81159,10 @@ __export(setup_mcp_exports, {
80049
81159
  });
80050
81160
  function parseTarget(arg) {
80051
81161
  if (!arg || arg === "all") return "all";
80052
- if (arg === "solutions" || arg === "exchange" || arg === "digests" || arg === "onboarding")
81162
+ if (arg === "solutions" || arg === "exchange" || arg === "digests" || arg === "onboarding" || arg === "skills")
80053
81163
  return arg;
80054
81164
  throw new Error(
80055
- `Unknown target: ${arg}. Use one of: solutions, exchange, digests, onboarding, all (default).`
81165
+ `Unknown target: ${arg}. Use one of: solutions, exchange, digests, onboarding, skills, all (default).`
80056
81166
  );
80057
81167
  }
80058
81168
  function httpServerUrl(path2) {
@@ -80192,6 +81302,13 @@ var init_setup_mcp = __esm({
80192
81302
  target: "onboarding",
80193
81303
  name: "haansi-onboarding",
80194
81304
  path: "/api/v1/memory/onboarding/mcp"
81305
+ },
81306
+ // Published team skills, loaded progressively by coding agents
81307
+ // (docs/product/plans/2026-10-10-memory-skills.md §7).
81308
+ {
81309
+ target: "skills",
81310
+ name: "haansi-skills",
81311
+ path: "/api/v1/memory/skills/mcp"
80195
81312
  }
80196
81313
  ];
80197
81314
  WRITERS = {