



  

<!DOCTYPE html>
<html
  lang="en"
  
  data-color-mode="auto" data-light-theme="light" data-dark-theme="dark"
  data-a11y-animated-images="system" data-a11y-link-underlines="true"
  
  >




  <head>
    <meta charset="utf-8">
  <link rel="dns-prefetch" href="https://github.githubassets.com">
  <link rel="dns-prefetch" href="https://avatars.githubusercontent.com">
  <link rel="dns-prefetch" href="https://github-cloud.s3.amazonaws.com">
  <link rel="dns-prefetch" href="https://user-images.githubusercontent.com/">
  <link rel="preconnect" href="https://github.githubassets.com" crossorigin>
  <link rel="preconnect" href="https://avatars.githubusercontent.com">

<script type="importmap">{"imports":{"react":"https://github.githubassets.com/assets/react-e27d1b3e03961e68.js","react-dom":"https://github.githubassets.com/assets/react-dom-e5fd46a22d5c4058.js","react-dom/client":"https://github.githubassets.com/assets/react-dom-client-1b4a3ee065998cea.js","react-is":"https://github.githubassets.com/assets/react-is-e0b593954b4706d8.js","react-reconciler":"https://github.githubassets.com/assets/react-reconciler-8e99e505c4429605.js","react/compiler-runtime":"https://github.githubassets.com/assets/react-compiler-runtime-4610bd6d3de9c049.js","react/jsx-dev-runtime":"https://github.githubassets.com/assets/react-jsx-dev-runtime-ea55d68667d559e5.js","react/jsx-runtime":"https://github.githubassets.com/assets/react-jsx-runtime-4915cb0f5b3aff04.js","scheduler":"https://github.githubassets.com/assets/scheduler-58b860b049ca307c.js"}}</script>
<meta name="react-profiling" content="0" data-turbo-transient="true" />
<meta name="react-import-map" content="react,react-dom,react-dom/client,react-dom/profiling,react-is,react-reconciler,react/compiler-runtime,react/jsx-dev-runtime,react/jsx-runtime,scheduler@777f63f87cd0" data-turbo-track="reload" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/react-e27d1b3e03961e68.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/react-compiler-runtime-4610bd6d3de9c049.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/scheduler-58b860b049ca307c.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/react-dom-e5fd46a22d5c4058.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/react-dom-client-1b4a3ee065998cea.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/react-is-e0b593954b4706d8.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/react-jsx-runtime-4915cb0f5b3aff04.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/react-reconciler-8e99e505c4429605.js" />

  


  <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/light-5c4e9fc574bf49f3.css" /><link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/light_high_contrast-fb37c309e0603a69.css" /><link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/dark-afff6c53aef9b9d1.css" /><link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/dark_high_contrast-c409873d7987dffa.css" /><link data-color-theme="light" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/light-5c4e9fc574bf49f3.css" /><link data-color-theme="light_high_contrast" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/light_high_contrast-fb37c309e0603a69.css" /><link data-color-theme="light_colorblind" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/light_colorblind-0f910d8806d5761b.css" /><link data-color-theme="light_colorblind_high_contrast" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/light_colorblind_high_contrast-a7abd4d4c49ac593.css" /><link data-color-theme="light_tritanopia" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/light_tritanopia-5fc4c4a9cb41e596.css" /><link data-color-theme="light_tritanopia_high_contrast" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/light_tritanopia_high_contrast-ed0cee9214dedabd.css" /><link data-color-theme="dark" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark-afff6c53aef9b9d1.css" /><link data-color-theme="dark_high_contrast" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark_high_contrast-c409873d7987dffa.css" /><link data-color-theme="dark_colorblind" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark_colorblind-78a2491d538dab5a.css" /><link data-color-theme="dark_colorblind_high_contrast" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark_colorblind_high_contrast-c26b63d7c532d0a2.css" /><link data-color-theme="dark_tritanopia" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark_tritanopia-a6f60cea68c08405.css" /><link data-color-theme="dark_tritanopia_high_contrast" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark_tritanopia_high_contrast-f66faf28b2ea18b6.css" /><link data-color-theme="dark_dimmed" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark_dimmed-6701f6218c53cf5b.css" /><link data-color-theme="dark_dimmed_high_contrast" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark_dimmed_high_contrast-7037fa46ae124d97.css" />

  <style type="text/css">
    :root {
      --tab-size-preference: 4;
    }

    pre, code {
      tab-size: var(--tab-size-preference);
    }
  </style>

    <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-primitives-97df7784617ce1ea.css" />
    <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-9be9fe6313f476af.css" />
    <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/global-62747e27e61258dc.css" />
    <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/github-7a418ea859d1654d.css" />
  <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/repository-72bd7d974b5ac1cc.css" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/code-4c5c0895f723f870.css" />

  

  <script type="application/json" id="client-env">{"locale":"en","featureFlags":["actions_caches_react_shell","actions_enable_background_steps","actions_new_hosted_runner_image_select_sizes_and_versions","actions_runners_react_shell","activity_diff_file_tree","activity_repos_file_tree","activity_repos_overview_header","activity_repos_overview_sidebar","agent_author_search_expansion","agent_author_search_expansion_ui_pulls","alternate_user_config_repo","async_conversion_coverage_enabled","billing_billable_licenses_cost_center_bucket_fix","billing_budget_expiration","billing_cost_center_list_assigned_resources","billing_discount_threshold_notification","code_quality_enablement_banner_targeting","code_quality_remove_preview","code_view_raf_sticky_lines","codespaces_prebuild_region_target_update","coding_agent_third_party_model_ui","copilot_agent_snippy","copilot_api_agentic_issue_marshal_yaml","copilot_automation_repo_mcp_servers","copilot_automations_pagination","copilot_base_model_policy_row","copilot_chat_auto_mode_v2","copilot_chat_clear_model_selection_for_default_change","copilot_chat_early_task_provisioning","copilot_chat_persist_session_drafts","copilot_chat_vision_dotcom_chat_ga_gate","copilot_css_textarea_autosize","copilot_custom_copilots","copilot_custom_copilots_feature_preview","copilot_duplicate_thread","copilot_extensions_removal_on_marketplace","copilot_fix_failed_workflows_all_skus","copilot_hide_hovercard","copilot_immersive_task_hyperlinking","copilot_mc_cli_resume_any_users_task","copilot_mission_control_agent_merge_fix_ci","copilot_mission_control_agent_merge_resolve_conflicts","copilot_mission_control_awps_batching","copilot_mission_control_early_stop","copilot_mission_control_managed_sandbox_environments","copilot_mission_control_needs_attention","copilot_mission_control_reasoning_effort","copilot_mission_control_sandbox_remote_bypass","copilot_mission_control_task_alive_updates","copilot_mission_control_task_sharing","copilot_org_policy_page_focus_mode","copilot_share_active_subthread","copilot_spaces_ga","copilot_spaces_individual_policies_ga","copilot_swe_agent_authorization_status_ui","copilot_swe_agent_automation_resource_scoped_writes","copilot_swe_agent_discussion_comment_trigger","copilot_swe_agent_discussion_opened_trigger","copilot_swe_agent_discussion_updated_trigger","copilot_swe_agent_hide_model_picker_if_only_auto","copilot_swe_agent_issue_assigned_trigger","copilot_swe_agent_issue_comment_trigger","copilot_swe_agent_issue_labeled_trigger","copilot_swe_agent_pr_comment_model_picker","copilot_swe_agent_pull_request_assigned_trigger","copilot_swe_agent_pull_request_comment_trigger","copilot_swe_agent_pull_request_labeled_trigger","copilot_swe_agent_pull_request_merged_trigger","copilot_swe_agent_pull_request_opened_trigger","copilot_swe_agent_pull_request_ready_for_review_trigger","copilot_swe_agent_pull_request_review_requested_trigger","copilot_swe_agent_pull_request_review_submitted_trigger","copilot_swe_agent_pull_request_synchronize_trigger","copilot_swe_agent_sub_issue_added_trigger","copilot_swe_agent_use_subagents","copilot_task_api_github_rest_style","copilot_task_scoped_alive_channel","copilot_token_based_billing","copilot_unconfigured_is_inherited","copilot_user_can_upgrade_plan_field","copilot_workbench_sunset_redirect","dashboard_agents_module_auth_token_check","dashboard_indexeddb_caching","dashboard_lists_max_age_filter","fgpat_permissions_selector_redesign","glc_code_quality_repo_settings_workflow_config","hyperspace_2025_logged_out_batch_1","hyperspace_2025_logged_out_batch_2","hyperspace_2025_logged_out_batch_3","in_product_messaging_datadog_monitoring","ipm_ubb_individual_budget_banner","issue_fields_multi_select","issue_inline_avatars","issue_pinned_views","issue_pinned_views_team_vs_personal","issue_relative_time_micro","issues_dashboard_sso_structured_errors","issues_expanded_file_types","issues_hide_closed_sub_issues","issues_index_data_router","issues_lazy_load_comment_box_suggestions","issues_react_chrome_container_query_fix","labels_archiving","labels_archiving_info","landing_pages_ninetailed","lifecycle_label_name_updates","marketing_cookie_consent_banner","marketing_pages_search_explore_provider","memex_default_issue_create_repository","memex_live_update_hovercard","memex_mwl_filter_field_delimiter","memex_remove_deprecated_type_issue","merge_queue_restricted_pushers_warning","merge_status_header_feedback","milestone_closed_issues_prioritization","milestone_show_data_router","octocaptcha_origin_optimization","primer_react_css_anchor_positioning","primer_react_merged_forwarded_refs","prs_copilot_app_open_action","prs_css_anchor_positioning","pull_request_copilot_attribution_header","pull_request_overview_merge_control","pull_request_overview_panel_edit_description","pull_request_persister","pull_request_stacks_navigation_shortcuts","pull_request_virtualization_image_estimate","pull_request_virtualization_loader_batching","pull_request_virtualization_scroll_compensation","pull_request_virtualization_scroll_intent","quick_search_lazy_suggestions","react_blob_isolate_code_lines","react_blob_ssr_content_visibility","react_data_router_tanstack_allowed","react_logged_out_repository_header","react_query_props_with_key","react_sandbox_future_tanstack","repo_app_turbo","repo_issues_sidebar_layout","repo_pulls_dashboard_declutter","repo_pulls_dashboard_ga","repo_pulls_dashboard_persistence","repo_pulls_dashboard_sidebar_links","repos_contributors_limited_default_range","review_involves_filter","rule_ignored_file_paths","rulesets_actor_list_editor","sample_network_conn_type","security_center_artifact_filters_popover","see_who_reacted","semantic_similarity_duplicate_issue_detection","session_logs_ungroup_reasoning_text","set_sha256_on_repo_creation_form","site_banner_desktop_copilot_app","site_ghca_pixel_mona","site_github_app_ga_page","site_github_app_ga_page_highlight","site_github_app_mobile_native_share","site_global_banner_dev_days_attendee","site_global_nav_spark_models_removed","speculation_rules_ui_service","suggest_custom_property_values_copilot","suppress_automated_browser_vitals","swp_forms_disable_octocaptcha","thread_resolution_reason","update_issue_suggestions","viewscreen_sandbox","warn_inaccessible_attachments","webp_support","workstream_plugin_bootstrap"],"githubDomain":"https://github.com","copilotApiOverrideUrl":"https://api.githubcopilot.com","cmcApiUrl":"https://api.github.com/cmc_internal/api"}</script>
<script crossorigin="anonymous" type="module" src="https://github.githubassets.com/assets/high-contrast-cookie-b752d992928a526c.js"></script>
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/wp-runtime-8b4dcdc2cb6562ff.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/app-foundation-2f08c823163591a8.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/app-runtime-633ad94fa06c334b.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/fetch-utilities-0fb4c544a797ec0d.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/ser-7c872b58ac9f1b39.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/tp-7e9817cf5c068979.js" />
<script crossorigin="anonymous" type="module" src="https://github.githubassets.com/assets/environment-8bdc8c2592df853b.js" defer="defer"></script>
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/app-runtime.bef03fb24bb03be8.module.css" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/catalyst-52ed81112a548e10.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/selector-observer-e8810f64c443fb9b.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/relative-time-element-0417f617ad744a0d.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/by-9da36843ba9d191d.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/ja9-9a1160b939f2cd52.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/3h-55903cf2303047ad.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/hk-f1498fec45311acd.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/jz5-7aa1ff0a0e750f41.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/hj-1d800d09e9a8720a.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/j0-9697c1c8f5cbf523.js" />
<script crossorigin="anonymous" type="module" src="https://github.githubassets.com/assets/github-elements-3f257b2433f975f0.js" defer="defer"></script>
<script crossorigin="anonymous" type="module" src="https://github.githubassets.com/assets/element-registry-95b267513b87193c.js" defer="defer"></script>
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/runtime-helpers-5e0b3ae3c4036207.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/aria-live-524a06aa946df6fc.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/hotkey-b1ee53c2200b172a.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/react-core-7bf7b9d689fe3d10.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/a9-ca3bc10b816bcc1f.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/ur-21ec1e439ee2d87c.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/4t-4c39f7195fc42cc8.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/pb-53cfff089eaac51b.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/6n-afcf86ccabc4cad2.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/zj-4d9cf83cf08e1643.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/wo-6991af5b3829ffe8.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/nfv-a6c14301d3ea6d67.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/b5-a2fde30ce0c692b9.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/sw-9d0489dbf9a9c942.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/ttf-7d5abe1b447ced2c.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/sj-c7a44023ee90d468.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/0pd-c46d8dfae89c2b64.js" />
<script crossorigin="anonymous" type="module" src="https://github.githubassets.com/assets/behaviors-2a35428577a46a82.js" defer="defer"></script>
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/react-core.255cab819a92f7e1.module.css" />
<script crossorigin="anonymous" type="module" src="https://github.githubassets.com/assets/code-menu-baecde86e879facb.js" defer="defer"></script>
  
  <link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/primer-react-8503588299651923.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/ncx-66e64888bc4615bb.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/qmp-c7cfba40cf5a93b0.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/j6-148a74299e5a01ab.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/t7l-a9404625ec30bbc7.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/4u-8f9f1d6be01d6a22.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/s0-68d09e5e8a80cb94.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/y5-0cb3f75cec297941.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/n4u-cd9039ad946274ad.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/j8s-00bd469c4017a543.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/la-27679061f16e1495.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/iu-2d03f5e6d4fdd54d.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/gru-2b0fac8887ff8c57.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/a0-234e967e7c6d22ce.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/gz3-5bdfbfceac967bb6.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/15m-64f3034430471048.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/1e-c15441bdb94d54ae.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/gzu-6ad9a0527a5d7afd.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/bi-3a42b8714e06c4d0.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/tu1-37e16844272ddefa.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/b1-ac66ededed061993.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/gmn-1e5497c041b66231.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/0l5-d1b2a63015b5eb6d.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/c2-e85522be441acf73.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/f9-baf0a4193779952e.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/sp-87dce99c82a27004.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/zkm-116b01b8485bbe3f.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/og-518d12c9e011601d.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/b9-947db365ea875cfa.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/14-8c7b4ad3c845664c.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/dsj-2e7d883cb843e9e6.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/ec-38012819307fd539.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/du-6607da3b8b579202.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/20-f43eb09c2be69457.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/ql0-107312645ac2b72d.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/wp-fa7cdc0f7656d876.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/p0l-fe491d943adf7508.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/v1e-d099d9fd21ad25e5.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/fn-7b298a45c55fb6fb.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/7cw-6e493a20eaaa04b5.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/ok-290e4b9465ba9c5b.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/6d-ca6d584a0fd6368c.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/y8-e2f18f5a24733f3b.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/xc-3fd4a2e7dcbad8e2.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/vjg-2874f95f389c4d3f.js" />
<script crossorigin="anonymous" type="module" src="https://github.githubassets.com/assets/code-view-b187f9df797cfbc1.js" defer="defer"></script>
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-react-css.e4334163c54cccf5.module.css" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/ql0.501e99879e15a48e.module.css" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/xc.de2b1dde0494f261.module.css" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/code-view.30fafa560a1eee5e.module.css" />


  <title>2dph/docs/runbook.md at main · eSlider/2dph · GitHub</title>



  <meta name="route-pattern" content="/:user_id/:repository/blob/*name(/*path)" data-turbo-transient>
  <meta name="route-controller" content="blob" data-turbo-transient>
  <meta name="route-action" content="show" data-turbo-transient>
  <meta name="fetch-nonce" content="v2:177ed06c-4296-0d7e-36a1-38619a95bf6a">

    
  <meta name="current-catalog-service-hash" content="f3abb0cc802f3d7b95fc8762b94bdcb13bf39634c40c357301c4aa1d67a256fb">


  <meta name="request-id" content="988E:3DCEC3:36C856:33BA71:6AC74872" data-pjax-transient="true"/><meta name="html-safe-nonce" content="d98cba09c56c0d5f15a805e6acf26357bab6b271c021059de2d6163490304622" data-pjax-transient="true"/><meta name="visitor-payload" content="eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiI5ODhFOjNEQ0VDMzozNkM4NTY6MzNCQTcxOjZBQzc0ODcyIiwidmlzaXRvcl9pZCI6IjE3MDYzMjc1ODI5MjYzOTE0MTAiLCJyZWdpb25fZWRnZSI6ImZyYSIsInJlZ2lvbl9yZW5kZXIiOiJmcmEifQ==" data-pjax-transient="true"/><meta name="visitor-hmac" content="41884cad59b04b36aae6539aca6c4b0cbbf428955272297f7cd51c2b61f18e8c" data-pjax-transient="true"/>


    <meta name="hovercard-subject-tag" content="repository:1330190220" data-turbo-transient>


  <meta name="github-keyboard-shortcuts" content="repository,source-code,file-tree,copilot" data-turbo-transient="true" />
  

  <meta name="selected-link" value="repo_source" data-turbo-transient>
  <link rel="assets" href="https://github.githubassets.com/">

    <meta name="google-site-verification" content="Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I">

<meta name="octolytics-url" content="https://collector.github.com/github/collect" />





  <meta name="analytics-location" content="/&lt;user-name&gt;/&lt;repo-name&gt;/blob/show" data-turbo-transient="true" />

  




    <meta name="user-login" content="">

  

    <meta name="viewport" content="width=device-width">

    

      <meta name="description" content="Fact evidence-first brain with property graph + hybrid RAG. Search is deduction. ETL included. - 2dph/docs/runbook.md at main · eSlider/2dph">

      <link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="GitHub">

    <link rel="fluid-icon" href="https://github.com/fluidicon.png" title="GitHub">
    <meta property="fb:app_id" content="1401488693436528">
    <meta name="apple-itunes-app" content="app-id=1477376905, app-argument=https://github.com/eSlider/2dph/blob/main/docs/runbook.md" />

      <meta name="twitter:image" content="https://opengraph.githubassets.com/89342bd3651d985130373c59d02e321d9314dc504075e0b51b8d3edc35d98365/eSlider/2dph" /><meta name="twitter:site" content="@github" /><meta name="twitter:card" content="summary_large_image" /><meta name="twitter:title" content="2dph/docs/runbook.md at main · eSlider/2dph" /><meta name="twitter:description" content="Fact evidence-first brain with property graph + hybrid RAG. Search is deduction. ETL included. - eSlider/2dph" />
  <meta property="og:image" content="https://opengraph.githubassets.com/89342bd3651d985130373c59d02e321d9314dc504075e0b51b8d3edc35d98365/eSlider/2dph" /><meta property="og:image:alt" content="Fact evidence-first brain with property graph + hybrid RAG. Search is deduction. ETL included. - eSlider/2dph" /><meta property="og:image:width" content="1200" /><meta property="og:image:height" content="600" /><meta property="og:site_name" content="GitHub" /><meta property="og:type" content="object" /><meta property="og:title" content="2dph/docs/runbook.md at main · eSlider/2dph" /><meta property="og:url" content="https://github.com/eSlider/2dph/blob/main/docs/runbook.md" /><meta property="og:description" content="Fact evidence-first brain with property graph + hybrid RAG. Search is deduction. ETL included. - eSlider/2dph" />
  




      <meta name="hostname" content="github.com">



        <meta name="expected-hostname" content="github.com">


  <meta http-equiv="x-pjax-version" content="8a04ed058e34f1ad63cd2ec184ddc8db8ab22004d2ce8345ee86788432baa4cf" data-turbo-track="reload">
  <meta http-equiv="x-pjax-csp-version" content="c4a65e47b0c850e1157ae8e66298070851d7e3b558038de5a3b4ff7a93e630ed" data-turbo-track="reload">
  <meta http-equiv="x-pjax-css-version" content="1ff3f1d4e1abd61f6f135b47d3b815e2f7169f8dd418f0118c6b1cf4de6cbc86" data-turbo-track="reload">
  <meta http-equiv="x-pjax-js-version" content="31760b6cf01794eaa3898a84a5be66fa89579904959fd0698672e3cbcd9741ca" data-turbo-track="reload">

  <meta name="turbo-cache-control" content="no-preview" data-turbo-transient="">

      <meta name="turbo-cache-control" content="no-cache" data-turbo-transient>

    <meta data-hydrostats="publish">

  <meta name="go-import" content="github.com/eSlider/2dph git https://github.com/eSlider/2dph.git">

  <meta name="octolytics-dimension-user_id" content="1188335" /><meta name="octolytics-dimension-user_login" content="eSlider" /><meta name="octolytics-dimension-repository_id" content="1330190220" /><meta name="octolytics-dimension-repository_nwo" content="eSlider/2dph" /><meta name="octolytics-dimension-repository_public" content="true" /><meta name="octolytics-dimension-repository_is_fork" content="false" /><meta name="octolytics-dimension-repository_network_root_id" content="1330190220" /><meta name="octolytics-dimension-repository_network_root_nwo" content="eSlider/2dph" />
  



    

    <meta name="turbo-body-classes" content="logged-out env-production page-responsive">
  <meta name="disable-turbo" content="false">


  <meta name="browser-stats-url" content="https://api.github.com/_private/browser/stats">


  <meta name="browser-errors-url" content="https://api.github.com/_private/browser/errors">

  <meta name="release" content="abcc1c729a3dae04e49a5be4a885c179f1620884" data-turbo-track="reload">
  <meta name="ui-target" content="full">

  <link rel="mask-icon" href="https://github.githubassets.com/assets/pinned-octocat-093da3e6fa40.svg" color="#000000">
  <link rel="alternate icon" class="js-site-favicon" type="image/png" href="https://github.githubassets.com/favicons/favicon.png">
  <link rel="icon" class="js-site-favicon" type="image/svg+xml" href="https://github.githubassets.com/favicons/favicon.svg" data-base-href="https://github.githubassets.com/favicons/favicon">

<meta name="theme-color" content="#1e2327">
<meta name="color-scheme" content="light dark" />


  <link rel="manifest" href="/manifest.json" crossOrigin="use-credentials">

  </head>

  <body class="logged-out env-production page-responsive" style="word-wrap: break-word;" >
    <div data-turbo-body class="logged-out env-production page-responsive" style="word-wrap: break-word;" >
      <div id="__primerPortalRoot__" style="z-index: 1000; position: absolute; width: 100%;" data-turbo-permanent></div>
      

    <div class="position-relative header-wrapper js-header-wrapper ">
      <a href="#start-of-content" data-skip-target-assigned="false" class="px-2 tmp-py-4 color-bg-accent-emphasis color-fg-on-emphasis show-on-focus js-skip-to-content">Skip to content</a>

      <span data-view-component="true" class="progress-pjax-loader Progress position-fixed width-full">
    <span style="width: 0%;" data-view-component="true" class="Progress-item progress-pjax-loader-bar left-0 top-0 color-bg-accent-emphasis"></span>
</span>      
      <link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/qc-1bbbe8787ea1cd4b.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/keyboard-shortcuts-dialog-a3b4cbcefa4e617e.js" fetchpriority="low" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-react-css.e4334163c54cccf5.module.css" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/keyboard-shortcuts-dialog.b8afe3d106c9bb64.module.css" />

<react-partial
  partial-name="keyboard-shortcuts-dialog"
  data-ssr="false"
  data-attempted-ssr="false"
  data-react-profiling="false"
>
  
  <script type="application/json" data-target="react-partial.embeddedData">{"props":{"docsUrl":"https://docs.github.com/get-started/accessibility/keyboard-shortcuts"}}</script>
  <div data-target="react-partial.reactRoot"></div>
</react-partial>





      

          <link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/app-install-banner-partial-173a78d38983be1c.js" fetchpriority="low" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-react-css.e4334163c54cccf5.module.css" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/app-install-banner-partial.882c17396978a47d.module.css" />

<react-partial
  partial-name="app-install-banner-partial"
  data-ssr="false"
  data-attempted-ssr="false"
  data-react-profiling="false"
>
  
  <script type="application/json" data-target="react-partial.embeddedData">{"props":{}}</script>
  <div data-target="react-partial.reactRoot"></div>
</react-partial>


          

                <link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/2l-e468179ba7123a2e.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/rwd-069fc3d9814ff9fe.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/fz-e662bc3745e171e7.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/jag-3559ee48016881a2.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/3d-7f22b36f29a9dd94.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/tua-4998592d21eb16f7.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/8i-ea1b3f2e11ed8cd3.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/marketing-header-b6853fcac2424bda.js" fetchpriority="low" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-react-css.e4334163c54cccf5.module.css" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-react-brand-css.05b219cdb8e80c43.module.css" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/8i.5d9e6dcb0dade694.module.css" />

<react-partial
  partial-name="marketing-header"
  data-ssr="true"
  data-attempted-ssr="true"
  data-react-profiling="false"
>
  
  <script type="application/json" data-target="react-partial.embeddedData">{"props":{"color_mode":"dark","logged_in":false,"marketing_page":false,"home_path":"/","login_path":"/login?return_to=https%3A%2F%2Fgithub.com%2FeSlider%2F2dph%2Fblob%2Fmain%2Fdocs%2Frunbook.md","signup_path":"/signup?ref_cta=Sign+up\u0026ref_loc=header+logged+out\u0026ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E%2Fblob%2Fshow\u0026source=header-repo\u0026source_repo=eSlider%2F2dph","signup_enabled":true,"is_signup_controller":false,"show_search_and_nav":true,"hide_search":false,"private_mode_enabled":false,"should_use_dotcom_links":true,"auth_hydro_click":"{\"event_type\":\"authentication.click\",\"payload\":{\"location_in_page\":\"site header menu\",\"repository_id\":null,\"auth_type\":\"SIGN_UP\",\"originating_url\":\"https://github.com/eSlider/2dph/blob/main/docs/runbook.md\",\"user_id\":null}}","auth_hydro_click_hmac":"f49c4a55616a5afe6193559baf2cc909b670c8a782b81e02331d783a8feee86f","overlay":false,"fixed":false}}</script>
  <div data-target="react-partial.reactRoot"><div data-color-mode="dark" data-light-theme="light" data-dark-theme="dark"><header class="MarketingHeader-module__root__Tk7n3 HeaderMktg header-logged-out" role="banner" data-marketing-header="true" data-color-mode="dark" data-light-theme="light" data-dark-theme="dark" data-is-top="true"><h2 class="MarketingHeader-module__visuallyHidden__sqKsl">Navigation Menu</h2><button type="button" class="MarketingHeader-module__backdrop__sw4RU" aria-label="Close navigation menu"></button><div class="MarketingHeader-module__bar__mBSyE"><div class="MarketingHeader-module__topRow__yeury"><div class="MarketingHeader-module__toggleSlot__hDxbh"><button type="button" class="HeaderMenuToggle-module__toggle__i8EiC" aria-label="Toggle navigation" aria-expanded="false"><span class="HeaderMenuToggle-module__toggleBar__jVN0H"></span><span class="HeaderMenuToggle-module__toggleBar__jVN0H"></span><span class="HeaderMenuToggle-module__toggleBar__jVN0H"></span></button></div><a href="/" aria-label="Homepage" class="HeaderLogo-module__logo__UFyHI" data-analytics-event="{&quot;action&quot;:&quot;homepage&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;logo&quot;,&quot;location&quot;:&quot;header&quot;,&quot;label&quot;:&quot;homepage_link_logo_header&quot;}"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-mark-github" viewBox="0 0 24 24" width="32" height="32" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M10.226 17.284c-2.965-.36-5.054-2.493-5.054-5.256 0-1.123.404-2.336 1.078-3.144-.292-.741-.247-2.314.09-2.965.898-.112 2.111.36 2.83 1.01.853-.269 1.752-.404 2.853-.404 1.1 0 1.999.135 2.807.382.696-.629 1.932-1.1 2.83-.988.315.606.36 2.179.067 2.942.72.854 1.101 2 1.101 3.167 0 2.763-2.089 4.852-5.098 5.234.763.494 1.28 1.572 1.28 2.807v2.336c0 .674.561 1.056 1.235.786 4.066-1.55 7.255-5.615 7.255-10.646C23.5 6.188 18.334 1 11.978 1 5.62 1 .5 6.188.5 12.545c0 4.986 3.167 9.12 7.435 10.669.606.225 1.19-.18 1.19-.786V20.63a2.9 2.9 0 0 1-1.078.224c-1.483 0-2.359-.808-2.987-2.313-.247-.607-.517-.966-1.034-1.033-.27-.023-.359-.135-.359-.27 0-.27.45-.471.898-.471.652 0 1.213.404 1.797 1.235.45.651.921.943 1.483.943.561 0 .92-.202 1.437-.719.382-.381.674-.718.944-.943"></path></svg></a><div class="AuthCTAs-module__mobileActions__NNzeV"><a class="Primer_Brand__Button-module__Button___scH9Z Primer_Brand__Button-module__Button--subtle___F7pEE Primer_Brand__Button-module__Button--size-small___zQrEw AuthCTAs-module__cta__WpwQq" href="/login?return_to=https%3A%2F%2Fgithub.com%2FeSlider%2F2dph%2Fblob%2Fmain%2Fdocs%2Frunbook.md" data-analytics-event="{&quot;action&quot;:&quot;sign_in&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;auth_cta&quot;,&quot;location&quot;:&quot;header&quot;,&quot;label&quot;:&quot;sign_in_link_auth_cta_header&quot;}" data-hydro-click="{&quot;event_type&quot;:&quot;authentication.click&quot;,&quot;payload&quot;:{&quot;location_in_page&quot;:&quot;site header menu&quot;,&quot;repository_id&quot;:null,&quot;auth_type&quot;:&quot;SIGN_UP&quot;,&quot;originating_url&quot;:&quot;https://github.com/eSlider/2dph/blob/main/docs/runbook.md&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="f49c4a55616a5afe6193559baf2cc909b670c8a782b81e02331d783a8feee86f"><span class="Primer_Brand__Button-module__Button__text___ED0bX"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ Primer_Brand__Button-module__Button--label___qrkyz Primer_Brand__Button-module__Button--label-subtle___8ndWH">Sign in</span></span></a><button class="Primer_Brand__Button-module__Button___scH9Z Primer_Brand__Button-module__Button--subtle___F7pEE Primer_Brand__Button-module__Button--size-small___zQrEw HeaderAppearanceSettings-module__trigger__hUheK" type="button" aria-haspopup="dialog" aria-labelledby="_R_3dd_"><span class="Primer_Brand__Button-module__Button__leading-visual___jjtTe" data-testid="Button-leading-visual"><svg data-component="Octicon" focusable="false" aria-hidden="true" class="octicon octicon-sliders Primer_Brand__Button-module__Button__icon-visual____qybb" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M15 2.75a.75.75 0 0 1-.75.75h-4a.75.75 0 0 1 0-1.5h4a.75.75 0 0 1 .75.75Zm-8.5.75v1.25a.75.75 0 0 0 1.5 0v-4a.75.75 0 0 0-1.5 0V2H1.75a.75.75 0 0 0 0 1.5H6.5Zm1.25 5.25a.75.75 0 0 0 0-1.5h-6a.75.75 0 0 0 0 1.5h6ZM15 8a.75.75 0 0 1-.75.75H11.5V10a.75.75 0 1 1-1.5 0V6a.75.75 0 0 1 1.5 0v1.25h2.75A.75.75 0 0 1 15 8Zm-9 5.25v-2a.75.75 0 0 0-1.5 0v1.25H1.75a.75.75 0 0 0 0 1.5H4.5v1.25a.75.75 0 0 0 1.5 0v-2Zm9 0a.75.75 0 0 1-.75.75h-6a.75.75 0 0 1 0-1.5h6a.75.75 0 0 1 .75.75Z"></path></svg></span><span class="Primer_Brand__Button-module__Button__text___ED0bX"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ Primer_Brand__Button-module__Button--label___qrkyz Primer_Brand__Button-module__Button--label-subtle___8ndWH"></span></span></button><div class="Primer_Brand__Tooltip-module__Tooltip___0Eipx" data-direction="s" aria-hidden="true" id="_R_3dd_">Appearance settings</div></div></div><div class="MarketingHeader-module__menu__GIy3y"><div class="MarketingHeader-module__menuWrapper__owstH"><nav class="MarketingNavigation-module__nav__W0KYY" aria-label="Global"><ul class="MarketingNavigation-module__list__tFbMb"><li><div class="NavDropdown-module__container__l2YeI"><button type="button" class="NavDropdown-module__button__PEHWX" aria-expanded="false" aria-controls="_R_nd_">Platform<svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-triangle-right NavDropdown-module__buttonIcon__Tkl8_" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m6.427 4.427 3.396 3.396a.25.25 0 0 1 0 .354l-3.396 3.396A.25.25 0 0 1 6 11.396V4.604a.25.25 0 0 1 .427-.177Z"></path></svg></button><div id="_R_nd_" class="NavDropdown-module__dropdown__xm1jd"><ul class="NavDropdown-module__list__zuCgG"><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_5knd_">AI CODE CREATION</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_5knd_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/features/copilot" data-analytics-event="{&quot;action&quot;:&quot;github_copilot&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;github_copilot_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-copilot NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M7.998 15.035c-4.562 0-7.873-2.914-7.998-3.749V9.338c.085-.628.677-1.686 1.588-2.065.013-.07.024-.143.036-.218.029-.183.06-.384.126-.612-.201-.508-.254-1.084-.254-1.656 0-.87.128-1.769.693-2.484.579-.733 1.494-1.124 2.724-1.261 1.206-.134 2.262.034 2.944.765.05.053.096.108.139.165.044-.057.094-.112.143-.165.682-.731 1.738-.899 2.944-.765 1.23.137 2.145.528 2.724 1.261.566.715.693 1.614.693 2.484 0 .572-.053 1.148-.254 1.656.066.228.098.429.126.612.012.076.024.148.037.218.924.385 1.522 1.471 1.591 2.095v1.872c0 .766-3.351 3.795-8.002 3.795Zm0-1.485c2.28 0 4.584-1.11 5.002-1.433V7.862l-.023-.116c-.49.21-1.075.291-1.727.291-1.146 0-2.059-.327-2.71-.991A3.222 3.222 0 0 1 8 6.303a3.24 3.24 0 0 1-.544.743c-.65.664-1.563.991-2.71.991-.652 0-1.236-.081-1.727-.291l-.023.116v4.255c.419.323 2.722 1.433 5.002 1.433ZM6.762 2.83c-.193-.206-.637-.413-1.682-.297-1.019.113-1.479.404-1.713.7-.247.312-.369.789-.369 1.554 0 .793.129 1.171.308 1.371.162.181.519.379 1.442.379.853 0 1.339-.235 1.638-.54.315-.322.527-.827.617-1.553.117-.935-.037-1.395-.241-1.614Zm4.155-.297c-1.044-.116-1.488.091-1.681.297-.204.219-.359.679-.242 1.614.091.726.303 1.231.618 1.553.299.305.784.54 1.638.54.922 0 1.28-.198 1.442-.379.179-.2.308-.578.308-1.371 0-.765-.123-1.242-.37-1.554-.233-.296-.693-.587-1.713-.7Z"></path><path d="M6.25 9.037a.75.75 0 0 1 .75.75v1.501a.75.75 0 0 1-1.5 0V9.787a.75.75 0 0 1 .75-.75Zm4.25.75v1.501a.75.75 0 0 1-1.5 0V9.787a.75.75 0 0 1 1.5 0Z"></path></svg>GitHub Copilot</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Write better code with AI</span></span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/features/ai/github-app" data-analytics-event="{&quot;action&quot;:&quot;github_copilot_app&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;github_copilot_app_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-mark-github NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M6.766 11.328c-2.063-.25-3.516-1.734-3.516-3.656 0-.781.281-1.625.75-2.188-.203-.515-.172-1.609.063-2.062.625-.078 1.468.25 1.968.703.594-.187 1.219-.281 1.985-.281.765 0 1.39.094 1.953.265.484-.437 1.344-.765 1.969-.687.218.422.25 1.515.046 2.047.5.593.766 1.39.766 2.203 0 1.922-1.453 3.375-3.547 3.64.531.344.89 1.094.89 1.954v1.625c0 .468.391.734.86.547C13.781 14.359 16 11.53 16 8.03 16 3.61 12.406 0 7.984 0 3.563 0 0 3.61 0 8.031a7.88 7.88 0 0 0 5.172 7.422c.422.156.828-.125.828-.547v-1.25c-.219.094-.5.156-.75.156-1.031 0-1.64-.562-2.078-1.609-.172-.422-.36-.672-.719-.719-.187-.015-.25-.093-.25-.187 0-.188.313-.328.625-.328.453 0 .844.281 1.25.86.313.452.64.655 1.031.655s.641-.14 1-.5c.266-.265.47-.5.657-.656"></path></svg>GitHub Copilot app</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Direct agents from issue to merge</span></span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/mcp" data-analytics-event="{&quot;action&quot;:&quot;mcp_registry&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;mcp_registry_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-mcp NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M5.52 1.12a3.578 3.578 0 0 1 6.078 2.98 3.578 3.578 0 0 1 2.982 6.08l-3.292 3.293a.252.252 0 0 0 0 .354l.843.843a.749.749 0 1 1-1.06 1.06l-.844-.843a1.75 1.75 0 0 1 0-2.474L13.52 9.12a2.08 2.08 0 0 0 0-2.94 2.08 2.08 0 0 0-2.94 0L7.731 9.03A.75.75 0 0 1 6.67 7.97l2.85-2.85a2.08 2.08 0 0 0 0-2.94 2.08 2.08 0 0 0-2.94 0l-4.799 4.8A.75.75 0 0 1 .72 5.92Z"></path><path d="M7.52 3.12a.749.749 0 1 1 1.06 1.06L5.731 7.03A2.079 2.079 0 0 0 8.67 9.97l2.85-2.85a.749.749 0 1 1 1.06 1.06l-2.849 2.85A3.578 3.578 0 0 1 4.67 5.97Z"></path></svg>MCP Registry</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Integrate external tools</span></span></a></li></ul></div></li><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_9knd_">DEVELOPER WORKFLOWS</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_9knd_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/features/actions" data-analytics-event="{&quot;action&quot;:&quot;actions&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;actions_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-workflow NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M0 1.75C0 .784.784 0 1.75 0h3.5C6.216 0 7 .784 7 1.75v3.5A1.75 1.75 0 0 1 5.25 7H4v4a1 1 0 0 0 1 1h4v-1.25C9 9.784 9.784 9 10.75 9h3.5c.966 0 1.75.784 1.75 1.75v3.5A1.75 1.75 0 0 1 14.25 16h-3.5A1.75 1.75 0 0 1 9 14.25v-.75H5A2.5 2.5 0 0 1 2.5 11V7h-.75A1.75 1.75 0 0 1 0 5.25Zm1.75-.25a.25.25 0 0 0-.25.25v3.5c0 .138.112.25.25.25h3.5a.25.25 0 0 0 .25-.25v-3.5a.25.25 0 0 0-.25-.25Zm9 9a.25.25 0 0 0-.25.25v3.5c0 .138.112.25.25.25h3.5a.25.25 0 0 0 .25-.25v-3.5a.25.25 0 0 0-.25-.25Z"></path></svg>Actions</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Automate any workflow</span></span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/features/codespaces" data-analytics-event="{&quot;action&quot;:&quot;codespaces&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;codespaces_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-codespaces NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M0 11.25c0-.966.784-1.75 1.75-1.75h12.5c.966 0 1.75.784 1.75 1.75v3A1.75 1.75 0 0 1 14.25 16H1.75A1.75 1.75 0 0 1 0 14.25Zm2-9.5C2 .784 2.784 0 3.75 0h8.5C13.216 0 14 .784 14 1.75v5a1.75 1.75 0 0 1-1.75 1.75h-8.5A1.75 1.75 0 0 1 2 6.75Zm1.75-.25a.25.25 0 0 0-.25.25v5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-5a.25.25 0 0 0-.25-.25Zm-2 9.5a.25.25 0 0 0-.25.25v3c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25v-3a.25.25 0 0 0-.25-.25Z"></path><path d="M7 12.75a.75.75 0 0 1 .75-.75h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1-.75-.75Zm-4 0a.75.75 0 0 1 .75-.75h.5a.75.75 0 0 1 0 1.5h-.5a.75.75 0 0 1-.75-.75Z"></path></svg>Codespaces</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Instant dev environments</span></span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/features/issues" data-analytics-event="{&quot;action&quot;:&quot;issues&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;issues_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-issue-opened NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M8 9.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z"></path><path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Z"></path></svg>Issues</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Plan and track work</span></span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/features/code-review" data-analytics-event="{&quot;action&quot;:&quot;code_review&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;code_review_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-code NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m11.28 3.22 4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734L13.94 8l-3.72-3.72a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215Zm-6.56 0a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042L2.06 8l3.72 3.72a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L.47 8.53a.75.75 0 0 1 0-1.06Z"></path></svg>Code Review</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Manage code changes</span></span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/features/code-quality" data-analytics-event="{&quot;action&quot;:&quot;code_quality&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;code_quality_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-codescan-checkmark NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M10.28 6.28a.75.75 0 1 0-1.06-1.06L6.25 8.19l-.97-.97a.75.75 0 0 0-1.06 1.06l1.5 1.5a.75.75 0 0 0 1.06 0l3.5-3.5Z"></path><path d="M7.5 15a7.5 7.5 0 1 1 5.807-2.754l2.473 2.474a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215l-2.474-2.473A7.472 7.472 0 0 1 7.5 15Zm0-13.5a6 6 0 1 0 4.094 10.386.748.748 0 0 1 .293-.292 6.002 6.002 0 0 0 1.117-6.486A6.002 6.002 0 0 0 7.5 1.5Z"></path></svg>Code Quality</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Enforce quality at merge</span></span></a></li></ul></div></li><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_dknd_">APPLICATION SECURITY</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_dknd_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/security/advanced-security" data-analytics-event="{&quot;action&quot;:&quot;github_advanced_security&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;github_advanced_security_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-shield-check NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m8.533.133 5.25 1.68A1.75 1.75 0 0 1 15 3.48V7c0 1.566-.32 3.182-1.303 4.682-.983 1.498-2.585 2.813-5.032 3.855a1.697 1.697 0 0 1-1.33 0c-2.447-1.042-4.049-2.357-5.032-3.855C1.32 10.182 1 8.566 1 7V3.48a1.75 1.75 0 0 1 1.217-1.667l5.25-1.68a1.748 1.748 0 0 1 1.066 0Zm-.61 1.429.001.001-5.25 1.68a.251.251 0 0 0-.174.237V7c0 1.36.275 2.666 1.057 3.859.784 1.194 2.121 2.342 4.366 3.298a.196.196 0 0 0 .154 0c2.245-.957 3.582-2.103 4.366-3.297C13.225 9.666 13.5 8.358 13.5 7V3.48a.25.25 0 0 0-.174-.238l-5.25-1.68a.25.25 0 0 0-.153 0ZM11.28 6.28l-3.5 3.5a.75.75 0 0 1-1.06 0l-1.5-1.5a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215l.97.97 2.97-2.97a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Z"></path></svg>GitHub Advanced Security</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Find and fix vulnerabilities</span></span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/security/advanced-security/code-security" data-analytics-event="{&quot;action&quot;:&quot;code_security&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;code_security_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-code-square NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v12.5A1.75 1.75 0 0 1 14.25 16H1.75A1.75 1.75 0 0 1 0 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25V1.75a.25.25 0 0 0-.25-.25Zm7.47 3.97a.75.75 0 0 1 1.06 0l2 2a.75.75 0 0 1 0 1.06l-2 2a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734L10.69 8 9.22 6.53a.75.75 0 0 1 0-1.06ZM6.78 6.53 5.31 8l1.47 1.47a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215l-2-2a.75.75 0 0 1 0-1.06l2-2a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Z"></path></svg>Code security</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Secure your code as you build</span></span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/security/advanced-security/secret-protection" data-analytics-event="{&quot;action&quot;:&quot;secret_protection&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;secret_protection_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-lock NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M4 4a4 4 0 0 1 8 0v2h.25c.966 0 1.75.784 1.75 1.75v5.5A1.75 1.75 0 0 1 12.25 15h-8.5A1.75 1.75 0 0 1 2 13.25v-5.5C2 6.784 2.784 6 3.75 6H4Zm8.25 3.5h-8.5a.25.25 0 0 0-.25.25v5.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-5.5a.25.25 0 0 0-.25-.25ZM10.5 6V4a2.5 2.5 0 1 0-5 0v2Z"></path></svg>Secret protection</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Stop leaks before they start</span></span></a></li></ul></div></li><li><div class="NavGroup-module__group__W8SqJ NavGroup-module__hasSeparator__FnMrN"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_hknd_">EXPLORE</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_hknd_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/why-github" data-analytics-event="{&quot;action&quot;:&quot;why_github&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;why_github_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Why GitHub</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--is-external___xsncV" href="https://docs.github.com" data-analytics-event="{&quot;action&quot;:&quot;documentation&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;documentation_link_platform_navbar&quot;}" target="_blank" rel="noreferrer"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Documentation</span><svg data-component="Octicon" focusable="false" aria-label="External link" class="octicon octicon-link-external" role="img" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path></svg></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--is-external___xsncV" href="https://github.blog" data-analytics-event="{&quot;action&quot;:&quot;blog&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;blog_link_platform_navbar&quot;}" target="_blank" rel="noreferrer"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Blog</span><svg data-component="Octicon" focusable="false" aria-label="External link" class="octicon octicon-link-external" role="img" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path></svg></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--is-external___xsncV" href="https://github.blog/changelog" data-analytics-event="{&quot;action&quot;:&quot;changelog&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;changelog_link_platform_navbar&quot;}" target="_blank" rel="noreferrer"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Changelog</span><svg data-component="Octicon" focusable="false" aria-label="External link" class="octicon octicon-link-external" role="img" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path></svg></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/marketplace" data-analytics-event="{&quot;action&quot;:&quot;marketplace&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;marketplace_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Marketplace</span></a></li></ul></div></li></ul><div class="NavDropdown-module__trailingLinkContainer__VgJGL"><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--arrow-end___esdN8" href="https://github.com/features" data-analytics-event="{&quot;action&quot;:&quot;view_all_features&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;view_all_features_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">View all features</span><svg class="Primer_Brand__ExpandableArrow-module__ExpandableArrow___aaZs9 Primer_Brand__Link-module__Link-arrow___yd78i" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true" focusable="false"><path fill="currentColor" d="M7.28033 3.21967C6.98744 2.92678 6.51256 2.92678 6.21967 3.21967C5.92678 3.51256 5.92678 3.98744 6.21967 4.28033L7.28033 3.21967ZM11 8L11.5303 8.53033C11.8232 8.23744 11.8232 7.76256 11.5303 7.46967L11 8ZM6.21967 11.7197C5.92678 12.0126 5.92678 12.4874 6.21967 12.7803C6.51256 13.0732 6.98744 13.0732 7.28033 12.7803L6.21967 11.7197ZM6.21967 4.28033L10.4697 8.53033L11.5303 7.46967L7.28033 3.21967L6.21967 4.28033ZM10.4697 7.46967L6.21967 11.7197L7.28033 12.7803L11.5303 8.53033L10.4697 7.46967Z"></path><path class="Primer_Brand__ExpandableArrow-module__ExpandableArrow-stem___0K8Hz" stroke="currentColor" d="M1.75 8H11" stroke-width="1.5" stroke-linecap="round"></path></svg></a></div></div></div></li><li><div class="NavDropdown-module__container__l2YeI"><button type="button" class="NavDropdown-module__button__PEHWX" aria-expanded="false" aria-controls="_R_17d_">Solutions<svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-triangle-right NavDropdown-module__buttonIcon__Tkl8_" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m6.427 4.427 3.396 3.396a.25.25 0 0 1 0 .354l-3.396 3.396A.25.25 0 0 1 6 11.396V4.604a.25.25 0 0 1 .427-.177Z"></path></svg></button><div id="_R_17d_" class="NavDropdown-module__dropdown__xm1jd"><ul class="NavDropdown-module__list__zuCgG"><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_5l7d_">BY COMPANY SIZE</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_5l7d_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/enterprise" data-analytics-event="{&quot;action&quot;:&quot;enterprises&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;enterprises_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Enterprises</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/team" data-analytics-event="{&quot;action&quot;:&quot;small_and_medium_teams&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;small_and_medium_teams_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Small and medium teams</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/enterprise/startups" data-analytics-event="{&quot;action&quot;:&quot;startups&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;startups_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Startups</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/solutions/industry/nonprofits" data-analytics-event="{&quot;action&quot;:&quot;nonprofits&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;nonprofits_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Nonprofits</span></a></li></ul></div></li><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_9l7d_">BY USE CASE</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_9l7d_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/solutions/use-case/app-modernization" data-analytics-event="{&quot;action&quot;:&quot;app_modernization&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;app_modernization_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">App Modernization</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/solutions/use-case/devsecops" data-analytics-event="{&quot;action&quot;:&quot;devsecops&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;devsecops_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">DevSecOps</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/solutions/use-case/devops" data-analytics-event="{&quot;action&quot;:&quot;devops&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;devops_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">DevOps</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/solutions/use-case/ci-cd" data-analytics-event="{&quot;action&quot;:&quot;ci/cd&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;ci/cd_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">CI/CD</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--arrow-end___esdN8" href="https://github.com/solutions/use-case" data-analytics-event="{&quot;action&quot;:&quot;view_all_use_cases&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;view_all_use_cases_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">View all use cases</span><svg class="Primer_Brand__ExpandableArrow-module__ExpandableArrow___aaZs9 Primer_Brand__Link-module__Link-arrow___yd78i" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true" focusable="false"><path fill="currentColor" d="M7.28033 3.21967C6.98744 2.92678 6.51256 2.92678 6.21967 3.21967C5.92678 3.51256 5.92678 3.98744 6.21967 4.28033L7.28033 3.21967ZM11 8L11.5303 8.53033C11.8232 8.23744 11.8232 7.76256 11.5303 7.46967L11 8ZM6.21967 11.7197C5.92678 12.0126 5.92678 12.4874 6.21967 12.7803C6.51256 13.0732 6.98744 13.0732 7.28033 12.7803L6.21967 11.7197ZM6.21967 4.28033L10.4697 8.53033L11.5303 7.46967L7.28033 3.21967L6.21967 4.28033ZM10.4697 7.46967L6.21967 11.7197L7.28033 12.7803L11.5303 8.53033L10.4697 7.46967Z"></path><path class="Primer_Brand__ExpandableArrow-module__ExpandableArrow-stem___0K8Hz" stroke="currentColor" d="M1.75 8H11" stroke-width="1.5" stroke-linecap="round"></path></svg></a></li></ul></div></li><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_dl7d_">BY INDUSTRY</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_dl7d_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/solutions/industry/healthcare" data-analytics-event="{&quot;action&quot;:&quot;healthcare&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;healthcare_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Healthcare</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/solutions/industry/financial-services" data-analytics-event="{&quot;action&quot;:&quot;financial_services&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;financial_services_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Financial services</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/solutions/industry/manufacturing" data-analytics-event="{&quot;action&quot;:&quot;manufacturing&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;manufacturing_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Manufacturing</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/solutions/industry/government" data-analytics-event="{&quot;action&quot;:&quot;government&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;government_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Government</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--arrow-end___esdN8" href="https://github.com/solutions/industry" data-analytics-event="{&quot;action&quot;:&quot;view_all_industries&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;view_all_industries_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">View all industries</span><svg class="Primer_Brand__ExpandableArrow-module__ExpandableArrow___aaZs9 Primer_Brand__Link-module__Link-arrow___yd78i" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true" focusable="false"><path fill="currentColor" d="M7.28033 3.21967C6.98744 2.92678 6.51256 2.92678 6.21967 3.21967C5.92678 3.51256 5.92678 3.98744 6.21967 4.28033L7.28033 3.21967ZM11 8L11.5303 8.53033C11.8232 8.23744 11.8232 7.76256 11.5303 7.46967L11 8ZM6.21967 11.7197C5.92678 12.0126 5.92678 12.4874 6.21967 12.7803C6.51256 13.0732 6.98744 13.0732 7.28033 12.7803L6.21967 11.7197ZM6.21967 4.28033L10.4697 8.53033L11.5303 7.46967L7.28033 3.21967L6.21967 4.28033ZM10.4697 7.46967L6.21967 11.7197L7.28033 12.7803L11.5303 8.53033L10.4697 7.46967Z"></path><path class="Primer_Brand__ExpandableArrow-module__ExpandableArrow-stem___0K8Hz" stroke="currentColor" d="M1.75 8H11" stroke-width="1.5" stroke-linecap="round"></path></svg></a></li></ul></div></li></ul><div class="NavDropdown-module__trailingLinkContainer__VgJGL"><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--arrow-end___esdN8" href="https://github.com/solutions" data-analytics-event="{&quot;action&quot;:&quot;view_all_solutions&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;view_all_solutions_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">View all solutions</span><svg class="Primer_Brand__ExpandableArrow-module__ExpandableArrow___aaZs9 Primer_Brand__Link-module__Link-arrow___yd78i" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true" focusable="false"><path fill="currentColor" d="M7.28033 3.21967C6.98744 2.92678 6.51256 2.92678 6.21967 3.21967C5.92678 3.51256 5.92678 3.98744 6.21967 4.28033L7.28033 3.21967ZM11 8L11.5303 8.53033C11.8232 8.23744 11.8232 7.76256 11.5303 7.46967L11 8ZM6.21967 11.7197C5.92678 12.0126 5.92678 12.4874 6.21967 12.7803C6.51256 13.0732 6.98744 13.0732 7.28033 12.7803L6.21967 11.7197ZM6.21967 4.28033L10.4697 8.53033L11.5303 7.46967L7.28033 3.21967L6.21967 4.28033ZM10.4697 7.46967L6.21967 11.7197L7.28033 12.7803L11.5303 8.53033L10.4697 7.46967Z"></path><path class="Primer_Brand__ExpandableArrow-module__ExpandableArrow-stem___0K8Hz" stroke="currentColor" d="M1.75 8H11" stroke-width="1.5" stroke-linecap="round"></path></svg></a></div></div></div></li><li><div class="NavDropdown-module__container__l2YeI"><button type="button" class="NavDropdown-module__button__PEHWX" aria-expanded="false" aria-controls="_R_1nd_">Resources<svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-triangle-right NavDropdown-module__buttonIcon__Tkl8_" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m6.427 4.427 3.396 3.396a.25.25 0 0 1 0 .354l-3.396 3.396A.25.25 0 0 1 6 11.396V4.604a.25.25 0 0 1 .427-.177Z"></path></svg></button><div id="_R_1nd_" class="NavDropdown-module__dropdown__xm1jd"><ul class="NavDropdown-module__list__zuCgG"><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_5lnd_">EXPLORE BY TOPIC</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_5lnd_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/resources/articles?topic=ai" data-analytics-event="{&quot;action&quot;:&quot;ai&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;ai_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">AI</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/resources/articles?topic=software-development" data-analytics-event="{&quot;action&quot;:&quot;software_development&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;software_development_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Software Development</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/resources/articles?topic=devops" data-analytics-event="{&quot;action&quot;:&quot;devops&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;devops_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">DevOps</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/resources/articles?topic=security" data-analytics-event="{&quot;action&quot;:&quot;security&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;security_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Security</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--arrow-end___esdN8" href="https://github.com/resources/articles" data-analytics-event="{&quot;action&quot;:&quot;view_all_topics&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;view_all_topics_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">View all topics</span><svg class="Primer_Brand__ExpandableArrow-module__ExpandableArrow___aaZs9 Primer_Brand__Link-module__Link-arrow___yd78i" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true" focusable="false"><path fill="currentColor" d="M7.28033 3.21967C6.98744 2.92678 6.51256 2.92678 6.21967 3.21967C5.92678 3.51256 5.92678 3.98744 6.21967 4.28033L7.28033 3.21967ZM11 8L11.5303 8.53033C11.8232 8.23744 11.8232 7.76256 11.5303 7.46967L11 8ZM6.21967 11.7197C5.92678 12.0126 5.92678 12.4874 6.21967 12.7803C6.51256 13.0732 6.98744 13.0732 7.28033 12.7803L6.21967 11.7197ZM6.21967 4.28033L10.4697 8.53033L11.5303 7.46967L7.28033 3.21967L6.21967 4.28033ZM10.4697 7.46967L6.21967 11.7197L7.28033 12.7803L11.5303 8.53033L10.4697 7.46967Z"></path><path class="Primer_Brand__ExpandableArrow-module__ExpandableArrow-stem___0K8Hz" stroke="currentColor" d="M1.75 8H11" stroke-width="1.5" stroke-linecap="round"></path></svg></a></li></ul></div></li><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_9lnd_">EXPLORE BY TYPE</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_9lnd_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/customer-stories" data-analytics-event="{&quot;action&quot;:&quot;customer_stories&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;customer_stories_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Customer stories</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/resources/events" data-analytics-event="{&quot;action&quot;:&quot;events__webinars&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;events__webinars_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Events &amp; webinars</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/resources/whitepapers" data-analytics-event="{&quot;action&quot;:&quot;ebooks__reports&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;ebooks__reports_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Ebooks &amp; reports</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/solutions/executive-insights" data-analytics-event="{&quot;action&quot;:&quot;business_insights&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;business_insights_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Business insights</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--is-external___xsncV" href="https://skills.github.com" data-analytics-event="{&quot;action&quot;:&quot;github_skills&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;github_skills_link_resources_navbar&quot;}" target="_blank" rel="noreferrer"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">GitHub Skills</span><svg data-component="Octicon" focusable="false" aria-label="External link" class="octicon octicon-link-external" role="img" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path></svg></a></li></ul></div></li><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_dlnd_">SUPPORT &amp; SERVICES</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_dlnd_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--is-external___xsncV" href="https://docs.github.com" data-analytics-event="{&quot;action&quot;:&quot;documentation&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;documentation_link_resources_navbar&quot;}" target="_blank" rel="noreferrer"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Documentation</span><svg data-component="Octicon" focusable="false" aria-label="External link" class="octicon octicon-link-external" role="img" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path></svg></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--is-external___xsncV" href="https://support.github.com" data-analytics-event="{&quot;action&quot;:&quot;customer_support&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;customer_support_link_resources_navbar&quot;}" target="_blank" rel="noreferrer"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Customer support</span><svg data-component="Octicon" focusable="false" aria-label="External link" class="octicon octicon-link-external" role="img" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path></svg></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/orgs/community/discussions" data-analytics-event="{&quot;action&quot;:&quot;community_forum&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;community_forum_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Community forum</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/trust-center" data-analytics-event="{&quot;action&quot;:&quot;trust_center&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;trust_center_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Trust center</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/partners" data-analytics-event="{&quot;action&quot;:&quot;partners&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;partners_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Partners</span></a></li></ul></div></li></ul><div class="NavDropdown-module__trailingLinkContainer__VgJGL"><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--arrow-end___esdN8" href="https://github.com/resources" data-analytics-event="{&quot;action&quot;:&quot;view_all_resources&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;view_all_resources_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">View all resources</span><svg class="Primer_Brand__ExpandableArrow-module__ExpandableArrow___aaZs9 Primer_Brand__Link-module__Link-arrow___yd78i" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true" focusable="false"><path fill="currentColor" d="M7.28033 3.21967C6.98744 2.92678 6.51256 2.92678 6.21967 3.21967C5.92678 3.51256 5.92678 3.98744 6.21967 4.28033L7.28033 3.21967ZM11 8L11.5303 8.53033C11.8232 8.23744 11.8232 7.76256 11.5303 7.46967L11 8ZM6.21967 11.7197C5.92678 12.0126 5.92678 12.4874 6.21967 12.7803C6.51256 13.0732 6.98744 13.0732 7.28033 12.7803L6.21967 11.7197ZM6.21967 4.28033L10.4697 8.53033L11.5303 7.46967L7.28033 3.21967L6.21967 4.28033ZM10.4697 7.46967L6.21967 11.7197L7.28033 12.7803L11.5303 8.53033L10.4697 7.46967Z"></path><path class="Primer_Brand__ExpandableArrow-module__ExpandableArrow-stem___0K8Hz" stroke="currentColor" d="M1.75 8H11" stroke-width="1.5" stroke-linecap="round"></path></svg></a></div></div></div></li><li><div class="NavDropdown-module__container__l2YeI"><button type="button" class="NavDropdown-module__button__PEHWX" aria-expanded="false" aria-controls="_R_27d_">Open Source<svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-triangle-right NavDropdown-module__buttonIcon__Tkl8_" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m6.427 4.427 3.396 3.396a.25.25 0 0 1 0 .354l-3.396 3.396A.25.25 0 0 1 6 11.396V4.604a.25.25 0 0 1 .427-.177Z"></path></svg></button><div id="_R_27d_" class="NavDropdown-module__dropdown__xm1jd"><ul class="NavDropdown-module__list__zuCgG"><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_5m7d_">COMMUNITY</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_5m7d_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/open-source/sponsors" data-analytics-event="{&quot;action&quot;:&quot;github_sponsors&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;open_source&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;github_sponsors_link_open_source_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-sponsor-tiers NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M10.586 1C12.268 1 13.5 2.37 13.5 4.25c0 1.745-.996 3.359-2.622 4.831-.166.15-.336.297-.509.438l1.116 5.584a.75.75 0 0 1-.991.852l-2.409-.876a.25.25 0 0 0-.17 0l-2.409.876a.75.75 0 0 1-.991-.852L5.63 9.519a13.78 13.78 0 0 1-.51-.438C3.497 7.609 2.5 5.995 2.5 4.25 2.5 2.37 3.732 1 5.414 1c.963 0 1.843.403 2.474 1.073L8 2.198l.112-.125a3.385 3.385 0 0 1 2.283-1.068L10.586 1Zm-3.621 9.495-.718 3.594 1.155-.42a1.75 1.75 0 0 1 1.028-.051l.168.051 1.154.42-.718-3.592c-.199.13-.37.235-.505.314l-.169.097a.75.75 0 0 1-.72 0 9.54 9.54 0 0 1-.515-.308l-.16-.105ZM10.586 2.5c-.863 0-1.611.58-1.866 1.459-.209.721-1.231.721-1.44 0C7.025 3.08 6.277 2.5 5.414 2.5 4.598 2.5 4 3.165 4 4.25c0 1.23.786 2.504 2.128 3.719.49.443 1.018.846 1.546 1.198l.325.21.076-.047.251-.163a13.341 13.341 0 0 0 1.546-1.198C11.214 6.754 12 5.479 12 4.25c0-1.085-.598-1.75-1.414-1.75Z"></path></svg>GitHub Sponsors</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Fund open source developers</span></span></a></li></ul></div></li><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_9m7d_">PROGRAMS</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_9m7d_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--is-external___xsncV" href="https://securitylab.github.com" data-analytics-event="{&quot;action&quot;:&quot;security_lab&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;open_source&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;security_lab_link_open_source_navbar&quot;}" target="_blank" rel="noreferrer"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Security Lab</span><svg data-component="Octicon" focusable="false" aria-label="External link" class="octicon octicon-link-external" role="img" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path></svg></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--is-external___xsncV" href="https://maintainers.github.com" data-analytics-event="{&quot;action&quot;:&quot;maintainer_community&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;open_source&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;maintainer_community_link_open_source_navbar&quot;}" target="_blank" rel="noreferrer"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Maintainer Community</span><svg data-component="Octicon" focusable="false" aria-label="External link" class="octicon octicon-link-external" role="img" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path></svg></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--is-external___xsncV" href="https://stars.github.com" data-analytics-event="{&quot;action&quot;:&quot;github_stars&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;open_source&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;github_stars_link_open_source_navbar&quot;}" target="_blank" rel="noreferrer"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">GitHub Stars</span><svg data-component="Octicon" focusable="false" aria-label="External link" class="octicon octicon-link-external" role="img" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path></svg></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--is-external___xsncV" href="https://archiveprogram.github.com" data-analytics-event="{&quot;action&quot;:&quot;archive_program&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;open_source&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;archive_program_link_open_source_navbar&quot;}" target="_blank" rel="noreferrer"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Archive Program</span><svg data-component="Octicon" focusable="false" aria-label="External link" class="octicon octicon-link-external" role="img" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path></svg></a></li></ul></div></li><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_dm7d_">REPOSITORIES</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_dm7d_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/topics" data-analytics-event="{&quot;action&quot;:&quot;topics&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;open_source&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;topics_link_open_source_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Topics</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/trending" data-analytics-event="{&quot;action&quot;:&quot;trending&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;open_source&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;trending_link_open_source_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Trending</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/collections" data-analytics-event="{&quot;action&quot;:&quot;collections&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;open_source&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;collections_link_open_source_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Collections</span></a></li></ul></div></li></ul></div></div></li><li><div class="NavDropdown-module__container__l2YeI"><button type="button" class="NavDropdown-module__button__PEHWX" aria-expanded="false" aria-controls="_R_2nd_">Enterprise<svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-triangle-right NavDropdown-module__buttonIcon__Tkl8_" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m6.427 4.427 3.396 3.396a.25.25 0 0 1 0 .354l-3.396 3.396A.25.25 0 0 1 6 11.396V4.604a.25.25 0 0 1 .427-.177Z"></path></svg></button><div id="_R_2nd_" class="NavDropdown-module__dropdown__xm1jd"><ul class="NavDropdown-module__list__zuCgG"><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_5mnd_">ENTERPRISE SOLUTIONS</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_5mnd_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/enterprise" data-analytics-event="{&quot;action&quot;:&quot;enterprise_platform&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;enterprise&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;enterprise_platform_link_enterprise_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-stack NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M7.122.392a1.75 1.75 0 0 1 1.756 0l5.003 2.902c.83.481.83 1.68 0 2.162L8.878 8.358a1.75 1.75 0 0 1-1.756 0L2.119 5.456a1.251 1.251 0 0 1 0-2.162ZM8.125 1.69a.248.248 0 0 0-.25 0l-4.63 2.685 4.63 2.685a.248.248 0 0 0 .25 0l4.63-2.685ZM1.601 7.789a.75.75 0 0 1 1.025-.273l5.249 3.044a.248.248 0 0 0 .25 0l5.249-3.044a.75.75 0 0 1 .752 1.298l-5.248 3.044a1.75 1.75 0 0 1-1.756 0L1.874 8.814A.75.75 0 0 1 1.6 7.789Zm0 3.5a.75.75 0 0 1 1.025-.273l5.249 3.044a.248.248 0 0 0 .25 0l5.249-3.044a.75.75 0 0 1 .752 1.298l-5.248 3.044a1.75 1.75 0 0 1-1.756 0l-5.248-3.044a.75.75 0 0 1-.273-1.025Z"></path></svg>Enterprise platform</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">AI-powered developer platform</span></span></a></li></ul></div></li><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_9mnd_">AVAILABLE ADD-ONS</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_9mnd_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/security/advanced-security" data-analytics-event="{&quot;action&quot;:&quot;github_advanced_security&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;enterprise&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;github_advanced_security_link_enterprise_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-shield-check NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m8.533.133 5.25 1.68A1.75 1.75 0 0 1 15 3.48V7c0 1.566-.32 3.182-1.303 4.682-.983 1.498-2.585 2.813-5.032 3.855a1.697 1.697 0 0 1-1.33 0c-2.447-1.042-4.049-2.357-5.032-3.855C1.32 10.182 1 8.566 1 7V3.48a1.75 1.75 0 0 1 1.217-1.667l5.25-1.68a1.748 1.748 0 0 1 1.066 0Zm-.61 1.429.001.001-5.25 1.68a.251.251 0 0 0-.174.237V7c0 1.36.275 2.666 1.057 3.859.784 1.194 2.121 2.342 4.366 3.298a.196.196 0 0 0 .154 0c2.245-.957 3.582-2.103 4.366-3.297C13.225 9.666 13.5 8.358 13.5 7V3.48a.25.25 0 0 0-.174-.238l-5.25-1.68a.25.25 0 0 0-.153 0ZM11.28 6.28l-3.5 3.5a.75.75 0 0 1-1.06 0l-1.5-1.5a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215l.97.97 2.97-2.97a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Z"></path></svg>GitHub Advanced Security</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Enterprise-grade security features</span></span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/features/copilot/copilot-business" data-analytics-event="{&quot;action&quot;:&quot;copilot_for_business&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;enterprise&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;copilot_for_business_link_enterprise_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-copilot NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M7.998 15.035c-4.562 0-7.873-2.914-7.998-3.749V9.338c.085-.628.677-1.686 1.588-2.065.013-.07.024-.143.036-.218.029-.183.06-.384.126-.612-.201-.508-.254-1.084-.254-1.656 0-.87.128-1.769.693-2.484.579-.733 1.494-1.124 2.724-1.261 1.206-.134 2.262.034 2.944.765.05.053.096.108.139.165.044-.057.094-.112.143-.165.682-.731 1.738-.899 2.944-.765 1.23.137 2.145.528 2.724 1.261.566.715.693 1.614.693 2.484 0 .572-.053 1.148-.254 1.656.066.228.098.429.126.612.012.076.024.148.037.218.924.385 1.522 1.471 1.591 2.095v1.872c0 .766-3.351 3.795-8.002 3.795Zm0-1.485c2.28 0 4.584-1.11 5.002-1.433V7.862l-.023-.116c-.49.21-1.075.291-1.727.291-1.146 0-2.059-.327-2.71-.991A3.222 3.222 0 0 1 8 6.303a3.24 3.24 0 0 1-.544.743c-.65.664-1.563.991-2.71.991-.652 0-1.236-.081-1.727-.291l-.023.116v4.255c.419.323 2.722 1.433 5.002 1.433ZM6.762 2.83c-.193-.206-.637-.413-1.682-.297-1.019.113-1.479.404-1.713.7-.247.312-.369.789-.369 1.554 0 .793.129 1.171.308 1.371.162.181.519.379 1.442.379.853 0 1.339-.235 1.638-.54.315-.322.527-.827.617-1.553.117-.935-.037-1.395-.241-1.614Zm4.155-.297c-1.044-.116-1.488.091-1.681.297-.204.219-.359.679-.242 1.614.091.726.303 1.231.618 1.553.299.305.784.54 1.638.54.922 0 1.28-.198 1.442-.379.179-.2.308-.578.308-1.371 0-.765-.123-1.242-.37-1.554-.233-.296-.693-.587-1.713-.7Z"></path><path d="M6.25 9.037a.75.75 0 0 1 .75.75v1.501a.75.75 0 0 1-1.5 0V9.787a.75.75 0 0 1 .75-.75Zm4.25.75v1.501a.75.75 0 0 1-1.5 0V9.787a.75.75 0 0 1 1.5 0Z"></path></svg>Copilot for Business</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Enterprise-grade AI features</span></span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/enterprise/premium-support" data-analytics-event="{&quot;action&quot;:&quot;premium_support&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;enterprise&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;premium_support_link_enterprise_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-comment-discussion NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.75 1h8.5c.966 0 1.75.784 1.75 1.75v5.5A1.75 1.75 0 0 1 10.25 10H7.061l-2.574 2.573A1.458 1.458 0 0 1 2 11.543V10h-.25A1.75 1.75 0 0 1 0 8.25v-5.5C0 1.784.784 1 1.75 1ZM1.5 2.75v5.5c0 .138.112.25.25.25h1a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h3.5a.25.25 0 0 0 .25-.25v-5.5a.25.25 0 0 0-.25-.25h-8.5a.25.25 0 0 0-.25.25Zm13 2a.25.25 0 0 0-.25-.25h-.5a.75.75 0 0 1 0-1.5h.5c.966 0 1.75.784 1.75 1.75v5.5A1.75 1.75 0 0 1 14.25 12H14v1.543a1.458 1.458 0 0 1-2.487 1.03L9.22 12.28a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215l2.22 2.22v-2.19a.75.75 0 0 1 .75-.75h1a.25.25 0 0 0 .25-.25Z"></path></svg>Premium Support</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Enterprise-grade 24/7 support</span></span></a></li></ul></div></li></ul></div></div></li><li><a href="https://github.com/pricing" data-analytics-event="{&quot;action&quot;:&quot;pricing&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;pricing&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;pricing_link_pricing_navbar&quot;}" class="MarketingNavigation-module__navLink__hUomM">Pricing</a></li></ul></nav><div class="MarketingHeader-module__ctaContainer__tBmPz"><div class="HeaderSearch-module__searchSlot__oVOUS"><button class="Primer_Brand__Button-module__Button___scH9Z Primer_Brand__Button-module__Button--subtle___F7pEE Primer_Brand__Button-module__Button--size-small___zQrEw HeaderSearch-module__trigger__zsF9q" type="button" aria-haspopup="dialog" aria-expanded="false" aria-label="Search or jump to, type / to search" data-analytics-event="{&quot;action&quot;:&quot;searchbar&quot;,&quot;tag&quot;:&quot;input&quot;,&quot;context&quot;:&quot;global&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;searchbar_input_global_navbar&quot;}"><span class="Primer_Brand__Button-module__Button__text___ED0bX"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ Primer_Brand__Button-module__Button--label___qrkyz Primer_Brand__Button-module__Button--label-subtle___8ndWH"><span class="HeaderSearch-module__content__kMpxU"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-search HeaderSearch-module__icon__wcrHX" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M10.68 11.74a6 6 0 0 1-7.922-8.982 6 6 0 0 1 8.982 7.922l3.04 3.04a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215ZM11.5 7a4.499 4.499 0 1 0-8.997 0A4.499 4.499 0 0 0 11.5 7Z"></path></svg><span class="HeaderSearch-module__label__d1iWG">Search</span><kbd class="HeaderSearch-module__kbd__HNG0o" aria-hidden="true">/</kbd></span></span></span></button><div class="d-none"></div></div><div class="AuthCTAs-module__signInWrap__q2P60"><a class="Primer_Brand__Button-module__Button___scH9Z Primer_Brand__Button-module__Button--subtle___F7pEE Primer_Brand__Button-module__Button--size-small___zQrEw AuthCTAs-module__cta__WpwQq AuthCTAs-module__desktopActionGap__UZuXT AuthCTAs-module__hiddenBelowLg__BfKBw" href="/login?return_to=https%3A%2F%2Fgithub.com%2FeSlider%2F2dph%2Fblob%2Fmain%2Fdocs%2Frunbook.md" data-analytics-event="{&quot;action&quot;:&quot;sign_in&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;auth_cta&quot;,&quot;location&quot;:&quot;header&quot;,&quot;label&quot;:&quot;sign_in_link_auth_cta_header&quot;}" data-hydro-click="{&quot;event_type&quot;:&quot;authentication.click&quot;,&quot;payload&quot;:{&quot;location_in_page&quot;:&quot;site header menu&quot;,&quot;repository_id&quot;:null,&quot;auth_type&quot;:&quot;SIGN_UP&quot;,&quot;originating_url&quot;:&quot;https://github.com/eSlider/2dph/blob/main/docs/runbook.md&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="f49c4a55616a5afe6193559baf2cc909b670c8a782b81e02331d783a8feee86f"><span class="Primer_Brand__Button-module__Button__text___ED0bX"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ Primer_Brand__Button-module__Button--label___qrkyz Primer_Brand__Button-module__Button--label-subtle___8ndWH">Sign in</span></span></a></div><a class="Primer_Brand__Button-module__Button___scH9Z Primer_Brand__Button-module__Button--secondary___gHnw_ Primer_Brand__Button-module__Button--size-small___zQrEw AuthCTAs-module__cta__WpwQq" href="/signup?ref_cta=Sign+up&amp;ref_loc=header+logged+out&amp;ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E%2Fblob%2Fshow&amp;source=header-repo&amp;source_repo=eSlider%2F2dph" data-analytics-event="{&quot;action&quot;:&quot;sign_up&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;auth_cta&quot;,&quot;location&quot;:&quot;header&quot;,&quot;label&quot;:&quot;sign_up_link_auth_cta_header&quot;}" data-hydro-click="{&quot;event_type&quot;:&quot;authentication.click&quot;,&quot;payload&quot;:{&quot;location_in_page&quot;:&quot;site header menu&quot;,&quot;repository_id&quot;:null,&quot;auth_type&quot;:&quot;SIGN_UP&quot;,&quot;originating_url&quot;:&quot;https://github.com/eSlider/2dph/blob/main/docs/runbook.md&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="f49c4a55616a5afe6193559baf2cc909b670c8a782b81e02331d783a8feee86f"><span class="Primer_Brand__Button-module__Button__text___ED0bX"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ Primer_Brand__Button-module__Button--label___qrkyz Primer_Brand__Button-module__Button--label-secondary___eJ0_a">Sign up</span></span></a><button class="Primer_Brand__Button-module__Button___scH9Z Primer_Brand__Button-module__Button--subtle___F7pEE Primer_Brand__Button-module__Button--size-small___zQrEw HeaderAppearanceSettings-module__trigger__hUheK" type="button" aria-haspopup="dialog" aria-labelledby="_R_fbd_"><span class="Primer_Brand__Button-module__Button__leading-visual___jjtTe" data-testid="Button-leading-visual"><svg data-component="Octicon" focusable="false" aria-hidden="true" class="octicon octicon-sliders Primer_Brand__Button-module__Button__icon-visual____qybb" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M15 2.75a.75.75 0 0 1-.75.75h-4a.75.75 0 0 1 0-1.5h4a.75.75 0 0 1 .75.75Zm-8.5.75v1.25a.75.75 0 0 0 1.5 0v-4a.75.75 0 0 0-1.5 0V2H1.75a.75.75 0 0 0 0 1.5H6.5Zm1.25 5.25a.75.75 0 0 0 0-1.5h-6a.75.75 0 0 0 0 1.5h6ZM15 8a.75.75 0 0 1-.75.75H11.5V10a.75.75 0 1 1-1.5 0V6a.75.75 0 0 1 1.5 0v1.25h2.75A.75.75 0 0 1 15 8Zm-9 5.25v-2a.75.75 0 0 0-1.5 0v1.25H1.75a.75.75 0 0 0 0 1.5H4.5v1.25a.75.75 0 0 0 1.5 0v-2Zm9 0a.75.75 0 0 1-.75.75h-6a.75.75 0 0 1 0-1.5h6a.75.75 0 0 1 .75.75Z"></path></svg></span><span class="Primer_Brand__Button-module__Button__text___ED0bX"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ Primer_Brand__Button-module__Button--label___qrkyz Primer_Brand__Button-module__Button--label-subtle___8ndWH"></span></span></button><div class="Primer_Brand__Tooltip-module__Tooltip___0Eipx" data-direction="s" aria-hidden="true" id="_R_fbd_">Appearance settings</div></div></div></div></div><div class="MarketingHeader-module__bottomBorder__uZT38" aria-hidden="true"></div></header></div></div>
</react-partial>



      <div hidden="hidden" data-view-component="true" class="js-stale-session-flash stale-session-flash flash flash-warn flash-full">
  
        <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-alert">
    <path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path>
</svg>
        <span class="js-stale-session-flash-signed-in" hidden>You signed in with another tab or window. <a class="Link--inTextBlock" href="">Reload</a> to refresh your session.</span>
        <span class="js-stale-session-flash-signed-out" hidden>You signed out in another tab or window. <a class="Link--inTextBlock" href="">Reload</a> to refresh your session.</span>
        <span class="js-stale-session-flash-switched" hidden>You switched accounts on another tab or window. <a class="Link--inTextBlock" href="">Reload</a> to refresh your session.</span>

    <button id="icon-button-652d7109-9e9e-4427-94c7-b7bcab574a51" aria-labelledby="tooltip-d5e79539-f8e1-4023-8b18-ccdfdb863bd9" type="button" data-view-component="true" class="Button Button--iconOnly Button--invisible Button--medium flash-close js-flash-close">  <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-x Button-visual">
    <path d="M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l3.22 3.22a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-3.22 3.22a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z"></path>
</svg>
</button><tool-tip id="tooltip-d5e79539-f8e1-4023-8b18-ccdfdb863bd9" for="icon-button-652d7109-9e9e-4427-94c7-b7bcab574a51" popover="manual" data-direction="s" data-type="label" data-view-component="true" class="sr-only position-absolute">Dismiss alert</tool-tip>


  
</div>
    </div>

  <div id="start-of-content" class="show-on-focus"></div>








    <div id="js-flash-container" class="flash-container" data-turbo-replace>






  <template class="js-flash-template">
    
<div class="flash flash-full   {{ className }}">
  <div >
    <button autofocus class="flash-close js-flash-close" type="button" aria-label="Dismiss this message">
      <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-x">
    <path d="M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l3.22 3.22a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-3.22 3.22a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z"></path>
</svg>
    </button>
    <div aria-atomic="true" role="alert" class="js-flash-alert">
      
      <div>{{ message }}</div>

    </div>
  </div>
</div>
  </template>
</div>


    






  <div
    class="application-main "
    data-commit-hovercards-enabled
    data-discussion-hovercards-enabled
    data-issue-and-pr-hovercards-enabled
    data-project-hovercards-enabled
  >
        <div itemscope itemtype="http://schema.org/SoftwareSourceCode" class="">
    <main id="js-repo-pjax-container" >
      
      








  

    <link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/ym-1a7ce813a378e1f7.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/mn-bb458474353a279e.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/t5-26d19272bbf73ca3.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/2d5-9abb2f6ba5c935d0.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/24-b11e1c4108322883.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/mrn-12afac2ecd2be980.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/dgq-bbf7670f9cdba2e0.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/7w-740b4e942974ccba.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/we-c9f93392098ad6c9.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/775-f58305f9bcded6f1.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/bn-fcef0645a47703fc.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/xgw-677b1ad5da5c3c6d.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/ge-ca1c0ba8656f9680.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/9f-3f61950dd9225c4a.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/global-nav-bar-76f3d16d4cab2423.js" fetchpriority="low" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-react-css.e4334163c54cccf5.module.css" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/ql0.501e99879e15a48e.module.css" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/global-nav-bar.679bccc49e90d4e2.module.css" />

<react-partial
  partial-name="global-nav-bar"
  data-ssr="true"
  data-attempted-ssr="true"
  data-react-profiling="false"
>
  
  <script type="application/json" data-target="react-partial.embeddedData">{"props":{"contextRegion":{"crumbs":[{"crumb_type":"user","label":"eSlider","is_root":false,"href":"/eSlider"},{"crumb_type":"repository","label":"2dph","is_root":false,"href":"/eSlider/2dph"}],"localNavigation":[{"id":"code","icon":"code","label":"Code","href":"/eSlider/2dph","selectedLinks":["repo_source","repo_downloads","repo_commits","repo_releases","repo_tags","repo_branches","repo_packages","repo_deployments","repo_attestations"],"popoverTarget":false,"commandId":"repositories:go-to-code","reactNav":{"appTarget":"code-view","anchor":"code-view-repo-link"},"turboNav":{"frame":"repo-content-turbo-frame"}},{"id":"issues","icon":"issue-opened","label":"Issues","href":"/eSlider/2dph/issues","selectedLinks":["repo_issues","repo_labels","repo_milestones"],"count":0,"popoverTarget":false,"commandId":"repositories:go-to-issues","reactNav":{"appTarget":"repo","anchor":null},"turboNav":{"frame":"repo-content-turbo-frame"}},{"id":"pull-requests","icon":"git-pull-request","label":"Pull requests","href":"/eSlider/2dph/pulls","selectedLinks":["repo_pulls","checks"],"count":0,"popoverTarget":false,"commandId":"repositories:go-to-pull-requests","reactNav":{"appTarget":null,"anchor":null},"turboNav":{"frame":"repo-content-turbo-frame"}},{"id":"actions","icon":"play","label":"Actions","href":"/eSlider/2dph/actions","selectedLinks":["repo_actions"],"popoverTarget":false,"commandId":"repositories:go-to-actions","reactNav":{"appTarget":"actions-workflows","anchor":null},"turboNav":{"frame":"repo-content-turbo-frame"}},{"id":"projects","icon":"table","label":"Projects","href":"/eSlider/2dph/projects","selectedLinks":["repo_projects","new_repo_project","repo_project"],"popoverTarget":false,"commandId":"repositories:go-to-projects","reactNav":{"appTarget":"repo","anchor":null},"turboNav":{"frame":"repo-content-turbo-frame"}},{"id":"security-and-quality","icon":"shield","label":"Security and quality","href":"/eSlider/2dph/security","selectedLinks":["security","overview","alerts","policy","token_scanning","code_scanning"],"count":0,"popoverTarget":false,"commandId":"repositories:go-to-security","reactNav":{"appTarget":null,"anchor":null},"turboNav":{"frame":"repo-content-turbo-frame"}},{"id":"insights","icon":"graph","label":"Insights","href":"/eSlider/2dph/pulse","selectedLinks":["repo_graphs","repo_contributors","dependency_graph","dependabot_updates","pulse","people","community"],"popoverTarget":false,"commandId":"repositories:go-to-insights","reactNav":{"appTarget":null,"anchor":null},"turboNav":{"frame":"repo-content-turbo-frame"}}],"localNavigationUpdateChannel":null,"selectedLink":"repo_source"},"owner":null,"headerLogo":{"href":"/","aria-label":"Homepage "},"notifications":{"indicatorMode":"disabled","websocketChannel":null,"fetchIndicatorSrc":"/notifications/indicator","fetchIndicatorEnabled":false},"issues":{"href":"/issues"},"pulls":{"href":"/pulls"},"contributedRepos":{"href":"/repos"},"copilot":{"show":false,"showAgentsButton":false,"showRelaunchAnnouncement":false,"copilotChatUrl":null,"copilotApiUrl":"https://api.githubcopilot.com"},"search":{"show":true,"showCommandPalette":false,"isSearchPage":false,"isJumpToSearch":false,"searchContext":{"scope":"repo:eSlider/2dph","current_repo_name":"2dph","current_repo_nwo":"eSlider/2dph","user_id":"eSlider"}},"commandPalette":null,"enterpriseBar":{"show":false},"globalTransactionalMessage":[],"payloadsUrl":"/_global-navigation/payloads.json?can_toggle_site_admin_and_employee_status=0\u0026is_admin_mode_on=0\u0026is_ui_opted_out=0\u0026show_ui_opt_out=0\u0026v=6","contextRegionUrl":"/_global-navigation/context-region.json"}}</script>
  <div data-target="react-partial.reactRoot"><header aria-label="Global navigation menu" data-component="Stack" class="GlobalNav styles-module__appHeader__YzYWk prc-Stack-Stack-UQ9k6" data-gap="none" data-direction="vertical" data-align="stretch" data-wrap="nowrap" data-justify="start" data-padding="none"><div data-component="Stack" class="prc-Stack-Stack-UQ9k6" data-direction="horizontal" data-align="center" data-wrap="nowrap" data-justify="center" data-padding="none"><div data-testid="top-nav-center" data-component="Stack" class="styles-module__center__R3QRv styles-module__withLocalNavigation__rjTJ_ GlobalNavBar-module__loggedOut__Jv1rk prc-Stack-Stack-UQ9k6" data-gap="condensed" data-direction="horizontal" data-align="stretch" data-wrap="nowrap" data-justify="start" data-padding="normal"><nav class="styles-module__contextRegion__VbSp2 prc-Breadcrumbs-BreadcrumbsBase-3Gb-B" aria-label="Breadcrumbs" data-overflow="menu" data-variant="normal" data-component="Breadcrumbs"><ol class="prc-Breadcrumbs-BreadcrumbsList-BKjpe"><li class="prc-Breadcrumbs-ItemWrapper-k0NLn"><a class="styles-module__contextCrumb__IzGIq prc-Breadcrumbs-Item-jcraJ" data-component="Breadcrumbs.Item" href="/eSlider" data-discover="true"><span class="">eSlider</span></a></li><li class="prc-Breadcrumbs-ItemWrapper-k0NLn"><a class="styles-module__contextCrumb__IzGIq prc-Breadcrumbs-Item-jcraJ" data-component="Breadcrumbs.Item" href="/eSlider/2dph" data-discover="true"><span class="styles-module__contextCrumbLast__tI2e3">2dph</span></a></li></ol></nav></div><div data-testid="top-nav-right" data-component="Stack" class="styles-module__right__mlBQg styles-module__withLocalNavigation__rjTJ_ styles-module__rightWithResponsiveCreateButton__SKn2W prc-Stack-Stack-UQ9k6" data-gap="condensed" data-direction="horizontal" data-align="center" data-wrap="nowrap" data-justify="start" data-padding="normal"></div></div><h2 class="prc-src-InternalVisuallyHidden-2YaI6">Repository navigation</h2><nav class="prc-components-UnderlineWrapper-eT-Yj prc-UnderlineNav-UnderlineWrapper-GWONT LocalNavigation-module__LocalNavigation__b0Xc0" aria-label="Repository" data-variant="inset" data-overflow-mode="wrap" data-hide-icons-breakpoint="medium"><ul class="prc-UnderlineNav-ItemsList-oj8gN prc-components-UnderlineItemList-xKlKC" role="list"><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li class="prc-UnderlineNav-UnderlineNavItem-syRjR"><a aria-current="page" data-tab-item="code" data-react-nav="code-view" data-react-nav-anchor="code-view-repo-link" data-turbo-frame="repo-content-turbo-frame" class="prc-components-UnderlineItem-7fP-n" href="/eSlider/2dph" data-discover="true"><span data-component="icon"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-code" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m11.28 3.22 4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734L13.94 8l-3.72-3.72a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215Zm-6.56 0a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042L2.06 8l3.72 3.72a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L.47 8.53a.75.75 0 0 1 0-1.06Z"></path></svg></span><span data-component="text" data-content="Code">Code</span></a></li><li class="prc-UnderlineNav-UnderlineNavItem-syRjR"><a data-tab-item="issues" data-react-nav="repo" data-turbo-frame="repo-content-turbo-frame" class="prc-components-UnderlineItem-7fP-n" href="/eSlider/2dph/issues" data-discover="true"><span data-component="icon"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-issue-opened" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M8 9.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z"></path><path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Z"></path></svg></span><span data-component="text" data-content="Issues">Issues</span></a></li><li class="prc-UnderlineNav-UnderlineNavItem-syRjR"><a data-tab-item="pull-requests" data-turbo-frame="repo-content-turbo-frame" class="prc-components-UnderlineItem-7fP-n" href="/eSlider/2dph/pulls" data-discover="true"><span data-component="icon"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-git-pull-request" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.5 3.25a2.25 2.25 0 1 1 3 2.122v5.256a2.251 2.251 0 1 1-1.5 0V5.372A2.25 2.25 0 0 1 1.5 3.25Zm5.677-.177L9.573.677A.25.25 0 0 1 10 .854V2.5h1A2.5 2.5 0 0 1 13.5 5v5.628a2.251 2.251 0 1 1-1.5 0V5a1 1 0 0 0-1-1h-1v1.646a.25.25 0 0 1-.427.177L7.177 3.427a.25.25 0 0 1 0-.354ZM3.75 2.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm0 9.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm8.25.75a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Z"></path></svg></span><span data-component="text" data-content="Pull requests">Pull requests</span></a></li><li class="prc-UnderlineNav-UnderlineNavItem-syRjR"><a data-tab-item="actions" data-react-nav="actions-workflows" data-turbo-frame="repo-content-turbo-frame" class="prc-components-UnderlineItem-7fP-n" href="/eSlider/2dph/actions" data-discover="true"><span data-component="icon"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-play" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Zm4.879-2.773 4.264 2.559a.25.25 0 0 1 0 .428l-4.264 2.559A.25.25 0 0 1 6 10.559V5.442a.25.25 0 0 1 .379-.215Z"></path></svg></span><span data-component="text" data-content="Actions">Actions</span></a></li><li class="prc-UnderlineNav-UnderlineNavItem-syRjR"><a data-tab-item="projects" data-react-nav="repo" data-turbo-frame="repo-content-turbo-frame" class="prc-components-UnderlineItem-7fP-n" href="/eSlider/2dph/projects" data-discover="true"><span data-component="icon"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-table" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v12.5A1.75 1.75 0 0 1 14.25 16H1.75A1.75 1.75 0 0 1 0 14.25ZM6.5 6.5v8h7.75a.25.25 0 0 0 .25-.25V6.5Zm8-1.5V1.75a.25.25 0 0 0-.25-.25H6.5V5Zm-13 1.5v7.75c0 .138.112.25.25.25H5v-8ZM5 5V1.5H1.75a.25.25 0 0 0-.25.25V5Z"></path></svg></span><span data-component="text" data-content="Projects">Projects</span></a></li><li class="prc-UnderlineNav-UnderlineNavItem-syRjR"><a data-tab-item="security-and-quality" data-turbo-frame="repo-content-turbo-frame" class="prc-components-UnderlineItem-7fP-n" href="/eSlider/2dph/security" data-discover="true"><span data-component="icon"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-shield" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M7.467.133a1.748 1.748 0 0 1 1.066 0l5.25 1.68A1.75 1.75 0 0 1 15 3.48V7c0 1.566-.32 3.182-1.303 4.682-.983 1.498-2.585 2.813-5.032 3.855a1.697 1.697 0 0 1-1.33 0c-2.447-1.042-4.049-2.357-5.032-3.855C1.32 10.182 1 8.566 1 7V3.48a1.75 1.75 0 0 1 1.217-1.667Zm.61 1.429a.25.25 0 0 0-.153 0l-5.25 1.68a.25.25 0 0 0-.174.238V7c0 1.358.275 2.666 1.057 3.86.784 1.194 2.121 2.34 4.366 3.297a.196.196 0 0 0 .154 0c2.245-.956 3.582-2.104 4.366-3.298C13.225 9.666 13.5 8.36 13.5 7V3.48a.251.251 0 0 0-.174-.237l-5.25-1.68ZM8.75 4.75v3a.75.75 0 0 1-1.5 0v-3a.75.75 0 0 1 1.5 0ZM9 10.5a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path></svg></span><span data-component="text" data-content="Security and quality">Security and quality</span></a></li><li class="prc-UnderlineNav-UnderlineNavItem-syRjR"><a data-tab-item="insights" data-turbo-frame="repo-content-turbo-frame" class="prc-components-UnderlineItem-7fP-n" href="/eSlider/2dph/pulse" data-discover="true"><span data-component="icon"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-graph" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.5 1.75V13.5h13.75a.75.75 0 0 1 0 1.5H.75a.75.75 0 0 1-.75-.75V1.75a.75.75 0 0 1 1.5 0Zm14.28 2.53-5.25 5.25a.75.75 0 0 1-1.06 0L7 7.06 4.28 9.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.25-3.25a.75.75 0 0 1 1.06 0L10 7.94l4.72-4.72a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Z"></path></svg></span><span data-component="text" data-content="Insights">Insights</span></a></li></ul><div class="prc-UnderlineNav-MoreButtonContainer-Dnrq6"><div class="prc-UnderlineNav-MoreButtonDivider-dN0a-"></div><button data-component="overflow-menu-button" type="button" aria-haspopup="true" aria-expanded="false" tabindex="0" class="prc-Button-ButtonBase-9n-Xk prc-UnderlineNav-MoreButton-Y8soj" data-loading="false" data-size="medium" data-variant="invisible" id="_R_2ktl_"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-Iohp5"><span data-component="text" class="prc-Button-Label-FWkx3"><span>More<span class="prc-src-InternalVisuallyHidden-2YaI6"> items</span></span></span></span><span data-component="trailingAction" class="prc-Button-Visual-YNt2F prc-Button-VisualWrap-E4cnq"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-triangle-down" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m4.427 7.427 3.396 3.396a.25.25 0 0 0 .354 0l3.396-3.396A.25.25 0 0 0 11.396 7H4.604a.25.25 0 0 0-.177.427Z"></path></svg></span></button></div></nav><div class="d-none"></div></header></div>
</react-partial>


  



<turbo-frame id="repo-content-turbo-frame" target="_top" data-turbo-action="advance" class="">
    <div id="repo-content-pjax-container" class="repository-content " >
    



    
      
    








<react-app
  app-name="code-view"
  initial-path="/eSlider/2dph/blob/main/docs/runbook.md"
  style="display: block; min-height: calc(100vh - 64px);"
  data-attempted-ssr="true"
  data-ssr="true"
  data-lazy="false"
  data-alternate="false"
  data-data-router-enabled="true"
  data-react-profiling="false"
>
  
  <script type="application/json" data-target="react-app.embeddedData">{"payload":{"codeViewBlobRoute":{"csv":null,"csvError":null,"headerInfo":{"toc":[{"level":1,"text":"Run 2dph (portable)","anchor":"run-2dph-portable","htmlText":"Run 2dph (portable)"},{"level":2,"text":"Toolchain","anchor":"toolchain","htmlText":"Toolchain"},{"level":2,"text":"Config","anchor":"config","htmlText":"Config"},{"level":2,"text":"Index then search","anchor":"index-then-search","htmlText":"Index then search"},{"level":2,"text":"HTTP / MCP","anchor":"http--mcp","htmlText":"HTTP / MCP"},{"level":3,"text":"Brain health-check \u0026 restart (#180)","anchor":"brain-health-check--restart-180","htmlText":"Brain health-check \u0026amp; restart (#180)"},{"level":2,"text":"Бенчмарки поиска (#202)","anchor":"бенчмарки-поиска-202","htmlText":"Бенчмарки поиска (#202)"},{"level":3,"text":"ANN-индекс в проде (#204/#206)","anchor":"ann-индекс-в-проде-204206","htmlText":"ANN-индекс в проде (#204/#206)"},{"level":2,"text":"Disk mail/contacts import (#79)","anchor":"disk-mailcontacts-import-79","htmlText":"Disk mail/contacts import (#79)"},{"level":2,"text":"Sync wave + OO CRM (AGENTS.md, #81)","anchor":"sync-wave--oo-crm-agentsmd-81","htmlText":"Sync wave + OO CRM (AGENTS.md, #81)"},{"level":3,"text":"chats step (--with-chats, #195)","anchor":"chats-step---with-chats-195","htmlText":"chats step (--with-chats, #195)"},{"level":3,"text":"var/ permissions (uid 1001 vs 1000)","anchor":"var-permissions-uid-1001-vs-1000","htmlText":"var/ permissions (uid 1001 vs 1000)"},{"level":3,"text":"index-sync — gator→graph→index cycle (#292)","anchor":"index-sync--gatorgraphindex-cycle-292","htmlText":"index-sync — gator→graph→index cycle (#292)"},{"level":3,"text":"OnlyOffice CRM tools","anchor":"onlyoffice-crm-tools","htmlText":"OnlyOffice CRM tools"},{"level":2,"text":"Browser sync (periodic, #163)","anchor":"browser-sync-periodic-163","htmlText":"Browser sync (periodic, #163)"},{"level":2,"text":"Reasoner (optional, D18)","anchor":"reasoner-optional-d18","htmlText":"Reasoner (optional, D18)"},{"level":2,"text":"GitHub mirror sync (#194)","anchor":"github-mirror-sync-194","htmlText":"GitHub mirror sync (#194)"}]},"issueTemplate":null,"discussionTemplate":null,"richText":"\u003carticle class=\"markdown-body entry-content container-lg\" itemprop=\"text\"\u003e\u003cmarkdown-accessiblity-table\u003e\u003ctable\u003e\n  \u003ctbody\u003e\n  \u003ctr\u003e\n    \u003cth\u003etype\u003c/th\u003e\n    \u003ctd\u003ehowto\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003cth\u003estatus\u003c/th\u003e\n    \u003ctd\u003ecurrent\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003cth\u003erelated\u003c/th\u003e\n    \u003ctd\u003e\u003ctable\u003e\n  \u003ctbody\u003e\n  \u003ctr\u003e\n  \u003ctd\u003e\u003cdiv dir=\"auto\"\u003edocs/README.md\u003c/div\u003e\u003c/td\u003e\n  \u003ctd\u003e\u003cdiv dir=\"auto\"\u003ePLAN.md\u003c/div\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\u003c/markdown-accessiblity-table\u003e\n\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch1 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eRun 2dph (portable)\u003c/h1\u003e\u003ca id=\"user-content-run-2dph-portable\" class=\"anchor\" aria-label=\"Permalink: Run 2dph (portable)\" href=\"#run-2dph-portable\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eNo laptop-absolute paths. Config lives in env files under \u003ccode\u003e$HOME/.config/brain/\u003c/code\u003e\n(mode 0600), not in git.\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eToolchain\u003c/h2\u003e\u003ca id=\"user-content-toolchain\" class=\"anchor\" aria-label=\"Permalink: Toolchain\" href=\"#toolchain\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003eGo (see \u003ccode\u003ego.mod\u003c/code\u003e)\u003c/li\u003e\n\u003cli\u003eOptional: Docker, Zig CGO via \u003ccode\u003ebin/cgo/zig\u003c/code\u003e (not gcc)\u003c/li\u003e\n\u003cli\u003eOptional: poppler (\u003ccode\u003epdftotext\u003c/code\u003e/\u003ccode\u003epdftoppm\u003c/code\u003e) + tesseract \u003ccode\u003eeng+deu\u003c/code\u003e for mail OCR\u003c/li\u003e\n\u003cli\u003eOptional: ghostscript \u003ccode\u003egs\u003c/code\u003e — normalizes export-locked / oversized PDFs before\nextraction (strips export-protection, shrinks; original preserved, artifact\nin \u003ccode\u003evar/tmp\u003c/code\u003e). Clean PDFs skip it, so the pdftotext fast path stays fast.\u003c/li\u003e\n\u003cli\u003eOptional: \u003cstrong\u003eliteparse service\u003c/strong\u003e (epic #219) — документы → структурированный\nJSON/YAML. \u003ccode\u003edocker compose up -d liteparse\u003c/code\u003e (демон с warmup), вызов через\n\u003ccode\u003edocker compose exec -T liteparse lit parse ...\u003c/code\u003e (~100ms против ~700ms\n\u003ccode\u003edocker run\u003c/code\u003e). Инструменты: \u003ccode\u003ebin/research/convert.go\u003c/code\u003e (→ \u003ccode\u003evar/struct-data/\u0026lt;sha256\u0026gt;.yml\u003c/code\u003e,\nidempotent), \u003ccode\u003ebin/research/bench.go\u003c/code\u003e (p95). Детали: \u003ccode\u003edocs/research-liteparse.md\u003c/code\u003e.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"eval \u0026quot;$(bin/cgo/zig env)\u0026quot;   # optional; bin/brain/{search,get,stats,eval,serve}.go auto-wrap zig\ngo test ./...\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c1\"\u003eeval\u003c/span\u003e \u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e\u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e$(\u003c/span\u003ebin/cgo/zig env\u003cspan class=\"pl-pds\"\u003e)\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e\u003c/span\u003e   \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e optional; bin/brain/{search,get,stats,eval,serve}.go auto-wrap zig\u003c/span\u003e\ngo \u003cspan class=\"pl-c1\"\u003etest\u003c/span\u003e ./...\u003c/pre\u003e\u003c/div\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eConfig\u003c/h2\u003e\u003ca id=\"user-content-config\" class=\"anchor\" aria-label=\"Permalink: Config\" href=\"#config\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cmarkdown-accessiblity-table\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eFile / env\u003c/th\u003e\n\u003cth\u003ePurpose\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003e$BRAIN_SEARCH_ENV\u003c/code\u003e (default \u003ccode\u003e$HOME/.config/brain/search.env\u003c/code\u003e)\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003eBRAIN_SEARCH_URL\u003c/code\u003e (SearXNG). Optional Basic Auth.\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003e$HOME/.config/brain/db-profiles.yml\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eread-only Postgres profiles (OnlyOffice via tunnel)\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\u003c/markdown-accessiblity-table\u003e\n\u003cp dir=\"auto\"\u003eIf the host already runs SearXNG, point \u003ccode\u003eBRAIN_SEARCH_URL\u003c/code\u003e at it. Do not start\na second copy (D3). Optional Compose instance:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"SEARXNG_SECRET=$(openssl rand -hex 32) docker compose --profile searxng up -d\"\u003e\u003cpre\u003eSEARXNG_SECRET=\u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e$(\u003c/span\u003eopenssl rand -hex 32\u003cspan class=\"pl-pds\"\u003e)\u003c/span\u003e\u003c/span\u003e docker compose --profile searxng up -d\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eThat binds \u003ccode\u003e127.0.0.1:8888\u003c/code\u003e. JSON format must stay enabled.\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eIndex then search\u003c/h2\u003e\u003ca id=\"user-content-index-then-search\" class=\"anchor\" aria-label=\"Permalink: Index then search\" href=\"#index-then-search\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eWrite path is \u003ccode\u003ebin/brain/add.go\u003c/code\u003e for a leaf (or \u003ccode\u003ePOST /ingest\u003c/code\u003e). Bulk\ncorpus rebuild remains \u003ccode\u003ebin/brain/index.go --rebuild\u003c/code\u003e (Compose profile\n\u003ccode\u003eindex\u003c/code\u003e). Do not DROP INDEX on Ladybug 0.19.\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"bin/brain/add.go --text \u0026quot;arc-1 runs Matrix\u0026quot; --root facts --source \u0026quot;compose.yml x docker ps\u0026quot;\nbin/brain/index.go --rebuild --with-facts --with-chats\nbin/brain/search.go \u0026quot;LadybugDB vector index\u0026quot;     # facts → info → web (D17)\nbin/brain/search.go \u0026quot;upstream flag\u0026quot; --no-web\nbin/brain/search.go \u0026quot;who works where\u0026quot; --as-of 2025-01-01  # D24 intervals\nsource \u0026lt;(./bin/shell/complete.go bash)            # D23 flaggy complete\nbin/brain/get.go \u0026lt;id\u0026gt; --body\nbin/brain/stats.go\"\u003e\u003cpre\u003ebin/brain/add.go --text \u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003earc-1 runs Matrix\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e\u003c/span\u003e --root facts --source \u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003ecompose.yml x docker ps\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e\u003c/span\u003e\nbin/brain/index.go --rebuild --with-facts --with-chats\nbin/brain/search.go \u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003eLadybugDB vector index\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e\u003c/span\u003e     \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e facts → info → web (D17)\u003c/span\u003e\nbin/brain/search.go \u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003eupstream flag\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e\u003c/span\u003e --no-web\nbin/brain/search.go \u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003ewho works where\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e\u003c/span\u003e --as-of 2025-01-01  \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e D24 intervals\u003c/span\u003e\n\u003cspan class=\"pl-c1\"\u003esource\u003c/span\u003e \u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e\u0026lt;(\u003c/span\u003e./bin/shell/complete.go bash\u003cspan class=\"pl-pds\"\u003e)\u003c/span\u003e\u003c/span\u003e            \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e D23 flaggy complete\u003c/span\u003e\nbin/brain/get.go \u003cspan class=\"pl-k\"\u003e\u0026lt;\u003c/span\u003eid\u003cspan class=\"pl-k\"\u003e\u0026gt;\u003c/span\u003e --body\nbin/brain/stats.go\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003e--hop N\u003c/code\u003e walks File → Commit → Person from each hit. \u003ccode\u003e--as-of YYYY-MM-DD\u003c/code\u003e\nkeeps leafs whose \u003ccode\u003evalid_from\u003c/code\u003e/\u003ccode\u003evalid_to\u003c/code\u003e cover that day (empty interval =\nlegacy always-on). Empty web results are \u003ccode\u003ethrottled\u003c/code\u003e, not absence.\nGap to v1: \u003ca href=\"/eSlider/2dph/blob/main/docs/roadmap.md\"\u003eroadmap\u003c/a\u003e / \u003ca href=\"https://git.produktor.io/eSlider/2dph/issues/16\" rel=\"nofollow\"\u003eepic #16\u003c/a\u003e.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eLadybug 0.19: never \u003ccode\u003eDROP INDEX\u003c/code\u003e FTS/VECTOR (ghost catalog). Fresh indexes =\ndelete \u003ccode\u003evar/kb.lbug\u003c/code\u003e then \u003ccode\u003e--rebuild\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eHTTP / MCP\u003c/h2\u003e\u003ca id=\"user-content-http--mcp\" class=\"anchor\" aria-label=\"Permalink: HTTP / MCP\" href=\"#http--mcp\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"scripts/stack/start                 # brain :8630, wait until MCP search/get/audit\nscripts/stack/status                # YAML: brain / reasoner / picoclaw / mail_sync\nscripts/stack/start-mail-sync       # compose ETL: OO+Gmail sync→import (300s; no auto-rebuild)\nscripts/stack/start-assistant       # + qwen3.5:9b + PicoClaw agent (ask the brain)\nscripts/stack/start-assistant --no-attach\nscripts/stack/stop                  # compose stop; volumes kept\"\u003e\u003cpre\u003escripts/stack/start                 \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e brain :8630, wait until MCP search/get/audit\u003c/span\u003e\nscripts/stack/status                \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e YAML: brain / reasoner / picoclaw / mail_sync\u003c/span\u003e\nscripts/stack/start-mail-sync       \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e compose ETL: OO+Gmail sync→import (300s; no auto-rebuild)\u003c/span\u003e\nscripts/stack/start-assistant       \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e + qwen3.5:9b + PicoClaw agent (ask the brain)\u003c/span\u003e\nscripts/stack/start-assistant --no-attach\nscripts/stack/stop                  \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e compose stop; volumes kept\u003c/span\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eSame Compose services by hand:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"docker compose up -d brain                       # :8630 Zig CGO serve\ndocker compose up -d mail-sync                   # ETL loop into kb-var\ndocker compose --profile index run --rm index    # rebuild\ndocker compose --profile picoclaw up brain-mcp   # MCP 127.0.0.1:8630\"\u003e\u003cpre\u003edocker compose up -d brain                       \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e :8630 Zig CGO serve\u003c/span\u003e\ndocker compose up -d mail-sync                   \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e ETL loop into kb-var\u003c/span\u003e\ndocker compose --profile index run --rm index    \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e rebuild\u003c/span\u003e\ndocker compose --profile picoclaw up brain-mcp   \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e MCP 127.0.0.1:8630\u003c/span\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003eGET /openapi.json\u003c/code\u003e, \u003ccode\u003ePOST /mcp\u003c/code\u003e. Agent tool order: \u003ccode\u003esearch\u003c/code\u003e → \u003ccode\u003eget\u003c/code\u003e → \u003ccode\u003eaudit\u003c/code\u003e.\nSee \u003ca href=\"/eSlider/2dph/blob/main/docs/picoclaw.md\"\u003epicoclaw.md\u003c/a\u003e.\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eBrain health-check \u0026amp; restart (#180)\u003c/h3\u003e\u003ca id=\"user-content-brain-health-check--restart-180\" class=\"anchor\" aria-label=\"Permalink: Brain health-check \u0026amp; restart (#180)\" href=\"#brain-health-check--restart-180\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eThe brain wedged once (2026-08-24): a \u003ccode\u003e/ingest\u003c/code\u003e write blocked forever inside\nthe Ladybug C layer, holding the read/write lock, so every search/get/audit\nhung while \u003ccode\u003e/health\u003c/code\u003e still answered \u003ccode\u003eok\u003c/code\u003e (it never touches the DB). The static\nhealthcheck let the container sit \"healthy\" but unresponsive until a manual\nrestart.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e\u003cstrong\u003eHealth-check\u003c/strong\u003e — probe the DB read path, not \u003ccode\u003e/health\u003c/code\u003e:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"# fast (healthy): a real MATCH over kb.lbug\ncurl -sS --max-time 5 http://127.0.0.1:8630/stats   # -\u0026gt; {\u0026quot;total\u0026quot;:9204,\u0026quot;by_root\u0026quot;:{...}}\n# live check of every agent tool\ncurl -sS --max-time 10 \u0026quot;http://127.0.0.1:8630/search?q=test\u0026amp;n=1\u0026amp;noweb=1\u0026quot;\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e fast (healthy): a real MATCH over kb.lbug\u003c/span\u003e\ncurl -sS --max-time 5 http://127.0.0.1:8630/stats   \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e -\u0026gt; {\"total\":9204,\"by_root\":{...}}\u003c/span\u003e\n\u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e live check of every agent tool\u003c/span\u003e\ncurl -sS --max-time 10 \u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003ehttp://127.0.0.1:8630/search?q=test\u0026amp;n=1\u0026amp;noweb=1\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e\u003c/span\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003e/stats\u003c/code\u003e returns quickly only when the read handle is healthy; a wedged brain\ntimes out. The compose \u003ccode\u003ebrain\u003c/code\u003e healthcheck uses \u003ccode\u003e/stats\u003c/code\u003e for exactly this\nreason (see \u003ccode\u003ecompose.yaml\u003c/code\u003e), so docker auto-restarts a deadlocked container\n(\u003ccode\u003erestart: unless-stopped\u003c/code\u003e) instead of leaving it \"healthy\".\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e\u003cstrong\u003eRestart\u003c/strong\u003e:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"docker compose up -d brain          # or: docker restart 2dph-brain-1\n# verify: process not a zombie, leaf count unchanged\nps -eo pid,ppid,stat,args | grep -E \u0026quot;brain-(serve|search)\u0026quot;   # no \u0026lt;defunct\u0026gt;\ncurl -sS --max-time 5 http://127.0.0.1:8630/stats            # total unchanged\"\u003e\u003cpre\u003edocker compose up -d brain          \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e or: docker restart 2dph-brain-1\u003c/span\u003e\n\u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e verify: process not a zombie, leaf count unchanged\u003c/span\u003e\nps -eo pid,ppid,stat,args \u003cspan class=\"pl-k\"\u003e|\u003c/span\u003e grep -E \u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003ebrain-(serve|search)\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e\u003c/span\u003e   \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e no \u0026lt;defunct\u0026gt;\u003c/span\u003e\ncurl -sS --max-time 5 http://127.0.0.1:8630/stats            \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e total unchanged\u003c/span\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eData lives in \u003ccode\u003evar/kb.lbug\u003c/code\u003e (host path, gitignored) and survives a container\nrestart unchanged — verify with \u003ccode\u003esha256sum var/kb.lbug\u003c/code\u003e before/after. A stuck\nwrite never commits, so the on-disk DB is the last successful state. A full\nrebuild is only needed after a deliberate \u003ccode\u003e--rebuild\u003c/code\u003e or a corrupt DB, not\nafter a restart.\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eБенчмарки поиска (#202)\u003c/h2\u003e\u003ca id=\"user-content-бенчмарки-поиска-202\" class=\"anchor\" aria-label=\"Permalink: Бенчмарки поиска (#202)\" href=\"#бенчмарки-поиска-202\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eA/B-харнесс \u003ccode\u003ebin/brain/bench.go\u003c/code\u003e гоняет фиксированный golden-set запросов\n(\u003ccode\u003einternal/brain/testdata/golden-set.json\u003c/code\u003e, ~50, рус+англ, темы\nfacts/mail/docs/git/ssh) через поиск и печатает latency p50/p95/mean,\nrecall@5/@10 и CPU/RSS. \u003cstrong\u003eНикакой кандидат не принимается без замера\u003c/strong\u003e\n(эпик #201): baseline — текущий линейный скан.\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"# baseline против живого brain (HTTP MCP :8630, read-only; контейнер можно не трогать)\n./bin/brain/bench.go\n\n# machine-отчёт (для issue/CI)\n./bin/brain/bench.go --json\n\n# кандидат: исполняемый бинарь с CLI bin/brain/search.go --json -n N --no-web \u0026quot;q\u0026quot;\n./bin/brain/bench.go --candidate /path/to/candidate\n\n# кандидат: другой serve (:8631) — A/B recall vs baseline + latency ratio\n./bin/brain/bench.go --candidate http://127.0.0.1:8631\n\n# локально, без сервера: открыть var/kb.lbug read-only в процессе\n# (сначала quiesce: docker compose stop brain — single-writer!)\n./bin/brain/bench.go --inproc\n\n# локально против отдельной копии БД (напр. свежий --rebuild --db /tmp/kb.lbug)\n./bin/brain/bench.go --inproc --db /tmp/kb.lbug\n\n# ресурсы сервера: семплировать PID brain-контейнера вместо собственного\n./bin/brain/bench.go --pid $(docker inspect -f '{{.State.Pid}}' 2dph-brain-1)\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e baseline против живого brain (HTTP MCP :8630, read-only; контейнер можно не трогать)\u003c/span\u003e\n./bin/brain/bench.go\n\n\u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e machine-отчёт (для issue/CI)\u003c/span\u003e\n./bin/brain/bench.go --json\n\n\u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e кандидат: исполняемый бинарь с CLI bin/brain/search.go --json -n N --no-web \"q\"\u003c/span\u003e\n./bin/brain/bench.go --candidate /path/to/candidate\n\n\u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e кандидат: другой serve (:8631) — A/B recall vs baseline + latency ratio\u003c/span\u003e\n./bin/brain/bench.go --candidate http://127.0.0.1:8631\n\n\u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e локально, без сервера: открыть var/kb.lbug read-only в процессе\u003c/span\u003e\n\u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e (сначала quiesce: docker compose stop brain — single-writer!)\u003c/span\u003e\n./bin/brain/bench.go --inproc\n\n\u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e локально против отдельной копии БД (напр. свежий --rebuild --db /tmp/kb.lbug)\u003c/span\u003e\n./bin/brain/bench.go --inproc --db /tmp/kb.lbug\n\n\u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e ресурсы сервера: семплировать PID brain-контейнера вместо собственного\u003c/span\u003e\n./bin/brain/bench.go --pid \u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e$(\u003c/span\u003edocker inspect -f \u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e'\u003c/span\u003e{{.State.Pid}}\u003cspan class=\"pl-pds\"\u003e'\u003c/span\u003e\u003c/span\u003e 2dph-brain-1\u003cspan class=\"pl-pds\"\u003e)\u003c/span\u003e\u003c/span\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eМетрики и гейты:\u003c/p\u003e\n\u003cmarkdown-accessiblity-table\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eМетрика\u003c/th\u003e\n\u003cth\u003eСмысл\u003c/th\u003e\n\u003cth\u003eГейт\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003elatency p50/p95/mean, ms\u003c/td\u003e\n\u003ctd\u003eраспределение длительности поиска на запрос\u003c/td\u003e\n\u003ctd\u003eкандидат p50 ≤ baseline p50 × 1.5\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003erecall@5/@10 (fragment)\u003c/td\u003e\n\u003ctd\u003eдоля запросов, у которых ожидаемый фрагмент (из корпуса) в top-k\u003c/td\u003e\n\u003ctd\u003ebaseline recall@5 ≥ 0.95\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003erecall@5/@10 (vs baseline)\u003c/td\u003e\n\u003ctd\u003eкандидат не теряет известные baseline-хиты (top-k IDs)\u003c/td\u003e\n\u003ctd\u003eкандидат ≥ 0.95\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eCPU/RSS\u003c/td\u003e\n\u003ctd\u003euser+sys jiffies, RSS до/после, пик (VmHWM) из /proc\u003c/td\u003e\n\u003ctd\u003eдля сравнения при «тех же ресурсах»\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\u003c/markdown-accessiblity-table\u003e\n\u003cp dir=\"auto\"\u003eExit codes: \u003ccode\u003e0\u003c/code\u003e — гейты прошли, \u003ccode\u003e2\u003c/code\u003e — recall/ratio FAIL, \u003ccode\u003e1\u003c/code\u003e — ошибка\n(нет golden-set, не поднялся searcher, и т.п.).\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eЧтение: p50/p95 по per-query latency при \u003ccode\u003e--workers 1\u003c/code\u003e (по умолчанию) —\nчистый одиночный запрос; \u003ccode\u003e--workers N\u003c/code\u003e меряет сервер под нагрузкой.\nПовторяемость: два прогона подряд, разброс p50 \u0026lt; 10 % (линейный скан\nдетерминирован; разброс даёт только фоновая нагрузка). Baseline-эталон:\np50 ≈ 32s на 313k leafs (линейный скан, #192); после внедрения кандидата\nцель эпика p50 \u0026lt; 500ms, p95 \u0026lt; 2s.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eВ CI (GitHub, оффлайн): \u003ccode\u003e--rebuild\u003c/code\u003e корпуса → \u003ccode\u003ebench --inproc --json\u003c/code\u003e\n(та же схема, что и recall-gate \u003ccode\u003ebin/brain/eval.go\u003c/code\u003e).\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eANN-индекс в проде (#204/#206)\u003c/h3\u003e\u003ca id=\"user-content-ann-индекс-в-проде-204206\" class=\"anchor\" aria-label=\"Permalink: ANN-индекс в проде (#204/#206)\" href=\"#ann-индекс-в-проде-204206\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eВекторный поиск brain идёт через инкрементальный индекс \u003cstrong\u003eвне liblbug\u003c/strong\u003e (его\nсобственный HNSW крашится на росте графа, #192; fallback — линейный скан).\nАлгоритм: \u003cstrong\u003eIVF\u003c/strong\u003e (k-means cells + точный косинус внутри probed-кластеров,\nчистый Go, без внешних зависимостей). \u003ccode\u003ecoder/hnsw\u003c/code\u003e (HNSW) отброшен на\nэмпирике: на 313k реальных эмбеддингов из entry достижимы лишь 34% узлов\n(изоляция при вытеснении соседей) → recall@5 падает до 0.16; IVF держит\nrecall@30 ≥ 0.93 при NList=2000/NProbe=128 (замеры probe #204). Индекс\nживёт в \u003ccode\u003evar/state/vector.ann\u003c/code\u003e (+ append-only WAL \u003ccode\u003evector.ann.wal\u003c/code\u003e).\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e\u003cstrong\u003eВключён по умолчанию\u003c/strong\u003e (#206): \u003ccode\u003evector.ann.enabled: true\u003c/code\u003e — serve и CLI\nищут через ANN; при отсутствии/повреждении индекса поиск автоматически\nпадает на линейный скан (никогда не ломается). \u003ccode\u003eserve\u003c/code\u003e грузит индекс при\nстарте (warm start, без rebuild); если индекс пуст — serve отвечает через\nскан, а волна (шаг \u003ccode\u003eann-build\u003c/code\u003e) строит его.\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"# полный build из БД (~30s extract + ~4min k-means + assign на 313k, NList=2000).\n# Источник векторов: embedding-колонка когда есть (legacy/ANN-off БД); у БД\n# после --rebuild при ANN-on колонки нет (issue #248 B3) — build/upsert/ensure\n# эмбедят l.text моделью напрямую (~16k/s, +модель ~1.5GB в этом процессе).\nKB_BUFFER_POOL=4294967296 ./bin/brain/ann.go build\n\n# волна/ручной режим: build если индекса нет или он устарел (\u0026gt;10% отставания\n# по счётчику leafs), иначе инкрементальный WAL-upsert новых leafs (\u0026lt;1s, НЕ rebuild)\n./bin/brain/ann.go ensure\n\n# инкрементальный upsert новых leafs (append WAL, НЕ rebuild) — для ручных прогонов\n./bin/brain/ann.go upsert\n\n# статистика индекса\n./bin/brain/ann.go stats\n\n# CLI-поиск через ANN (fallback на скан при отсутствии индекса)\n./bin/brain/ann.go --json -n 10 \u0026quot;query\u0026quot;\n\n# HTTP-сервер поиска через ANN (для bench --candidate http://127.0.0.1:8631)\n./bin/brain/ann.go api --port 8631\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e полный build из БД (~30s extract + ~4min k-means + assign на 313k, NList=2000).\u003c/span\u003e\n\u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e Источник векторов: embedding-колонка когда есть (legacy/ANN-off БД); у БД\u003c/span\u003e\n\u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e после --rebuild при ANN-on колонки нет (issue #248 B3) — build/upsert/ensure\u003c/span\u003e\n\u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e эмбедят l.text моделью напрямую (~16k/s, +модель ~1.5GB в этом процессе).\u003c/span\u003e\nKB_BUFFER_POOL=4294967296 ./bin/brain/ann.go build\n\n\u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e волна/ручной режим: build если индекса нет или он устарел (\u0026gt;10% отставания\u003c/span\u003e\n\u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e по счётчику leafs), иначе инкрементальный WAL-upsert новых leafs (\u0026lt;1s, НЕ rebuild)\u003c/span\u003e\n./bin/brain/ann.go ensure\n\n\u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e инкрементальный upsert новых leafs (append WAL, НЕ rebuild) — для ручных прогонов\u003c/span\u003e\n./bin/brain/ann.go upsert\n\n\u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e статистика индекса\u003c/span\u003e\n./bin/brain/ann.go stats\n\n\u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e CLI-поиск через ANN (fallback на скан при отсутствии индекса)\u003c/span\u003e\n./bin/brain/ann.go --json -n 10 \u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003equery\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e\u003c/span\u003e\n\n\u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e HTTP-сервер поиска через ANN (для bench --candidate http://127.0.0.1:8631)\u003c/span\u003e\n./bin/brain/ann.go api --port 8631\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e\u003cstrong\u003eИнкрементальность\u003c/strong\u003e (критично: волна идёт каждые N минут, полный rebuild\nна каждую волну НЕДОПУСТИМ): \u003ccode\u003eensure\u003c/code\u003e сравнивает счётчик leafs в БД с\nмощностью индекса; steady-state рост (+100 писем на 313k) всегда уходит в\nWAL-upsert (\u0026lt;1s), полный build — только когда индекса нет или он отстал\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp dir=\"auto\"\u003e10% (битый/частичный/после bulk-import). Доказано тестом\n\u003ccode\u003eTestAnnEnsureIncrementalUpsertNoRebuild\u003c/code\u003e (+100 leafs → rebuild=false,\nupsert \u0026lt;1s) и замером на проде.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp dir=\"auto\"\u003eA/B (изолирует векторный слой: baseline = скан, кандидат = ANN, один\nпроцесс, одна БД):\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"# quiesce сначала (single-writer): docker compose stop brain\nKB_BUFFER_POOL=4294967296 ./bin/brain/bench.go --inproc --candidate inproc-ann\n# эталон: ./bin/brain/bench.go --inproc                 (baseline, ~26 min на 50 запросов)\n# через отдельный serve: ./bin/brain/bench.go --candidate http://127.0.0.1:8631\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e quiesce сначала (single-writer): docker compose stop brain\u003c/span\u003e\nKB_BUFFER_POOL=4294967296 ./bin/brain/bench.go --inproc --candidate inproc-ann\n\u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e эталон: ./bin/brain/bench.go --inproc                 (baseline, ~26 min на 50 запросов)\u003c/span\u003e\n\u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e через отдельный serve: ./bin/brain/bench.go --candidate http://127.0.0.1:8631\u003c/span\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e\u003cstrong\u003enprobe-дефолт = 2000\u003c/strong\u003e (решение #206, замерено на живом корпусе 313689\nвекторов, 11 запросов golden-set): полный probe даёт порядок кандидатов,\nсовпадающий с точным сканом (recall@5 vs baseline = 1.0) при p50 ~470ms\n(гейт \u0026lt;500ms); снижение до 256 ускоряет поиск (p50 ~260ms), но recall@5\nпадает до ~0.82 — теряются точные top-5. Выбор: корректность сначала,\nтюнинг (256/512) — отдельной задачей, если понадобится p50 \u0026lt; 300ms.\nКонфиг (\u003ccode\u003eetc/brain/config.yml\u003c/code\u003e, секция \u003ccode\u003evector.ann\u003c/code\u003e): \u003ccode\u003eenabled\u003c/code\u003e, \u003ccode\u003eindex\u003c/code\u003e,\n\u003ccode\u003edim\u003c/code\u003e (256), \u003ccode\u003enlist\u003c/code\u003e (2000 — k-means cells, ~150 векторов на ячейку при\n313k), \u003ccode\u003enprobe\u003c/code\u003e (2000). В compose весь \u003ccode\u003eetc/brain\u003c/code\u003e монтируется в\n\u003ccode\u003e/data/etc/brain:ro\u003c/code\u003e (repo root в контейнере — \u003ccode\u003e/data\u003c/code\u003e, #208); без mount\nserve работает на код-default (nprobe=128). Идемпотентность: повторный\n\u003ccode\u003eupsert\u003c/code\u003e не дублирует (idCell — map), повторный \u003ccode\u003ebuild\u003c/code\u003e из той же БД даёт\nту же мощность.\nA/B-отчёты: #204 (кандидат), #206 (прод, nprobe 256 vs 2000).\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eDisk mail/contacts import (#79)\u003c/h2\u003e\u003ca id=\"user-content-disk-mailcontacts-import-79\" class=\"anchor\" aria-label=\"Permalink: Disk mail/contacts import (#79)\" href=\"#disk-mailcontacts-import-79\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eSources on \u003ccode\u003e/mnt/8TB\u003c/code\u003e (TB mbox, .eml, VCF/MAB) → corpus pipeline; see\n\u003ca href=\"/eSlider/2dph/blob/main/docs/mail-sources.md\"\u003email-sources.md\u003c/a\u003e for the authoritative source table + status.\nPST stays blocked until \u003ccode\u003elibpst-utils\u003c/code\u003e (\u003ccode\u003ereadpst\u003c/code\u003e) is installed.\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"./bin/mail/convert-mbox.go --in \u0026lt;tb-root\u0026gt; --out var/corpus/mail --source tb-profile --dry-run\n./bin/mail/convert-mbox.go --in \u0026lt;tb-root\u0026gt; --out var/corpus/mail --source tb-profile\n./bin/mail/import.go --from-eml var/corpus/mail\"\u003e\u003cpre\u003e./bin/mail/convert-mbox.go --in \u003cspan class=\"pl-k\"\u003e\u0026lt;\u003c/span\u003etb-root\u003cspan class=\"pl-k\"\u003e\u0026gt;\u003c/span\u003e --out var/corpus/mail --source tb-profile --dry-run\n./bin/mail/convert-mbox.go --in \u003cspan class=\"pl-k\"\u003e\u0026lt;\u003c/span\u003etb-root\u003cspan class=\"pl-k\"\u003e\u0026gt;\u003c/span\u003e --out var/corpus/mail --source tb-profile\n./bin/mail/import.go --from-eml var/corpus/mail\u003c/pre\u003e\u003c/div\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eSync wave + OO CRM (AGENTS.md, #81)\u003c/h2\u003e\u003ca id=\"user-content-sync-wave--oo-crm-agentsmd-81\" class=\"anchor\" aria-label=\"Permalink: Sync wave + OO CRM (AGENTS.md, #81)\" href=\"#sync-wave--oo-crm-agentsmd-81\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eDeterministic ingest/reconcile wave; every step is a reconcile/upsert, so the\nwave is idempotent. \u003ccode\u003e--dry-run\u003c/code\u003e prints the fixed order without executing.\nBulk rebuild is NOT part of the wave (use \u003ccode\u003ebin/brain/index.go --rebuild\u003c/code\u003e).\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"go run ./bin/stack/sync.go --dry-run                     # print the wave\ngo run ./bin/stack/sync.go                               # default wave\ngo run ./bin/stack/sync.go --only mail,mail-import       # subset, order kept\ngo run ./bin/stack/sync.go --with-mail --only mail,mail-import,mail-index  # mail → brain index (#199)\ngo run ./bin/stack/sync.go --with-chats --contacts \u0026lt;vcf\u0026gt; # + chats, contacts\ngo run ./bin/stack/sync.go --with-chats --git-root \u0026lt;dir\u0026gt; # + chats, git history\"\u003e\u003cpre\u003ego run ./bin/stack/sync.go --dry-run                     \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e print the wave\u003c/span\u003e\ngo run ./bin/stack/sync.go                               \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e default wave\u003c/span\u003e\ngo run ./bin/stack/sync.go --only mail,mail-import       \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e subset, order kept\u003c/span\u003e\ngo run ./bin/stack/sync.go --with-mail --only mail,mail-import,mail-index  \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e mail → brain index (#199)\u003c/span\u003e\ngo run ./bin/stack/sync.go --with-chats --contacts \u003cspan class=\"pl-k\"\u003e\u0026lt;\u003c/span\u003evcf\u003cspan class=\"pl-k\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e + chats, contacts\u003c/span\u003e\ngo run ./bin/stack/sync.go --with-chats --git-root \u003cspan class=\"pl-k\"\u003e\u0026lt;\u003c/span\u003edir\u003cspan class=\"pl-k\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e + chats, git history\u003c/span\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003e--only\u003c/code\u003e takes actual step names (fixed order): \u003ccode\u003email\u003c/code\u003e, \u003ccode\u003email-import\u003c/code\u003e,\n\u003ccode\u003email-index\u003c/code\u003e, \u003ccode\u003echats\u003c/code\u003e, \u003ccode\u003econtact-brain\u003c/code\u003e, \u003ccode\u003egit-brain\u003c/code\u003e, \u003ccode\u003econtact-crm\u003c/code\u003e.\n\u003ccode\u003e--with-mail\u003c/code\u003e включает шаг \u003ccode\u003email-index\u003c/code\u003e (mail leafs обоих корпусов → brain,\n#199); без флага шаг SKIP, не FAIL.\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003echats step (--with-chats, #195)\u003c/h3\u003e\u003ca id=\"user-content-chats-step---with-chats-195\" class=\"anchor\" aria-label=\"Permalink: chats step (--with-chats, #195)\" href=\"#chats-step---with-chats-195\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eThe \u003ccode\u003echats\u003c/code\u003e step is one logical step name that runs two sub-commands\nsequentially: \u003ccode\u003ebin/chat/sync.go telegram\u003c/code\u003e, then \u003ccode\u003ebin/chat/sync.go linkedin\u003c/code\u003e.\nBoth download messages to \u003ccode\u003evar/corpus/chats/\u0026lt;platform\u0026gt;/\u003c/code\u003e.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eCredentials come from the environment:\u003c/p\u003e\n\u003cmarkdown-accessiblity-table\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eEnv var\u003c/th\u003e\n\u003cth\u003ePurpose\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eTELEGRAM_API_ID\u003c/code\u003e / \u003ccode\u003eTELEGRAM_API_HASH\u003c/code\u003e / \u003ccode\u003eTELEGRAM_PHONE\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eTelegram app credentials\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eTELEGRAM_MCP_DIR\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003epath to the telegram-mcp checkout (session read from its \u003ccode\u003e.env\u003c/code\u003e)\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eTELEGRAM_SESSION_STRING\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eoptional; otherwise read from \u003ccode\u003eTELEGRAM_MCP_DIR/.env\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eLINKEDIN_USER_DATA_DIR\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eLinkedIn profile dir with session files (default \u003ccode\u003e~/.linkedin-mcp/profile\u003c/code\u003e)\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\u003c/markdown-accessiblity-table\u003e\n\u003cp dir=\"auto\"\u003e\u003cstrong\u003eSKIP semantics\u003c/strong\u003e: a platform whose credentials/session are missing prints\n\u003ccode\u003echats: SKIP …\u003c/code\u003e and exits with code 3 (\u003ccode\u003epkg/cli.ExitSkip\u003c/code\u003e) — the wave shows\n\u003ccode\u003eSKIP\u003c/code\u003e, never \u003ccode\u003eFAIL\u003c/code\u003e. The same convention applies to \u003ccode\u003email\u003c/code\u003e sources without\ncreds (onlyoffice/m365/imap). Tools with credentials present but broken\n(config paths, invalid API id) still FAIL loudly.\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003evar/ permissions (uid 1001 vs 1000)\u003c/h3\u003e\u003ca id=\"user-content-var-permissions-uid-1001-vs-1000\" class=\"anchor\" aria-label=\"Permalink: var/ permissions (uid 1001 vs 1000)\" href=\"#var-permissions-uid-1001-vs-1000\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eThe brain/mail-sync containers write \u003ccode\u003evar/**\u003c/code\u003e as uid 1001 (Dockerfile\n\u003ccode\u003euseradd --uid 1001\u003c/code\u003e) while host tools run as uid 1000. A container write\nleaves \u003ccode\u003evar/kb.lbug.wal\u003c/code\u003e and \u003ccode\u003evar/corpus/**\u003c/code\u003e unopenable by the wave\n(\u003ccode\u003eOpenDatabase … status 1\u003c/code\u003e). Since compose.yaml now runs the containers as\nthe host uid (\u003ccode\u003euser: \"${KB_UID:-1000}:${KB_GID:-1000}\"\u003c/code\u003e), new writes align\nautomatically; a one-time repair for already-misowned files goes through\ndocker busybox (no sudo on this host):\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"scripts/stack/fix-var-perms            # chown -R var/ to the caller uid:gid\nscripts/stack/fix-var-perms --check    # report files NOT owned by the caller\"\u003e\u003cpre\u003escripts/stack/fix-var-perms            \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e chown -R var/ to the caller uid:gid\u003c/span\u003e\nscripts/stack/fix-var-perms --check    \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e report files NOT owned by the caller\u003c/span\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eRun it after any container write or when a wave step fails on \u003ccode\u003evar/**\u003c/code\u003e.\nThe wave also quiesces the compose brain container around write steps\n(\u003ccode\u003edocker compose stop brain\u003c/code\u003e → writes → \u003ccode\u003estart brain\u003c/code\u003e), so \u003ccode\u003egit-brain\u003c/code\u003e does\nnot collide with the running serve process holding \u003ccode\u003ekb.lbug\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eindex-sync — gator→graph→index cycle (#292)\u003c/h3\u003e\u003ca id=\"user-content-index-sync--gatorgraphindex-cycle-292\" class=\"anchor\" aria-label=\"Permalink: index-sync — gator→graph→index cycle (#292)\" href=\"#index-sync--gatorgraphindex-cycle-292\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eThe chain \u003ccode\u003egator sync→etl→pack → 2dph import(graph) → index(kb.lbug)\u003c/code\u003e is\nclosed by the compose service \u003cstrong\u003e\u003ccode\u003eindex-sync\u003c/code\u003e\u003c/strong\u003e (profile \u003ccode\u003eindex\u003c/code\u003e). No systemd.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eOne cycle: discover the documents hive (\u003ccode\u003e\u0026lt;hive-doc\u0026gt;/source=*/channel=*\u003c/code\u003e) →\nstop the \u003ccode\u003ebrain\u003c/code\u003e container (Ladybug is single-writer) → import the mail canon →\n\u003ccode\u003ebrain-index --skip\u003c/code\u003e (docs corpus + indexes) → (gator mode only) \u003ccode\u003email-graph --commit\u003c/code\u003e per channel (idempotent MERGE) → ANN \u003ccode\u003eensure\u003c/code\u003e → start brain and wait\nhealthy → write freshness state. A cycle is skipped when \u003ccode\u003ekb.lbug\u003c/code\u003e is already\nnewer than the newest upstream state and the last run did not error, so brain is\nnot bounced for nothing.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e\u003cstrong\u003eANN index build/maintenance (#204/#206).\u003c/strong\u003e The IVF vector index is maintained\nby \u003ccode\u003ebin/brain/ann.go ensure\u003c/code\u003e, which \u003ccode\u003eindex-sync\u003c/code\u003e runs at the end of every cycle.\nThe binary is read from \u003ccode\u003eANN_BIN\u003c/code\u003e (flag \u003ccode\u003e--ann\u003c/code\u003e wins; empty = skip). Compose\nsets \u003ccode\u003eANN_BIN=/usr/local/bin/brain-ann\u003c/code\u003e; the \u003ccode\u003eapi-build\u003c/code\u003e stage builds it with\n\u003ccode\u003e-tags system_ladybug,brain_ann\u003c/code\u003e and the \u003ccode\u003eapi\u003c/code\u003e stage copies it, so no manual\ninstall is needed. \u003ccode\u003eensure\u003c/code\u003e does a full k-means build only when the index is\nmissing or stale (\u0026gt;10% behind the DB leaf count); steady-state growth is an\nincremental WAL-upsert (\u0026lt;1s, never a rebuild). The index path is\n\u003ccode\u003evector.ann.index\u003c/code\u003e, default \u003ccode\u003e\u0026lt;root\u0026gt;/var/state/vector.ann\u003c/code\u003e (+ \u003ccode\u003e.wal\u003c/code\u003e); in the\ncontainer \u003ccode\u003eKB_ROOT=/data\u003c/code\u003e and \u003ccode\u003e./var\u003c/code\u003e is mounted at \u003ccode\u003e/data/var\u003c/code\u003e, so the index\nlands in the host \u003ccode\u003evar/state/\u003c/code\u003e on the persistent volume. Empty/absent index is\nnever fatal: serve/CLI fall back to the linear scan and \u003ccode\u003e/stats\u003c/code\u003e reports\n\u003ccode\u003eann.loaded:false\u003c/code\u003e.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e\u003cstrong\u003eMail canon — T10-A (\u003ccode\u003eMAIL_SOURCE\u003c/code\u003e, default \u003ccode\u003ecorpus\u003c/code\u003e).\u003c/strong\u003e The info-corpus mail\ncanon is the \u003cstrong\u003elocal 2dph M365 corpus\u003c/strong\u003e, not the gator \u003ccode\u003eparquet/mail\u003c/code\u003e hive: the\nEdelweiss boxes are M365/OAuth and are synced by 2dph into \u003ccode\u003evar/corpus/mail\u003c/code\u003e\n(gator's mail client is plain IMAP). Selecting the canon keeps a single source\nof Leafs (local corpus ids are content-address, gator ids are \u003ccode\u003emessage_id\u003c/code\u003e, so\nindexing both would duplicate).\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003ecorpus\u003c/code\u003e (default): \u003ccode\u003ebrain-index --with-mail\u003c/code\u003e indexes \u003ccode\u003evar/corpus/mail/m365\u003c/code\u003e\n(plus the legacy \u003ccode\u003evar/mail\u003c/code\u003e); the gator \u003ccode\u003eparquet/mail\u003c/code\u003e leaf+graph steps are\n\u003cstrong\u003eskipped\u003c/strong\u003e. \u003ccode\u003e--since YYYY-MM-DD\u003c/code\u003e (env \u003ccode\u003eMAIL_SINCE\u003c/code\u003e) bounds the corpus mail if\nset. Corpus-mail mtime drives freshness.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003egator\u003c/code\u003e: unchanged legacy path — \u003ccode\u003email-leaf\u003c/code\u003e over \u003ccode\u003eparquet/mail\u003c/code\u003e plus\n\u003ccode\u003email-graph --commit\u003c/code\u003e per channel.\u003c/li\u003e\n\u003cli\u003eThe gator \u003ccode\u003eparquet/documents\u003c/code\u003e → \u003ccode\u003edoc-leaf\u003c/code\u003e step runs in \u003cstrong\u003eboth\u003c/strong\u003e modes.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp dir=\"auto\"\u003e\u003cstrong\u003eDocument body text — T12 (\u003ccode\u003eGATOR_DOC_TEXT\u003c/code\u003e, default off).\u003c/strong\u003e \u003ccode\u003edoc-leaf\u003c/code\u003e\nindexes metadata only (\u003ccode\u003etitle\u003c/code\u003e + \u003ccode\u003efilename\u003c/code\u003e + \u003ccode\u003eoo_path\u003c/code\u003e). With the flag the PDF\nis fetched from OnlyOffice by \u003ccode\u003eoo_path\u003c/code\u003e (or a \u003ccode\u003efileid\u003c/code\u003e in \u003ccode\u003eref\u003c/code\u003e) and its text is\nappended to \u003ccode\u003eLeaf.Text\u003c/code\u003e (capped, default 256 KiB). Enable with\n\u003ccode\u003e--document-text\u003c/code\u003e on \u003ccode\u003edoc-leaf\u003c/code\u003e/\u003ccode\u003eindex-loop\u003c/code\u003e or \u003ccode\u003eGATOR_DOC_TEXT=1\u003c/code\u003e; requires\n\u003ccode\u003eONLYOFFICE_URL/USER/PASS\u003c/code\u003e and \u003ccode\u003eGATOR_DOC_ROOT_ID\u003c/code\u003e (the Files folder rooting\n\u003ccode\u003eoo_path\u003c/code\u003e; optional \u003ccode\u003eGATOR_DOC_ROOT_PREFIX\u003c/code\u003e, default \u003ccode\u003eexternal\u003c/code\u003e), plus optional\n\u003ccode\u003eGATOR_DOC_TEXT_LIMIT\u003c/code\u003e. A fetch/extract failure keeps the metadata leaf. Body\ntext changes the content hash, so the first enabled run writes a new version;\nextraction runs before the \u003ccode\u003e--skip\u003c/code\u003e dedup, so an enabled cycle re-fetches.\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"scripts/stack/start-index-sync                 # compose up -d index-sync (loop, 1h)\ndocker compose --profile index up -d index-sync\ndocker compose --profile index run --rm index-sync index-loop --once   # one manual cycle\n# one-off canon override:\ndocker compose --profile index run --rm index-sync index-loop --once --mail-source gator\ndocker compose --profile index logs -f index-sync\"\u003e\u003cpre\u003escripts/stack/start-index-sync                 \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e compose up -d index-sync (loop, 1h)\u003c/span\u003e\ndocker compose --profile index up -d index-sync\ndocker compose --profile index run --rm index-sync index-loop --once   \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e one manual cycle\u003c/span\u003e\n\u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e one-off canon override:\u003c/span\u003e\ndocker compose --profile index run --rm index-sync index-loop --once --mail-source gator\ndocker compose --profile index logs -f index-sync\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eRequirements:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003eAPI image rebuilt with \u003ccode\u003email-graph\u003c/code\u003e + \u003ccode\u003eindex-loop\u003c/code\u003e + \u003ccode\u003ebrain-ann\u003c/code\u003e\n(\u003ccode\u003edocker compose --profile index build index-sync\u003c/code\u003e); the older\n\u003ccode\u003eghcr.io/eslider/2dph:api\u003c/code\u003e lacks them. Without \u003ccode\u003ebrain-ann\u003c/code\u003e the ANN step\nis skipped and vector search uses the linear scan.\u003c/li\u003e\n\u003cli\u003eIn \u003ccode\u003ecorpus\u003c/code\u003e mode the gator mail hive is not required; \u003ccode\u003e./var\u003c/code\u003e must be mounted\nat \u003ccode\u003e/data/var\u003c/code\u003e so \u003ccode\u003evar/corpus/mail/m365\u003c/code\u003e is visible (it is).\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eGATOR_VAR_HOST=\u0026lt;gator repo\u0026gt;/var/gator\u003c/code\u003e in \u003ccode\u003e.env\u003c/code\u003e (gitignored) — the host\ngator canon is bind-mounted read-only at \u003ccode\u003e/gator\u003c/code\u003e. \u003ccode\u003eGATOR_MAIL_HIVE\u003c/code\u003e\ninside the container is \u003ccode\u003e/gator/parquet/mail\u003c/code\u003e (used only in \u003ccode\u003egator\u003c/code\u003e mode);\nthe same mount covers \u003ccode\u003eparquet/documents\u003c/code\u003e (kind=document), set as\n\u003ccode\u003eGATOR_DOCUMENTS_HIVE\u003c/code\u003e (\u003ccode\u003e/gator/parquet/documents\u003c/code\u003e). An absent/empty document\ntree is skipped.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e/var/run/docker.sock\u003c/code\u003e is mounted: the cycle stops/starts the sibling\n\u003ccode\u003ebrain\u003c/code\u003e container. \u003ccode\u003eindex-sync\u003c/code\u003e therefore has Docker control — keep it on\nthe local host only. Because the container runs with an explicit \u003ccode\u003euser:\u003c/code\u003e,\nit does not inherit the host's supplementary \u003ccode\u003edocker\u003c/code\u003e group; set\n\u003ccode\u003eDOCKER_GID\u003c/code\u003e (default 991) to the socket's group so the added \u003ccode\u003egroup_add\u003c/code\u003e\ngrants access, otherwise the quiesce step fails with\n\u003ccode\u003edial unix /var/run/docker.sock: connect: permission denied\u003c/code\u003e\n(\u003ccode\u003estat -c '%g' /var/run/docker.sock\u003c/code\u003e).\u003c/li\u003e\n\u003cli\u003eThe container runs as the host uid (\u003ccode\u003eKB_UID\u003c/code\u003e/\u003ccode\u003eKB_GID\u003c/code\u003e) so \u003ccode\u003ekb.lbug\u003c/code\u003e stays\nwritable by host tools (#195).\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp dir=\"auto\"\u003eFreshness is observable: the state file is \u003ccode\u003e\u0026lt;root\u0026gt;/var/state/index-freshness.yml\u003c/code\u003e,\n\u003ccode\u003e/stats\u003c/code\u003e returns \u003ccode\u003eindex_at\u003c/code\u003e/\u003ccode\u003eimport_at\u003c/code\u003e/\u003ccode\u003ekb_mtime\u003c/code\u003e/\u003ccode\u003epack_mtime\u003c/code\u003e/\u003ccode\u003estale\u003c/code\u003e/\n\u003ccode\u003estale_after\u003c/code\u003e, and \u003ccode\u003escripts/stack/status\u003c/code\u003e prints the same (stale from \u003ccode\u003e/stats\u003c/code\u003e\nwhen the brain answers, otherwise \u003ccode\u003eunknown\u003c/code\u003e — the on-disk state stays readable\nwhile the brain is down).\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"curl -s localhost:8630/stats | yq '.index_at,.import_at,.kb_mtime,.stale'\nscripts/stack/status | yq '.freshness'\nls -la --time-style=full-iso var/kb.lbug      # mtime newer than the gator pack\"\u003e\u003cpre\u003ecurl -s localhost:8630/stats \u003cspan class=\"pl-k\"\u003e|\u003c/span\u003e yq \u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e'\u003c/span\u003e.index_at,.import_at,.kb_mtime,.stale\u003cspan class=\"pl-pds\"\u003e'\u003c/span\u003e\u003c/span\u003e\nscripts/stack/status \u003cspan class=\"pl-k\"\u003e|\u003c/span\u003e yq \u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e'\u003c/span\u003e.freshness\u003cspan class=\"pl-pds\"\u003e'\u003c/span\u003e\u003c/span\u003e\nls -la --time-style=full-iso var/kb.lbug      \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e mtime newer than the gator pack\u003c/span\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eOnlyOffice CRM tools\u003c/h3\u003e\u003ca id=\"user-content-onlyoffice-crm-tools\" class=\"anchor\" aria-label=\"Permalink: OnlyOffice CRM tools\" href=\"#onlyoffice-crm-tools\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eOnlyOffice CRM tools need \u003ccode\u003eONLYOFFICE_URL\u003c/code\u003e/\u003ccode\u003eONLYOFFICE_USER\u003c/code\u003e/\u003ccode\u003eONLYOFFICE_PASS\u003c/code\u003e\nenv. Both are read-only (report) by default; \u003ccode\u003e--write\u003c/code\u003e applies changes.\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"# reconcile report — human mail senders vs OO CRM persons (match by email)\ngo run -tags=onlyoffice_reconcile_contact ./bin/onlyoffice/reconcile-contact.go\ngo run -tags=onlyoffice_reconcile_contact ./bin/onlyoffice/reconcile-contact.go --write --limit 200\n\n# interaction write — mail → history note on the person's opportunity\ngo run -tags=onlyoffice_import_interaction ./bin/onlyoffice/import-interaction.go\ngo run -tags=onlyoffice_import_interaction ./bin/onlyoffice/import-interaction.go --write --limit 50\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e reconcile report — human mail senders vs OO CRM persons (match by email)\u003c/span\u003e\ngo run -tags=onlyoffice_reconcile_contact ./bin/onlyoffice/reconcile-contact.go\ngo run -tags=onlyoffice_reconcile_contact ./bin/onlyoffice/reconcile-contact.go --write --limit 200\n\n\u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e interaction write — mail → history note on the person's opportunity\u003c/span\u003e\ngo run -tags=onlyoffice_import_interaction ./bin/onlyoffice/import-interaction.go\ngo run -tags=onlyoffice_import_interaction ./bin/onlyoffice/import-interaction.go --write --limit 50\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eBoth scan \u003ccode\u003evar/corpus/mail\u003c/code\u003e by default (\u003ccode\u003e--sources\u003c/code\u003e to override) and build an\nemail → person index in one pass over the whole CRM. Machine senders\n(newsletter/bounce/platform) never become contacts.\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eBrowser sync (periodic, #163)\u003c/h2\u003e\u003ca id=\"user-content-browser-sync-periodic-163\" class=\"anchor\" aria-label=\"Permalink: Browser sync (periodic, #163)\" href=\"#browser-sync-periodic-163\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003ePushes the browser-extracted corpus (\u003ccode\u003evar/corpus/{gmail,linkedin,djinni}\u003c/code\u003e) into\nthe brain as leafs every 6 h. Thorium down is tolerated: extraction is skipped\nand the last-known corpus is still ingested.\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"./bin/cron/browser-sync.go                  # one cycle, default paths\n./bin/cron/browser-sync.go --skip-extract   # no Thorium probe\n./bin/cron/browser-sync.go --interval 6h    # run continuously\nsudo scripts/browser-sync-install.sh        # install the 6-hourly systemd timer\"\u003e\u003cpre\u003e./bin/cron/browser-sync.go                  \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e one cycle, default paths\u003c/span\u003e\n./bin/cron/browser-sync.go --skip-extract   \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e no Thorium probe\u003c/span\u003e\n./bin/cron/browser-sync.go --interval 6h    \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e run continuously\u003c/span\u003e\nsudo scripts/browser-sync-install.sh        \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e install the 6-hourly systemd timer\u003c/span\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eLog: \u003ccode\u003evar/log/browser-sync.log\u003c/code\u003e. The systemd units\n(\u003ccode\u003escripts/browser-sync.{service,timer}\u003c/code\u003e) use an \u003ccode\u003e@REPO_ROOT@\u003c/code\u003e placeholder; the\ninstall script substitutes the real root at install time.\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eReasoner (optional, D18)\u003c/h2\u003e\u003ca id=\"user-content-reasoner-optional-d18\" class=\"anchor\" aria-label=\"Permalink: Reasoner (optional, D18)\" href=\"#reasoner-optional-d18\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eCPU sidecar on \u003ccode\u003e127.0.0.1:11435\u003c/code\u003e. Weights are not in the 2dph image.\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"docker compose --profile reasoner up -d reasoner\nREASONER_BASE_URL=http://127.0.0.1:11435/v1 ./bin/reasoner/bench.go --json\"\u003e\u003cpre\u003edocker compose --profile reasoner up -d reasoner\nREASONER_BASE_URL=http://127.0.0.1:11435/v1 ./bin/reasoner/bench.go --json\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eSee \u003ca href=\"/eSlider/2dph/blob/main/docs/reasoner.md\"\u003ereasoner.md\u003c/a\u003e.\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eGitHub mirror sync (#194)\u003c/h2\u003e\u003ca id=\"user-content-github-mirror-sync-194\" class=\"anchor\" aria-label=\"Permalink: GitHub mirror sync (#194)\" href=\"#github-mirror-sync-194\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eGitea is the canonical home; GitHub is a publish-only \u003cstrong\u003eshowcase\u003c/strong\u003e (\u003ccode\u003e#190\u003c/code\u003e,\n\u003ccode\u003e#213\u003c/code\u003e). \u003ccode\u003emain\u003c/code\u003e is the single working line and the public face (Gitea \u003ccode\u003e#213\u003c/code\u003e).\nPRs merge into \u003ccode\u003emain\u003c/code\u003e directly; no \u003ccode\u003erelease/v1\u003c/code\u003e branch is kept. After each\nmerge into \u003ccode\u003emain\u003c/code\u003e, sync \u003cstrong\u003eonly \u003ccode\u003emain\u003c/code\u003e\u003c/strong\u003e (plus tags) to GitHub:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"git fetch origin --tags --force\ngit push github main:main --force-with-lease\ngit push github --tags --force\"\u003e\u003cpre\u003egit fetch origin --tags --force\ngit push github main:main --force-with-lease\ngit push github --tags --force\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eVerify zero drift on \u003ccode\u003emain\u003c/code\u003e:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"git rev-list --count origin/main..github/main   # 0\ngit rev-list --count github/main..origin/main   # 0\"\u003e\u003cpre\u003egit rev-list --count origin/main..github/main   \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e 0\u003c/span\u003e\ngit rev-list --count github/main..origin/main   \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e 0\u003c/span\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eBranch policy (\u003ccode\u003e#213\u003c/code\u003e): both Gitea and GitHub keep \u003ccode\u003emain\u003c/code\u003e only. Old/merged\nfeature branches are backed up locally under \u003ccode\u003erefs/backup/*\u003c/code\u003e before deletion.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eContent guard (\u003ccode\u003e#190\u003c/code\u003e): before pushing to GitHub, confirm the diff is project\ncontent only — no internal paths/hosts/ports, no secrets. Precedent #142/#194:\nleaks are purged from GitHub history with \u003ccode\u003egit filter-repo\u003c/code\u003e; Gitea stays the\ncanonical home and is never rewritten for the mirror.\u003c/p\u003e\n\u003c/article\u003e","richTextTruncated":false,"renderedFileInfo":null,"symbols":{"timed_out":false,"not_analyzed":false,"symbols":[{"name":"Run 2dph (portable)","fully_qualified_name":"Run 2dph (portable)","kind":"section_1","ident_start":79,"ident_end":98,"extent_start":77,"extent_end":26090,"ident_utf16":{"start":{"line_number":8,"utf16_col":2},"end":{"line_number":8,"utf16_col":21}},"extent_utf16":{"start":{"line_number":8,"utf16_col":0},"end":{"line_number":507,"utf16_col":0}}},{"name":"Toolchain","fully_qualified_name":"Toolchain","kind":"section_2","ident_start":210,"ident_end":219,"extent_start":207,"extent_end":1211,"ident_utf16":{"start":{"line_number":13,"utf16_col":3},"end":{"line_number":13,"utf16_col":12}},"extent_utf16":{"start":{"line_number":13,"utf16_col":0},"end":{"line_number":32,"utf16_col":0}}},{"name":"Config","fully_qualified_name":"Config","kind":"section_2","ident_start":1214,"ident_end":1220,"extent_start":1211,"extent_end":1768,"ident_utf16":{"start":{"line_number":32,"utf16_col":3},"end":{"line_number":32,"utf16_col":9}},"extent_utf16":{"start":{"line_number":32,"utf16_col":0},"end":{"line_number":48,"utf16_col":0}}},{"name":"Index then search","fully_qualified_name":"Index then search","kind":"section_2","ident_start":1771,"ident_end":1788,"extent_start":1768,"extent_end":2891,"ident_utf16":{"start":{"line_number":48,"utf16_col":3},"end":{"line_number":48,"utf16_col":20}},"extent_utf16":{"start":{"line_number":48,"utf16_col":0},"end":{"line_number":73,"utf16_col":0}}},{"name":"HTTP / MCP","fully_qualified_name":"HTTP / MCP","kind":"section_2","ident_start":2894,"ident_end":2904,"extent_start":2891,"extent_end":5401,"ident_utf16":{"start":{"line_number":73,"utf16_col":3},"end":{"line_number":73,"utf16_col":13}},"extent_utf16":{"start":{"line_number":73,"utf16_col":0},"end":{"line_number":133,"utf16_col":0}}},{"name":"Brain health-check \u0026 restart (#180)","fully_qualified_name":"Brain health-check \u0026 restart (#180)","kind":"section_3","ident_start":3813,"ident_end":3848,"extent_start":3809,"extent_end":5401,"ident_utf16":{"start":{"line_number":96,"utf16_col":4},"end":{"line_number":96,"utf16_col":39}},"extent_utf16":{"start":{"line_number":96,"utf16_col":0},"end":{"line_number":133,"utf16_col":0}}},{"name":"Бенчмарки поиска (#202)","fully_qualified_name":"Бенчмарки поиска (#202)","kind":"section_2","ident_start":5404,"ident_end":5442,"extent_start":5401,"extent_end":13856,"ident_utf16":{"start":{"line_number":133,"utf16_col":3},"end":{"line_number":133,"utf16_col":26}},"extent_utf16":{"start":{"line_number":133,"utf16_col":0},"end":{"line_number":261,"utf16_col":0}}},{"name":"ANN-индекс в проде (#204/#206)","fully_qualified_name":"ANN-индекс в проде (#204/#206)","kind":"section_3","ident_start":8657,"ident_end":8699,"extent_start":8653,"extent_end":13856,"ident_utf16":{"start":{"line_number":187,"utf16_col":4},"end":{"line_number":187,"utf16_col":34}},"extent_utf16":{"start":{"line_number":187,"utf16_col":0},"end":{"line_number":261,"utf16_col":0}}},{"name":"Disk mail/contacts import (#79)","fully_qualified_name":"Disk mail/contacts import (#79)","kind":"section_2","ident_start":13859,"ident_end":13890,"extent_start":13856,"extent_end":14349,"ident_utf16":{"start":{"line_number":261,"utf16_col":3},"end":{"line_number":261,"utf16_col":34}},"extent_utf16":{"start":{"line_number":261,"utf16_col":0},"end":{"line_number":273,"utf16_col":0}}},{"name":"Sync wave + OO CRM (AGENTS.md, #81)","fully_qualified_name":"Sync wave + OO CRM (AGENTS.md, #81)","kind":"section_2","ident_start":14352,"ident_end":14387,"extent_start":14349,"extent_end":24066,"ident_utf16":{"start":{"line_number":273,"utf16_col":3},"end":{"line_number":273,"utf16_col":38}},"extent_utf16":{"start":{"line_number":273,"utf16_col":0},"end":{"line_number":452,"utf16_col":0}}},{"name":"chats step (--with-chats, #195)","fully_qualified_name":"chats step (--with-chats, #195)","kind":"section_3","ident_start":15411,"ident_end":15442,"extent_start":15407,"extent_end":16462,"ident_utf16":{"start":{"line_number":293,"utf16_col":4},"end":{"line_number":293,"utf16_col":35}},"extent_utf16":{"start":{"line_number":293,"utf16_col":0},"end":{"line_number":314,"utf16_col":0}}},{"name":"var/ permissions (uid 1001 vs 1000)","fully_qualified_name":"var/ permissions (uid 1001 vs 1000)","kind":"section_3","ident_start":16466,"ident_end":16501,"extent_start":16462,"extent_end":17433,"ident_utf16":{"start":{"line_number":314,"utf16_col":4},"end":{"line_number":314,"utf16_col":39}},"extent_utf16":{"start":{"line_number":314,"utf16_col":0},"end":{"line_number":334,"utf16_col":0}}},{"name":"index-sync — gator→graph→index cycle (#292)","fully_qualified_name":"index-sync — gator→graph→index cycle (#292)","kind":"section_3","ident_start":17437,"ident_end":17486,"extent_start":17433,"extent_end":23158,"ident_utf16":{"start":{"line_number":334,"utf16_col":4},"end":{"line_number":334,"utf16_col":47}},"extent_utf16":{"start":{"line_number":334,"utf16_col":0},"end":{"line_number":433,"utf16_col":0}}},{"name":"OnlyOffice CRM tools","fully_qualified_name":"OnlyOffice CRM tools","kind":"section_3","ident_start":23162,"ident_end":23182,"extent_start":23158,"extent_end":24066,"ident_utf16":{"start":{"line_number":433,"utf16_col":4},"end":{"line_number":433,"utf16_col":24}},"extent_utf16":{"start":{"line_number":433,"utf16_col":0},"end":{"line_number":452,"utf16_col":0}}},{"name":"Browser sync (periodic, #163)","fully_qualified_name":"Browser sync (periodic, #163)","kind":"section_2","ident_start":24069,"ident_end":24098,"extent_start":24066,"extent_end":24785,"ident_utf16":{"start":{"line_number":452,"utf16_col":3},"end":{"line_number":452,"utf16_col":32}},"extent_utf16":{"start":{"line_number":452,"utf16_col":0},"end":{"line_number":469,"utf16_col":0}}},{"name":"Reasoner (optional, D18)","fully_qualified_name":"Reasoner (optional, D18)","kind":"section_2","ident_start":24788,"ident_end":24812,"extent_start":24785,"extent_end":25054,"ident_utf16":{"start":{"line_number":469,"utf16_col":3},"end":{"line_number":469,"utf16_col":27}},"extent_utf16":{"start":{"line_number":469,"utf16_col":0},"end":{"line_number":480,"utf16_col":0}}},{"name":"GitHub mirror sync (#194)","fully_qualified_name":"GitHub mirror sync (#194)","kind":"section_2","ident_start":25057,"ident_end":25082,"extent_start":25054,"extent_end":26090,"ident_utf16":{"start":{"line_number":480,"utf16_col":3},"end":{"line_number":480,"utf16_col":28}},"extent_utf16":{"start":{"line_number":480,"utf16_col":0},"end":{"line_number":507,"utf16_col":0}}}]}},"codeViewLayoutRoute":{"repo":{"id":1330190220,"defaultBranch":"main","name":"2dph","ownerLogin":"eSlider","currentUserCanPush":false,"isFork":false,"isEmpty":false,"createdAt":"2026-08-10T19:42:33.000Z","ownerAvatar":"https://avatars.githubusercontent.com/u/1188335?v=4","public":true,"private":false,"isOrgOwned":false,"isArchived":false},"currentUser":null,"uploadToken":"tEXLYEnB0vgN0R8qV_CYtuXTAQ5pgNVweqtD-yDcgyh2qa1oWztyL-XKZqoVf03LmjpMDzwKEvA_he0D0uDEUg","allShortcutsEnabled":false,"treeExpanded":true,"path":"docs/runbook.md","symbolsExpanded":false,"refInfo":{"name":"main","listCacheKey":"v0:1790197666.0","canEdit":false,"currentOid":"19912d995208769bdc7a35dbcb356daff215660f"},"helpUrl":"https://docs.github.com","githubDevUrl":null},"codeViewFileTreeLayoutRoute":{"fileTree":{"docs":{"items":[{"name":"brain","path":"docs/brain","contentType":"directory"},{"name":"facts","path":"docs/facts","contentType":"directory"},{"name":"rules","path":"docs/rules","contentType":"directory"},{"name":"README.md","path":"docs/README.md","contentType":"file"},{"name":"brain-synapse.md","path":"docs/brain-synapse.md","contentType":"file"},{"name":"design.md","path":"docs/design.md","contentType":"file"},{"name":"mail-sources.md","path":"docs/mail-sources.md","contentType":"file"},{"name":"picoclaw.md","path":"docs/picoclaw.md","contentType":"file"},{"name":"reasoner.md","path":"docs/reasoner.md","contentType":"file"},{"name":"research-docpipe.md","path":"docs/research-docpipe.md","contentType":"file"},{"name":"research-liteparse.md","path":"docs/research-liteparse.md","contentType":"file"},{"name":"roadmap.md","path":"docs/roadmap.md","contentType":"file"},{"name":"runbook.md","path":"docs/runbook.md","contentType":"file"}],"totalCount":13},"":{"items":[{"name":".github","path":".github","contentType":"directory"},{"name":"bin","path":"bin","contentType":"directory"},{"name":"deploy","path":"deploy","contentType":"directory"},{"name":"docs","path":"docs","contentType":"directory"},{"name":"etc","path":"etc","contentType":"directory"},{"name":"internal","path":"internal","contentType":"directory"},{"name":"pkg","path":"pkg","contentType":"directory"},{"name":"scripts","path":"scripts","contentType":"directory"},{"name":"skills","path":"skills","contentType":"directory"},{"name":"test","path":"test","contentType":"directory"},{"name":".dockerignore","path":".dockerignore","contentType":"file"},{"name":".gitignore","path":".gitignore","contentType":"file"},{"name":".gitleaks.toml","path":".gitleaks.toml","contentType":"file"},{"name":"AGENTS.md","path":"AGENTS.md","contentType":"file"},{"name":"Dockerfile","path":"Dockerfile","contentType":"file"},{"name":"Dockerfile.liteparse","path":"Dockerfile.liteparse","contentType":"file"},{"name":"LICENSE","path":"LICENSE","contentType":"file"},{"name":"PERF.md","path":"PERF.md","contentType":"file"},{"name":"PLAN.md","path":"PLAN.md","contentType":"file"},{"name":"README.md","path":"README.md","contentType":"file"},{"name":"compose.yaml","path":"compose.yaml","contentType":"file"},{"name":"go.mod","path":"go.mod","contentType":"file"},{"name":"go.sum","path":"go.sum","contentType":"file"},{"name":"go.work","path":"go.work","contentType":"file"},{"name":"go.work.sum","path":"go.work.sum","contentType":"file"}],"totalCount":25}},"fileTreeProcessingTime":18.408676,"foldersToFetch":[]},"codeViewBlobLayoutRoute":{"codeLineWrapEnabled":false,"refInfo":{"name":"main","listCacheKey":"v0:1790197666.0","canEdit":false,"refType":"branch","currentOid":"19912d995208769bdc7a35dbcb356daff215660f","canEditOnDefaultBranch":false,"fileExistsOnDefault":true},"path":"docs/runbook.md","blob":{"copilotSWEAgentEnabled":false,"dependabotInfo":{"showConfigurationBanner":false,"configFilePath":null,"networkDependabotPath":"/eSlider/2dph/network/updates","dismissConfigurationNoticePath":"/settings/dismiss-notice/dependabot_configuration_notice","configurationNoticeDismissed":null},"displayName":"runbook.md","displayUrl":"https://github.com/eSlider/2dph/blob/main/docs/runbook.md?raw=true","headerInfo":{"blobSize":"25.5 KB","deleteTooltip":"You must be signed in to make or propose changes","editTooltip":"You must be signed in to make or propose changes","ghDesktopPath":"https://desktop.github.com","isGitLfs":false,"onBranch":true,"shortPath":"9c5cfbe","siteNavLoginPath":"/login?return_to=https%3A%2F%2Fgithub.com%2FeSlider%2F2dph%2Fblob%2Fmain%2Fdocs%2Frunbook.md","isCSV":false,"isRichtext":true,"lineInfo":{"truncatedLoc":"507","truncatedSloc":"404"},"mode":"file"},"image":false,"isCodeownersFile":null,"isPlain":false,"isValidLegacyIssueTemplate":false,"isIssueTemplate":false,"isDiscussionTemplate":false,"language":"Markdown","languageID":222,"large":false,"planSupportInfo":{"repoIsFork":null,"repoOwnedByCurrentUser":null,"requestFullPath":"/eSlider/2dph/blob/main/docs/runbook.md","showFreeOrgGatedFeatureMessage":null,"showPlanSupportBanner":null,"upgradeDataAttributes":null,"upgradePath":null},"publishBannersInfo":{"dismissActionNoticePath":"/settings/dismiss-notice/publish_action_from_dockerfile","releasePath":"/eSlider/2dph/releases/new?marketplace=true","showPublishActionBanner":false},"rawBlobUrl":"https://github.com/eSlider/2dph/raw/refs/heads/main/docs/runbook.md","renderImageOrRaw":false,"shortPath":null,"symbolsEnabled":true,"tabSize":4,"topBannersInfo":{"overridingGlobalFundingFile":false,"globalPreferredFundingPath":null,"showInvalidCitationWarning":false,"citationHelpUrl":"https://docs.github.com/github/creating-cloning-and-archiving-repositories/creating-a-repository-on-github/about-citation-files","actionsOnboardingTip":null},"truncated":false,"viewable":true,"workflowRedirectUrl":null},"copilotInfo":null,"copilotAccessAllowed":false,"copilotSpacesEnabled":false,"modelsAccessAllowed":false,"modelsRepoIntegrationEnabled":false,"isMarketplaceEnabled":true},"codeViewBlobLayoutRoute.StyledBlob":{"rawLines":["---","type: howto","status: current","related:","  - docs/README.md","  - PLAN.md","---","","# Run 2dph (portable)","","No laptop-absolute paths. Config lives in env files under `$HOME/.config/brain/`","(mode 0600), not in git.","","## Toolchain","","- Go (see `go.mod`)","- Optional: Docker, Zig CGO via `bin/cgo/zig` (not gcc)","- Optional: poppler (`pdftotext`/`pdftoppm`) + tesseract `eng+deu` for mail OCR","- Optional: ghostscript `gs` — normalizes export-locked / oversized PDFs before","  extraction (strips export-protection, shrinks; original preserved, artifact","  in `var/tmp`). Clean PDFs skip it, so the pdftotext fast path stays fast.","- Optional: **liteparse service** (epic #219) — документы → структурированный","  JSON/YAML. `docker compose up -d liteparse` (демон с warmup), вызов через","  `docker compose exec -T liteparse lit parse ...` (~100ms против ~700ms","  `docker run`). Инструменты: `bin/research/convert.go` (→ `var/struct-data/\u003csha256\u003e.yml`,","  idempotent), `bin/research/bench.go` (p95). Детали: `docs/research-liteparse.md`.","","```bash","eval \"$(bin/cgo/zig env)\"   # optional; bin/brain/{search,get,stats,eval,serve}.go auto-wrap zig","go test ./...","```","","## Config","","| File / env | Purpose |","|------------|---------|","| `$BRAIN_SEARCH_ENV` (default `$HOME/.config/brain/search.env`) | `BRAIN_SEARCH_URL` (SearXNG). Optional Basic Auth. |","| `$HOME/.config/brain/db-profiles.yml` | read-only Postgres profiles (OnlyOffice via tunnel) |","","If the host already runs SearXNG, point `BRAIN_SEARCH_URL` at it. Do not start","a second copy (D3). Optional Compose instance:","","```bash","SEARXNG_SECRET=$(openssl rand -hex 32) docker compose --profile searxng up -d","```","","That binds `127.0.0.1:8888`. JSON format must stay enabled.","","## Index then search","","Write path is `bin/brain/add.go` for a leaf (or `POST /ingest`). Bulk","corpus rebuild remains `bin/brain/index.go --rebuild` (Compose profile","`index`). Do not DROP INDEX on Ladybug 0.19.","","```bash","bin/brain/add.go --text \"arc-1 runs Matrix\" --root facts --source \"compose.yml x docker ps\"","bin/brain/index.go --rebuild --with-facts --with-chats","bin/brain/search.go \"LadybugDB vector index\"     # facts → info → web (D17)","bin/brain/search.go \"upstream flag\" --no-web","bin/brain/search.go \"who works where\" --as-of 2025-01-01  # D24 intervals","source \u003c(./bin/shell/complete.go bash)            # D23 flaggy complete","bin/brain/get.go \u003cid\u003e --body","bin/brain/stats.go","```","","`--hop N` walks File → Commit → Person from each hit. `--as-of YYYY-MM-DD`","keeps leafs whose `valid_from`/`valid_to` cover that day (empty interval =","legacy always-on). Empty web results are `throttled`, not absence.","Gap to v1: [roadmap](roadmap.md) / [epic #16](https://git.produktor.io/eSlider/2dph/issues/16).","","Ladybug 0.19: never `DROP INDEX` FTS/VECTOR (ghost catalog). Fresh indexes =","delete `var/kb.lbug` then `--rebuild`.","","## HTTP / MCP","","```bash","scripts/stack/start                 # brain :8630, wait until MCP search/get/audit","scripts/stack/status                # YAML: brain / reasoner / picoclaw / mail_sync","scripts/stack/start-mail-sync       # compose ETL: OO+Gmail sync→import (300s; no auto-rebuild)","scripts/stack/start-assistant       # + qwen3.5:9b + PicoClaw agent (ask the brain)","scripts/stack/start-assistant --no-attach","scripts/stack/stop                  # compose stop; volumes kept","```","","Same Compose services by hand:","","```bash","docker compose up -d brain                       # :8630 Zig CGO serve","docker compose up -d mail-sync                   # ETL loop into kb-var","docker compose --profile index run --rm index    # rebuild","docker compose --profile picoclaw up brain-mcp   # MCP 127.0.0.1:8630","```","","`GET /openapi.json`, `POST /mcp`. Agent tool order: `search` → `get` → `audit`.","See [picoclaw.md](picoclaw.md).","","### Brain health-check \u0026 restart (#180)","","The brain wedged once (2026-08-24): a `/ingest` write blocked forever inside","the Ladybug C layer, holding the read/write lock, so every search/get/audit","hung while `/health` still answered `ok` (it never touches the DB). The static","healthcheck let the container sit \"healthy\" but unresponsive until a manual","restart.","","**Health-check** — probe the DB read path, not `/health`:","","```bash","# fast (healthy): a real MATCH over kb.lbug","curl -sS --max-time 5 http://127.0.0.1:8630/stats   # -\u003e {\"total\":9204,\"by_root\":{...}}","# live check of every agent tool","curl -sS --max-time 10 \"http://127.0.0.1:8630/search?q=test\u0026n=1\u0026noweb=1\"","```","","`/stats` returns quickly only when the read handle is healthy; a wedged brain","times out. The compose `brain` healthcheck uses `/stats` for exactly this","reason (see `compose.yaml`), so docker auto-restarts a deadlocked container","(`restart: unless-stopped`) instead of leaving it \"healthy\".","","**Restart**:","","```bash","docker compose up -d brain          # or: docker restart 2dph-brain-1","# verify: process not a zombie, leaf count unchanged","ps -eo pid,ppid,stat,args | grep -E \"brain-(serve|search)\"   # no \u003cdefunct\u003e","curl -sS --max-time 5 http://127.0.0.1:8630/stats            # total unchanged","```","","Data lives in `var/kb.lbug` (host path, gitignored) and survives a container","restart unchanged — verify with `sha256sum var/kb.lbug` before/after. A stuck","write never commits, so the on-disk DB is the last successful state. A full","rebuild is only needed after a deliberate `--rebuild` or a corrupt DB, not","after a restart.","","## Бенчмарки поиска (#202)","","A/B-харнесс `bin/brain/bench.go` гоняет фиксированный golden-set запросов","(`internal/brain/testdata/golden-set.json`, ~50, рус+англ, темы","facts/mail/docs/git/ssh) через поиск и печатает latency p50/p95/mean,","recall@5/@10 и CPU/RSS. **Никакой кандидат не принимается без замера**","(эпик #201): baseline — текущий линейный скан.","","```bash","# baseline против живого brain (HTTP MCP :8630, read-only; контейнер можно не трогать)","./bin/brain/bench.go","","# machine-отчёт (для issue/CI)","./bin/brain/bench.go --json","","# кандидат: исполняемый бинарь с CLI bin/brain/search.go --json -n N --no-web \"q\"","./bin/brain/bench.go --candidate /path/to/candidate","","# кандидат: другой serve (:8631) — A/B recall vs baseline + latency ratio","./bin/brain/bench.go --candidate http://127.0.0.1:8631","","# локально, без сервера: открыть var/kb.lbug read-only в процессе","# (сначала quiesce: docker compose stop brain — single-writer!)","./bin/brain/bench.go --inproc","","# локально против отдельной копии БД (напр. свежий --rebuild --db /tmp/kb.lbug)","./bin/brain/bench.go --inproc --db /tmp/kb.lbug","","# ресурсы сервера: семплировать PID brain-контейнера вместо собственного","./bin/brain/bench.go --pid $(docker inspect -f '{{.State.Pid}}' 2dph-brain-1)","```","","Метрики и гейты:","","| Метрика | Смысл | Гейт |","|---------|-------|------|","| latency p50/p95/mean, ms | распределение длительности поиска на запрос | кандидат p50 ≤ baseline p50 × 1.5 |","| recall@5/@10 (fragment) | доля запросов, у которых ожидаемый фрагмент (из корпуса) в top-k | baseline recall@5 ≥ 0.95 |","| recall@5/@10 (vs baseline) | кандидат не теряет известные baseline-хиты (top-k IDs) | кандидат ≥ 0.95 |","| CPU/RSS | user+sys jiffies, RSS до/после, пик (VmHWM) из /proc | для сравнения при «тех же ресурсах» |","","Exit codes: `0` — гейты прошли, `2` — recall/ratio FAIL, `1` — ошибка","(нет golden-set, не поднялся searcher, и т.п.).","","Чтение: p50/p95 по per-query latency при `--workers 1` (по умолчанию) —","чистый одиночный запрос; `--workers N` меряет сервер под нагрузкой.","Повторяемость: два прогона подряд, разброс p50 \u003c 10 % (линейный скан","детерминирован; разброс даёт только фоновая нагрузка). Baseline-эталон:","p50 ≈ 32s на 313k leafs (линейный скан, #192); после внедрения кандидата","цель эпика p50 \u003c 500ms, p95 \u003c 2s.","","В CI (GitHub, оффлайн): `--rebuild` корпуса → `bench --inproc --json`","(та же схема, что и recall-gate `bin/brain/eval.go`).","","### ANN-индекс в проде (#204/#206)","","Векторный поиск brain идёт через инкрементальный индекс **вне liblbug** (его","собственный HNSW крашится на росте графа, #192; fallback — линейный скан).","Алгоритм: **IVF** (k-means cells + точный косинус внутри probed-кластеров,","чистый Go, без внешних зависимостей). `coder/hnsw` (HNSW) отброшен на","эмпирике: на 313k реальных эмбеддингов из entry достижимы лишь 34% узлов","(изоляция при вытеснении соседей) → recall@5 падает до 0.16; IVF держит","recall@30 ≥ 0.93 при NList=2000/NProbe=128 (замеры probe #204). Индекс","живёт в `var/state/vector.ann` (+ append-only WAL `vector.ann.wal`).","","**Включён по умолчанию** (#206): `vector.ann.enabled: true` — serve и CLI","ищут через ANN; при отсутствии/повреждении индекса поиск автоматически","падает на линейный скан (никогда не ломается). `serve` грузит индекс при","старте (warm start, без rebuild); если индекс пуст — serve отвечает через","скан, а волна (шаг `ann-build`) строит его.","","```bash","# полный build из БД (~30s extract + ~4min k-means + assign на 313k, NList=2000).","# Источник векторов: embedding-колонка когда есть (legacy/ANN-off БД); у БД","# после --rebuild при ANN-on колонки нет (issue #248 B3) — build/upsert/ensure","# эмбедят l.text моделью напрямую (~16k/s, +модель ~1.5GB в этом процессе).","KB_BUFFER_POOL=4294967296 ./bin/brain/ann.go build","","# волна/ручной режим: build если индекса нет или он устарел (\u003e10% отставания","# по счётчику leafs), иначе инкрементальный WAL-upsert новых leafs (\u003c1s, НЕ rebuild)","./bin/brain/ann.go ensure","","# инкрементальный upsert новых leafs (append WAL, НЕ rebuild) — для ручных прогонов","./bin/brain/ann.go upsert","","# статистика индекса","./bin/brain/ann.go stats","","# CLI-поиск через ANN (fallback на скан при отсутствии индекса)","./bin/brain/ann.go --json -n 10 \"query\"","","# HTTP-сервер поиска через ANN (для bench --candidate http://127.0.0.1:8631)","./bin/brain/ann.go api --port 8631","```","","**Инкрементальность** (критично: волна идёт каждые N минут, полный rebuild","на каждую волну НЕДОПУСТИМ): `ensure` сравнивает счётчик leafs в БД с","мощностью индекса; steady-state рост (+100 писем на 313k) всегда уходит в","WAL-upsert (\u003c1s), полный build — только когда индекса нет или он отстал","\u003e10% (битый/частичный/после bulk-import). Доказано тестом","`TestAnnEnsureIncrementalUpsertNoRebuild` (+100 leafs → rebuild=false,","upsert \u003c1s) и замером на проде.","","A/B (изолирует векторный слой: baseline = скан, кандидат = ANN, один","процесс, одна БД):","","```bash","# quiesce сначала (single-writer): docker compose stop brain","KB_BUFFER_POOL=4294967296 ./bin/brain/bench.go --inproc --candidate inproc-ann","# эталон: ./bin/brain/bench.go --inproc                 (baseline, ~26 min на 50 запросов)","# через отдельный serve: ./bin/brain/bench.go --candidate http://127.0.0.1:8631","```","","**nprobe-дефолт = 2000** (решение #206, замерено на живом корпусе 313689","векторов, 11 запросов golden-set): полный probe даёт порядок кандидатов,","совпадающий с точным сканом (recall@5 vs baseline = 1.0) при p50 ~470ms","(гейт \u003c500ms); снижение до 256 ускоряет поиск (p50 ~260ms), но recall@5","падает до ~0.82 — теряются точные top-5. Выбор: корректность сначала,","тюнинг (256/512) — отдельной задачей, если понадобится p50 \u003c 300ms.","Конфиг (`etc/brain/config.yml`, секция `vector.ann`): `enabled`, `index`,","`dim` (256), `nlist` (2000 — k-means cells, ~150 векторов на ячейку при","313k), `nprobe` (2000). В compose весь `etc/brain` монтируется в","`/data/etc/brain:ro` (repo root в контейнере — `/data`, #208); без mount","serve работает на код-default (nprobe=128). Идемпотентность: повторный","`upsert` не дублирует (idCell — map), повторный `build` из той же БД даёт","ту же мощность.","A/B-отчёты: #204 (кандидат), #206 (прод, nprobe 256 vs 2000).","","## Disk mail/contacts import (#79)","","Sources on `/mnt/8TB` (TB mbox, .eml, VCF/MAB) → corpus pipeline; see","[mail-sources.md](mail-sources.md) for the authoritative source table + status.","PST stays blocked until `libpst-utils` (`readpst`) is installed.","","```bash","./bin/mail/convert-mbox.go --in \u003ctb-root\u003e --out var/corpus/mail --source tb-profile --dry-run","./bin/mail/convert-mbox.go --in \u003ctb-root\u003e --out var/corpus/mail --source tb-profile","./bin/mail/import.go --from-eml var/corpus/mail","```","","## Sync wave + OO CRM (AGENTS.md, #81)","","Deterministic ingest/reconcile wave; every step is a reconcile/upsert, so the","wave is idempotent. `--dry-run` prints the fixed order without executing.","Bulk rebuild is NOT part of the wave (use `bin/brain/index.go --rebuild`).","","```bash","go run ./bin/stack/sync.go --dry-run                     # print the wave","go run ./bin/stack/sync.go                               # default wave","go run ./bin/stack/sync.go --only mail,mail-import       # subset, order kept","go run ./bin/stack/sync.go --with-mail --only mail,mail-import,mail-index  # mail → brain index (#199)","go run ./bin/stack/sync.go --with-chats --contacts \u003cvcf\u003e # + chats, contacts","go run ./bin/stack/sync.go --with-chats --git-root \u003cdir\u003e # + chats, git history","```","","`--only` takes actual step names (fixed order): `mail`, `mail-import`,","`mail-index`, `chats`, `contact-brain`, `git-brain`, `contact-crm`.","`--with-mail` включает шаг `mail-index` (mail leafs обоих корпусов → brain,","#199); без флага шаг SKIP, не FAIL.","","### chats step (--with-chats, #195)","","The `chats` step is one logical step name that runs two sub-commands","sequentially: `bin/chat/sync.go telegram`, then `bin/chat/sync.go linkedin`.","Both download messages to `var/corpus/chats/\u003cplatform\u003e/`.","","Credentials come from the environment:","","| Env var | Purpose |","|---------|---------|","| `TELEGRAM_API_ID` / `TELEGRAM_API_HASH` / `TELEGRAM_PHONE` | Telegram app credentials |","| `TELEGRAM_MCP_DIR` | path to the telegram-mcp checkout (session read from its `.env`) |","| `TELEGRAM_SESSION_STRING` | optional; otherwise read from `TELEGRAM_MCP_DIR/.env` |","| `LINKEDIN_USER_DATA_DIR` | LinkedIn profile dir with session files (default `~/.linkedin-mcp/profile`) |","","**SKIP semantics**: a platform whose credentials/session are missing prints","`chats: SKIP …` and exits with code 3 (`pkg/cli.ExitSkip`) — the wave shows","`SKIP`, never `FAIL`. The same convention applies to `mail` sources without","creds (onlyoffice/m365/imap). Tools with credentials present but broken","(config paths, invalid API id) still FAIL loudly.","","### var/ permissions (uid 1001 vs 1000)","","The brain/mail-sync containers write `var/**` as uid 1001 (Dockerfile","`useradd --uid 1001`) while host tools run as uid 1000. A container write","leaves `var/kb.lbug.wal` and `var/corpus/**` unopenable by the wave","(`OpenDatabase … status 1`). Since compose.yaml now runs the containers as","the host uid (`user: \"${KB_UID:-1000}:${KB_GID:-1000}\"`), new writes align","automatically; a one-time repair for already-misowned files goes through","docker busybox (no sudo on this host):","","```bash","scripts/stack/fix-var-perms            # chown -R var/ to the caller uid:gid","scripts/stack/fix-var-perms --check    # report files NOT owned by the caller","```","","Run it after any container write or when a wave step fails on `var/**`.","The wave also quiesces the compose brain container around write steps","(`docker compose stop brain` → writes → `start brain`), so `git-brain` does","not collide with the running serve process holding `kb.lbug`.","","### index-sync — gator→graph→index cycle (#292)","","The chain `gator sync→etl→pack → 2dph import(graph) → index(kb.lbug)` is","closed by the compose service **`index-sync`** (profile `index`). No systemd.","","One cycle: discover the documents hive (`\u003chive-doc\u003e/source=*/channel=*`) →","stop the `brain` container (Ladybug is single-writer) → import the mail canon →","`brain-index --skip` (docs corpus + indexes) → (gator mode only) `mail-graph","--commit` per channel (idempotent MERGE) → ANN `ensure` → start brain and wait","healthy → write freshness state. A cycle is skipped when `kb.lbug` is already","newer than the newest upstream state and the last run did not error, so brain is","not bounced for nothing.","","**ANN index build/maintenance (#204/#206).** The IVF vector index is maintained","by `bin/brain/ann.go ensure`, which `index-sync` runs at the end of every cycle.","The binary is read from `ANN_BIN` (flag `--ann` wins; empty = skip). Compose","sets `ANN_BIN=/usr/local/bin/brain-ann`; the `api-build` stage builds it with","`-tags system_ladybug,brain_ann` and the `api` stage copies it, so no manual","install is needed. `ensure` does a full k-means build only when the index is","missing or stale (\u003e10% behind the DB leaf count); steady-state growth is an","incremental WAL-upsert (\u003c1s, never a rebuild). The index path is","`vector.ann.index`, default `\u003croot\u003e/var/state/vector.ann` (+ `.wal`); in the","container `KB_ROOT=/data` and `./var` is mounted at `/data/var`, so the index","lands in the host `var/state/` on the persistent volume. Empty/absent index is","never fatal: serve/CLI fall back to the linear scan and `/stats` reports","`ann.loaded:false`.","","**Mail canon — T10-A (`MAIL_SOURCE`, default `corpus`).** The info-corpus mail","canon is the **local 2dph M365 corpus**, not the gator `parquet/mail` hive: the","Edelweiss boxes are M365/OAuth and are synced by 2dph into `var/corpus/mail`","(gator's mail client is plain IMAP). Selecting the canon keeps a single source","of Leafs (local corpus ids are content-address, gator ids are `message_id`, so","indexing both would duplicate).","","- `corpus` (default): `brain-index --with-mail` indexes `var/corpus/mail/m365`","  (plus the legacy `var/mail`); the gator `parquet/mail` leaf+graph steps are","  **skipped**. `--since YYYY-MM-DD` (env `MAIL_SINCE`) bounds the corpus mail if","  set. Corpus-mail mtime drives freshness.","- `gator`: unchanged legacy path — `mail-leaf` over `parquet/mail` plus","  `mail-graph --commit` per channel.","- The gator `parquet/documents` → `doc-leaf` step runs in **both** modes.","","**Document body text — T12 (`GATOR_DOC_TEXT`, default off).** `doc-leaf`","indexes metadata only (`title` + `filename` + `oo_path`). With the flag the PDF","is fetched from OnlyOffice by `oo_path` (or a `fileid` in `ref`) and its text is","appended to `Leaf.Text` (capped, default 256 KiB). Enable with","`--document-text` on `doc-leaf`/`index-loop` or `GATOR_DOC_TEXT=1`; requires","`ONLYOFFICE_URL/USER/PASS` and `GATOR_DOC_ROOT_ID` (the Files folder rooting","`oo_path`; optional `GATOR_DOC_ROOT_PREFIX`, default `external`), plus optional","`GATOR_DOC_TEXT_LIMIT`. A fetch/extract failure keeps the metadata leaf. Body","text changes the content hash, so the first enabled run writes a new version;","extraction runs before the `--skip` dedup, so an enabled cycle re-fetches.","","```bash","scripts/stack/start-index-sync                 # compose up -d index-sync (loop, 1h)","docker compose --profile index up -d index-sync","docker compose --profile index run --rm index-sync index-loop --once   # one manual cycle","# one-off canon override:","docker compose --profile index run --rm index-sync index-loop --once --mail-source gator","docker compose --profile index logs -f index-sync","```","","Requirements:","","- API image rebuilt with `mail-graph` + `index-loop` + `brain-ann`","  (`docker compose --profile index build index-sync`); the older","  `ghcr.io/eslider/2dph:api` lacks them. Without `brain-ann` the ANN step","  is skipped and vector search uses the linear scan.","- In `corpus` mode the gator mail hive is not required; `./var` must be mounted","  at `/data/var` so `var/corpus/mail/m365` is visible (it is).","- `GATOR_VAR_HOST=\u003cgator repo\u003e/var/gator` in `.env` (gitignored) — the host","  gator canon is bind-mounted read-only at `/gator`. `GATOR_MAIL_HIVE`","  inside the container is `/gator/parquet/mail` (used only in `gator` mode);","  the same mount covers `parquet/documents` (kind=document), set as","  `GATOR_DOCUMENTS_HIVE` (`/gator/parquet/documents`). An absent/empty document","  tree is skipped.","- `/var/run/docker.sock` is mounted: the cycle stops/starts the sibling","  `brain` container. `index-sync` therefore has Docker control — keep it on","  the local host only. Because the container runs with an explicit `user:`,","  it does not inherit the host's supplementary `docker` group; set","  `DOCKER_GID` (default 991) to the socket's group so the added `group_add`","  grants access, otherwise the quiesce step fails with","  `dial unix /var/run/docker.sock: connect: permission denied`","  (`stat -c '%g' /var/run/docker.sock`).","- The container runs as the host uid (`KB_UID`/`KB_GID`) so `kb.lbug` stays","  writable by host tools (#195).","","Freshness is observable: the state file is `\u003croot\u003e/var/state/index-freshness.yml`,","`/stats` returns `index_at`/`import_at`/`kb_mtime`/`pack_mtime`/`stale`/","`stale_after`, and `scripts/stack/status` prints the same (stale from `/stats`","when the brain answers, otherwise `unknown` — the on-disk state stays readable","while the brain is down).","","```bash","curl -s localhost:8630/stats | yq '.index_at,.import_at,.kb_mtime,.stale'","scripts/stack/status | yq '.freshness'","ls -la --time-style=full-iso var/kb.lbug      # mtime newer than the gator pack","```","","### OnlyOffice CRM tools","","OnlyOffice CRM tools need `ONLYOFFICE_URL`/`ONLYOFFICE_USER`/`ONLYOFFICE_PASS`","env. Both are read-only (report) by default; `--write` applies changes.","","```bash","# reconcile report — human mail senders vs OO CRM persons (match by email)","go run -tags=onlyoffice_reconcile_contact ./bin/onlyoffice/reconcile-contact.go","go run -tags=onlyoffice_reconcile_contact ./bin/onlyoffice/reconcile-contact.go --write --limit 200","","# interaction write — mail → history note on the person's opportunity","go run -tags=onlyoffice_import_interaction ./bin/onlyoffice/import-interaction.go","go run -tags=onlyoffice_import_interaction ./bin/onlyoffice/import-interaction.go --write --limit 50","```","","Both scan `var/corpus/mail` by default (`--sources` to override) and build an","email → person index in one pass over the whole CRM. Machine senders","(newsletter/bounce/platform) never become contacts.","","## Browser sync (periodic, #163)","","Pushes the browser-extracted corpus (`var/corpus/{gmail,linkedin,djinni}`) into","the brain as leafs every 6 h. Thorium down is tolerated: extraction is skipped","and the last-known corpus is still ingested.","","```bash","./bin/cron/browser-sync.go                  # one cycle, default paths","./bin/cron/browser-sync.go --skip-extract   # no Thorium probe","./bin/cron/browser-sync.go --interval 6h    # run continuously","sudo scripts/browser-sync-install.sh        # install the 6-hourly systemd timer","```","","Log: `var/log/browser-sync.log`. The systemd units","(`scripts/browser-sync.{service,timer}`) use an `@REPO_ROOT@` placeholder; the","install script substitutes the real root at install time.","","## Reasoner (optional, D18)","","CPU sidecar on `127.0.0.1:11435`. Weights are not in the 2dph image.","","```bash","docker compose --profile reasoner up -d reasoner","REASONER_BASE_URL=http://127.0.0.1:11435/v1 ./bin/reasoner/bench.go --json","```","","See [reasoner.md](reasoner.md).","","## GitHub mirror sync (#194)","","Gitea is the canonical home; GitHub is a publish-only **showcase** (`#190`,","`#213`). `main` is the single working line and the public face (Gitea `#213`).","PRs merge into `main` directly; no `release/v1` branch is kept. After each","merge into `main`, sync **only `main`** (plus tags) to GitHub:","","```bash","git fetch origin --tags --force","git push github main:main --force-with-lease","git push github --tags --force","```","","Verify zero drift on `main`:","","```bash","git rev-list --count origin/main..github/main   # 0","git rev-list --count github/main..origin/main   # 0","```","","Branch policy (`#213`): both Gitea and GitHub keep `main` only. Old/merged","feature branches are backed up locally under `refs/backup/*` before deletion.","","Content guard (`#190`): before pushing to GitHub, confirm the diff is project","content only — no internal paths/hosts/ports, no secrets. Precedent #142/#194:","leaks are purged from GitHub history with `git filter-repo`; Gitea stays the","canonical home and is never rewritten for the mirror."],"stylingDirectives":[[[0,3,"pl-s"]],[[0,4,"pl-ent"],[6,11,"pl-s"]],[[0,6,"pl-ent"],[8,15,"pl-s"]],[[0,7,"pl-ent"]],[[4,18,"pl-s"]],[[4,11,"pl-s"]],[[0,3,"pl-s"]],[],[[0,21,"pl-mh"],[2,21,"pl-en"]],[],[[58,59,"pl-s"],[59,79,"pl-c1"],[79,80,"pl-s"]],[],[],[[0,12,"pl-mh"],[3,12,"pl-en"]],[],[[0,1,"pl-v"],[10,11,"pl-s"],[11,17,"pl-c1"],[17,18,"pl-s"]],[[0,1,"pl-v"],[32,33,"pl-s"],[33,44,"pl-c1"],[44,45,"pl-s"]],[[0,1,"pl-v"],[21,22,"pl-s"],[22,31,"pl-c1"],[31,32,"pl-s"],[33,34,"pl-s"],[34,42,"pl-c1"],[42,43,"pl-s"],[57,58,"pl-s"],[58,65,"pl-c1"],[65,66,"pl-s"]],[[0,1,"pl-v"],[24,25,"pl-s"],[25,27,"pl-c1"],[27,28,"pl-s"]],[],[[5,6,"pl-s"],[6,13,"pl-c1"],[13,14,"pl-s"]],[[0,1,"pl-v"],[12,14,"pl-s"],[31,33,"pl-s"],[40,44,"pl-s"],[41,44,"pl-corl"]],[[13,14,"pl-s"],[14,44,"pl-c1"],[44,45,"pl-s"]],[[2,3,"pl-s"],[3,49,"pl-c1"],[49,50,"pl-s"],[52,53,"pl-s"],[66,67,"pl-s"]],[[2,3,"pl-s"],[3,13,"pl-c1"],[13,14,"pl-s"],[30,31,"pl-s"],[31,54,"pl-c1"],[54,55,"pl-s"],[59,60,"pl-s"],[60,88,"pl-c1"],[88,89,"pl-s"]],[[15,16,"pl-s"],[16,37,"pl-c1"],[37,38,"pl-s"],[54,55,"pl-s"],[55,81,"pl-c1"],[81,82,"pl-s"]],[],[[0,3,"pl-s"],[3,7,"pl-en"]],[[0,4,"pl-c1"],[5,25,"pl-s"],[5,6,"pl-pds"],[6,24,"pl-s"],[6,8,"pl-pds"],[23,24,"pl-pds"],[24,25,"pl-pds"],[28,96,"pl-c"],[28,29,"pl-c"]],[[3,7,"pl-c1"]],[[0,3,"pl-s"]],[],[[0,9,"pl-mh"],[3,9,"pl-en"]],[],[[0,1,"pl-ml"],[13,14,"pl-ml"],[23,24,"pl-ml"]],[[0,1,"pl-ml"],[13,14,"pl-ml"],[23,24,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,20,"pl-c1"],[20,21,"pl-s"],[31,32,"pl-s"],[32,62,"pl-c1"],[62,63,"pl-s"],[65,66,"pl-ml"],[67,68,"pl-s"],[68,84,"pl-c1"],[84,85,"pl-s"],[118,119,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,38,"pl-c1"],[38,39,"pl-s"],[40,41,"pl-ml"],[94,95,"pl-ml"]],[],[[40,41,"pl-s"],[41,57,"pl-c1"],[57,58,"pl-s"]],[],[],[[0,3,"pl-s"],[3,7,"pl-en"]],[[15,38,"pl-s"],[15,17,"pl-pds"],[37,38,"pl-pds"]],[[0,3,"pl-s"]],[],[[11,12,"pl-s"],[12,26,"pl-c1"],[26,27,"pl-s"]],[],[[0,20,"pl-mh"],[3,20,"pl-en"]],[],[[14,15,"pl-s"],[15,31,"pl-c1"],[31,32,"pl-s"],[48,49,"pl-s"],[49,61,"pl-c1"],[61,62,"pl-s"]],[[23,24,"pl-s"],[24,52,"pl-c1"],[52,53,"pl-s"]],[[0,1,"pl-s"],[1,6,"pl-c1"],[6,7,"pl-s"]],[],[[0,3,"pl-s"],[3,7,"pl-en"]],[[24,43,"pl-s"],[24,25,"pl-pds"],[42,43,"pl-pds"],[66,91,"pl-s"],[66,67,"pl-pds"],[90,91,"pl-pds"]],[],[[20,44,"pl-s"],[20,21,"pl-pds"],[43,44,"pl-pds"],[49,75,"pl-c"],[49,50,"pl-c"]],[[20,35,"pl-s"],[20,21,"pl-pds"],[34,35,"pl-pds"]],[[20,37,"pl-s"],[20,21,"pl-pds"],[36,37,"pl-pds"],[58,73,"pl-c"],[58,59,"pl-c"]],[[0,6,"pl-c1"],[7,38,"pl-s"],[7,9,"pl-pds"],[37,38,"pl-pds"],[50,71,"pl-c"],[50,51,"pl-c"]],[[17,18,"pl-k"],[20,21,"pl-k"]],[],[[0,3,"pl-s"]],[],[[0,1,"pl-s"],[1,8,"pl-c1"],[8,9,"pl-s"],[54,55,"pl-s"],[55,73,"pl-c1"],[73,74,"pl-s"]],[[18,19,"pl-s"],[19,29,"pl-c1"],[29,30,"pl-s"],[31,32,"pl-s"],[32,40,"pl-c1"],[40,41,"pl-s"]],[[41,42,"pl-s"],[42,51,"pl-c1"],[51,52,"pl-s"]],[[11,12,"pl-s"],[19,20,"pl-s"],[20,21,"pl-s"],[21,31,"pl-corl"],[31,32,"pl-s"],[35,36,"pl-s"],[41,44,"pl-s"],[42,44,"pl-corl"],[44,45,"pl-s"],[45,46,"pl-s"],[46,93,"pl-corl"],[93,94,"pl-s"]],[],[[20,21,"pl-s"],[21,31,"pl-c1"],[31,32,"pl-s"]],[[7,8,"pl-s"],[8,19,"pl-c1"],[19,20,"pl-s"],[26,27,"pl-s"],[27,36,"pl-c1"],[36,37,"pl-s"]],[],[[0,13,"pl-mh"],[3,13,"pl-en"]],[],[[0,3,"pl-s"],[3,7,"pl-en"]],[[36,82,"pl-c"],[36,37,"pl-c"]],[[36,83,"pl-c"],[36,37,"pl-c"]],[[36,95,"pl-c"],[36,37,"pl-c"]],[[36,83,"pl-c"],[36,37,"pl-c"]],[],[[36,64,"pl-c"],[36,37,"pl-c"]],[[0,3,"pl-s"]],[],[],[],[[0,3,"pl-s"],[3,7,"pl-en"]],[[49,70,"pl-c"],[49,50,"pl-c"]],[[49,71,"pl-c"],[49,50,"pl-c"]],[[49,58,"pl-c"],[49,50,"pl-c"]],[[49,69,"pl-c"],[49,50,"pl-c"]],[[0,3,"pl-s"]],[],[[0,1,"pl-s"],[1,18,"pl-c1"],[18,19,"pl-s"],[21,22,"pl-s"],[22,31,"pl-c1"],[31,32,"pl-s"],[52,53,"pl-s"],[53,59,"pl-c1"],[59,60,"pl-s"],[63,64,"pl-s"],[64,67,"pl-c1"],[67,68,"pl-s"],[71,72,"pl-s"],[72,77,"pl-c1"],[77,78,"pl-s"]],[[4,5,"pl-s"],[16,17,"pl-s"],[17,18,"pl-s"],[18,29,"pl-corl"],[29,30,"pl-s"]],[],[[0,39,"pl-mh"],[4,39,"pl-en"],[34,38,"pl-s"],[35,38,"pl-corl"]],[],[[38,39,"pl-s"],[39,46,"pl-c1"],[46,47,"pl-s"]],[],[[11,12,"pl-s"],[12,19,"pl-c1"],[19,20,"pl-s"],[36,37,"pl-s"],[37,39,"pl-c1"],[39,40,"pl-s"]],[],[],[],[[0,2,"pl-s"],[14,16,"pl-s"],[47,48,"pl-s"],[48,55,"pl-c1"],[55,56,"pl-s"]],[],[[0,3,"pl-s"],[3,7,"pl-en"]],[[0,43,"pl-c"],[0,1,"pl-c"]],[[52,87,"pl-c"],[52,53,"pl-c"]],[[0,32,"pl-c"],[0,1,"pl-c"]],[[23,72,"pl-s"],[23,24,"pl-pds"],[71,72,"pl-pds"]],[[0,3,"pl-s"]],[],[[0,1,"pl-s"],[1,7,"pl-c1"],[7,8,"pl-s"]],[[23,24,"pl-s"],[24,29,"pl-c1"],[29,30,"pl-s"],[48,49,"pl-s"],[49,55,"pl-c1"],[55,56,"pl-s"]],[[12,13,"pl-s"],[13,25,"pl-c1"],[25,26,"pl-s"]],[[1,2,"pl-s"],[2,25,"pl-c1"],[25,26,"pl-s"]],[],[[0,2,"pl-s"],[9,11,"pl-s"]],[],[[0,3,"pl-s"],[3,7,"pl-en"]],[[36,69,"pl-c"],[36,37,"pl-c"]],[[0,52,"pl-c"],[0,1,"pl-c"]],[[26,27,"pl-k"],[36,58,"pl-s"],[36,37,"pl-pds"],[57,58,"pl-pds"],[61,75,"pl-c"],[61,62,"pl-c"]],[[61,78,"pl-c"],[61,62,"pl-c"]],[[0,3,"pl-s"]],[],[[14,15,"pl-s"],[15,26,"pl-c1"],[26,27,"pl-s"]],[[32,33,"pl-s"],[33,54,"pl-c1"],[54,55,"pl-s"]],[],[[42,43,"pl-s"],[43,52,"pl-c1"],[52,53,"pl-s"]],[],[],[[0,26,"pl-mh"],[3,26,"pl-en"],[21,25,"pl-s"],[22,25,"pl-corl"]],[],[[12,13,"pl-s"],[13,31,"pl-c1"],[31,32,"pl-s"]],[[1,2,"pl-s"],[2,41,"pl-c1"],[41,42,"pl-s"],[44,45,"pl-s"]],[],[[9,12,"pl-s"],[10,12,"pl-corl"],[24,26,"pl-s"],[68,70,"pl-s"]],[[6,10,"pl-s"],[7,10,"pl-corl"]],[],[[0,3,"pl-s"],[3,7,"pl-en"]],[[0,86,"pl-c"],[0,1,"pl-c"]],[],[],[[0,30,"pl-c"],[0,1,"pl-c"]],[],[],[[0,81,"pl-c"],[0,1,"pl-c"]],[],[],[[0,73,"pl-c"],[0,1,"pl-c"]],[],[],[[0,65,"pl-c"],[0,1,"pl-c"]],[[0,63,"pl-c"],[0,1,"pl-c"]],[],[],[[0,79,"pl-c"],[0,1,"pl-c"]],[],[],[[0,72,"pl-c"],[0,1,"pl-c"]],[[27,77,"pl-s"],[27,29,"pl-pds"],[47,63,"pl-s"],[47,48,"pl-pds"],[62,63,"pl-pds"],[76,77,"pl-pds"]],[[0,3,"pl-s"]],[],[],[],[[0,1,"pl-ml"],[10,11,"pl-ml"],[18,19,"pl-ml"],[25,26,"pl-ml"]],[[0,1,"pl-ml"],[10,11,"pl-ml"],[18,19,"pl-ml"],[25,26,"pl-ml"]],[[0,1,"pl-ml"],[27,28,"pl-ml"],[73,74,"pl-ml"],[109,110,"pl-ml"]],[[0,1,"pl-ml"],[11,14,"pl-s"],[12,14,"pl-corl"],[26,27,"pl-ml"],[93,94,"pl-ml"],[120,121,"pl-ml"]],[[0,1,"pl-ml"],[11,14,"pl-s"],[12,14,"pl-corl"],[29,30,"pl-ml"],[86,87,"pl-ml"],[104,105,"pl-ml"]],[[0,1,"pl-ml"],[10,11,"pl-ml"],[65,66,"pl-ml"],[103,104,"pl-ml"]],[],[[12,13,"pl-s"],[13,14,"pl-c1"],[14,15,"pl-s"],[32,33,"pl-s"],[33,34,"pl-c1"],[34,35,"pl-s"],[57,58,"pl-s"],[58,59,"pl-c1"],[59,60,"pl-s"]],[],[],[[41,42,"pl-s"],[42,53,"pl-c1"],[53,54,"pl-s"]],[[25,26,"pl-s"],[26,37,"pl-c1"],[37,38,"pl-s"]],[],[],[[40,44,"pl-s"],[41,44,"pl-corl"]],[],[],[[24,25,"pl-s"],[25,34,"pl-c1"],[34,35,"pl-s"],[46,47,"pl-s"],[47,68,"pl-c1"],[68,69,"pl-s"]],[[32,33,"pl-s"],[33,50,"pl-c1"],[50,51,"pl-s"]],[],[[0,34,"pl-mh"],[4,34,"pl-en"],[24,28,"pl-s"],[25,28,"pl-corl"],[29,33,"pl-s"],[30,33,"pl-corl"]],[],[[56,58,"pl-s"],[69,71,"pl-s"]],[[42,46,"pl-s"],[43,46,"pl-corl"]],[[10,12,"pl-s"],[15,17,"pl-s"]],[[38,39,"pl-s"],[39,49,"pl-c1"],[49,50,"pl-s"]],[],[],[[57,61,"pl-s"],[58,61,"pl-corl"]],[[8,9,"pl-s"],[9,29,"pl-c1"],[29,30,"pl-s"],[50,51,"pl-s"],[51,65,"pl-c1"],[65,66,"pl-s"]],[],[[0,2,"pl-s"],[22,24,"pl-s"],[26,30,"pl-s"],[27,30,"pl-corl"],[33,34,"pl-s"],[34,58,"pl-c1"],[58,59,"pl-s"]],[],[[47,48,"pl-s"],[48,53,"pl-c1"],[53,54,"pl-s"]],[],[[19,20,"pl-s"],[20,29,"pl-c1"],[29,30,"pl-s"]],[],[[0,3,"pl-s"],[3,7,"pl-en"]],[[0,81,"pl-c"],[0,1,"pl-c"]],[[0,75,"pl-c"],[0,1,"pl-c"]],[[0,78,"pl-c"],[0,1,"pl-c"]],[[0,75,"pl-c"],[0,1,"pl-c"]],[],[],[[0,76,"pl-c"],[0,1,"pl-c"]],[[0,84,"pl-c"],[0,1,"pl-c"]],[],[],[[0,83,"pl-c"],[0,1,"pl-c"]],[],[],[[0,20,"pl-c"],[0,1,"pl-c"]],[],[],[[0,63,"pl-c"],[0,1,"pl-c"]],[[32,39,"pl-s"],[32,33,"pl-pds"],[38,39,"pl-pds"]],[],[[0,76,"pl-c"],[0,1,"pl-c"]],[],[[0,3,"pl-s"]],[],[[0,2,"pl-s"],[19,21,"pl-s"]],[[29,30,"pl-s"],[30,36,"pl-c1"],[36,37,"pl-s"]],[],[],[[0,57,"pl-ent"],[0,1,"pl-ent"]],[[0,0,"pl-ent"],[0,1,"pl-s"],[1,40,"pl-c1"],[40,41,"pl-s"]],[],[],[],[],[],[[0,3,"pl-s"],[3,7,"pl-en"]],[[0,60,"pl-c"],[0,1,"pl-c"]],[],[[0,90,"pl-c"],[0,1,"pl-c"]],[[0,79,"pl-c"],[0,1,"pl-c"]],[[0,3,"pl-s"]],[],[[0,2,"pl-s"],[22,24,"pl-s"],[34,38,"pl-s"],[35,38,"pl-corl"]],[],[[65,66,"pl-s"]],[[51,52,"pl-s"]],[[10,11,"pl-s"]],[],[[8,9,"pl-s"],[9,29,"pl-c1"],[29,30,"pl-s"],[39,40,"pl-s"],[40,50,"pl-c1"],[50,51,"pl-s"],[54,55,"pl-s"],[55,62,"pl-c1"],[62,63,"pl-s"],[65,66,"pl-s"],[66,71,"pl-c1"],[71,72,"pl-s"]],[[0,1,"pl-s"],[1,4,"pl-c1"],[4,5,"pl-s"],[13,14,"pl-s"],[14,19,"pl-c1"],[19,20,"pl-s"],[44,45,"pl-s"]],[[7,8,"pl-s"],[8,14,"pl-c1"],[14,15,"pl-s"],[39,40,"pl-s"],[40,49,"pl-c1"],[49,50,"pl-s"]],[[0,1,"pl-s"],[1,19,"pl-c1"],[19,20,"pl-s"],[47,48,"pl-s"],[48,53,"pl-c1"],[53,54,"pl-s"],[56,60,"pl-s"],[57,60,"pl-corl"]],[],[[0,1,"pl-s"],[1,7,"pl-c1"],[7,8,"pl-s"],[48,49,"pl-s"],[49,54,"pl-c1"],[54,55,"pl-s"]],[],[[12,16,"pl-s"],[13,16,"pl-corl"],[29,33,"pl-s"],[30,33,"pl-corl"]],[],[[0,34,"pl-mh"],[3,34,"pl-en"],[30,33,"pl-s"],[31,33,"pl-corl"]],[],[[11,12,"pl-s"],[12,20,"pl-c1"],[20,21,"pl-s"]],[[0,1,"pl-s"],[16,17,"pl-s"],[17,18,"pl-s"],[18,33,"pl-corl"],[33,34,"pl-s"]],[[24,25,"pl-s"],[25,37,"pl-c1"],[37,38,"pl-s"],[40,41,"pl-s"],[41,48,"pl-c1"],[48,49,"pl-s"]],[],[[0,3,"pl-s"],[3,7,"pl-en"]],[[32,33,"pl-k"],[40,41,"pl-k"]],[[32,33,"pl-k"],[40,41,"pl-k"]],[],[[0,3,"pl-s"]],[],[[0,38,"pl-mh"],[3,38,"pl-en"],[34,37,"pl-s"],[35,37,"pl-corl"]],[],[],[[20,21,"pl-s"],[21,30,"pl-c1"],[30,31,"pl-s"]],[[42,43,"pl-s"],[43,71,"pl-c1"],[71,72,"pl-s"]],[],[[0,3,"pl-s"],[3,7,"pl-en"]],[[57,73,"pl-c"],[57,58,"pl-c"]],[[57,71,"pl-c"],[57,58,"pl-c"]],[[57,77,"pl-c"],[57,58,"pl-c"]],[[75,102,"pl-c"],[75,76,"pl-c"]],[[51,52,"pl-k"],[55,56,"pl-k"],[57,76,"pl-c"],[57,58,"pl-c"]],[[51,52,"pl-k"],[55,56,"pl-k"],[57,79,"pl-c"],[57,58,"pl-c"]],[[0,3,"pl-s"]],[],[[0,1,"pl-s"],[1,7,"pl-c1"],[7,8,"pl-s"],[48,49,"pl-s"],[49,53,"pl-c1"],[53,54,"pl-s"],[56,57,"pl-s"],[57,68,"pl-c1"],[68,69,"pl-s"]],[[0,1,"pl-s"],[1,11,"pl-c1"],[11,12,"pl-s"],[14,15,"pl-s"],[15,20,"pl-c1"],[20,21,"pl-s"],[23,24,"pl-s"],[24,37,"pl-c1"],[37,38,"pl-s"],[40,41,"pl-s"],[41,50,"pl-c1"],[50,51,"pl-s"],[53,54,"pl-s"],[54,65,"pl-c1"],[65,66,"pl-s"]],[[0,1,"pl-s"],[1,12,"pl-c1"],[12,13,"pl-s"],[27,28,"pl-s"],[28,38,"pl-c1"],[38,39,"pl-s"]],[[0,4,"pl-s"],[1,4,"pl-corl"]],[],[[0,35,"pl-mh"],[4,35,"pl-en"],[30,34,"pl-s"],[31,34,"pl-corl"]],[],[[4,5,"pl-s"],[5,10,"pl-c1"],[10,11,"pl-s"]],[[14,15,"pl-s"],[15,40,"pl-c1"],[40,41,"pl-s"],[48,49,"pl-s"],[49,74,"pl-c1"],[74,75,"pl-s"]],[[26,27,"pl-s"],[27,55,"pl-c1"],[55,56,"pl-s"]],[],[],[],[[0,1,"pl-ml"],[10,11,"pl-ml"],[20,21,"pl-ml"]],[[0,1,"pl-ml"],[10,11,"pl-ml"],[20,21,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,18,"pl-c1"],[18,19,"pl-s"],[22,23,"pl-s"],[23,40,"pl-c1"],[40,41,"pl-s"],[44,45,"pl-s"],[45,59,"pl-c1"],[59,60,"pl-s"],[61,62,"pl-ml"],[88,89,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,19,"pl-c1"],[19,20,"pl-s"],[21,22,"pl-ml"],[80,81,"pl-s"],[81,85,"pl-c1"],[85,86,"pl-s"],[88,89,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,26,"pl-c1"],[26,27,"pl-s"],[28,29,"pl-ml"],[60,61,"pl-s"],[61,82,"pl-c1"],[82,83,"pl-s"],[84,85,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,25,"pl-c1"],[25,26,"pl-s"],[27,28,"pl-ml"],[78,79,"pl-s"],[79,102,"pl-c1"],[102,103,"pl-s"],[105,106,"pl-ml"]],[],[[0,2,"pl-s"],[16,18,"pl-s"]],[[0,1,"pl-s"],[1,14,"pl-c1"],[14,15,"pl-s"],[39,40,"pl-s"],[40,56,"pl-c1"],[56,57,"pl-s"]],[[0,1,"pl-s"],[1,5,"pl-c1"],[5,6,"pl-s"],[14,15,"pl-s"],[15,19,"pl-c1"],[19,20,"pl-s"],[53,54,"pl-s"],[54,58,"pl-c1"],[58,59,"pl-s"]],[],[],[],[[0,39,"pl-mh"],[4,39,"pl-en"]],[],[[37,38,"pl-s"],[38,44,"pl-c1"],[44,45,"pl-s"]],[[0,1,"pl-s"],[1,19,"pl-c1"],[19,20,"pl-s"]],[[7,8,"pl-s"],[8,23,"pl-c1"],[23,24,"pl-s"],[29,30,"pl-s"],[30,43,"pl-c1"],[43,44,"pl-s"]],[[1,2,"pl-s"],[2,25,"pl-c1"],[25,26,"pl-s"]],[[14,15,"pl-s"],[15,54,"pl-c1"],[54,55,"pl-s"]],[],[],[],[[0,3,"pl-s"],[3,7,"pl-en"]],[[39,76,"pl-c"],[39,40,"pl-c"]],[[39,77,"pl-c"],[39,40,"pl-c"]],[[0,3,"pl-s"]],[],[[62,63,"pl-s"],[63,69,"pl-c1"],[69,70,"pl-s"]],[],[[1,2,"pl-s"],[2,27,"pl-c1"],[27,28,"pl-s"],[40,41,"pl-s"],[41,52,"pl-c1"],[52,53,"pl-s"],[59,60,"pl-s"],[60,69,"pl-c1"],[69,70,"pl-s"]],[[51,52,"pl-s"],[52,59,"pl-c1"],[59,60,"pl-s"]],[],[[0,47,"pl-mh"],[4,47,"pl-en"],[42,46,"pl-s"],[43,46,"pl-corl"]],[],[[10,11,"pl-s"],[11,68,"pl-c1"],[68,69,"pl-s"]],[[30,32,"pl-s"],[32,33,"pl-s"],[33,43,"pl-c1"],[43,44,"pl-s"],[44,46,"pl-s"],[56,57,"pl-s"],[57,62,"pl-c1"],[62,63,"pl-s"]],[],[[40,41,"pl-s"],[41,70,"pl-c1"],[70,71,"pl-s"]],[[9,10,"pl-s"],[10,15,"pl-c1"],[15,16,"pl-s"]],[[0,1,"pl-s"],[1,19,"pl-c1"],[19,20,"pl-s"]],[[8,9,"pl-s"],[9,47,"pl-c1"],[47,48,"pl-s"]],[[57,58,"pl-s"],[58,65,"pl-c1"],[65,66,"pl-s"]],[],[],[],[[0,2,"pl-s"],[31,35,"pl-s"],[32,35,"pl-corl"],[36,40,"pl-s"],[37,40,"pl-corl"],[42,44,"pl-s"]],[[3,4,"pl-s"],[4,27,"pl-c1"],[27,28,"pl-s"],[36,37,"pl-s"],[37,47,"pl-c1"],[47,48,"pl-s"]],[[24,25,"pl-s"],[25,32,"pl-c1"],[32,33,"pl-s"],[40,41,"pl-s"],[41,46,"pl-c1"],[46,47,"pl-s"]],[[5,6,"pl-s"],[6,38,"pl-c1"],[38,39,"pl-s"],[45,46,"pl-s"],[46,55,"pl-c1"],[55,56,"pl-s"]],[[0,1,"pl-s"],[1,31,"pl-c1"],[31,32,"pl-s"],[41,42,"pl-s"],[42,45,"pl-c1"],[45,46,"pl-s"]],[[19,20,"pl-s"],[20,26,"pl-c1"],[26,27,"pl-s"]],[],[],[[0,1,"pl-s"],[1,17,"pl-c1"],[17,18,"pl-s"],[28,29,"pl-s"],[29,56,"pl-c1"],[56,57,"pl-s"],[61,62,"pl-s"],[62,66,"pl-c1"],[66,67,"pl-s"]],[[10,11,"pl-s"],[11,24,"pl-c1"],[24,25,"pl-s"],[30,31,"pl-s"],[31,36,"pl-c1"],[36,37,"pl-s"],[52,53,"pl-s"],[53,62,"pl-c1"],[62,63,"pl-s"]],[[18,19,"pl-s"],[19,29,"pl-c1"],[29,30,"pl-s"]],[[56,57,"pl-s"],[57,63,"pl-c1"],[63,64,"pl-s"]],[[0,1,"pl-s"],[1,17,"pl-c1"],[17,18,"pl-s"]],[],[[0,2,"pl-s"],[22,23,"pl-s"],[23,34,"pl-c1"],[34,35,"pl-s"],[45,46,"pl-s"],[46,52,"pl-c1"],[52,53,"pl-s"],[55,57,"pl-s"]],[[13,15,"pl-s"],[37,39,"pl-s"],[55,56,"pl-s"],[56,68,"pl-c1"],[68,69,"pl-s"]],[[59,60,"pl-s"],[60,75,"pl-c1"],[75,76,"pl-s"]],[],[[62,63,"pl-s"],[63,73,"pl-c1"],[73,74,"pl-s"]],[],[],[[0,1,"pl-v"],[2,3,"pl-s"],[3,9,"pl-c1"],[9,10,"pl-s"],[22,23,"pl-s"],[23,46,"pl-c1"],[46,47,"pl-s"],[56,57,"pl-s"],[57,77,"pl-c1"],[77,78,"pl-s"]],[[19,20,"pl-s"],[20,28,"pl-c1"],[28,29,"pl-s"],[42,43,"pl-s"],[43,55,"pl-c1"],[55,56,"pl-s"]],[[2,4,"pl-s"],[11,13,"pl-s"],[15,16,"pl-s"],[16,34,"pl-c1"],[34,35,"pl-s"],[41,42,"pl-s"],[42,52,"pl-c1"],[52,53,"pl-s"]],[],[[0,1,"pl-v"],[2,3,"pl-s"],[3,8,"pl-c1"],[8,9,"pl-s"],[35,36,"pl-s"],[36,45,"pl-c1"],[45,46,"pl-s"],[52,53,"pl-s"],[53,65,"pl-c1"],[65,66,"pl-s"]],[[2,3,"pl-s"],[3,22,"pl-c1"],[22,23,"pl-s"]],[[0,1,"pl-v"],[12,13,"pl-s"],[13,30,"pl-c1"],[30,31,"pl-s"],[34,35,"pl-s"],[35,43,"pl-c1"],[43,44,"pl-s"],[58,60,"pl-s"],[64,66,"pl-s"]],[],[[0,2,"pl-s"],[28,29,"pl-s"],[29,43,"pl-c1"],[43,44,"pl-s"],[59,61,"pl-s"],[62,63,"pl-s"],[63,71,"pl-c1"],[71,72,"pl-s"]],[[23,24,"pl-s"],[24,29,"pl-c1"],[29,30,"pl-s"],[33,34,"pl-s"],[34,42,"pl-c1"],[42,43,"pl-s"],[46,47,"pl-s"],[47,54,"pl-c1"],[54,55,"pl-s"]],[[30,31,"pl-s"],[31,38,"pl-c1"],[38,39,"pl-s"],[46,47,"pl-s"],[47,53,"pl-c1"],[53,54,"pl-s"],[58,59,"pl-s"],[59,62,"pl-c1"],[62,63,"pl-s"]],[[12,13,"pl-s"],[13,22,"pl-c1"],[22,23,"pl-s"]],[[0,1,"pl-s"],[1,16,"pl-c1"],[16,17,"pl-s"],[21,22,"pl-s"],[22,30,"pl-c1"],[30,31,"pl-s"],[32,33,"pl-s"],[33,43,"pl-c1"],[43,44,"pl-s"],[48,49,"pl-s"],[49,65,"pl-c1"],[65,66,"pl-s"]],[[0,1,"pl-s"],[1,25,"pl-c1"],[25,26,"pl-s"],[31,32,"pl-s"],[32,49,"pl-c1"],[49,50,"pl-s"]],[[0,1,"pl-s"],[1,8,"pl-c1"],[8,9,"pl-s"],[20,21,"pl-s"],[21,42,"pl-c1"],[42,43,"pl-s"],[53,54,"pl-s"],[54,62,"pl-c1"],[62,63,"pl-s"]],[[0,1,"pl-s"],[1,21,"pl-c1"],[21,22,"pl-s"]],[],[[27,28,"pl-s"],[28,34,"pl-c1"],[34,35,"pl-s"]],[],[[0,3,"pl-s"],[3,7,"pl-en"]],[[47,84,"pl-c"],[47,48,"pl-c"]],[],[[71,89,"pl-c"],[71,72,"pl-c"]],[[0,25,"pl-c"],[0,1,"pl-c"]],[],[],[[0,3,"pl-s"]],[],[],[],[[0,1,"pl-v"],[25,26,"pl-s"],[26,36,"pl-c1"],[36,37,"pl-s"],[40,41,"pl-s"],[41,51,"pl-c1"],[51,52,"pl-s"],[55,56,"pl-s"],[56,65,"pl-c1"],[65,66,"pl-s"]],[[3,4,"pl-s"],[4,51,"pl-c1"],[51,52,"pl-s"]],[[2,3,"pl-s"],[3,27,"pl-c1"],[27,28,"pl-s"],[49,50,"pl-s"],[50,59,"pl-c1"],[59,60,"pl-s"]],[],[[0,1,"pl-v"],[5,6,"pl-s"],[6,12,"pl-c1"],[12,13,"pl-s"],[56,57,"pl-s"],[57,62,"pl-c1"],[62,63,"pl-s"]],[[5,6,"pl-s"],[6,15,"pl-c1"],[15,16,"pl-s"],[20,21,"pl-s"],[21,41,"pl-c1"],[41,42,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,40,"pl-c1"],[40,41,"pl-s"],[45,46,"pl-s"],[46,50,"pl-c1"],[50,51,"pl-s"]],[[43,44,"pl-s"],[44,50,"pl-c1"],[50,51,"pl-s"],[53,54,"pl-s"],[54,69,"pl-c1"],[69,70,"pl-s"]],[[26,27,"pl-s"],[27,46,"pl-c1"],[46,47,"pl-s"],[62,63,"pl-s"],[63,68,"pl-c1"],[68,69,"pl-s"]],[[24,25,"pl-s"],[25,42,"pl-c1"],[42,43,"pl-s"]],[[2,3,"pl-s"],[3,23,"pl-c1"],[23,24,"pl-s"],[26,27,"pl-s"],[27,51,"pl-c1"],[51,52,"pl-s"]],[],[[0,1,"pl-v"],[2,3,"pl-s"],[3,23,"pl-c1"],[23,24,"pl-s"]],[[2,3,"pl-s"],[3,8,"pl-c1"],[8,9,"pl-s"],[21,22,"pl-s"],[22,32,"pl-c1"],[32,33,"pl-s"]],[[67,68,"pl-s"],[68,73,"pl-c1"],[73,74,"pl-s"]],[[47,48,"pl-s"],[48,54,"pl-c1"],[54,55,"pl-s"]],[[2,3,"pl-s"],[3,13,"pl-c1"],[13,14,"pl-s"],[64,65,"pl-s"],[65,74,"pl-c1"],[74,75,"pl-s"]],[],[[2,3,"pl-s"],[3,61,"pl-c1"],[61,62,"pl-s"]],[[3,4,"pl-s"],[4,37,"pl-c1"],[37,38,"pl-s"]],[[0,1,"pl-v"],[38,39,"pl-s"],[39,45,"pl-c1"],[45,46,"pl-s"],[47,48,"pl-s"],[48,54,"pl-c1"],[54,55,"pl-s"],[60,61,"pl-s"],[61,68,"pl-c1"],[68,69,"pl-s"]],[[26,30,"pl-s"],[27,30,"pl-corl"]],[],[[43,44,"pl-s"],[44,80,"pl-c1"],[80,81,"pl-s"]],[[0,1,"pl-s"],[1,7,"pl-c1"],[7,8,"pl-s"],[17,18,"pl-s"],[18,26,"pl-c1"],[26,27,"pl-s"],[28,29,"pl-s"],[29,38,"pl-c1"],[38,39,"pl-s"],[40,41,"pl-s"],[41,49,"pl-c1"],[49,50,"pl-s"],[51,52,"pl-s"],[52,62,"pl-c1"],[62,63,"pl-s"],[64,65,"pl-s"],[65,70,"pl-c1"],[70,71,"pl-s"]],[[0,1,"pl-s"],[1,12,"pl-c1"],[12,13,"pl-s"],[19,20,"pl-s"],[20,40,"pl-c1"],[40,41,"pl-s"],[70,71,"pl-s"],[71,77,"pl-c1"],[77,78,"pl-s"]],[[34,35,"pl-s"],[35,42,"pl-c1"],[42,43,"pl-s"]],[],[],[[0,3,"pl-s"],[3,7,"pl-en"]],[[29,30,"pl-k"],[34,73,"pl-s"],[34,35,"pl-pds"],[72,73,"pl-pds"]],[[21,22,"pl-k"],[26,38,"pl-s"],[26,27,"pl-pds"],[37,38,"pl-pds"]],[[46,79,"pl-c"],[46,47,"pl-c"]],[[0,3,"pl-s"]],[],[[0,24,"pl-mh"],[4,24,"pl-en"]],[],[[26,27,"pl-s"],[27,41,"pl-c1"],[41,42,"pl-s"],[43,44,"pl-s"],[44,59,"pl-c1"],[59,60,"pl-s"],[61,62,"pl-s"],[62,77,"pl-c1"],[77,78,"pl-s"]],[[45,46,"pl-s"],[46,53,"pl-c1"],[53,54,"pl-s"]],[],[[0,3,"pl-s"],[3,7,"pl-en"]],[[0,74,"pl-c"],[0,1,"pl-c"]],[],[],[],[[0,69,"pl-c"],[0,1,"pl-c"]],[],[],[[0,3,"pl-s"]],[],[[10,11,"pl-s"],[11,26,"pl-c1"],[26,27,"pl-s"],[40,41,"pl-s"],[41,50,"pl-c1"],[50,51,"pl-s"]],[],[],[],[[0,32,"pl-mh"],[3,32,"pl-en"],[27,31,"pl-s"],[28,31,"pl-corl"]],[],[[37,38,"pl-s"],[38,72,"pl-c1"],[72,73,"pl-s"]],[],[],[],[[0,3,"pl-s"],[3,7,"pl-en"]],[[44,70,"pl-c"],[44,45,"pl-c"]],[[44,62,"pl-c"],[44,45,"pl-c"]],[[44,62,"pl-c"],[44,45,"pl-c"]],[[44,80,"pl-c"],[44,45,"pl-c"]],[[0,3,"pl-s"]],[],[[5,6,"pl-s"],[6,30,"pl-c1"],[30,31,"pl-s"]],[[1,2,"pl-s"],[2,38,"pl-c1"],[38,39,"pl-s"],[48,49,"pl-s"],[49,60,"pl-c1"],[60,61,"pl-s"]],[],[],[[0,27,"pl-mh"],[3,27,"pl-en"]],[],[[15,16,"pl-s"],[16,31,"pl-c1"],[31,32,"pl-s"]],[],[[0,3,"pl-s"],[3,7,"pl-en"]],[],[],[[0,3,"pl-s"]],[],[[4,5,"pl-s"],[16,17,"pl-s"],[17,18,"pl-s"],[18,29,"pl-corl"],[29,30,"pl-s"]],[],[[0,28,"pl-mh"],[3,28,"pl-en"],[23,27,"pl-s"],[24,27,"pl-corl"]],[],[[54,56,"pl-s"],[64,66,"pl-s"],[68,69,"pl-s"],[69,73,"pl-c1"],[73,74,"pl-s"]],[[0,1,"pl-s"],[1,5,"pl-c1"],[5,6,"pl-s"],[9,10,"pl-s"],[10,14,"pl-c1"],[14,15,"pl-s"],[70,71,"pl-s"],[71,75,"pl-c1"],[75,76,"pl-s"]],[[15,16,"pl-s"],[16,20,"pl-c1"],[20,21,"pl-s"],[35,36,"pl-s"],[36,46,"pl-c1"],[46,47,"pl-s"]],[[11,12,"pl-s"],[12,16,"pl-c1"],[16,17,"pl-s"],[24,26,"pl-s"],[31,32,"pl-s"],[32,36,"pl-c1"],[36,37,"pl-s"],[37,39,"pl-s"]],[],[[0,3,"pl-s"],[3,7,"pl-en"]],[],[],[],[[0,3,"pl-s"]],[],[[21,22,"pl-s"],[22,26,"pl-c1"],[26,27,"pl-s"]],[],[[0,3,"pl-s"],[3,7,"pl-en"]],[[48,51,"pl-c"],[48,49,"pl-c"]],[[48,51,"pl-c"],[48,49,"pl-c"]],[[0,3,"pl-s"]],[],[[15,16,"pl-s"],[16,20,"pl-c1"],[20,21,"pl-s"],[51,52,"pl-s"],[52,56,"pl-c1"],[56,57,"pl-s"]],[[45,46,"pl-s"],[46,59,"pl-c1"],[59,60,"pl-s"]],[],[[15,16,"pl-s"],[16,20,"pl-c1"],[20,21,"pl-s"]],[[68,72,"pl-s"],[69,72,"pl-corl"],[73,77,"pl-s"],[74,77,"pl-corl"]],[[42,43,"pl-s"],[43,58,"pl-c1"],[58,59,"pl-s"]],[]],"colorizedLines":null}},"title":"2dph/docs/runbook.md at main · eSlider/2dph","appPayload":{},"meta":{"title":"2dph/docs/runbook.md at main · eSlider/2dph"}}</script>
  <div data-target="react-app.reactRoot"><meta name="github-code-view-meta-stats" id="github-code-view-meta-stats" data-hydrostats="publish"/> <!-- --> <a hidden="" id="code-view-repo-link" href="/eSlider/2dph" data-discover="true"></a> <div class="d-none"></div><div><div style="--spacing:var(--spacing-none)" class="prc-PageLayout-PageLayoutRoot--KH-d" data-component="SplitPageLayout" data-has-sidebar="true"><div class="prc-PageLayout-SidebarWrapper-kLG4B CopilotSidePanelSidebar-module__SidePanel__L3O0C CopilotSidePanelSidebar-module__HiddenSidePanel__TBRGn" style="--spacing-column:var(--spacing-none)" data-is-hidden="false" data-position="end" data-sticky="true" data-responsive-variant="fullscreen"><div class="prc-PageLayout-VerticalDivider-9QRmK prc-PageLayout-SidebarVerticalDivider-0Rl0V" data-component="PageLayout.VerticalDivider" data-variant="line" data-position="end" style="--spacing:var(--spacing-none)"><div class="prc-PageLayout-DraggableHandle-9s6B4" data-component="PageLayout.DragHandle" role="slider" aria-label="Draggable pane splitter" aria-valuemin="450" aria-valuemax="768" aria-valuenow="544" aria-valuetext="Pane width 544 pixels" tabindex="0"></div></div><div class="prc-PageLayout-Sidebar-iciWg" data-component="SplitPageLayout.Sidebar" data-resizable="true" style="--spacing:var(--spacing-normal);--pane-min-width:450px;--pane-max-width:768px;--pane-width-custom:544px;--pane-width-size:var(--pane-width-custom);--pane-width:544px"><div class="height-full" data-testid="copilot-code-view-side-panel"><div id="copilot-side-panel-content" class="height-full"></div></div></div></div><div class="prc-PageLayout-PageLayoutWrapper-2BhU2" data-width="full"><div class="prc-PageLayout-PageLayoutContent-BneH9"><div id="repos-file-tree-sidebar" class="CodeViewFileTreeLayout-module__sidebar__n_Aau" tabindex="0"><div class="prc-PageLayout-PaneWrapper-pHPop ReposFileTreePane-module__Pane__rBZpI ReposFileTreePane-module__HideTree__AYZnm ReposFileTreePane-module__HidePane__VHAVt" style="--offset-header:0px;--spacing-row:var(--spacing-none);--spacing-column:var(--spacing-none)" data-is-hidden="false" data-position="start" data-sticky="true"><div class="prc-PageLayout-HorizontalDivider-JLVqp prc-PageLayout-PaneHorizontalDivider-9tbnE" data-component="PageLayout.HorizontalDivider" data-variant-regular="none" data-variant-narrow="none" data-position="start" style="--spacing-divider:var(--spacing-none);--spacing:var(--spacing-none)"></div><div class="prc-PageLayout-Pane-AyzHK" data-component="SplitPageLayout.Pane" data-resizable="true" style="--spacing:var(--spacing-none);--pane-min-width:256px;--pane-max-width:calc(100vw - var(--pane-max-width-diff));--pane-width-size:var(--pane-width-large);--pane-width:320px"></div><div class="prc-PageLayout-VerticalDivider-9QRmK prc-PageLayout-PaneVerticalDivider-le57g" data-component="PageLayout.VerticalDivider" data-variant-narrow="none" data-variant-regular="line" data-variant-wide="line" data-position="start" style="--spacing:var(--spacing-none)"><div class="prc-PageLayout-DraggableHandle-9s6B4" data-component="PageLayout.DragHandle" role="slider" aria-label="Draggable pane splitter" aria-valuemin="256" aria-valuemax="600" aria-valuenow="320" aria-valuetext="Pane width 320 pixels" tabindex="0"></div></div></div></div><div data-component="SplitPageLayout.Content" class="prc-PageLayout-ContentWrapper-gR9eG"><div class="prc-PageLayout-Content-xWL-A" data-width="full" style="--spacing:var(--spacing-none)"><div class="SharedPageLayout-module__content__IwGAp" data-selector="repos-split-pane-content" id="repos-split-pane-content" tabindex="0"> <!-- --> <div class="container CodeViewHeader-module__Box__JkPOb"><div class="CodeViewHeader-module__StickyHeader__Qn7UN" id="StickyHeader"><div class="CodeViewHeader-module__Box_1__SbNDV"><div class="CodeViewHeader-module__Box_2__TB46f"><div class="react-code-view-header-wrap--narrow CodeViewHeader-module__Box_3__q1zUL"><div class="CodeViewHeader-module__treeToggleWrapper__RQ__9"><h2 class="use-tree-pane-module__Heading__s4QbZ prc-Heading-Heading-MtWFE" data-component="Heading"><button data-component="Button" type="button" aria-label="Expand file tree" data-testid="expand-file-tree-button-mobile" class="prc-Button-ButtonBase-9n-Xk ExpandFileTreeButton-module__Button_1__Svs95" data-loading="false" data-size="medium" data-variant="invisible"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-Iohp5"><span data-component="leadingVisual" class="prc-Button-Visual-YNt2F prc-Button-LeadingVisual-UySKu prc-Button-VisualWrap-E4cnq"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-arrow-left" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M7.78 12.53a.75.75 0 0 1-1.06 0L2.47 8.28a.75.75 0 0 1 0-1.06l4.25-4.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042L4.81 7h7.44a.75.75 0 0 1 0 1.5H4.81l2.97 2.97a.75.75 0 0 1 0 1.06Z"></path></svg></span><span data-component="text" class="prc-Button-Label-FWkx3">Files</span></span></button><button data-component="IconButton" type="button" data-testid="expand-file-tree-button" aria-controls="repos-file-tree" class="prc-Button-ButtonBase-9n-Xk position-relative ExpandFileTreeButton-module__expandButton__hDOcv ExpandFileTreeButton-module__filesButtonBreakpoint__zEvz3 fgColor-muted prc-Button-IconButton-fyge7" data-loading="false" data-no-visuals="true" data-size="medium" data-variant="invisible" aria-labelledby="_R_4lla9lik5_"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-sidebar-collapse" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M6.823 7.823a.25.25 0 0 1 0 .354l-2.396 2.396A.25.25 0 0 1 4 10.396V5.604a.25.25 0 0 1 .427-.177Z"></path><path d="M1.75 0h12.5C15.216 0 16 .784 16 1.75v12.5A1.75 1.75 0 0 1 14.25 16H1.75A1.75 1.75 0 0 1 0 14.25V1.75C0 .784.784 0 1.75 0ZM1.5 1.75v12.5c0 .138.112.25.25.25H9.5v-13H1.75a.25.25 0 0 0-.25.25ZM11 14.5h3.25a.25.25 0 0 0 .25-.25V1.75a.25.25 0 0 0-.25-.25H11Z"></path></svg></button><span class="prc-TooltipV2-Tooltip-tLeuB" data-direction="se" data-component="Tooltip" aria-hidden="true" id="_R_4lla9lik5_">Expand file tree</span><div class="d-none"></div></h2></div><div class="react-code-view-header-mb--narrow mr-2"><button data-component="Button" type="button" aria-haspopup="true" aria-expanded="false" tabindex="0" aria-label="main branch" data-testid="anchor-button" data-icv-name="Switch branches/tags" class="prc-Button-ButtonBase-9n-Xk ref-selector-class RefSelectorAnchoredOverlay-module__RefSelectorOverlayBtn__a3WK3" data-loading="false" data-size="medium" data-variant="default" id="ref-picker-repos-header-ref-selector-wide"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-Iohp5"><span data-component="leadingVisual" class="prc-Button-Visual-YNt2F prc-Button-LeadingVisual-UySKu prc-Button-VisualWrap-E4cnq"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-git-branch" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M9.5 3.25a2.25 2.25 0 1 1 3 2.122V6A2.5 2.5 0 0 1 10 8.5H6a1 1 0 0 0-1 1v1.128a2.251 2.251 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.5 0v1.836A2.493 2.493 0 0 1 6 7h4a1 1 0 0 0 1-1v-.628A2.25 2.25 0 0 1 9.5 3.25Zm-6 0a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Zm8.25-.75a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5ZM4.25 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Z"></path></svg></span><span data-component="text" class="prc-Button-Label-FWkx3"><div class="RefSelectorAnchoredOverlay-module__RefSelectorOverlayContainer__yaf4p"><div style="max-width:125px" class="ref-selector-button-text-container RefSelectorAnchoredOverlay-module__RefSelectorBtnTextContainer__Di3rk"><span class="RefSelectorAnchoredOverlay-module__RefSelectorText__w_fmP">main</span></div></div></span><span data-component="trailingVisual" class="prc-Button-Visual-YNt2F prc-Button-VisualWrap-E4cnq"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-triangle-down" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m4.427 7.427 3.396 3.396a.25.25 0 0 0 .354 0l3.396-3.396A.25.25 0 0 0 11.396 7H4.604a.25.25 0 0 0-.177.427Z"></path></svg></span></span></button><div class="d-none"></div></div><div class="react-code-view-header-mb--narrow CodeViewHeader-module__Box_5__MQ0hL"><div class="Breadcrumb-module__container__Vxvev Breadcrumb-module__lg__Rjz0A"><nav data-testid="breadcrumbs" aria-labelledby="repos-header-breadcrumb-heading" id="repos-header-breadcrumb" class="Breadcrumb-module__nav__rQFDj"><h2 class="sr-only ScreenReaderHeading-module__userSelectNone__rwWIk prc-Heading-Heading-MtWFE" data-component="Heading" data-testid="screen-reader-heading" id="repos-header-breadcrumb-heading">Breadcrumbs</h2><ol class="Breadcrumb-module__list__ZH6zr"><li class="Breadcrumb-module__listItem__Ib0x_"><a class="Breadcrumb-module__repoLink__O2Nbs prc-Link-Link-9ZwDx" data-component="Link" data-testid="breadcrumbs-repo-link" href="/eSlider/2dph/tree/main" data-discover="true">2dph</a></li><li class="Breadcrumb-module__listItem__Ib0x_"><span class="Breadcrumb-module__separator__eNwsI Breadcrumb-module__lg__Rjz0A" aria-hidden="true">/</span><a class="Breadcrumb-module__directoryLink__kQy_t prc-Link-Link-9ZwDx" data-component="Link" href="/eSlider/2dph/tree/main/docs" data-discover="true">docs</a></li></ol></nav><div data-testid="breadcrumbs-filename" class="Breadcrumb-module__filename__equZR"><span class="Breadcrumb-module__separator__eNwsI Breadcrumb-module__lg__Rjz0A" aria-hidden="true">/</span><h1 class="Breadcrumb-module__filenameHeading__MNMtw Breadcrumb-module__lg__Rjz0A prc-Heading-Heading-MtWFE" data-component="Heading" tabindex="-1" id="file-name-id">runbook.md</h1></div><button data-component="IconButton" type="button" class="prc-Button-ButtonBase-9n-Xk ml-2 prc-Button-IconButton-fyge7" data-loading="false" data-no-visuals="true" data-size="small" data-variant="invisible" aria-labelledby="_R_7lla9lik5_"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-copy" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path></svg></button><span class="CopyToClipboardIconButton-module__tooltip__WyiwL prc-TooltipV2-Tooltip-tLeuB" data-direction="nw" data-component="Tooltip" aria-label="Copy path" aria-hidden="true" id="_R_7lla9lik5_">Copy path</span></div></div></div><div class="react-code-view-header-element--wide"><div class="CodeViewHeader-module__Box_7___0R6c"><div class="d-flex gap-2"><div><div class="CodeViewHeader-module__FileResultsList__JDzUy"><span class="d-flex FileResultsList-module__FilesSearchBox__ivVkc TextInput-wrapper prc-components-TextInputWrapper-Hpdqi prc-components-TextInputBaseWrapper-wY-n0" data-no-trailing-action="true" data-component="TextInput" data-leading-visual="true" data-trailing-visual="true" aria-busy="false"><span class="TextInput-icon" id="_R_1cpla9lik5_" aria-hidden="true" data-component="TextInput.LeadingVisual"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-search" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M10.68 11.74a6 6 0 0 1-7.922-8.982 6 6 0 0 1 8.982 7.922l3.04 3.04a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215ZM11.5 7a4.499 4.499 0 1 0-8.997 0A4.499 4.499 0 0 0 11.5 7Z"></path></svg></span><input type="text" aria-label="Go to file" role="combobox" aria-controls="file-results-list" aria-expanded="false" aria-haspopup="dialog" autoCorrect="off" spellCheck="false" placeholder="Go to file" aria-describedby="_R_1cpla9lik5_ _R_1cpla9lik5H1_" data-component="input" class="prc-components-Input-IwWrt" value=""/><span class="TextInput-icon" id="_R_1cpla9lik5H1_" aria-hidden="true" data-component="TextInput.TrailingVisual"></span></span></div><div class="d-none"></div></div><button data-component="Button" type="button" style="display:none" class="prc-Button-ButtonBase-9n-Xk NavigationMenu-module__Button__LpKgm" data-loading="false" data-no-visuals="true" data-size="medium" data-variant="default"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-Iohp5"><span data-component="text" class="prc-Button-Label-FWkx3">Blame</span></span></button><div class="d-none"></div><button data-component="IconButton" type="button" data-testid="more-file-actions-button-nav-menu-wide" aria-haspopup="true" aria-expanded="false" tabindex="0" class="prc-Button-ButtonBase-9n-Xk js-blob-dropdown-click NavigationMenu-module__IconButton__HpX3G prc-Button-IconButton-fyge7" data-loading="false" data-no-visuals="true" data-size="medium" data-variant="default" aria-labelledby="_R_7p9la9lik5_" id="_R_99la9lik5_"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-kebab-horizontal" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M8 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM1.5 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Zm13 0a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z"></path></svg></button><span class="prc-TooltipV2-Tooltip-tLeuB" data-direction="nw" data-component="Tooltip" aria-hidden="true" id="_R_7p9la9lik5_">More file actions</span></div></div></div><div class="react-code-view-header-element--narrow"><div class="CodeViewHeader-module__Box_7___0R6c"><div class="d-flex gap-2"><button data-component="Button" type="button" style="display:none" class="prc-Button-ButtonBase-9n-Xk NavigationMenu-module__Button__LpKgm" data-loading="false" data-no-visuals="true" data-size="medium" data-variant="default"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-Iohp5"><span data-component="text" class="prc-Button-Label-FWkx3">Blame</span></span></button><div class="d-none"></div><button data-component="IconButton" type="button" data-testid="more-file-actions-button-nav-menu-narrow" aria-haspopup="true" aria-expanded="false" tabindex="0" class="prc-Button-ButtonBase-9n-Xk js-blob-dropdown-click NavigationMenu-module__IconButton__HpX3G prc-Button-IconButton-fyge7" data-loading="false" data-no-visuals="true" data-size="medium" data-variant="default" aria-labelledby="_R_7pdla9lik5_" id="_R_9dla9lik5_"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-kebab-horizontal" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M8 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM1.5 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Zm13 0a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z"></path></svg></button><span class="prc-TooltipV2-Tooltip-tLeuB" data-direction="nw" data-component="Tooltip" aria-hidden="true" id="_R_7pdla9lik5_">More file actions</span></div></div></div></div></div></div></div><div class="CodeView-module__contentWrapper__cG2JH"><div class="react-code-view-bottom-padding"><div class="BlobTopBanners-module__Box__v_nvx"></div></div> <div class="d-none"></div><div class="d-flex flex-column border rounded-2 tmp-mb-3 pl-1"><div class="LatestCommit-module__Box__B25ZT"><h2 class="sr-only ScreenReaderHeading-module__userSelectNone__rwWIk prc-Heading-Heading-MtWFE" data-component="Heading" data-testid="screen-reader-heading">Latest commit</h2><div style="width:120px" class="Skeleton Skeleton--text" data-testid="loading"> </div><div class="d-flex flex-shrink-0 gap-2"><div data-testid="latest-commit-details" class="d-none d-sm-flex flex-items-center"></div><div class="d-flex gap-2"><h2 class="sr-only ScreenReaderHeading-module__userSelectNone__rwWIk prc-Heading-Heading-MtWFE" data-component="Heading" data-testid="screen-reader-heading">History</h2><a data-component="LinkButton" href="/eSlider/2dph/commits/main/docs/runbook.md" class="prc-Button-ButtonBase-9n-Xk d-none d-lg-flex LinkButton-module__linkButton__nFnov flex-items-center fgColor-default" data-loading="false" data-size="small" data-variant="invisible"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-Iohp5"><span data-component="leadingVisual" class="prc-Button-Visual-YNt2F prc-Button-LeadingVisual-UySKu prc-Button-VisualWrap-E4cnq"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-history" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m.427 1.927 1.215 1.215a8.002 8.002 0 1 1-1.6 5.685.75.75 0 1 1 1.493-.154 6.5 6.5 0 1 0 1.18-4.458l1.358 1.358A.25.25 0 0 1 3.896 6H.25A.25.25 0 0 1 0 5.75V2.104a.25.25 0 0 1 .427-.177ZM7.75 4a.75.75 0 0 1 .75.75v2.992l2.028.812a.75.75 0 0 1-.557 1.392l-2.5-1A.751.751 0 0 1 7 8.25v-3.5A.75.75 0 0 1 7.75 4Z"></path></svg></span><span data-component="text" class="prc-Button-Label-FWkx3"><span class="fgColor-default">History</span></span></span></a><div class="d-sm-none"></div><div class="d-flex d-lg-none"><a data-component="LinkButton" aria-label="View commit history for this file." href="/eSlider/2dph/commits/main/docs/runbook.md" class="prc-Button-ButtonBase-9n-Xk LinkButton-module__linkButton__nFnov flex-items-center fgColor-default" data-loading="false" data-size="small" data-variant="invisible" aria-describedby="_R_4mlala9lik5_"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-Iohp5"><span data-component="leadingVisual" class="prc-Button-Visual-YNt2F prc-Button-LeadingVisual-UySKu prc-Button-VisualWrap-E4cnq"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-history" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m.427 1.927 1.215 1.215a8.002 8.002 0 1 1-1.6 5.685.75.75 0 1 1 1.493-.154 6.5 6.5 0 1 0 1.18-4.458l1.358 1.358A.25.25 0 0 1 3.896 6H.25A.25.25 0 0 1 0 5.75V2.104a.25.25 0 0 1 .427-.177ZM7.75 4a.75.75 0 0 1 .75.75v2.992l2.028.812a.75.75 0 0 1-.557 1.392l-2.5-1A.751.751 0 0 1 7 8.25v-3.5A.75.75 0 0 1 7.75 4Z"></path></svg></span></span></a><span class="prc-TooltipV2-Tooltip-tLeuB" data-direction="s" data-component="Tooltip" role="tooltip" aria-hidden="true" id="_R_4mlala9lik5_">History</span></div></div></div></div></div><div class="d-flex flex-row"><div class="container BlobViewContent-module__blobContainer__DtH2d"><div class="react-code-size-details-banner BlobViewContent-module__codeSizeDetails__e5sUw"><div class="react-code-size-details-banner CodeSizeDetails-module__Box__VcD6l"><div class="text-mono CodeSizeDetails-module__Box_1__GVxQL"><div data-testid="blob-size" class="CodeSizeDetails-module__Truncate_1__lE93V prc-Truncate-Truncate-2G1eo" data-inline="true" title="25.5 KB" style="--truncate-max-width:100%"><span>507 lines (404 loc) · 25.5 KB</span></div></div></div></div><div class="react-blob-view-header-sticky BlobViewContent-module__stickyHeader__VwxB5" id="repos-sticky-header"><div class="BlobViewHeader-module__Box__yhm9u"><div class="react-blob-sticky-header"><div class="FileNameStickyHeader-module__outerWrapper__ZL4Xc FileNameStickyHeader-module__outerWrapperHidden__Zpynk"><div class="FileNameStickyHeader-module__Box_1__Hazu5"><div class="FileNameStickyHeader-module__Box_2__hoolP"><div class="FileNameStickyHeader-module__Box_3__MVKsk"><button data-component="Button" type="button" aria-haspopup="true" aria-expanded="false" tabindex="0" aria-label="main branch" data-testid="anchor-button" data-icv-name="Switch branches/tags" class="prc-Button-ButtonBase-9n-Xk ref-selector-class RefSelectorAnchoredOverlay-module__RefSelectorOverlayBtn__a3WK3" data-loading="false" data-size="medium" data-variant="default" id="ref-picker-repos-header-ref-selector"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-Iohp5"><span data-component="leadingVisual" class="prc-Button-Visual-YNt2F prc-Button-LeadingVisual-UySKu prc-Button-VisualWrap-E4cnq"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-git-branch" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M9.5 3.25a2.25 2.25 0 1 1 3 2.122V6A2.5 2.5 0 0 1 10 8.5H6a1 1 0 0 0-1 1v1.128a2.251 2.251 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.5 0v1.836A2.493 2.493 0 0 1 6 7h4a1 1 0 0 0 1-1v-.628A2.25 2.25 0 0 1 9.5 3.25Zm-6 0a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Zm8.25-.75a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5ZM4.25 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Z"></path></svg></span><span data-component="text" class="prc-Button-Label-FWkx3"><div class="RefSelectorAnchoredOverlay-module__RefSelectorOverlayContainer__yaf4p"><div style="max-width:125px" class="ref-selector-button-text-container RefSelectorAnchoredOverlay-module__RefSelectorBtnTextContainer__Di3rk"><span class="RefSelectorAnchoredOverlay-module__RefSelectorText__w_fmP">main</span></div></div></span><span data-component="trailingVisual" class="prc-Button-Visual-YNt2F prc-Button-VisualWrap-E4cnq"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-triangle-down" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m4.427 7.427 3.396 3.396a.25.25 0 0 0 .354 0l3.396-3.396A.25.25 0 0 0 11.396 7H4.604a.25.25 0 0 0-.177.427Z"></path></svg></span></span></button><div class="d-none"></div></div><div class="FileNameStickyHeader-module__Box_4__FLhtt"><div class="Breadcrumb-module__container__Vxvev Breadcrumb-module__md__Wb1Gs"><nav data-testid="breadcrumbs" aria-labelledby="sticky-breadcrumb-heading" id="sticky-breadcrumb" class="Breadcrumb-module__nav__rQFDj"><h2 class="sr-only ScreenReaderHeading-module__userSelectNone__rwWIk prc-Heading-Heading-MtWFE" data-component="Heading" data-testid="screen-reader-heading" id="sticky-breadcrumb-heading">Breadcrumbs</h2><ol class="Breadcrumb-module__list__ZH6zr"><li class="Breadcrumb-module__listItem__Ib0x_"><a class="Breadcrumb-module__repoLink__O2Nbs prc-Link-Link-9ZwDx" data-component="Link" data-testid="breadcrumbs-repo-link" href="/eSlider/2dph/tree/main" data-discover="true">2dph</a></li><li class="Breadcrumb-module__listItem__Ib0x_"><span class="Breadcrumb-module__separator__eNwsI Breadcrumb-module__md__Wb1Gs" aria-hidden="true">/</span><a class="Breadcrumb-module__directoryLink__kQy_t prc-Link-Link-9ZwDx" data-component="Link" href="/eSlider/2dph/tree/main/docs" data-discover="true">docs</a></li></ol></nav><div data-testid="breadcrumbs-filename" class="Breadcrumb-module__filename__equZR"><span class="Breadcrumb-module__separator__eNwsI Breadcrumb-module__md__Wb1Gs" aria-hidden="true">/</span><h1 class="Breadcrumb-module__filenameHeading__MNMtw Breadcrumb-module__md__Wb1Gs prc-Heading-Heading-MtWFE" data-component="Heading" tabindex="-1" id="sticky-file-name-id">runbook.md</h1></div><button data-component="IconButton" type="button" class="prc-Button-ButtonBase-9n-Xk ml-2 prc-Button-IconButton-fyge7" data-loading="false" data-no-visuals="true" data-size="small" data-variant="invisible" aria-labelledby="_R_7lcpala9lik5_"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-copy" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path></svg></button><span class="CopyToClipboardIconButton-module__tooltip__WyiwL prc-TooltipV2-Tooltip-tLeuB" data-direction="s" data-component="Tooltip" aria-label="Copy path" aria-hidden="true" id="_R_7lcpala9lik5_">Copy path</span></div></div></div><button data-component="Button" type="button" class="prc-Button-ButtonBase-9n-Xk FileNameStickyHeader-module__Button__LSEU_ FileNameStickyHeader-module__GoToTopButton__nxAFn" data-loading="false" data-size="small" data-variant="invisible"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-Iohp5"><span data-component="leadingVisual" class="prc-Button-Visual-YNt2F prc-Button-LeadingVisual-UySKu prc-Button-VisualWrap-E4cnq"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-arrow-up" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M3.47 7.78a.75.75 0 0 1 0-1.06l4.25-4.25a.75.75 0 0 1 1.06 0l4.25 4.25a.751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018L9 4.81v7.44a.75.75 0 0 1-1.5 0V4.81L4.53 7.78a.75.75 0 0 1-1.06 0Z"></path></svg></span><span data-component="text" class="prc-Button-Label-FWkx3">Top</span></span></button></div></div></div><div class="BlobViewHeader-module__Box_1__VEmuQ"><h2 class="sr-only ScreenReaderHeading-module__userSelectNone__rwWIk prc-Heading-Heading-MtWFE" data-component="Heading" data-testid="screen-reader-heading">File metadata and controls</h2><div class="BlobViewHeader-module__Box_2__icUs2"><ul aria-label="File view" class="prc-SegmentedControl-SegmentedControl-lqIXp BlobTabButtons-module__SegmentedControl__jen2u" data-variant="default" data-size="small" data-component="SegmentedControl"><li class="prc-SegmentedControl-Item-tSCQh" data-selected="" data-component="SegmentedControl.Button"><button aria-pressed="true" class="prc-SegmentedControl-Button-E48xz" type="button" style="--separator-color:transparent"><span class="prc-SegmentedControl-Content-1COlk segmentedControl-content"><div class="prc-SegmentedControl-Text-7S2y2 segmentedControl-text" data-text="Preview">Preview</div></span></button></li><li class="prc-SegmentedControl-Item-tSCQh" data-component="SegmentedControl.Button"><button aria-pressed="false" class="prc-SegmentedControl-Button-E48xz" type="button" style="--separator-color:var(--borderColor-default)"><span class="prc-SegmentedControl-Content-1COlk segmentedControl-content"><div class="prc-SegmentedControl-Text-7S2y2 segmentedControl-text" data-text="Code">Code</div></span></button></li><li class="prc-SegmentedControl-Item-tSCQh" data-component="SegmentedControl.Button"><button aria-pressed="false" class="prc-SegmentedControl-Button-E48xz" type="button" style="--separator-color:var(--borderColor-default)"><span class="prc-SegmentedControl-Content-1COlk segmentedControl-content"><div class="prc-SegmentedControl-Text-7S2y2 segmentedControl-text" data-text="Blame">Blame</div></span></button></li></ul><div class="d-none"></div><div class="react-code-size-details-in-header CodeSizeDetails-module__Box__VcD6l"><div class="text-mono CodeSizeDetails-module__Box_1__GVxQL"><div data-testid="blob-size" class="CodeSizeDetails-module__Truncate_1__lE93V prc-Truncate-Truncate-2G1eo" data-inline="true" title="25.5 KB" style="--truncate-max-width:100%"><span>507 lines (404 loc) · 25.5 KB</span></div></div></div></div><div class="BlobViewHeader-module__Box_3__ng6v2"><div class="d-none"></div><div class="react-blob-header-edit-and-raw-actions BlobViewHeader-module__Box_4__J4Y4W"><div class="d-none"></div><div class="prc-ButtonGroup-ButtonGroup-vFUrY" data-component="ButtonGroup"><div class="prc-ButtonGroup-Item-PqvDl"><a data-component="LinkButton" href="https://github.com/eSlider/2dph/raw/refs/heads/main/docs/runbook.md" data-testid="raw-button" class="prc-Button-ButtonBase-9n-Xk LinkButton-module__linkButton__nFnov BlobViewHeader-module__LinkButton__X9kx2" data-loading="false" data-no-visuals="true" data-size="small" data-variant="default"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-Iohp5"><span data-component="text" class="prc-Button-Label-FWkx3">Raw</span></span></a></div><div class="prc-ButtonGroup-Item-PqvDl"><button data-component="IconButton" type="button" data-testid="copy-raw-button" class="prc-Button-ButtonBase-9n-Xk prc-Button-IconButton-fyge7" data-loading="false" data-no-visuals="true" data-size="small" data-variant="default" aria-labelledby="_R_qaucpala9lik5_"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-copy" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path></svg></button><span class="prc-TooltipV2-Tooltip-tLeuB" data-direction="n" data-component="Tooltip" aria-hidden="true" id="_R_qaucpala9lik5_">Copy raw file</span></div><div class="prc-ButtonGroup-Item-PqvDl"><button data-component="IconButton" type="button" data-testid="download-raw-button" class="prc-Button-ButtonBase-9n-Xk BlobViewHeader-module__downloadButton__ef459 prc-Button-IconButton-fyge7" data-loading="false" data-no-visuals="true" data-size="small" data-variant="default" aria-labelledby="_R_eaucpala9lik5_"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-download" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2.75 14A1.75 1.75 0 0 1 1 12.25v-2.5a.75.75 0 0 1 1.5 0v2.5c0 .138.112.25.25.25h10.5a.25.25 0 0 0 .25-.25v-2.5a.75.75 0 0 1 1.5 0v2.5A1.75 1.75 0 0 1 13.25 14Z"></path><path d="M7.25 7.689V2a.75.75 0 0 1 1.5 0v5.689l1.97-1.969a.749.749 0 1 1 1.06 1.06l-3.25 3.25a.749.749 0 0 1-1.06 0L4.22 6.78a.749.749 0 1 1 1.06-1.06l1.97 1.969Z"></path></svg></button><span class="prc-TooltipV2-Tooltip-tLeuB" data-direction="n" data-component="Tooltip" aria-hidden="true" id="_R_eaucpala9lik5_">Download raw file</span></div></div></div><button data-component="IconButton" type="button" aria-pressed="false" class="prc-Button-ButtonBase-9n-Xk tmp-mr-2 TableOfContents-module__IconButton__jrlNM prc-Button-IconButton-fyge7" data-loading="false" data-no-visuals="true" data-size="small" data-variant="invisible" aria-labelledby="_R_3ucpala9lik5_"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-list-unordered" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M5.75 2.5h8.5a.75.75 0 0 1 0 1.5h-8.5a.75.75 0 0 1 0-1.5Zm0 5h8.5a.75.75 0 0 1 0 1.5h-8.5a.75.75 0 0 1 0-1.5Zm0 5h8.5a.75.75 0 0 1 0 1.5h-8.5a.75.75 0 0 1 0-1.5ZM2 14a1 1 0 1 1 0-2 1 1 0 0 1 0 2Zm1-6a1 1 0 1 1-2 0 1 1 0 0 1 2 0ZM2 4a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg></button><span class="prc-TooltipV2-Tooltip-tLeuB" data-direction="n" data-component="Tooltip" aria-hidden="true" id="_R_3ucpala9lik5_">Outline</span><div class="react-blob-header-edit-and-raw-actions-combined"><button data-component="IconButton" type="button" title="More file actions" data-testid="more-file-actions-button" aria-haspopup="true" aria-expanded="false" tabindex="0" class="prc-Button-ButtonBase-9n-Xk js-blob-dropdown-click BlobViewHeader-module__IconButton__XrMQY prc-Button-IconButton-fyge7" data-loading="false" data-no-visuals="true" data-size="small" data-variant="invisible" aria-labelledby="_R_fkecpala9lik5_" id="_R_kecpala9lik5_"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-kebab-horizontal" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M8 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM1.5 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Zm13 0a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z"></path></svg></button><span class="prc-TooltipV2-Tooltip-tLeuB" data-direction="nw" data-component="Tooltip" aria-hidden="true" id="_R_fkecpala9lik5_">Edit and raw actions</span></div></div></div></div><div></div></div><div class="BlobViewContent-module__blobContentWrapper__JS0W6"><section aria-labelledby="file-name-id-wide file-name-id-mobile" class="BlobContent-module__blobContentSection__VOgZq BlobContent-module__blobContentSectionMarkdown__mPLOK" style="margin-top:46px"><div class="js-snippet-clipboard-copy-unpositioned BlobContent-module__markdownBlob__T8jpG" data-hpc="true" containertiming="hpc"><article class="markdown-body entry-content container-lg" itemprop="text"><markdown-accessiblity-table><table>
  <tbody>
  <tr>
    <th>type</th>
    <td>howto</td>
  </tr>
  <tr>
    <th>status</th>
    <td>current</td>
  </tr>
  <tr>
    <th>related</th>
    <td><table>
  <tbody>
  <tr>
  <td><div dir="auto">docs/README.md</div></td>
  <td><div dir="auto">PLAN.md</div></td>
  </tr>
  </tbody>
</table>
</td>
  </tr>
  </tbody>
</table></markdown-accessiblity-table>

<div class="markdown-heading" dir="auto"><h1 tabindex="-1" class="heading-element" dir="auto">Run 2dph (portable)</h1><a id="user-content-run-2dph-portable" class="anchor" aria-label="Permalink: Run 2dph (portable)" href="#run-2dph-portable"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">No laptop-absolute paths. Config lives in env files under <code>$HOME/.config/brain/</code>
(mode 0600), not in git.</p>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">Toolchain</h2><a id="user-content-toolchain" class="anchor" aria-label="Permalink: Toolchain" href="#toolchain"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<ul dir="auto">
<li>Go (see <code>go.mod</code>)</li>
<li>Optional: Docker, Zig CGO via <code>bin/cgo/zig</code> (not gcc)</li>
<li>Optional: poppler (<code>pdftotext</code>/<code>pdftoppm</code>) + tesseract <code>eng+deu</code> for mail OCR</li>
<li>Optional: ghostscript <code>gs</code> — normalizes export-locked / oversized PDFs before
extraction (strips export-protection, shrinks; original preserved, artifact
in <code>var/tmp</code>). Clean PDFs skip it, so the pdftotext fast path stays fast.</li>
<li>Optional: <strong>liteparse service</strong> (epic #219) — документы → структурированный
JSON/YAML. <code>docker compose up -d liteparse</code> (демон с warmup), вызов через
<code>docker compose exec -T liteparse lit parse ...</code> (~100ms против ~700ms
<code>docker run</code>). Инструменты: <code>bin/research/convert.go</code> (→ <code>var/struct-data/&lt;sha256&gt;.yml</code>,
idempotent), <code>bin/research/bench.go</code> (p95). Детали: <code>docs/research-liteparse.md</code>.</li>
</ul>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="eval &quot;$(bin/cgo/zig env)&quot;   # optional; bin/brain/{search,get,stats,eval,serve}.go auto-wrap zig
go test ./..."><pre><span class="pl-c1">eval</span> <span class="pl-s"><span class="pl-pds">"</span><span class="pl-s"><span class="pl-pds">$(</span>bin/cgo/zig env<span class="pl-pds">)</span></span><span class="pl-pds">"</span></span>   <span class="pl-c"><span class="pl-c">#</span> optional; bin/brain/{search,get,stats,eval,serve}.go auto-wrap zig</span>
go <span class="pl-c1">test</span> ./...</pre></div>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">Config</h2><a id="user-content-config" class="anchor" aria-label="Permalink: Config" href="#config"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<markdown-accessiblity-table><table>
<thead>
<tr>
<th>File / env</th>
<th>Purpose</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>$BRAIN_SEARCH_ENV</code> (default <code>$HOME/.config/brain/search.env</code>)</td>
<td><code>BRAIN_SEARCH_URL</code> (SearXNG). Optional Basic Auth.</td>
</tr>
<tr>
<td><code>$HOME/.config/brain/db-profiles.yml</code></td>
<td>read-only Postgres profiles (OnlyOffice via tunnel)</td>
</tr>
</tbody>
</table></markdown-accessiblity-table>
<p dir="auto">If the host already runs SearXNG, point <code>BRAIN_SEARCH_URL</code> at it. Do not start
a second copy (D3). Optional Compose instance:</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="SEARXNG_SECRET=$(openssl rand -hex 32) docker compose --profile searxng up -d"><pre>SEARXNG_SECRET=<span class="pl-s"><span class="pl-pds">$(</span>openssl rand -hex 32<span class="pl-pds">)</span></span> docker compose --profile searxng up -d</pre></div>
<p dir="auto">That binds <code>127.0.0.1:8888</code>. JSON format must stay enabled.</p>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">Index then search</h2><a id="user-content-index-then-search" class="anchor" aria-label="Permalink: Index then search" href="#index-then-search"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Write path is <code>bin/brain/add.go</code> for a leaf (or <code>POST /ingest</code>). Bulk
corpus rebuild remains <code>bin/brain/index.go --rebuild</code> (Compose profile
<code>index</code>). Do not DROP INDEX on Ladybug 0.19.</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="bin/brain/add.go --text &quot;arc-1 runs Matrix&quot; --root facts --source &quot;compose.yml x docker ps&quot;
bin/brain/index.go --rebuild --with-facts --with-chats
bin/brain/search.go &quot;LadybugDB vector index&quot;     # facts → info → web (D17)
bin/brain/search.go &quot;upstream flag&quot; --no-web
bin/brain/search.go &quot;who works where&quot; --as-of 2025-01-01  # D24 intervals
source &lt;(./bin/shell/complete.go bash)            # D23 flaggy complete
bin/brain/get.go &lt;id&gt; --body
bin/brain/stats.go"><pre>bin/brain/add.go --text <span class="pl-s"><span class="pl-pds">"</span>arc-1 runs Matrix<span class="pl-pds">"</span></span> --root facts --source <span class="pl-s"><span class="pl-pds">"</span>compose.yml x docker ps<span class="pl-pds">"</span></span>
bin/brain/index.go --rebuild --with-facts --with-chats
bin/brain/search.go <span class="pl-s"><span class="pl-pds">"</span>LadybugDB vector index<span class="pl-pds">"</span></span>     <span class="pl-c"><span class="pl-c">#</span> facts → info → web (D17)</span>
bin/brain/search.go <span class="pl-s"><span class="pl-pds">"</span>upstream flag<span class="pl-pds">"</span></span> --no-web
bin/brain/search.go <span class="pl-s"><span class="pl-pds">"</span>who works where<span class="pl-pds">"</span></span> --as-of 2025-01-01  <span class="pl-c"><span class="pl-c">#</span> D24 intervals</span>
<span class="pl-c1">source</span> <span class="pl-s"><span class="pl-pds">&lt;(</span>./bin/shell/complete.go bash<span class="pl-pds">)</span></span>            <span class="pl-c"><span class="pl-c">#</span> D23 flaggy complete</span>
bin/brain/get.go <span class="pl-k">&lt;</span>id<span class="pl-k">&gt;</span> --body
bin/brain/stats.go</pre></div>
<p dir="auto"><code>--hop N</code> walks File → Commit → Person from each hit. <code>--as-of YYYY-MM-DD</code>
keeps leafs whose <code>valid_from</code>/<code>valid_to</code> cover that day (empty interval =
legacy always-on). Empty web results are <code>throttled</code>, not absence.
Gap to v1: <a href="/eSlider/2dph/blob/main/docs/roadmap.md">roadmap</a> / <a href="https://git.produktor.io/eSlider/2dph/issues/16" rel="nofollow">epic #16</a>.</p>
<p dir="auto">Ladybug 0.19: never <code>DROP INDEX</code> FTS/VECTOR (ghost catalog). Fresh indexes =
delete <code>var/kb.lbug</code> then <code>--rebuild</code>.</p>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">HTTP / MCP</h2><a id="user-content-http--mcp" class="anchor" aria-label="Permalink: HTTP / MCP" href="#http--mcp"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="scripts/stack/start                 # brain :8630, wait until MCP search/get/audit
scripts/stack/status                # YAML: brain / reasoner / picoclaw / mail_sync
scripts/stack/start-mail-sync       # compose ETL: OO+Gmail sync→import (300s; no auto-rebuild)
scripts/stack/start-assistant       # + qwen3.5:9b + PicoClaw agent (ask the brain)
scripts/stack/start-assistant --no-attach
scripts/stack/stop                  # compose stop; volumes kept"><pre>scripts/stack/start                 <span class="pl-c"><span class="pl-c">#</span> brain :8630, wait until MCP search/get/audit</span>
scripts/stack/status                <span class="pl-c"><span class="pl-c">#</span> YAML: brain / reasoner / picoclaw / mail_sync</span>
scripts/stack/start-mail-sync       <span class="pl-c"><span class="pl-c">#</span> compose ETL: OO+Gmail sync→import (300s; no auto-rebuild)</span>
scripts/stack/start-assistant       <span class="pl-c"><span class="pl-c">#</span> + qwen3.5:9b + PicoClaw agent (ask the brain)</span>
scripts/stack/start-assistant --no-attach
scripts/stack/stop                  <span class="pl-c"><span class="pl-c">#</span> compose stop; volumes kept</span></pre></div>
<p dir="auto">Same Compose services by hand:</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="docker compose up -d brain                       # :8630 Zig CGO serve
docker compose up -d mail-sync                   # ETL loop into kb-var
docker compose --profile index run --rm index    # rebuild
docker compose --profile picoclaw up brain-mcp   # MCP 127.0.0.1:8630"><pre>docker compose up -d brain                       <span class="pl-c"><span class="pl-c">#</span> :8630 Zig CGO serve</span>
docker compose up -d mail-sync                   <span class="pl-c"><span class="pl-c">#</span> ETL loop into kb-var</span>
docker compose --profile index run --rm index    <span class="pl-c"><span class="pl-c">#</span> rebuild</span>
docker compose --profile picoclaw up brain-mcp   <span class="pl-c"><span class="pl-c">#</span> MCP 127.0.0.1:8630</span></pre></div>
<p dir="auto"><code>GET /openapi.json</code>, <code>POST /mcp</code>. Agent tool order: <code>search</code> → <code>get</code> → <code>audit</code>.
See <a href="/eSlider/2dph/blob/main/docs/picoclaw.md">picoclaw.md</a>.</p>
<div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">Brain health-check &amp; restart (#180)</h3><a id="user-content-brain-health-check--restart-180" class="anchor" aria-label="Permalink: Brain health-check &amp; restart (#180)" href="#brain-health-check--restart-180"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">The brain wedged once (2026-08-24): a <code>/ingest</code> write blocked forever inside
the Ladybug C layer, holding the read/write lock, so every search/get/audit
hung while <code>/health</code> still answered <code>ok</code> (it never touches the DB). The static
healthcheck let the container sit "healthy" but unresponsive until a manual
restart.</p>
<p dir="auto"><strong>Health-check</strong> — probe the DB read path, not <code>/health</code>:</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="# fast (healthy): a real MATCH over kb.lbug
curl -sS --max-time 5 http://127.0.0.1:8630/stats   # -&gt; {&quot;total&quot;:9204,&quot;by_root&quot;:{...}}
# live check of every agent tool
curl -sS --max-time 10 &quot;http://127.0.0.1:8630/search?q=test&amp;n=1&amp;noweb=1&quot;"><pre><span class="pl-c"><span class="pl-c">#</span> fast (healthy): a real MATCH over kb.lbug</span>
curl -sS --max-time 5 http://127.0.0.1:8630/stats   <span class="pl-c"><span class="pl-c">#</span> -&gt; {"total":9204,"by_root":{...}}</span>
<span class="pl-c"><span class="pl-c">#</span> live check of every agent tool</span>
curl -sS --max-time 10 <span class="pl-s"><span class="pl-pds">"</span>http://127.0.0.1:8630/search?q=test&amp;n=1&amp;noweb=1<span class="pl-pds">"</span></span></pre></div>
<p dir="auto"><code>/stats</code> returns quickly only when the read handle is healthy; a wedged brain
times out. The compose <code>brain</code> healthcheck uses <code>/stats</code> for exactly this
reason (see <code>compose.yaml</code>), so docker auto-restarts a deadlocked container
(<code>restart: unless-stopped</code>) instead of leaving it "healthy".</p>
<p dir="auto"><strong>Restart</strong>:</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="docker compose up -d brain          # or: docker restart 2dph-brain-1
# verify: process not a zombie, leaf count unchanged
ps -eo pid,ppid,stat,args | grep -E &quot;brain-(serve|search)&quot;   # no &lt;defunct&gt;
curl -sS --max-time 5 http://127.0.0.1:8630/stats            # total unchanged"><pre>docker compose up -d brain          <span class="pl-c"><span class="pl-c">#</span> or: docker restart 2dph-brain-1</span>
<span class="pl-c"><span class="pl-c">#</span> verify: process not a zombie, leaf count unchanged</span>
ps -eo pid,ppid,stat,args <span class="pl-k">|</span> grep -E <span class="pl-s"><span class="pl-pds">"</span>brain-(serve|search)<span class="pl-pds">"</span></span>   <span class="pl-c"><span class="pl-c">#</span> no &lt;defunct&gt;</span>
curl -sS --max-time 5 http://127.0.0.1:8630/stats            <span class="pl-c"><span class="pl-c">#</span> total unchanged</span></pre></div>
<p dir="auto">Data lives in <code>var/kb.lbug</code> (host path, gitignored) and survives a container
restart unchanged — verify with <code>sha256sum var/kb.lbug</code> before/after. A stuck
write never commits, so the on-disk DB is the last successful state. A full
rebuild is only needed after a deliberate <code>--rebuild</code> or a corrupt DB, not
after a restart.</p>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">Бенчмарки поиска (#202)</h2><a id="user-content-бенчмарки-поиска-202" class="anchor" aria-label="Permalink: Бенчмарки поиска (#202)" href="#бенчмарки-поиска-202"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">A/B-харнесс <code>bin/brain/bench.go</code> гоняет фиксированный golden-set запросов
(<code>internal/brain/testdata/golden-set.json</code>, ~50, рус+англ, темы
facts/mail/docs/git/ssh) через поиск и печатает latency p50/p95/mean,
recall@5/@10 и CPU/RSS. <strong>Никакой кандидат не принимается без замера</strong>
(эпик #201): baseline — текущий линейный скан.</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="# baseline против живого brain (HTTP MCP :8630, read-only; контейнер можно не трогать)
./bin/brain/bench.go

# machine-отчёт (для issue/CI)
./bin/brain/bench.go --json

# кандидат: исполняемый бинарь с CLI bin/brain/search.go --json -n N --no-web &quot;q&quot;
./bin/brain/bench.go --candidate /path/to/candidate

# кандидат: другой serve (:8631) — A/B recall vs baseline + latency ratio
./bin/brain/bench.go --candidate http://127.0.0.1:8631

# локально, без сервера: открыть var/kb.lbug read-only в процессе
# (сначала quiesce: docker compose stop brain — single-writer!)
./bin/brain/bench.go --inproc

# локально против отдельной копии БД (напр. свежий --rebuild --db /tmp/kb.lbug)
./bin/brain/bench.go --inproc --db /tmp/kb.lbug

# ресурсы сервера: семплировать PID brain-контейнера вместо собственного
./bin/brain/bench.go --pid $(docker inspect -f '{{.State.Pid}}' 2dph-brain-1)"><pre><span class="pl-c"><span class="pl-c">#</span> baseline против живого brain (HTTP MCP :8630, read-only; контейнер можно не трогать)</span>
./bin/brain/bench.go

<span class="pl-c"><span class="pl-c">#</span> machine-отчёт (для issue/CI)</span>
./bin/brain/bench.go --json

<span class="pl-c"><span class="pl-c">#</span> кандидат: исполняемый бинарь с CLI bin/brain/search.go --json -n N --no-web "q"</span>
./bin/brain/bench.go --candidate /path/to/candidate

<span class="pl-c"><span class="pl-c">#</span> кандидат: другой serve (:8631) — A/B recall vs baseline + latency ratio</span>
./bin/brain/bench.go --candidate http://127.0.0.1:8631

<span class="pl-c"><span class="pl-c">#</span> локально, без сервера: открыть var/kb.lbug read-only в процессе</span>
<span class="pl-c"><span class="pl-c">#</span> (сначала quiesce: docker compose stop brain — single-writer!)</span>
./bin/brain/bench.go --inproc

<span class="pl-c"><span class="pl-c">#</span> локально против отдельной копии БД (напр. свежий --rebuild --db /tmp/kb.lbug)</span>
./bin/brain/bench.go --inproc --db /tmp/kb.lbug

<span class="pl-c"><span class="pl-c">#</span> ресурсы сервера: семплировать PID brain-контейнера вместо собственного</span>
./bin/brain/bench.go --pid <span class="pl-s"><span class="pl-pds">$(</span>docker inspect -f <span class="pl-s"><span class="pl-pds">'</span>{{.State.Pid}}<span class="pl-pds">'</span></span> 2dph-brain-1<span class="pl-pds">)</span></span></pre></div>
<p dir="auto">Метрики и гейты:</p>
<markdown-accessiblity-table><table>
<thead>
<tr>
<th>Метрика</th>
<th>Смысл</th>
<th>Гейт</th>
</tr>
</thead>
<tbody>
<tr>
<td>latency p50/p95/mean, ms</td>
<td>распределение длительности поиска на запрос</td>
<td>кандидат p50 ≤ baseline p50 × 1.5</td>
</tr>
<tr>
<td>recall@5/@10 (fragment)</td>
<td>доля запросов, у которых ожидаемый фрагмент (из корпуса) в top-k</td>
<td>baseline recall@5 ≥ 0.95</td>
</tr>
<tr>
<td>recall@5/@10 (vs baseline)</td>
<td>кандидат не теряет известные baseline-хиты (top-k IDs)</td>
<td>кандидат ≥ 0.95</td>
</tr>
<tr>
<td>CPU/RSS</td>
<td>user+sys jiffies, RSS до/после, пик (VmHWM) из /proc</td>
<td>для сравнения при «тех же ресурсах»</td>
</tr>
</tbody>
</table></markdown-accessiblity-table>
<p dir="auto">Exit codes: <code>0</code> — гейты прошли, <code>2</code> — recall/ratio FAIL, <code>1</code> — ошибка
(нет golden-set, не поднялся searcher, и т.п.).</p>
<p dir="auto">Чтение: p50/p95 по per-query latency при <code>--workers 1</code> (по умолчанию) —
чистый одиночный запрос; <code>--workers N</code> меряет сервер под нагрузкой.
Повторяемость: два прогона подряд, разброс p50 &lt; 10 % (линейный скан
детерминирован; разброс даёт только фоновая нагрузка). Baseline-эталон:
p50 ≈ 32s на 313k leafs (линейный скан, #192); после внедрения кандидата
цель эпика p50 &lt; 500ms, p95 &lt; 2s.</p>
<p dir="auto">В CI (GitHub, оффлайн): <code>--rebuild</code> корпуса → <code>bench --inproc --json</code>
(та же схема, что и recall-gate <code>bin/brain/eval.go</code>).</p>
<div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">ANN-индекс в проде (#204/#206)</h3><a id="user-content-ann-индекс-в-проде-204206" class="anchor" aria-label="Permalink: ANN-индекс в проде (#204/#206)" href="#ann-индекс-в-проде-204206"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Векторный поиск brain идёт через инкрементальный индекс <strong>вне liblbug</strong> (его
собственный HNSW крашится на росте графа, #192; fallback — линейный скан).
Алгоритм: <strong>IVF</strong> (k-means cells + точный косинус внутри probed-кластеров,
чистый Go, без внешних зависимостей). <code>coder/hnsw</code> (HNSW) отброшен на
эмпирике: на 313k реальных эмбеддингов из entry достижимы лишь 34% узлов
(изоляция при вытеснении соседей) → recall@5 падает до 0.16; IVF держит
recall@30 ≥ 0.93 при NList=2000/NProbe=128 (замеры probe #204). Индекс
живёт в <code>var/state/vector.ann</code> (+ append-only WAL <code>vector.ann.wal</code>).</p>
<p dir="auto"><strong>Включён по умолчанию</strong> (#206): <code>vector.ann.enabled: true</code> — serve и CLI
ищут через ANN; при отсутствии/повреждении индекса поиск автоматически
падает на линейный скан (никогда не ломается). <code>serve</code> грузит индекс при
старте (warm start, без rebuild); если индекс пуст — serve отвечает через
скан, а волна (шаг <code>ann-build</code>) строит его.</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="# полный build из БД (~30s extract + ~4min k-means + assign на 313k, NList=2000).
# Источник векторов: embedding-колонка когда есть (legacy/ANN-off БД); у БД
# после --rebuild при ANN-on колонки нет (issue #248 B3) — build/upsert/ensure
# эмбедят l.text моделью напрямую (~16k/s, +модель ~1.5GB в этом процессе).
KB_BUFFER_POOL=4294967296 ./bin/brain/ann.go build

# волна/ручной режим: build если индекса нет или он устарел (&gt;10% отставания
# по счётчику leafs), иначе инкрементальный WAL-upsert новых leafs (&lt;1s, НЕ rebuild)
./bin/brain/ann.go ensure

# инкрементальный upsert новых leafs (append WAL, НЕ rebuild) — для ручных прогонов
./bin/brain/ann.go upsert

# статистика индекса
./bin/brain/ann.go stats

# CLI-поиск через ANN (fallback на скан при отсутствии индекса)
./bin/brain/ann.go --json -n 10 &quot;query&quot;

# HTTP-сервер поиска через ANN (для bench --candidate http://127.0.0.1:8631)
./bin/brain/ann.go api --port 8631"><pre><span class="pl-c"><span class="pl-c">#</span> полный build из БД (~30s extract + ~4min k-means + assign на 313k, NList=2000).</span>
<span class="pl-c"><span class="pl-c">#</span> Источник векторов: embedding-колонка когда есть (legacy/ANN-off БД); у БД</span>
<span class="pl-c"><span class="pl-c">#</span> после --rebuild при ANN-on колонки нет (issue #248 B3) — build/upsert/ensure</span>
<span class="pl-c"><span class="pl-c">#</span> эмбедят l.text моделью напрямую (~16k/s, +модель ~1.5GB в этом процессе).</span>
KB_BUFFER_POOL=4294967296 ./bin/brain/ann.go build

<span class="pl-c"><span class="pl-c">#</span> волна/ручной режим: build если индекса нет или он устарел (&gt;10% отставания</span>
<span class="pl-c"><span class="pl-c">#</span> по счётчику leafs), иначе инкрементальный WAL-upsert новых leafs (&lt;1s, НЕ rebuild)</span>
./bin/brain/ann.go ensure

<span class="pl-c"><span class="pl-c">#</span> инкрементальный upsert новых leafs (append WAL, НЕ rebuild) — для ручных прогонов</span>
./bin/brain/ann.go upsert

<span class="pl-c"><span class="pl-c">#</span> статистика индекса</span>
./bin/brain/ann.go stats

<span class="pl-c"><span class="pl-c">#</span> CLI-поиск через ANN (fallback на скан при отсутствии индекса)</span>
./bin/brain/ann.go --json -n 10 <span class="pl-s"><span class="pl-pds">"</span>query<span class="pl-pds">"</span></span>

<span class="pl-c"><span class="pl-c">#</span> HTTP-сервер поиска через ANN (для bench --candidate http://127.0.0.1:8631)</span>
./bin/brain/ann.go api --port 8631</pre></div>
<p dir="auto"><strong>Инкрементальность</strong> (критично: волна идёт каждые N минут, полный rebuild
на каждую волну НЕДОПУСТИМ): <code>ensure</code> сравнивает счётчик leafs в БД с
мощностью индекса; steady-state рост (+100 писем на 313k) всегда уходит в
WAL-upsert (&lt;1s), полный build — только когда индекса нет или он отстал</p>
<blockquote>
<p dir="auto">10% (битый/частичный/после bulk-import). Доказано тестом
<code>TestAnnEnsureIncrementalUpsertNoRebuild</code> (+100 leafs → rebuild=false,
upsert &lt;1s) и замером на проде.</p>
</blockquote>
<p dir="auto">A/B (изолирует векторный слой: baseline = скан, кандидат = ANN, один
процесс, одна БД):</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="# quiesce сначала (single-writer): docker compose stop brain
KB_BUFFER_POOL=4294967296 ./bin/brain/bench.go --inproc --candidate inproc-ann
# эталон: ./bin/brain/bench.go --inproc                 (baseline, ~26 min на 50 запросов)
# через отдельный serve: ./bin/brain/bench.go --candidate http://127.0.0.1:8631"><pre><span class="pl-c"><span class="pl-c">#</span> quiesce сначала (single-writer): docker compose stop brain</span>
KB_BUFFER_POOL=4294967296 ./bin/brain/bench.go --inproc --candidate inproc-ann
<span class="pl-c"><span class="pl-c">#</span> эталон: ./bin/brain/bench.go --inproc                 (baseline, ~26 min на 50 запросов)</span>
<span class="pl-c"><span class="pl-c">#</span> через отдельный serve: ./bin/brain/bench.go --candidate http://127.0.0.1:8631</span></pre></div>
<p dir="auto"><strong>nprobe-дефолт = 2000</strong> (решение #206, замерено на живом корпусе 313689
векторов, 11 запросов golden-set): полный probe даёт порядок кандидатов,
совпадающий с точным сканом (recall@5 vs baseline = 1.0) при p50 ~470ms
(гейт &lt;500ms); снижение до 256 ускоряет поиск (p50 ~260ms), но recall@5
падает до ~0.82 — теряются точные top-5. Выбор: корректность сначала,
тюнинг (256/512) — отдельной задачей, если понадобится p50 &lt; 300ms.
Конфиг (<code>etc/brain/config.yml</code>, секция <code>vector.ann</code>): <code>enabled</code>, <code>index</code>,
<code>dim</code> (256), <code>nlist</code> (2000 — k-means cells, ~150 векторов на ячейку при
313k), <code>nprobe</code> (2000). В compose весь <code>etc/brain</code> монтируется в
<code>/data/etc/brain:ro</code> (repo root в контейнере — <code>/data</code>, #208); без mount
serve работает на код-default (nprobe=128). Идемпотентность: повторный
<code>upsert</code> не дублирует (idCell — map), повторный <code>build</code> из той же БД даёт
ту же мощность.
A/B-отчёты: #204 (кандидат), #206 (прод, nprobe 256 vs 2000).</p>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">Disk mail/contacts import (#79)</h2><a id="user-content-disk-mailcontacts-import-79" class="anchor" aria-label="Permalink: Disk mail/contacts import (#79)" href="#disk-mailcontacts-import-79"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Sources on <code>/mnt/8TB</code> (TB mbox, .eml, VCF/MAB) → corpus pipeline; see
<a href="/eSlider/2dph/blob/main/docs/mail-sources.md">mail-sources.md</a> for the authoritative source table + status.
PST stays blocked until <code>libpst-utils</code> (<code>readpst</code>) is installed.</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="./bin/mail/convert-mbox.go --in &lt;tb-root&gt; --out var/corpus/mail --source tb-profile --dry-run
./bin/mail/convert-mbox.go --in &lt;tb-root&gt; --out var/corpus/mail --source tb-profile
./bin/mail/import.go --from-eml var/corpus/mail"><pre>./bin/mail/convert-mbox.go --in <span class="pl-k">&lt;</span>tb-root<span class="pl-k">&gt;</span> --out var/corpus/mail --source tb-profile --dry-run
./bin/mail/convert-mbox.go --in <span class="pl-k">&lt;</span>tb-root<span class="pl-k">&gt;</span> --out var/corpus/mail --source tb-profile
./bin/mail/import.go --from-eml var/corpus/mail</pre></div>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">Sync wave + OO CRM (AGENTS.md, #81)</h2><a id="user-content-sync-wave--oo-crm-agentsmd-81" class="anchor" aria-label="Permalink: Sync wave + OO CRM (AGENTS.md, #81)" href="#sync-wave--oo-crm-agentsmd-81"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Deterministic ingest/reconcile wave; every step is a reconcile/upsert, so the
wave is idempotent. <code>--dry-run</code> prints the fixed order without executing.
Bulk rebuild is NOT part of the wave (use <code>bin/brain/index.go --rebuild</code>).</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="go run ./bin/stack/sync.go --dry-run                     # print the wave
go run ./bin/stack/sync.go                               # default wave
go run ./bin/stack/sync.go --only mail,mail-import       # subset, order kept
go run ./bin/stack/sync.go --with-mail --only mail,mail-import,mail-index  # mail → brain index (#199)
go run ./bin/stack/sync.go --with-chats --contacts &lt;vcf&gt; # + chats, contacts
go run ./bin/stack/sync.go --with-chats --git-root &lt;dir&gt; # + chats, git history"><pre>go run ./bin/stack/sync.go --dry-run                     <span class="pl-c"><span class="pl-c">#</span> print the wave</span>
go run ./bin/stack/sync.go                               <span class="pl-c"><span class="pl-c">#</span> default wave</span>
go run ./bin/stack/sync.go --only mail,mail-import       <span class="pl-c"><span class="pl-c">#</span> subset, order kept</span>
go run ./bin/stack/sync.go --with-mail --only mail,mail-import,mail-index  <span class="pl-c"><span class="pl-c">#</span> mail → brain index (#199)</span>
go run ./bin/stack/sync.go --with-chats --contacts <span class="pl-k">&lt;</span>vcf<span class="pl-k">&gt;</span> <span class="pl-c"><span class="pl-c">#</span> + chats, contacts</span>
go run ./bin/stack/sync.go --with-chats --git-root <span class="pl-k">&lt;</span>dir<span class="pl-k">&gt;</span> <span class="pl-c"><span class="pl-c">#</span> + chats, git history</span></pre></div>
<p dir="auto"><code>--only</code> takes actual step names (fixed order): <code>mail</code>, <code>mail-import</code>,
<code>mail-index</code>, <code>chats</code>, <code>contact-brain</code>, <code>git-brain</code>, <code>contact-crm</code>.
<code>--with-mail</code> включает шаг <code>mail-index</code> (mail leafs обоих корпусов → brain,
#199); без флага шаг SKIP, не FAIL.</p>
<div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">chats step (--with-chats, #195)</h3><a id="user-content-chats-step---with-chats-195" class="anchor" aria-label="Permalink: chats step (--with-chats, #195)" href="#chats-step---with-chats-195"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">The <code>chats</code> step is one logical step name that runs two sub-commands
sequentially: <code>bin/chat/sync.go telegram</code>, then <code>bin/chat/sync.go linkedin</code>.
Both download messages to <code>var/corpus/chats/&lt;platform&gt;/</code>.</p>
<p dir="auto">Credentials come from the environment:</p>
<markdown-accessiblity-table><table>
<thead>
<tr>
<th>Env var</th>
<th>Purpose</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>TELEGRAM_API_ID</code> / <code>TELEGRAM_API_HASH</code> / <code>TELEGRAM_PHONE</code></td>
<td>Telegram app credentials</td>
</tr>
<tr>
<td><code>TELEGRAM_MCP_DIR</code></td>
<td>path to the telegram-mcp checkout (session read from its <code>.env</code>)</td>
</tr>
<tr>
<td><code>TELEGRAM_SESSION_STRING</code></td>
<td>optional; otherwise read from <code>TELEGRAM_MCP_DIR/.env</code></td>
</tr>
<tr>
<td><code>LINKEDIN_USER_DATA_DIR</code></td>
<td>LinkedIn profile dir with session files (default <code>~/.linkedin-mcp/profile</code>)</td>
</tr>
</tbody>
</table></markdown-accessiblity-table>
<p dir="auto"><strong>SKIP semantics</strong>: a platform whose credentials/session are missing prints
<code>chats: SKIP …</code> and exits with code 3 (<code>pkg/cli.ExitSkip</code>) — the wave shows
<code>SKIP</code>, never <code>FAIL</code>. The same convention applies to <code>mail</code> sources without
creds (onlyoffice/m365/imap). Tools with credentials present but broken
(config paths, invalid API id) still FAIL loudly.</p>
<div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">var/ permissions (uid 1001 vs 1000)</h3><a id="user-content-var-permissions-uid-1001-vs-1000" class="anchor" aria-label="Permalink: var/ permissions (uid 1001 vs 1000)" href="#var-permissions-uid-1001-vs-1000"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">The brain/mail-sync containers write <code>var/**</code> as uid 1001 (Dockerfile
<code>useradd --uid 1001</code>) while host tools run as uid 1000. A container write
leaves <code>var/kb.lbug.wal</code> and <code>var/corpus/**</code> unopenable by the wave
(<code>OpenDatabase … status 1</code>). Since compose.yaml now runs the containers as
the host uid (<code>user: "${KB_UID:-1000}:${KB_GID:-1000}"</code>), new writes align
automatically; a one-time repair for already-misowned files goes through
docker busybox (no sudo on this host):</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="scripts/stack/fix-var-perms            # chown -R var/ to the caller uid:gid
scripts/stack/fix-var-perms --check    # report files NOT owned by the caller"><pre>scripts/stack/fix-var-perms            <span class="pl-c"><span class="pl-c">#</span> chown -R var/ to the caller uid:gid</span>
scripts/stack/fix-var-perms --check    <span class="pl-c"><span class="pl-c">#</span> report files NOT owned by the caller</span></pre></div>
<p dir="auto">Run it after any container write or when a wave step fails on <code>var/**</code>.
The wave also quiesces the compose brain container around write steps
(<code>docker compose stop brain</code> → writes → <code>start brain</code>), so <code>git-brain</code> does
not collide with the running serve process holding <code>kb.lbug</code>.</p>
<div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">index-sync — gator→graph→index cycle (#292)</h3><a id="user-content-index-sync--gatorgraphindex-cycle-292" class="anchor" aria-label="Permalink: index-sync — gator→graph→index cycle (#292)" href="#index-sync--gatorgraphindex-cycle-292"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">The chain <code>gator sync→etl→pack → 2dph import(graph) → index(kb.lbug)</code> is
closed by the compose service <strong><code>index-sync</code></strong> (profile <code>index</code>). No systemd.</p>
<p dir="auto">One cycle: discover the documents hive (<code>&lt;hive-doc&gt;/source=*/channel=*</code>) →
stop the <code>brain</code> container (Ladybug is single-writer) → import the mail canon →
<code>brain-index --skip</code> (docs corpus + indexes) → (gator mode only) <code>mail-graph --commit</code> per channel (idempotent MERGE) → ANN <code>ensure</code> → start brain and wait
healthy → write freshness state. A cycle is skipped when <code>kb.lbug</code> is already
newer than the newest upstream state and the last run did not error, so brain is
not bounced for nothing.</p>
<p dir="auto"><strong>ANN index build/maintenance (#204/#206).</strong> The IVF vector index is maintained
by <code>bin/brain/ann.go ensure</code>, which <code>index-sync</code> runs at the end of every cycle.
The binary is read from <code>ANN_BIN</code> (flag <code>--ann</code> wins; empty = skip). Compose
sets <code>ANN_BIN=/usr/local/bin/brain-ann</code>; the <code>api-build</code> stage builds it with
<code>-tags system_ladybug,brain_ann</code> and the <code>api</code> stage copies it, so no manual
install is needed. <code>ensure</code> does a full k-means build only when the index is
missing or stale (&gt;10% behind the DB leaf count); steady-state growth is an
incremental WAL-upsert (&lt;1s, never a rebuild). The index path is
<code>vector.ann.index</code>, default <code>&lt;root&gt;/var/state/vector.ann</code> (+ <code>.wal</code>); in the
container <code>KB_ROOT=/data</code> and <code>./var</code> is mounted at <code>/data/var</code>, so the index
lands in the host <code>var/state/</code> on the persistent volume. Empty/absent index is
never fatal: serve/CLI fall back to the linear scan and <code>/stats</code> reports
<code>ann.loaded:false</code>.</p>
<p dir="auto"><strong>Mail canon — T10-A (<code>MAIL_SOURCE</code>, default <code>corpus</code>).</strong> The info-corpus mail
canon is the <strong>local 2dph M365 corpus</strong>, not the gator <code>parquet/mail</code> hive: the
Edelweiss boxes are M365/OAuth and are synced by 2dph into <code>var/corpus/mail</code>
(gator's mail client is plain IMAP). Selecting the canon keeps a single source
of Leafs (local corpus ids are content-address, gator ids are <code>message_id</code>, so
indexing both would duplicate).</p>
<ul dir="auto">
<li><code>corpus</code> (default): <code>brain-index --with-mail</code> indexes <code>var/corpus/mail/m365</code>
(plus the legacy <code>var/mail</code>); the gator <code>parquet/mail</code> leaf+graph steps are
<strong>skipped</strong>. <code>--since YYYY-MM-DD</code> (env <code>MAIL_SINCE</code>) bounds the corpus mail if
set. Corpus-mail mtime drives freshness.</li>
<li><code>gator</code>: unchanged legacy path — <code>mail-leaf</code> over <code>parquet/mail</code> plus
<code>mail-graph --commit</code> per channel.</li>
<li>The gator <code>parquet/documents</code> → <code>doc-leaf</code> step runs in <strong>both</strong> modes.</li>
</ul>
<p dir="auto"><strong>Document body text — T12 (<code>GATOR_DOC_TEXT</code>, default off).</strong> <code>doc-leaf</code>
indexes metadata only (<code>title</code> + <code>filename</code> + <code>oo_path</code>). With the flag the PDF
is fetched from OnlyOffice by <code>oo_path</code> (or a <code>fileid</code> in <code>ref</code>) and its text is
appended to <code>Leaf.Text</code> (capped, default 256 KiB). Enable with
<code>--document-text</code> on <code>doc-leaf</code>/<code>index-loop</code> or <code>GATOR_DOC_TEXT=1</code>; requires
<code>ONLYOFFICE_URL/USER/PASS</code> and <code>GATOR_DOC_ROOT_ID</code> (the Files folder rooting
<code>oo_path</code>; optional <code>GATOR_DOC_ROOT_PREFIX</code>, default <code>external</code>), plus optional
<code>GATOR_DOC_TEXT_LIMIT</code>. A fetch/extract failure keeps the metadata leaf. Body
text changes the content hash, so the first enabled run writes a new version;
extraction runs before the <code>--skip</code> dedup, so an enabled cycle re-fetches.</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="scripts/stack/start-index-sync                 # compose up -d index-sync (loop, 1h)
docker compose --profile index up -d index-sync
docker compose --profile index run --rm index-sync index-loop --once   # one manual cycle
# one-off canon override:
docker compose --profile index run --rm index-sync index-loop --once --mail-source gator
docker compose --profile index logs -f index-sync"><pre>scripts/stack/start-index-sync                 <span class="pl-c"><span class="pl-c">#</span> compose up -d index-sync (loop, 1h)</span>
docker compose --profile index up -d index-sync
docker compose --profile index run --rm index-sync index-loop --once   <span class="pl-c"><span class="pl-c">#</span> one manual cycle</span>
<span class="pl-c"><span class="pl-c">#</span> one-off canon override:</span>
docker compose --profile index run --rm index-sync index-loop --once --mail-source gator
docker compose --profile index logs -f index-sync</pre></div>
<p dir="auto">Requirements:</p>
<ul dir="auto">
<li>API image rebuilt with <code>mail-graph</code> + <code>index-loop</code> + <code>brain-ann</code>
(<code>docker compose --profile index build index-sync</code>); the older
<code>ghcr.io/eslider/2dph:api</code> lacks them. Without <code>brain-ann</code> the ANN step
is skipped and vector search uses the linear scan.</li>
<li>In <code>corpus</code> mode the gator mail hive is not required; <code>./var</code> must be mounted
at <code>/data/var</code> so <code>var/corpus/mail/m365</code> is visible (it is).</li>
<li><code>GATOR_VAR_HOST=&lt;gator repo&gt;/var/gator</code> in <code>.env</code> (gitignored) — the host
gator canon is bind-mounted read-only at <code>/gator</code>. <code>GATOR_MAIL_HIVE</code>
inside the container is <code>/gator/parquet/mail</code> (used only in <code>gator</code> mode);
the same mount covers <code>parquet/documents</code> (kind=document), set as
<code>GATOR_DOCUMENTS_HIVE</code> (<code>/gator/parquet/documents</code>). An absent/empty document
tree is skipped.</li>
<li><code>/var/run/docker.sock</code> is mounted: the cycle stops/starts the sibling
<code>brain</code> container. <code>index-sync</code> therefore has Docker control — keep it on
the local host only. Because the container runs with an explicit <code>user:</code>,
it does not inherit the host's supplementary <code>docker</code> group; set
<code>DOCKER_GID</code> (default 991) to the socket's group so the added <code>group_add</code>
grants access, otherwise the quiesce step fails with
<code>dial unix /var/run/docker.sock: connect: permission denied</code>
(<code>stat -c '%g' /var/run/docker.sock</code>).</li>
<li>The container runs as the host uid (<code>KB_UID</code>/<code>KB_GID</code>) so <code>kb.lbug</code> stays
writable by host tools (#195).</li>
</ul>
<p dir="auto">Freshness is observable: the state file is <code>&lt;root&gt;/var/state/index-freshness.yml</code>,
<code>/stats</code> returns <code>index_at</code>/<code>import_at</code>/<code>kb_mtime</code>/<code>pack_mtime</code>/<code>stale</code>/
<code>stale_after</code>, and <code>scripts/stack/status</code> prints the same (stale from <code>/stats</code>
when the brain answers, otherwise <code>unknown</code> — the on-disk state stays readable
while the brain is down).</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="curl -s localhost:8630/stats | yq '.index_at,.import_at,.kb_mtime,.stale'
scripts/stack/status | yq '.freshness'
ls -la --time-style=full-iso var/kb.lbug      # mtime newer than the gator pack"><pre>curl -s localhost:8630/stats <span class="pl-k">|</span> yq <span class="pl-s"><span class="pl-pds">'</span>.index_at,.import_at,.kb_mtime,.stale<span class="pl-pds">'</span></span>
scripts/stack/status <span class="pl-k">|</span> yq <span class="pl-s"><span class="pl-pds">'</span>.freshness<span class="pl-pds">'</span></span>
ls -la --time-style=full-iso var/kb.lbug      <span class="pl-c"><span class="pl-c">#</span> mtime newer than the gator pack</span></pre></div>
<div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">OnlyOffice CRM tools</h3><a id="user-content-onlyoffice-crm-tools" class="anchor" aria-label="Permalink: OnlyOffice CRM tools" href="#onlyoffice-crm-tools"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">OnlyOffice CRM tools need <code>ONLYOFFICE_URL</code>/<code>ONLYOFFICE_USER</code>/<code>ONLYOFFICE_PASS</code>
env. Both are read-only (report) by default; <code>--write</code> applies changes.</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="# reconcile report — human mail senders vs OO CRM persons (match by email)
go run -tags=onlyoffice_reconcile_contact ./bin/onlyoffice/reconcile-contact.go
go run -tags=onlyoffice_reconcile_contact ./bin/onlyoffice/reconcile-contact.go --write --limit 200

# interaction write — mail → history note on the person's opportunity
go run -tags=onlyoffice_import_interaction ./bin/onlyoffice/import-interaction.go
go run -tags=onlyoffice_import_interaction ./bin/onlyoffice/import-interaction.go --write --limit 50"><pre><span class="pl-c"><span class="pl-c">#</span> reconcile report — human mail senders vs OO CRM persons (match by email)</span>
go run -tags=onlyoffice_reconcile_contact ./bin/onlyoffice/reconcile-contact.go
go run -tags=onlyoffice_reconcile_contact ./bin/onlyoffice/reconcile-contact.go --write --limit 200

<span class="pl-c"><span class="pl-c">#</span> interaction write — mail → history note on the person's opportunity</span>
go run -tags=onlyoffice_import_interaction ./bin/onlyoffice/import-interaction.go
go run -tags=onlyoffice_import_interaction ./bin/onlyoffice/import-interaction.go --write --limit 50</pre></div>
<p dir="auto">Both scan <code>var/corpus/mail</code> by default (<code>--sources</code> to override) and build an
email → person index in one pass over the whole CRM. Machine senders
(newsletter/bounce/platform) never become contacts.</p>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">Browser sync (periodic, #163)</h2><a id="user-content-browser-sync-periodic-163" class="anchor" aria-label="Permalink: Browser sync (periodic, #163)" href="#browser-sync-periodic-163"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Pushes the browser-extracted corpus (<code>var/corpus/{gmail,linkedin,djinni}</code>) into
the brain as leafs every 6 h. Thorium down is tolerated: extraction is skipped
and the last-known corpus is still ingested.</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="./bin/cron/browser-sync.go                  # one cycle, default paths
./bin/cron/browser-sync.go --skip-extract   # no Thorium probe
./bin/cron/browser-sync.go --interval 6h    # run continuously
sudo scripts/browser-sync-install.sh        # install the 6-hourly systemd timer"><pre>./bin/cron/browser-sync.go                  <span class="pl-c"><span class="pl-c">#</span> one cycle, default paths</span>
./bin/cron/browser-sync.go --skip-extract   <span class="pl-c"><span class="pl-c">#</span> no Thorium probe</span>
./bin/cron/browser-sync.go --interval 6h    <span class="pl-c"><span class="pl-c">#</span> run continuously</span>
sudo scripts/browser-sync-install.sh        <span class="pl-c"><span class="pl-c">#</span> install the 6-hourly systemd timer</span></pre></div>
<p dir="auto">Log: <code>var/log/browser-sync.log</code>. The systemd units
(<code>scripts/browser-sync.{service,timer}</code>) use an <code>@REPO_ROOT@</code> placeholder; the
install script substitutes the real root at install time.</p>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">Reasoner (optional, D18)</h2><a id="user-content-reasoner-optional-d18" class="anchor" aria-label="Permalink: Reasoner (optional, D18)" href="#reasoner-optional-d18"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">CPU sidecar on <code>127.0.0.1:11435</code>. Weights are not in the 2dph image.</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="docker compose --profile reasoner up -d reasoner
REASONER_BASE_URL=http://127.0.0.1:11435/v1 ./bin/reasoner/bench.go --json"><pre>docker compose --profile reasoner up -d reasoner
REASONER_BASE_URL=http://127.0.0.1:11435/v1 ./bin/reasoner/bench.go --json</pre></div>
<p dir="auto">See <a href="/eSlider/2dph/blob/main/docs/reasoner.md">reasoner.md</a>.</p>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">GitHub mirror sync (#194)</h2><a id="user-content-github-mirror-sync-194" class="anchor" aria-label="Permalink: GitHub mirror sync (#194)" href="#github-mirror-sync-194"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Gitea is the canonical home; GitHub is a publish-only <strong>showcase</strong> (<code>#190</code>,
<code>#213</code>). <code>main</code> is the single working line and the public face (Gitea <code>#213</code>).
PRs merge into <code>main</code> directly; no <code>release/v1</code> branch is kept. After each
merge into <code>main</code>, sync <strong>only <code>main</code></strong> (plus tags) to GitHub:</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="git fetch origin --tags --force
git push github main:main --force-with-lease
git push github --tags --force"><pre>git fetch origin --tags --force
git push github main:main --force-with-lease
git push github --tags --force</pre></div>
<p dir="auto">Verify zero drift on <code>main</code>:</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="git rev-list --count origin/main..github/main   # 0
git rev-list --count github/main..origin/main   # 0"><pre>git rev-list --count origin/main..github/main   <span class="pl-c"><span class="pl-c">#</span> 0</span>
git rev-list --count github/main..origin/main   <span class="pl-c"><span class="pl-c">#</span> 0</span></pre></div>
<p dir="auto">Branch policy (<code>#213</code>): both Gitea and GitHub keep <code>main</code> only. Old/merged
feature branches are backed up locally under <code>refs/backup/*</code> before deletion.</p>
<p dir="auto">Content guard (<code>#190</code>): before pushing to GitHub, confirm the diff is project
content only — no internal paths/hosts/ports, no secrets. Precedent #142/#194:
leaks are purged from GitHub history with <code>git filter-repo</code>; Gitea stays the
canonical home and is never rewritten for the mirror.</p>
</article></div><div class="d-none"></div></section></div></div></div> </div> <!-- --> </div></div></div></div></div></div><div class="ScrollMarksContainer-module__scrollMarksContainer__Eu7uU" id="find-result-marks-container"></div><div class="d-none"></div><div class="d-none"></div></div> <!-- --> <!-- --> </div>
</react-app>




  </div>

</turbo-frame>

    </main>
  </div>

  </div>

          <footer class="footer f6 color-fg-muted color-border-subtle tmp-pt-7 tmp-pb-6 p-responsive" role="contentinfo"  >
  <h2 class='sr-only'>Footer</h2>

  


  <div class="d-flex flex-justify-center flex-items-center flex-column-reverse flex-lg-row flex-wrap flex-lg-nowrap">
    <div class="d-flex flex-items-center flex-shrink-0 mx-2">
      <a aria-label="GitHub Homepage" class="footer-octicon mr-2" href="https://github.com">
        <svg aria-hidden="true" data-component="Octicon" height="24" viewBox="0 0 24 24" version="1.1" width="24" data-view-component="true" class="octicon octicon-mark-github">
    <path d="M10.226 17.284c-2.965-.36-5.054-2.493-5.054-5.256 0-1.123.404-2.336 1.078-3.144-.292-.741-.247-2.314.09-2.965.898-.112 2.111.36 2.83 1.01.853-.269 1.752-.404 2.853-.404 1.1 0 1.999.135 2.807.382.696-.629 1.932-1.1 2.83-.988.315.606.36 2.179.067 2.942.72.854 1.101 2 1.101 3.167 0 2.763-2.089 4.852-5.098 5.234.763.494 1.28 1.572 1.28 2.807v2.336c0 .674.561 1.056 1.235.786 4.066-1.55 7.255-5.615 7.255-10.646C23.5 6.188 18.334 1 11.978 1 5.62 1 .5 6.188.5 12.545c0 4.986 3.167 9.12 7.435 10.669.606.225 1.19-.18 1.19-.786V20.63a2.9 2.9 0 0 1-1.078.224c-1.483 0-2.359-.808-2.987-2.313-.247-.607-.517-.966-1.034-1.033-.27-.023-.359-.135-.359-.27 0-.27.45-.471.898-.471.652 0 1.213.404 1.797 1.235.45.651.921.943 1.483.943.561 0 .92-.202 1.437-.719.382-.381.674-.718.944-.943"></path>
</svg>
</a>
      <span>
        &copy; 2026 GitHub,&nbsp;Inc.
      </span>
    </div>

    <nav aria-label="Footer">
      <h3 class="sr-only" id="sr-footer-heading">Footer navigation</h3>

      <ul class="list-style-none d-flex flex-justify-center flex-wrap mb-2 mb-lg-0" aria-labelledby="sr-footer-heading">


          <li class="mx-2">
            <a data-analytics-event="{&quot;category&quot;:&quot;Footer&quot;,&quot;action&quot;:&quot;go to Terms&quot;,&quot;label&quot;:&quot;text:terms&quot;}" href="https://docs.github.com/site-policy/github-terms/github-terms-of-service" data-view-component="true" class="Link--secondary Link">Terms</a>
          </li>

          <li class="mx-2">
            <a data-analytics-event="{&quot;category&quot;:&quot;Footer&quot;,&quot;action&quot;:&quot;go to privacy&quot;,&quot;label&quot;:&quot;text:privacy&quot;}" href="https://docs.github.com/site-policy/privacy-policies/github-privacy-statement" data-view-component="true" class="Link--secondary Link">Privacy</a>
          </li>


            <li class="mx-2">
              <a data-analytics-event="{&quot;category&quot;:&quot;Footer&quot;,&quot;action&quot;:&quot;go to security&quot;,&quot;label&quot;:&quot;text:security&quot;}" href="https://github.com/security" data-view-component="true" class="Link--secondary Link">Security</a>
            </li>

            <li class="mx-2">
              <a data-analytics-event="{&quot;category&quot;:&quot;Footer&quot;,&quot;action&quot;:&quot;go to status&quot;,&quot;label&quot;:&quot;text:status&quot;}" href="https://www.githubstatus.com/" data-view-component="true" class="Link--secondary Link">Status</a>
            </li>

          <li class="mx-2">
            <a data-analytics-event="{&quot;category&quot;:&quot;Footer&quot;,&quot;action&quot;:&quot;go to community&quot;,&quot;label&quot;:&quot;text:community&quot;}" href="https://github.community/" data-view-component="true" class="Link--secondary Link">Community</a>
          </li>

          <li class="mx-2">
            <a data-analytics-event="{&quot;category&quot;:&quot;Footer&quot;,&quot;action&quot;:&quot;go to docs&quot;,&quot;label&quot;:&quot;text:docs&quot;}" href="https://docs.github.com/" data-view-component="true" class="Link--secondary Link">Docs</a>
          </li>

          <li class="mx-2">
            <a data-analytics-event="{&quot;category&quot;:&quot;Footer&quot;,&quot;action&quot;:&quot;go to contact&quot;,&quot;label&quot;:&quot;text:contact&quot;}" href="https://support.github.com?tags=dotcom-footer" data-view-component="true" class="Link--secondary Link">Contact</a>
          </li>

          
<li class="mx-2" >
  <cookie-consent-link>
    <button
      type="button"
      class="Link--secondary underline-on-hover border-0 p-0 color-bg-transparent"
      data-action="click:cookie-consent-link#showConsentManagement"
      data-analytics-event="{&quot;location&quot;:&quot;footer&quot;,&quot;action&quot;:&quot;cookies&quot;,&quot;context&quot;:&quot;subfooter&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;label&quot;:&quot;cookies_link_subfooter_footer&quot;}"
    >
      Manage cookies
    </button>
  </cookie-consent-link>
</li>

  <li class="mx-2">
    <cookie-consent-link>
      <button
        type="button"
        class="Link--secondary underline-on-hover border-0 p-0 color-bg-transparent text-left"
        data-action="click:cookie-consent-link#showConsentManagement"
        data-analytics-event="{&quot;location&quot;:&quot;footer&quot;,&quot;action&quot;:&quot;dont_share_info&quot;,&quot;context&quot;:&quot;subfooter&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;label&quot;:&quot;dont_share_info_link_subfooter_footer&quot;}"
      >
        Do not share my personal information
      </button>
    </cookie-consent-link>
  </li>

      </ul>
    </nav>
  </div>
</footer>



    <ghcc-consent id="ghcc" class="position-fixed bottom-0 left-0" style="z-index: 999999"
      data-locale="en"
      data-initial-cookie-consent-allowed=""
      data-cookie-consent-required="true"
    ></ghcc-consent>




  <div id="ajax-error-message" class="ajax-error-message flash flash-error" hidden>
    <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-alert">
    <path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path>
</svg>
    <button type="button" class="flash-close js-ajax-error-dismiss" aria-label="Dismiss error">
      <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-x">
    <path d="M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l3.22 3.22a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-3.22 3.22a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z"></path>
</svg>
    </button>
    You can’t perform that action at this time.
  </div>

    <template id="site-details-dialog">
  <details class="details-reset details-overlay details-overlay-dark lh-default color-fg-default hx_rsm" open>
    <summary role="button" aria-label="Close dialog"></summary>
    <details-dialog class="Box Box--overlay d-flex flex-column anim-fade-in fast hx_rsm-dialog hx_rsm-modal">
      <button class="Box-btn-octicon m-0 btn-octicon position-absolute right-0 top-0" type="button" aria-label="Close dialog" data-close-dialog>
        <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-x">
    <path d="M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l3.22 3.22a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-3.22 3.22a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z"></path>
</svg>
      </button>
      <div class="octocat-spinner tmp-my-6 js-details-dialog-spinner"></div>
    </details-dialog>
  </details>
</template>

    <div class="Popover js-hovercard-content position-absolute" style="display: none; outline: none;">
  <div class="Popover-message Popover-message--bottom-left Popover-message--large Box color-shadow-large" style="width:360px;">
  </div>
</div>

    <template id="snippet-clipboard-copy-button">
  <div class="zeroclipboard-container position-absolute right-0 top-0">
    <clipboard-copy aria-label="Copy code to clipboard" class="ClipboardButton btn js-clipboard-copy m-2 p-0" data-copy-feedback="Copied!" data-tooltip-direction="w">
      <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-copy js-clipboard-copy-icon m-2 tmp-m-2">
    <path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path>
</svg>
      <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-check js-clipboard-check-icon color-fg-success d-none m-2 tmp-m-2">
    <path d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z"></path>
</svg>
    </clipboard-copy>
  </div>
</template>
<template id="snippet-clipboard-copy-button-unpositioned">
  <div class="zeroclipboard-container">
    <clipboard-copy aria-label="Copy code to clipboard" class="ClipboardButton btn btn-invisible js-clipboard-copy m-2 p-0 d-flex flex-justify-center flex-items-center" data-copy-feedback="Copied!" data-tooltip-direction="w">
      <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-copy js-clipboard-copy-icon">
    <path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path>
</svg>
      <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-check js-clipboard-check-icon color-fg-success d-none">
    <path d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z"></path>
</svg>
    </clipboard-copy>
  </div>
</template>




    </div>
    <div id="js-global-screen-reader-notice" class="sr-only mt-n1" aria-live="polite" aria-atomic="true" ></div>
    <div id="js-global-screen-reader-notice-assertive" class="sr-only mt-n1" aria-live="assertive" aria-atomic="true"></div>
  </body>
</html>

