haansi 0.1.44 → 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 +1190 -37
  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.44",
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
 
@@ -21538,12 +21723,30 @@ var init_client = __esm({
21538
21723
  }
21539
21724
  });
21540
21725
 
21726
+ // ../../packages/@company/db/src/listener-connection.ts
21727
+ var init_listener_connection = __esm({
21728
+ "../../packages/@company/db/src/listener-connection.ts"() {
21729
+ "use strict";
21730
+ }
21731
+ });
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
+
21541
21743
  // ../../packages/@company/db/src/byod/routed-pool.ts
21542
21744
  var logger, ROUTED_TENANT_CONTEXT, ROUTED_TENANT_RAW_POOL, ADMITTED_TENANT_CONTEXT;
21543
21745
  var init_routed_pool = __esm({
21544
21746
  "../../packages/@company/db/src/byod/routed-pool.ts"() {
21545
21747
  "use strict";
21546
21748
  init_src2();
21749
+ init_release_after_callback();
21547
21750
  logger = createLogger("db-byod-routed-pool");
21548
21751
  ROUTED_TENANT_CONTEXT = Symbol.for("haansi.db.routedTenantContext");
21549
21752
  ROUTED_TENANT_RAW_POOL = Symbol.for("haansi.db.routedTenantRawPool");
@@ -21552,18 +21755,28 @@ var init_routed_pool = __esm({
21552
21755
  });
21553
21756
 
21554
21757
  // ../../packages/@company/db/src/org-context.ts
21555
- var observability3, DECRYPTING_CLIENT;
21758
+ var observability4, DECRYPTING_CLIENT;
21556
21759
  var init_org_context = __esm({
21557
21760
  "../../packages/@company/db/src/org-context.ts"() {
21558
21761
  "use strict";
21559
21762
  init_src4();
21560
21763
  init_src2();
21561
21764
  init_routed_pool();
21562
- observability3 = createObservabilityService("db-org-context");
21765
+ init_release_after_callback();
21766
+ observability4 = createObservabilityService("db-org-context");
21563
21767
  DECRYPTING_CLIENT = Symbol.for("haansi.db.decryptingClient");
21564
21768
  }
21565
21769
  });
21566
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
+
21567
21780
  // ../../packages/@company/types/src/timeline.ts
21568
21781
  var init_timeline = __esm({
21569
21782
  "../../packages/@company/types/src/timeline.ts"() {
@@ -21571,6 +21784,13 @@ var init_timeline = __esm({
21571
21784
  }
21572
21785
  });
21573
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
+
21574
21794
  // ../../packages/@company/types/src/artifact.ts
21575
21795
  var init_artifact = __esm({
21576
21796
  "../../packages/@company/types/src/artifact.ts"() {
@@ -21697,12 +21917,15 @@ var init_taxonomy = __esm({
21697
21917
  },
21698
21918
  report_onboarding: {
21699
21919
  category: "report",
21700
- label: "Onboarding Packet",
21701
- description: "Onboarding guide assembled from team knowledge, scoped to a project or role.",
21702
- icon: "GraduationCap",
21920
+ label: "Onboarding Guide",
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.",
21922
+ icon: "Sprout",
21703
21923
  aliases: ["onboarding_packet", "onboarding-packet"],
21704
21924
  autoApproved: false,
21705
- producedBy: ["POST /api/v1/memory/reports/onboarding"]
21925
+ producedBy: ["POST /api/v1/memory/onboarding/guides/:id/generate"],
21926
+ // docs/product/plans/2026-09-24-onboarding-guides.md D11: no generic
21927
+ // reader or writer may touch a guide.
21928
+ reserved: true
21706
21929
  },
21707
21930
  report_pr: {
21708
21931
  category: "report",
@@ -21711,7 +21934,9 @@ var init_taxonomy = __esm({
21711
21934
  icon: "GitPullRequest",
21712
21935
  aliases: ["pr_context", "pr-context"],
21713
21936
  autoApproved: true,
21714
- 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: []
21715
21940
  },
21716
21941
  expert: {
21717
21942
  category: "expert",
@@ -21742,6 +21967,18 @@ var init_taxonomy = __esm({
21742
21967
  autoApproved: true,
21743
21968
  producedBy: ["POST /api/v1/memory/recall/messages/:id/feedback"]
21744
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
+ },
21745
21982
  digest_entry: {
21746
21983
  category: "digest",
21747
21984
  label: "Digest Entry",
@@ -21832,16 +22069,825 @@ var init_report_template = __esm({
21832
22069
  }
21833
22070
  });
21834
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
+
21835
22879
  // ../../packages/@company/types/src/index.ts
21836
22880
  var init_src5 = __esm({
21837
22881
  "../../packages/@company/types/src/index.ts"() {
21838
22882
  init_timeline();
22883
+ init_qna();
21839
22884
  init_artifact();
21840
22885
  init_taxonomy();
21841
22886
  init_embedding();
21842
22887
  init_repo_file_url();
21843
22888
  init_query_dialect();
21844
22889
  init_report_template();
22890
+ init_mining_config_fields();
21845
22891
  }
21846
22892
  });
21847
22893
 
@@ -21859,7 +22905,7 @@ var init_search_vector_write = __esm({
21859
22905
  });
21860
22906
 
21861
22907
  // ../../packages/@company/db/src/org-purge.ts
21862
- var observability4;
22908
+ var observability5;
21863
22909
  var init_org_purge = __esm({
21864
22910
  "../../packages/@company/db/src/org-purge.ts"() {
21865
22911
  "use strict";
@@ -21867,12 +22913,12 @@ var init_org_purge = __esm({
21867
22913
  init_src2();
21868
22914
  init_client();
21869
22915
  init_org_context();
21870
- observability4 = createObservabilityService("org-purge");
22916
+ observability5 = createObservabilityService("org-purge");
21871
22917
  }
21872
22918
  });
21873
22919
 
21874
22920
  // ../../packages/@company/db/src/org-orphan-scan.ts
21875
- var observability5;
22921
+ var observability6;
21876
22922
  var init_org_orphan_scan = __esm({
21877
22923
  "../../packages/@company/db/src/org-orphan-scan.ts"() {
21878
22924
  "use strict";
@@ -21880,7 +22926,7 @@ var init_org_orphan_scan = __esm({
21880
22926
  init_src2();
21881
22927
  init_org_context();
21882
22928
  init_org_purge();
21883
- observability5 = createObservabilityService("org-orphan-scan");
22929
+ observability6 = createObservabilityService("org-orphan-scan");
21884
22930
  }
21885
22931
  });
21886
22932
 
@@ -21928,12 +22974,13 @@ var init_expert_areas = __esm({
21928
22974
  });
21929
22975
 
21930
22976
  // ../../packages/@company/db/src/exchange-context.ts
21931
- var observability6;
22977
+ var observability7;
21932
22978
  var init_exchange_context = __esm({
21933
22979
  "../../packages/@company/db/src/exchange-context.ts"() {
21934
22980
  "use strict";
21935
22981
  init_src2();
21936
- observability6 = createObservabilityService("db-exchange-context");
22982
+ init_org_context();
22983
+ observability7 = createObservabilityService("db-exchange-context");
21937
22984
  }
21938
22985
  });
21939
22986
 
@@ -21962,14 +23009,30 @@ var init_data_access = __esm({
21962
23009
  }
21963
23010
  });
21964
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
+
21965
23026
  // ../../packages/@company/db/src/byod/pool-manager.ts
21966
- var observability7, IDLE_TIMEOUT_MS;
23027
+ var observability8, IDLE_TIMEOUT_MS;
21967
23028
  var init_pool_manager = __esm({
21968
23029
  "../../packages/@company/db/src/byod/pool-manager.ts"() {
21969
23030
  "use strict";
21970
23031
  init_esm();
21971
23032
  init_src2();
21972
- observability7 = createObservabilityService("byod-pool-manager");
23033
+ init_host_guard();
23034
+ init_admission_policy();
23035
+ observability8 = createObservabilityService("byod-pool-manager");
21973
23036
  IDLE_TIMEOUT_MS = 5 * 60 * 1e3;
21974
23037
  }
21975
23038
  });
@@ -21988,6 +23051,7 @@ var init_config_cache = __esm({
21988
23051
  "use strict";
21989
23052
  init_org_context();
21990
23053
  init_degradation2();
23054
+ init_listener_connection();
21991
23055
  }
21992
23056
  });
21993
23057
 
@@ -22003,6 +23067,7 @@ var init_org_db_resolver = __esm({
22003
23067
  "../../packages/@company/db/src/byod/org-db-resolver.ts"() {
22004
23068
  "use strict";
22005
23069
  init_pool_manager();
23070
+ init_admission_policy();
22006
23071
  init_routed_pool();
22007
23072
  }
22008
23073
  });
@@ -22049,7 +23114,7 @@ function entry(database, dbName, table, orgScoping, opts = {}) {
22049
23114
  identity: {
22050
23115
  kind: "columns",
22051
23116
  columns: opts.mergePolicy.identityKey,
22052
- version: 1
23117
+ version: opts.identityVersion ?? 1
22053
23118
  }
22054
23119
  } : DEFAULT_RECONCILIATION)
22055
23120
  };
@@ -22058,15 +23123,27 @@ function standardIntegrationTables(database, dbName) {
22058
23123
  return [
22059
23124
  entry(database, dbName, "source_events", "direct", {
22060
23125
  scopingColumn: "organization_id",
23126
+ // v2: the identity gained source_account_id (A0).
23127
+ identityVersion: 2,
22061
23128
  // PK `source_event_id` is a generated uuid; identity is the natural key.
22062
23129
  // Two databases capturing the same event agree on the natural key and
22063
23130
  // disagree on the uuid, so a PK arbiter never fires and the insert
22064
23131
  // violates the natural key instead — the failure that made move-back
22065
- // impossible. This is also exactly the key the write path already treats
22066
- // as identity (`ON CONFLICT … DO NOTHING` in appendSourceEvent), so
22067
- // 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.
22068
23140
  mergePolicy: {
22069
- 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
+ ],
22070
23147
  // Keep the destination's uuid on conflict. NOT because it is the more
22071
23148
  // referenced one — measured on dev, 6,124 of 7,717 referenced uuids
22072
23149
  // (79%) existed only on the BYOD side — but because rewriting a primary
@@ -22132,6 +23209,14 @@ var init_table_registry = __esm({
22132
23209
  entry("intelligence", INTELLIGENCE_DB, "qna_conversation_shares", "direct", {
22133
23210
  scopingColumn: "organization_id"
22134
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
+ }),
22135
23220
  entry("intelligence", INTELLIGENCE_DB, "incidents", "direct", {
22136
23221
  scopingColumn: "organization_id",
22137
23222
  hasVectorColumn: true
@@ -22214,6 +23299,14 @@ var init_table_registry = __esm({
22214
23299
  entry("intelligence", INTELLIGENCE_DB, "expert_profile_facts", "direct", {
22215
23300
  scopingColumn: "organization_id"
22216
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
+ }),
23306
+ // A member's "this wasn't me", awaiting the identities admin (object-graph/107).
23307
+ entry("intelligence", INTELLIGENCE_DB, "identity_disputes", "direct", {
23308
+ scopingColumn: "organization_id"
23309
+ }),
22217
23310
  // Expert profile synthesis (object-graph/096).
22218
23311
  entry("intelligence", INTELLIGENCE_DB, "expert_evidence_refs", "direct", {
22219
23312
  scopingColumn: "organization_id"
@@ -22240,6 +23333,10 @@ var init_table_registry = __esm({
22240
23333
  entry("intelligence", INTELLIGENCE_DB, "expert_area_merge_log", "direct", {
22241
23334
  scopingColumn: "organization_id"
22242
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
+ }),
22243
23340
  entry("intelligence", INTELLIGENCE_DB, "expert_profile_curation", "direct", {
22244
23341
  scopingColumn: "organization_id"
22245
23342
  }),
@@ -22266,12 +23363,53 @@ var init_table_registry = __esm({
22266
23363
  entry("intelligence", INTELLIGENCE_DB, "digest_work_logs", "direct", {
22267
23364
  scopingColumn: "organization_id"
22268
23365
  }),
23366
+ // Onboarding guides (migrations/onboarding-guides/001): the guide lifecycle
23367
+ // row and its source refs. They reference report_onboarding rows in
23368
+ // knowledge_artifacts, so they ride on the same database as the artifacts.
23369
+ entry("intelligence", INTELLIGENCE_DB, "onboarding_guides", "direct", {
23370
+ scopingColumn: "organization_id"
23371
+ }),
23372
+ entry(
23373
+ "intelligence",
23374
+ INTELLIGENCE_DB,
23375
+ "onboarding_guide_source_refs",
23376
+ "direct",
23377
+ { scopingColumn: "organization_id" }
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
+ }),
22269
23398
  entry("intelligence", INTELLIGENCE_DB, "activity_graph_edges", "direct", {
22270
23399
  scopingColumn: "organization_id"
22271
23400
  }),
22272
23401
  entry("intelligence", INTELLIGENCE_DB, "process_registry", "direct", {
22273
23402
  scopingColumn: "organization_id"
22274
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
+ }),
22275
23413
  // Incident overlay + evidence signals + audit — the incident-specific
22276
23414
  // projection keyed by (organization_id, process_id) on top of
22277
23415
  // process_registry (category='incident_response'). Rides to BYOD with the
@@ -22385,19 +23523,12 @@ var init_table_registry = __esm({
22385
23523
  entry("intelligence", INTELLIGENCE_DB, "artifact_cluster_members", "direct", {
22386
23524
  scopingColumn: "organization_id"
22387
23525
  }),
22388
- // Graph-enrichment watermark + deadletter ride to BYOD with the object graph
22389
- // they walk. audit round 4 (#3).
22390
- entry("intelligence", INTELLIGENCE_DB, "graph_enrichment_state", "direct", {
22391
- scopingColumn: "organization_id"
22392
- }),
22393
23526
  entry(
22394
23527
  "intelligence",
22395
23528
  INTELLIGENCE_DB,
22396
- "graph_enrichment_deadletter",
23529
+ "artifact_cluster_workstreams",
22397
23530
  "direct",
22398
- {
22399
- scopingColumn: "organization_id"
22400
- }
23531
+ { scopingColumn: "organization_id" }
22401
23532
  )
22402
23533
  ];
22403
23534
  INTEGRATIONS_DB = "haansi_integrations_prod";
@@ -22424,6 +23555,7 @@ var init_schema_provisioner = __esm({
22424
23555
  path = __toESM(require("path"));
22425
23556
  init_src2();
22426
23557
  init_pool_manager();
23558
+ init_host_guard();
22427
23559
  init_table_registry();
22428
23560
  SCHEMAS_DIR = path.join(__dirname, "schemas");
22429
23561
  logger2 = createLogger("db-byod-schema-provisioner");
@@ -22431,7 +23563,7 @@ var init_schema_provisioner = __esm({
22431
23563
  });
22432
23564
 
22433
23565
  // ../../packages/@company/db/src/byod/host-resolver.ts
22434
- var observability8;
23566
+ var observability9;
22435
23567
  var init_host_resolver = __esm({
22436
23568
  "../../packages/@company/db/src/byod/host-resolver.ts"() {
22437
23569
  "use strict";
@@ -22439,7 +23571,9 @@ var init_host_resolver = __esm({
22439
23571
  init_src2();
22440
23572
  init_config_cache();
22441
23573
  init_pool_manager();
22442
- observability8 = createObservabilityService("byod-host-resolver");
23574
+ init_host_guard();
23575
+ init_admission_policy();
23576
+ observability9 = createObservabilityService("byod-host-resolver");
22443
23577
  }
22444
23578
  });
22445
23579
 
@@ -23367,6 +24501,7 @@ var init_migration_service = __esm({
23367
24501
  "use strict";
23368
24502
  init_esm();
23369
24503
  init_pool_manager();
24504
+ init_host_guard();
23370
24505
  init_org_context();
23371
24506
  init_client();
23372
24507
  init_src2();
@@ -23401,6 +24536,8 @@ var init_byod = __esm({
23401
24536
  "../../packages/@company/db/src/byod/index.ts"() {
23402
24537
  "use strict";
23403
24538
  init_pool_manager();
24539
+ init_host_guard();
24540
+ init_admission_policy();
23404
24541
  init_config_cache();
23405
24542
  init_control_plane_residency();
23406
24543
  init_integration_storage();
@@ -23433,7 +24570,9 @@ var init_report_generation_jobs = __esm({
23433
24570
  var init_src6 = __esm({
23434
24571
  "../../packages/@company/db/src/index.ts"() {
23435
24572
  init_client();
24573
+ init_listener_connection();
23436
24574
  init_org_context();
24575
+ init_admin_pools();
23437
24576
  init_search_vector_write();
23438
24577
  init_org_purge();
23439
24578
  init_org_orphan_scan();
@@ -80020,10 +81159,10 @@ __export(setup_mcp_exports, {
80020
81159
  });
80021
81160
  function parseTarget(arg) {
80022
81161
  if (!arg || arg === "all") return "all";
80023
- if (arg === "solutions" || arg === "exchange" || arg === "digests")
81162
+ if (arg === "solutions" || arg === "exchange" || arg === "digests" || arg === "onboarding" || arg === "skills")
80024
81163
  return arg;
80025
81164
  throw new Error(
80026
- `Unknown target: ${arg}. Use one of: solutions, exchange, digests, all (default).`
81165
+ `Unknown target: ${arg}. Use one of: solutions, exchange, digests, onboarding, skills, all (default).`
80027
81166
  );
80028
81167
  }
80029
81168
  function httpServerUrl(path2) {
@@ -80156,6 +81295,20 @@ var init_setup_mcp = __esm({
80156
81295
  target: "digests",
80157
81296
  name: "haansi-digests",
80158
81297
  path: "/api/v1/memory/digests/mcp"
81298
+ },
81299
+ // Published onboarding guides, so an agent can pick up a workstream from
81300
+ // its handoff doc (docs/product/plans/2026-09-24-onboarding-guides.md §8.3).
81301
+ {
81302
+ target: "onboarding",
81303
+ name: "haansi-onboarding",
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"
80159
81312
  }
80160
81313
  ];
80161
81314
  WRITERS = {