brainlayer 1.5.78__tar.gz → 1.5.79__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (266) hide show
  1. {brainlayer-1.5.78 → brainlayer-1.5.79}/PKG-INFO +1 -1
  2. {brainlayer-1.5.78 → brainlayer-1.5.79}/pyproject.toml +1 -1
  3. {brainlayer-1.5.78 → brainlayer-1.5.79}/server.json +2 -2
  4. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/__init__.py +1 -1
  5. brainlayer-1.5.79/src/brainlayer/_build.py +1 -0
  6. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/brainbar_auto_activation.py +13 -4
  7. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/job_alerts.py +10 -1
  8. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/maintenance_calendar.py +17 -4
  9. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/service_supervisor.py +3 -1
  10. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/supervisor_guard.py +72 -5
  11. brainlayer-1.5.78/src/brainlayer/_build.py +0 -1
  12. {brainlayer-1.5.78 → brainlayer-1.5.79}/.gitignore +0 -0
  13. {brainlayer-1.5.78 → brainlayer-1.5.79}/LICENSE +0 -0
  14. {brainlayer-1.5.78 → brainlayer-1.5.79}/README.md +0 -0
  15. {brainlayer-1.5.78 → brainlayer-1.5.79}/scripts/hotlane_brainbar_daemon.py +0 -0
  16. {brainlayer-1.5.78 → brainlayer-1.5.79}/scripts/launchd/backup-daily.sh +0 -0
  17. {brainlayer-1.5.78 → brainlayer-1.5.79}/scripts/launchd/brainlayer-env-run.sh +0 -0
  18. {brainlayer-1.5.78 → brainlayer-1.5.79}/scripts/launchd/brainlayer.env.example +0 -0
  19. {brainlayer-1.5.78 → brainlayer-1.5.79}/scripts/launchd/com.brainlayer.backup-daily.plist +0 -0
  20. {brainlayer-1.5.78 → brainlayer-1.5.79}/scripts/launchd/com.brainlayer.daemon.plist +0 -0
  21. {brainlayer-1.5.78 → brainlayer-1.5.79}/scripts/launchd/com.brainlayer.decay.plist +0 -0
  22. {brainlayer-1.5.78 → brainlayer-1.5.79}/scripts/launchd/com.brainlayer.drain.plist +0 -0
  23. {brainlayer-1.5.78 → brainlayer-1.5.79}/scripts/launchd/com.brainlayer.health-check.plist +0 -0
  24. {brainlayer-1.5.78 → brainlayer-1.5.79}/scripts/launchd/com.brainlayer.hotlane-brainbar.plist +0 -0
  25. {brainlayer-1.5.78 → brainlayer-1.5.79}/scripts/launchd/com.brainlayer.index.plist +0 -0
  26. {brainlayer-1.5.78 → brainlayer-1.5.79}/scripts/launchd/com.brainlayer.jsonl-backup.plist +0 -0
  27. {brainlayer-1.5.78 → brainlayer-1.5.79}/scripts/launchd/com.brainlayer.log-cap.plist +0 -0
  28. {brainlayer-1.5.78 → brainlayer-1.5.79}/scripts/launchd/com.brainlayer.maintenance-nightly.plist +0 -0
  29. {brainlayer-1.5.78 → brainlayer-1.5.79}/scripts/launchd/com.brainlayer.maintenance-weekly.plist +0 -0
  30. {brainlayer-1.5.78 → brainlayer-1.5.79}/scripts/launchd/com.brainlayer.maintenance.plist +0 -0
  31. {brainlayer-1.5.78 → brainlayer-1.5.79}/scripts/launchd/com.brainlayer.observability.plist +0 -0
  32. {brainlayer-1.5.78 → brainlayer-1.5.79}/scripts/launchd/com.brainlayer.p0-counter.plist +0 -0
  33. {brainlayer-1.5.78 → brainlayer-1.5.79}/scripts/launchd/com.brainlayer.repair-fts.plist +0 -0
  34. {brainlayer-1.5.78 → brainlayer-1.5.79}/scripts/launchd/com.brainlayer.t3-ingest.plist +0 -0
  35. {brainlayer-1.5.78 → brainlayer-1.5.79}/scripts/launchd/com.brainlayer.throughput-watchdog.plist +0 -0
  36. {brainlayer-1.5.78 → brainlayer-1.5.79}/scripts/launchd/com.brainlayer.tier0-watchdog.plist +0 -0
  37. {brainlayer-1.5.78 → brainlayer-1.5.79}/scripts/launchd/com.brainlayer.wal-checkpoint.plist +0 -0
  38. {brainlayer-1.5.78 → brainlayer-1.5.79}/scripts/launchd/com.brainlayer.watch.plist +0 -0
  39. {brainlayer-1.5.78 → brainlayer-1.5.79}/scripts/launchd/com.etanhey.brainlayer-fleet-watchdog.plist +0 -0
  40. {brainlayer-1.5.78 → brainlayer-1.5.79}/scripts/launchd/fleet-watchdog.sh +0 -0
  41. {brainlayer-1.5.78 → brainlayer-1.5.79}/scripts/launchd/install.sh +0 -0
  42. {brainlayer-1.5.78 → brainlayer-1.5.79}/scripts/launchd/jsonl-backup.sh +0 -0
  43. {brainlayer-1.5.78 → brainlayer-1.5.79}/scripts/launchd/throughput-watchdog.py +0 -0
  44. {brainlayer-1.5.78 → brainlayer-1.5.79}/scripts/release-verify-signatures.sh +0 -0
  45. {brainlayer-1.5.78 → brainlayer-1.5.79}/scripts/tier0-watchdog.sh +0 -0
  46. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/__main__.py +0 -0
  47. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/_helpers.py +0 -0
  48. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/agent_profiles.py +0 -0
  49. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/agent_provenance.py +0 -0
  50. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/alarm.py +0 -0
  51. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/archive_collapse.py +0 -0
  52. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/archive_lifecycle.py +0 -0
  53. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/backup_daily.py +0 -0
  54. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/backup_retention_invariant.py +0 -0
  55. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/badge_state.py +0 -0
  56. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/bitemporal.py +0 -0
  57. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/brainbar_activation.py +0 -0
  58. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/brainbar_hybrid_helper.py +0 -0
  59. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/brainbar_identity.py +0 -0
  60. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/cachedelete_purge.swift +0 -0
  61. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/calibrate.py +0 -0
  62. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/chunk_origin.py +0 -0
  63. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/chunk_origin_backfill.py +0 -0
  64. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/chunk_origin_wipe.py +0 -0
  65. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/chunk_write.py +0 -0
  66. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/classify.py +0 -0
  67. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/claude_paths.py +0 -0
  68. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/cli/__init__.py +0 -0
  69. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/cli/wizard.py +0 -0
  70. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/cli_jobs.py +0 -0
  71. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/cli_new.py +0 -0
  72. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/cloud_backfill.py +0 -0
  73. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/clustering.py +0 -0
  74. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/component_jobs.py +0 -0
  75. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/config.py +0 -0
  76. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/consolidation_plan.py +0 -0
  77. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/content_class.py +0 -0
  78. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/correction_judge.py +0 -0
  79. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/data/agents_registry.yaml +0 -0
  80. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/data/sandbox_seeds/skill-eval-baseline.json +0 -0
  81. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/db_shrink.py +0 -0
  82. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/decay.py +0 -0
  83. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/decay_backfill.py +0 -0
  84. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/decay_job.py +0 -0
  85. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/dedupe.py +0 -0
  86. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/dedupe_merge.py +0 -0
  87. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/deploy_drift.py +0 -0
  88. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/disk_space.py +0 -0
  89. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/doctor.py +0 -0
  90. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/drain.py +0 -0
  91. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/drain_liveness.py +0 -0
  92. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/drive_credentials.py +0 -0
  93. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/embeddings.py +0 -0
  94. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/engine.py +0 -0
  95. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/enrichment_replay.py +0 -0
  96. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/eval/__init__.py +0 -0
  97. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/eval/abcde_enrich_runner.py +0 -0
  98. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/eval/abcde_variants.py +0 -0
  99. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/eval/abcde_variants.yaml +0 -0
  100. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/eval/benchmark.py +0 -0
  101. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/eval/enrichment_gold.py +0 -0
  102. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/eval/enrichment_graders.py +0 -0
  103. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/eval/enrichment_judge.py +0 -0
  104. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/eval/enrichment_llm_judge.py +0 -0
  105. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/eval/enrichment_quality_benchmark.py +0 -0
  106. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/eval/experiment_store.py +0 -0
  107. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/eval/fixtures/README.md +0 -0
  108. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/eval/labeling/enrichment-labeler.html +0 -0
  109. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/eval/phoenix_gate/__init__.py +0 -0
  110. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/eval/phoenix_gate/__main__.py +0 -0
  111. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/eval/phoenix_gate/baseline_store.py +0 -0
  112. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/eval/phoenix_gate/cli.py +0 -0
  113. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/eval/phoenix_gate/models.py +0 -0
  114. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/eval/phoenix_gate/phoenix_client.py +0 -0
  115. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/eval/phoenix_gate/regression_gate.py +0 -0
  116. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/eval/phoenix_gate/triggers.py +0 -0
  117. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/eval/pilot_artifact_report.py +0 -0
  118. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/fallback_replay.py +0 -0
  119. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/git_learning.py +0 -0
  120. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/health_check.py +0 -0
  121. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/hook_python.py +0 -0
  122. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/hooks/__init__.py +0 -0
  123. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/hooks/indexer.py +0 -0
  124. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/import_sweep.py +0 -0
  125. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/index_admission.py +0 -0
  126. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/index_budget.py +0 -0
  127. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/index_completeness.py +0 -0
  128. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/index_new.py +0 -0
  129. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/index_watchdog.py +0 -0
  130. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/ingest/__init__.py +0 -0
  131. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/ingest/codex.py +0 -0
  132. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/ingest/t3.py +0 -0
  133. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/ingest_denylist.py +0 -0
  134. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/ingest_guard.py +0 -0
  135. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/isolation_proof.py +0 -0
  136. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/job_lifecycle_health.py +0 -0
  137. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/jobs.py +0 -0
  138. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/jsonl_backup.py +0 -0
  139. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/kg/__init__.py +0 -0
  140. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/kg_cleanup.py +0 -0
  141. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/kg_judge.py +0 -0
  142. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/kg_promotion.py +0 -0
  143. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/kg_repo.py +0 -0
  144. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/kg_review_session.py +0 -0
  145. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/kg_session_finish.py +0 -0
  146. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/kg_session_harvest.py +0 -0
  147. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/launchd_consolidation.py +0 -0
  148. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/launchd_install.py +0 -0
  149. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/launchd_primitive.py +0 -0
  150. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/launchd_revert.py +0 -0
  151. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/lexical_defense.py +0 -0
  152. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/lexical_defense_dictionary.json +0 -0
  153. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/log_cap.py +0 -0
  154. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/maintenance.py +0 -0
  155. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/maintenance_control.py +0 -0
  156. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/mcp/__init__.py +0 -0
  157. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/mcp/_format.py +0 -0
  158. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/mcp/_shared.py +0 -0
  159. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/mcp/enrich_handler.py +0 -0
  160. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/mcp/entity_handler.py +0 -0
  161. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/mcp/palette.py +0 -0
  162. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/mcp/search_handler.py +0 -0
  163. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/mcp/store_handler.py +0 -0
  164. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/mcp/tags_handler.py +0 -0
  165. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/mcp_socket_config.py +0 -0
  166. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/mcp_stdio_bridge.py +0 -0
  167. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/memory_types.py +0 -0
  168. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/migrate.py +0 -0
  169. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/migration_custody.py +0 -0
  170. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/migration_journal.py +0 -0
  171. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/migration_runtime.py +0 -0
  172. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/notification_policy.py +0 -0
  173. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/observability_backup.py +0 -0
  174. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/observability_surface.py +0 -0
  175. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/observability_transport.py +0 -0
  176. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/p0_longitudinal_count.py +0 -0
  177. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/parent_death.py +0 -0
  178. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/paths.py +0 -0
  179. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pause.py +0 -0
  180. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/phonetic.py +0 -0
  181. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/__init__.py +0 -0
  182. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/agent_enrichment.py +0 -0
  183. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/analyze_communication.py +0 -0
  184. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/batch_extraction.py +0 -0
  185. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/brain_graph.py +0 -0
  186. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/chat_tags.py +0 -0
  187. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/chunk.py +0 -0
  188. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/classify.py +0 -0
  189. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/cloud_scrub.py +0 -0
  190. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/cluster_sampling.py +0 -0
  191. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/code_intelligence.py +0 -0
  192. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/correction_detection.py +0 -0
  193. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/correction_mining.py +0 -0
  194. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/digest.py +0 -0
  195. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/enrichment_prompts.py +0 -0
  196. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/enrichment_results.py +0 -0
  197. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/enrichment_tiers.py +0 -0
  198. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/entity_extraction.py +0 -0
  199. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/entity_resolution.py +0 -0
  200. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/extract.py +0 -0
  201. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/extract_claude_desktop.py +0 -0
  202. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/extract_corrections.py +0 -0
  203. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/extract_markdown.py +0 -0
  204. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/extract_whatsapp.py +0 -0
  205. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/frustration_mining.py +0 -0
  206. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/git_overlay.py +0 -0
  207. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/kg_extraction.py +0 -0
  208. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/kg_extraction_groq.py +0 -0
  209. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/longitudinal_analyzer.py +0 -0
  210. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/obsidian_export.py +0 -0
  211. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/operation_grouping.py +0 -0
  212. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/plan_linking.py +0 -0
  213. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/rate_limiter.py +0 -0
  214. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/relation_backfill.py +0 -0
  215. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/relation_verification.py +0 -0
  216. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/sanitize.py +0 -0
  217. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/secret_scrub.py +0 -0
  218. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/semantic_style.py +0 -0
  219. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/sentiment.py +0 -0
  220. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/session_history.py +0 -0
  221. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/style_embed.py +0 -0
  222. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/style_index.py +0 -0
  223. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/tag_entity_promotion.py +0 -0
  224. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/temporal_chains.py +0 -0
  225. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/time_batcher.py +0 -0
  226. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/unified_timeline.py +0 -0
  227. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/pipeline/write_queue.py +0 -0
  228. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/provenance.py +0 -0
  229. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/provenance_autosupersede.py +0 -0
  230. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/provenance_integration.py +0 -0
  231. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/queue_io.py +0 -0
  232. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/queue_merge.py +0 -0
  233. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/reembed_backfill.py +0 -0
  234. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/retired_services.py +0 -0
  235. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/runtime_store.py +0 -0
  236. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/sandbox_db.py +0 -0
  237. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/scoping.py +0 -0
  238. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/scrub_at_rest.py +0 -0
  239. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/search_fanout.py +0 -0
  240. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/search_profile.py +0 -0
  241. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/search_repo.py +0 -0
  242. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/session_repo.py +0 -0
  243. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/setup.py +0 -0
  244. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/socket_hygiene.py +0 -0
  245. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/source_class_migration.py +0 -0
  246. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/spotlight.py +0 -0
  247. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/storage.py +0 -0
  248. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/store.py +0 -0
  249. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/supervisor_adapters.py +0 -0
  250. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/supervisor_child.py +0 -0
  251. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/supervisor_diagnostics.py +0 -0
  252. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/supervisor_jobs.py +0 -0
  253. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/system_prompt_guard.py +0 -0
  254. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/t3_linkage_alarm.py +0 -0
  255. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/t3_provenance.py +0 -0
  256. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/t3_sources.py +0 -0
  257. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/tag_normalization.py +0 -0
  258. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/taxonomy.json +0 -0
  259. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/telemetry.py +0 -0
  260. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/timestamp_iso.py +0 -0
  261. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/types.py +0 -0
  262. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/vector_store.py +0 -0
  263. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/wal_checkpoint.py +0 -0
  264. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/watcher.py +0 -0
  265. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/watcher_bridge.py +0 -0
  266. {brainlayer-1.5.78 → brainlayer-1.5.79}/src/brainlayer/writer_telemetry.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: brainlayer
3
- Version: 1.5.78
3
+ Version: 1.5.79
4
4
  Summary: Persistent local memory for AI agents — 16 BrainBar MCP tools, semantic search, knowledge graph, SQLite
5
5
  Project-URL: Homepage, https://brainlayer.etanheyman.com
6
6
  Project-URL: Repository, https://github.com/EtanHey/brainlayer
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "brainlayer"
3
- version = "1.5.78"
3
+ version = "1.5.79"
4
4
  description = "Persistent local memory for AI agents — 16 BrainBar MCP tools, semantic search, knowledge graph, SQLite"
5
5
  license = {text = "Apache-2.0"}
6
6
  readme = "README.md"
@@ -3,7 +3,7 @@
3
3
  "name": "io.github.etanhey/brainlayer",
4
4
  "title": "BrainLayer",
5
5
  "description": "Persistent local memory for AI agents \u2014 semantic search, knowledge graph, and 16 BrainBar MCP tools with ToolAnnotations over SQLite.",
6
- "version": "1.5.78",
6
+ "version": "1.5.79",
7
7
  "websiteUrl": "https://brainlayer.etanheyman.com",
8
8
  "repository": {
9
9
  "url": "https://github.com/EtanHey/brainlayer",
@@ -14,7 +14,7 @@
14
14
  "registryType": "pypi",
15
15
  "registryBaseUrl": "https://pypi.org",
16
16
  "identifier": "brainlayer",
17
- "version": "1.5.78",
17
+ "version": "1.5.79",
18
18
  "runtimeHint": "pipx",
19
19
  "transport": {
20
20
  "type": "stdio"
@@ -2,7 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- __version__ = "1.5.78"
5
+ __version__ = "1.5.79"
6
6
  # Git sha the distributed package was built from; stamped by the release build, None in a source tree.
7
7
  # scripts/sprint_gate.py reads it in "keg" mode to prove the served package is the code under test.
8
8
  # No runtime `str | None` annotation: launchd/install.sh imports this under /usr/bin/python3 (3.9).
@@ -0,0 +1 @@
1
+ BUILD_SHA = "d13c7d37d66306b8b40e04c320893e621145d74e"
@@ -31,12 +31,15 @@ def brew_processes():
31
31
  # Include brew's Ruby entrypoint and Homebrew-owned child executables, even
32
32
  # after reparenting. Unknown/unreadable process tables defer activation.
33
33
  rows = process_table().splitlines()
34
- matches = []
34
+ matches, readable = [], 0
35
35
  for row in rows:
36
36
  fields = row.split(None, 3)
37
- if len(fields) != 4 or not fields[0].isascii() or not fields[0].isdecimal():
38
- raise ValueError("unreadable process table")
39
- tokens = fields[3].split()
37
+ # A process exiting or exec-ing mid-scan can tear one row: empty args
38
+ # are still checked by comm, a row without a pid and comm is skipped.
39
+ if len(fields) < 3 or not fields[0].isascii() or not fields[0].isdecimal():
40
+ continue
41
+ readable += 1
42
+ tokens = fields[3].split() if len(fields) == 4 else []
40
43
  if Path(fields[2]).name == "brew" or any(
41
44
  Path(token).name in {"brew", "brew.rb"} or "/Homebrew/" in token or "/homebrew/Library/Homebrew/" in token
42
45
  for token in tokens
@@ -44,6 +47,8 @@ def brew_processes():
44
47
  # argv is detection-only: Homebrew curl headers can carry tokens.
45
48
  executable = scrub_secrets(Path(fields[2]).name).text[:128]
46
49
  matches.append(f"{fields[0]} {executable}")
50
+ if not readable:
51
+ raise ValueError("unreadable process table")
47
52
  return matches
48
53
 
49
54
 
@@ -85,6 +90,9 @@ class AutoActivator:
85
90
  processes = brew_processes()
86
91
  except (OSError, subprocess.SubprocessError) as exc:
87
92
  raise ProcessScanUnavailable(type(exc).__name__) from None
93
+ except ValueError:
94
+ # Counted and alerted only after 12 ticks, never an immediate refusal.
95
+ raise ProcessScanUnavailable("unreadable process table") from None
88
96
  # Recover before activation can report a new failure on the same alert key.
89
97
  self.state.with_suffix(".scan.json").unlink(missing_ok=True)
90
98
  reason = active_alerts().get("brainbar-activation", "")
@@ -97,6 +105,7 @@ class AutoActivator:
97
105
  (
98
106
  "BrainBar activation process scan failed for >=12 ticks:",
99
107
  "BrainBar activation monitor refused: Command '['/bin/ps',",
108
+ "BrainBar activation monitor refused: unreadable process table",
100
109
  )
101
110
  ):
102
111
  report("brainbar-activation", None)
@@ -65,7 +65,12 @@ def report(
65
65
  path: Path | None = None,
66
66
  notify: Callable[[str], None] = _notify,
67
67
  ) -> bool:
68
- """Set or clear one episode. Return true only when the visible state changes."""
68
+ """Set or clear one episode. Return true only when the visible state changes.
69
+
70
+ Only a new failure posts a notification, and only when no other active key already
71
+ carries the identical message (one incident fanned out across keys posts once).
72
+ Recovery updates the alert file and the health log but never notifies.
73
+ """
69
74
  destination = path or alert_path()
70
75
  with _locked(destination):
71
76
  current = active_alerts(destination)
@@ -75,15 +80,19 @@ def report(
75
80
  return False
76
81
  current.pop(key)
77
82
  message = f"BrainLayer {key} recovered"
83
+ post = False
78
84
  else:
79
85
  if before is not None and (before == reason or key != "drive-consent"):
80
86
  return False
87
+ post = reason not in (value for other, value in current.items() if other != key)
81
88
  current[key] = reason
82
89
  message = reason
83
90
  from brainlayer.drive_credentials import _atomic_write
84
91
 
85
92
  _atomic_write(destination, current)
86
93
  LOG.warning("health event job=%s state=%s reason=%s", key, "recovered" if reason is None else "failed", message)
94
+ if not post:
95
+ return True
87
96
  try:
88
97
  notify(message)
89
98
  except (OSError, subprocess.SubprocessError) as exc:
@@ -28,7 +28,16 @@ from .drive_credentials import _atomic_write
28
28
  from .index_budget import CALENDAR_INDEX_UNWIND_S, calendar_index_budget_s
29
29
  from .job_alerts import active_alerts, report
30
30
  from .maintenance_control import MaintenanceHold
31
- from .supervisor_guard import Custody, gated_spawn, guard_lease, launch_guard, start_time, stop_group
31
+ from .supervisor_guard import (
32
+ MAX_STALL_GRACE_S,
33
+ Custody,
34
+ configured_guard_timeout,
35
+ gated_spawn,
36
+ guard_lease,
37
+ launch_guard,
38
+ start_time,
39
+ stop_group,
40
+ )
32
41
  from .supervisor_jobs import TRANSCRIPT_RECOVERY_EXIT_TIMEOUT, TRANSCRIPT_RECOVERY_RUN_TIMEOUT
33
42
 
34
43
  LOG = logging.getLogger(__name__)
@@ -636,12 +645,14 @@ async def _admit_calendar(steps, directory, date, transcript_recovery, guard_tim
636
645
  if len(recovery) != 1:
637
646
  raise ValueError("full calendar admission requires one bounded transcript step")
638
647
  # Cover the actual step bound, the supervisor backstop and its exit grace,
639
- # plus guard startup/expiry and group/guard shutdown. The suspend-aware
640
- # elapsed clock cannot be extended by a wall-clock change or sleep.
648
+ # plus guard startup/expiry (its host-stall grace ends at an absolute
649
+ # missed-deadline + MAX_STALL_GRACE_S) and group/guard shutdown. The
650
+ # suspend-aware elapsed clock cannot be extended by a wall-clock change or sleep.
641
651
  deadline += (
642
652
  max(recovery[0].timeout, TRANSCRIPT_RECOVERY_RUN_TIMEOUT)
643
653
  + TRANSCRIPT_RECOVERY_EXIT_TIMEOUT
644
654
  + guard_timeout
655
+ + MAX_STALL_GRACE_S
645
656
  + 2 * shutdown_timeout
646
657
  + 30
647
658
  )
@@ -681,7 +692,7 @@ async def run_calendar(
681
692
  directory,
682
693
  logs,
683
694
  *,
684
- guard_timeout=30,
695
+ guard_timeout=None,
685
696
  shutdown_timeout=15,
686
697
  operation_timeout=120,
687
698
  install_signals=False,
@@ -691,6 +702,8 @@ async def run_calendar(
691
702
  defer_after=3,
692
703
  transcript_recovery=False,
693
704
  ):
705
+ if guard_timeout is None:
706
+ guard_timeout = await _blocking(configured_guard_timeout)
694
707
  if (
695
708
  not all(math.isfinite(v) and v > 0 for v in (guard_timeout, shutdown_timeout, operation_timeout))
696
709
  or defer_after < 1
@@ -90,13 +90,15 @@ class Supervisor:
90
90
  path: Path | None = None,
91
91
  *,
92
92
  backoff: float = 10,
93
- guard_timeout: float = 30,
93
+ guard_timeout: float | None = None,
94
94
  shutdown_timeout: float = 15,
95
95
  stable_uptime: float = 60,
96
96
  operation_timeout: float = 120,
97
97
  ):
98
98
  if any(name.removeprefix("com.brainlayer.") in {"enrich", "enrichment"} for name in components):
99
99
  raise ValueError("enrichment is retired")
100
+ if guard_timeout is None:
101
+ guard_timeout = supervisor_guard.configured_guard_timeout()
100
102
  if not all(
101
103
  math.isfinite(v) and v > 0
102
104
  for v in (backoff, guard_timeout, shutdown_timeout, stable_uptime, operation_timeout)
@@ -23,6 +23,40 @@ from pathlib import Path
23
23
 
24
24
  from .supervisor_diagnostics import DiagnosticSink, emit
25
25
 
26
+ STALL_OVERSHOOT_S = 5.0
27
+ MAX_STALL_REARMS = 3
28
+ MAX_STALL_GRACE_S = 300.0
29
+ GUARD_TIMEOUT_ENV = "BRAINLAYER_SUPERVISOR_GUARD_TIMEOUT_SECONDS"
30
+ # Background launchd jobs under host load stalled 31-36 s on 2026-10-28; 30 s killed live parents.
31
+ # A dead parent is still caught at once by pipe EOF, so this only bounds an alive-but-silent one.
32
+ DEFAULT_GUARD_TIMEOUT_SECONDS = 120.0
33
+ GUARD_TIMEOUT_BOUNDS = (10.0, 900.0)
34
+
35
+
36
+ def configured_guard_timeout() -> float:
37
+ """Heartbeat budget for the daemon and calendar guards; an invalid override raises, never falls back."""
38
+ from .config import _split_launchd_env_assignment, configured_brainlayer_env_value, get_user_env_path
39
+
40
+ path = Path(os.environ.get("BRAINLAYER_ENV_FILE", get_user_env_path()))
41
+ configured = configured_brainlayer_env_value(GUARD_TIMEOUT_ENV, path)
42
+ if path.exists():
43
+ # The shared parser skips a substitution line as absent; here that would be a silent fallback.
44
+ for line in path.read_text(encoding="utf-8").splitlines():
45
+ assignment = _split_launchd_env_assignment(line)
46
+ if assignment and assignment[0] == GUARD_TIMEOUT_ENV and ("$(" in assignment[1] or "`" in assignment[1]):
47
+ raise ValueError(f"{GUARD_TIMEOUT_ENV} in {path} uses unsupported command substitution")
48
+ raw = configured if configured is not None else os.environ.get(GUARD_TIMEOUT_ENV)
49
+ if raw is None:
50
+ return DEFAULT_GUARD_TIMEOUT_SECONDS
51
+ low, high = GUARD_TIMEOUT_BOUNDS
52
+ try:
53
+ value = float(raw)
54
+ except ValueError:
55
+ value = math.nan
56
+ if not (math.isfinite(value) and low <= value <= high):
57
+ raise ValueError(f"{GUARD_TIMEOUT_ENV} must be a finite number of seconds in [{low:g}, {high:g}]: {raw!r}")
58
+ return value
59
+
26
60
 
27
61
  @contextmanager
28
62
  def component_gate(directory: Path, *, exclusive=False):
@@ -248,14 +282,48 @@ def run_guard(
248
282
  os.close(ready)
249
283
  groups, buffer, quiesced = set(), b"", False
250
284
  timed_out = False
285
+ stall_rearms = 0
286
+ stall_grace_end = None # Absolute end of this incident's grace: missed deadline + MAX_STALL_GRACE_S.
251
287
  last_heartbeat = time.monotonic()
252
288
  deadline = last_heartbeat + timeout
253
289
  channel = socket.socket(fileno=lease_control) if lease_control is not None else None
254
290
  lease = None
255
291
  try:
256
292
  while True:
257
- remaining = max(0, deadline - time.monotonic())
293
+ wait_started = time.monotonic()
294
+ remaining = max(0, deadline - wait_started)
258
295
  readable = select.select([0, channel] if channel else [0], [], [], remaining)[0]
296
+ now = time.monotonic()
297
+ overshoot = now - wait_started - remaining
298
+ data = os.read(0, 65536) if 0 in readable else None
299
+ if data == b"":
300
+ break # A dead parent needs cleanup, never stall grace.
301
+ # A starved guard cannot judge parent liveness. Give it a fresh window,
302
+ # bounded by both incident caps; only a real heartbeat resets them. The
303
+ # grace is an absolute end, so stall overshoots count against it too.
304
+ if not quiesced and overshoot > STALL_OVERSHOOT_S and stall_grace_end is None:
305
+ stall_grace_end = deadline + MAX_STALL_GRACE_S
306
+ if (
307
+ not quiesced
308
+ and overshoot > STALL_OVERSHOOT_S
309
+ and stall_rearms < MAX_STALL_REARMS
310
+ and now < stall_grace_end
311
+ ):
312
+ stall_rearms += 1
313
+ deadline = min(now + timeout, stall_grace_end)
314
+ stall_grace_used = deadline - (stall_grace_end - MAX_STALL_GRACE_S)
315
+ emit(
316
+ diagnostics,
317
+ event="guard-stall-rearm",
318
+ parent_pid=parent,
319
+ parent_start_time=parent_birth,
320
+ guard_pid=os.getpid(),
321
+ wait_overshoot_s=overshoot,
322
+ stall_rearms=stall_rearms,
323
+ grace_used_s=stall_grace_used,
324
+ )
325
+ if not readable:
326
+ continue
259
327
  timeout_event = dict(
260
328
  event="guard-timeout",
261
329
  health_event="supervisor_guard_timeout",
@@ -264,7 +332,7 @@ def run_guard(
264
332
  guard_pid=os.getpid(),
265
333
  mode="shutdown" if quiesced else "heartbeat",
266
334
  guard_mode="Q" if quiesced else "H",
267
- last_heartbeat_age_s=time.monotonic() - last_heartbeat,
335
+ last_heartbeat_age_s=now - last_heartbeat,
268
336
  registered_pgids=sorted(groups),
269
337
  reason="shutdown-timeout" if quiesced else "heartbeat-timeout",
270
338
  )
@@ -293,13 +361,12 @@ def run_guard(
293
361
  channel.sendall(b"R")
294
362
  if 0 not in readable:
295
363
  continue
296
- data = os.read(0, 65536)
297
- if not data:
298
- break # Even Q cannot abandon groups if shutdown itself was killed.
299
364
  buffer += data
300
365
  while b"\n" in buffer:
301
366
  line, buffer = buffer.split(b"\n", 1)
302
367
  if line == b"H" and not quiesced:
368
+ stall_rearms = 0
369
+ stall_grace_end = None
303
370
  last_heartbeat = time.monotonic()
304
371
  deadline = last_heartbeat + timeout
305
372
  elif line == b"Q" and not quiesced:
@@ -1 +0,0 @@
1
- BUILD_SHA = "67a98f996619eb35ed4358610757936f5c5ef583"
File without changes
File without changes
File without changes