brainiac-cli 0.20.34__tar.gz → 0.20.35__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 (464) hide show
  1. {brainiac_cli-0.20.34/src/brainiac_cli.egg-info → brainiac_cli-0.20.35}/PKG-INFO +1 -1
  2. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/pyproject.toml +1 -1
  3. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/AGENTS.md +46 -1
  4. brainiac_cli-0.20.35/src/brain/_assets/scripts/brainiac-egress-guard.sh +71 -0
  5. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_version.py +1 -1
  6. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli.py +6 -0
  7. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/lifecycle.py +42 -7
  8. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/lifecycle_parser.py +28 -0
  9. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/maintenance.py +11 -27
  10. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/core/_health.py +39 -1
  11. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/deliverables_shelf.py +6 -1
  12. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/doctor_checks.py +2 -2
  13. brainiac_cli-0.20.35/src/brain/egress_terms.py +130 -0
  14. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/weekly.py +35 -0
  15. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/session_hook.py +132 -44
  16. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/update.py +26 -3
  17. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/update_channels_2.py +10 -3
  18. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/vmstaging.py +1 -1
  19. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35/src/brainiac_cli.egg-info}/PKG-INFO +1 -1
  20. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brainiac_cli.egg-info/SOURCES.txt +2 -0
  21. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/LICENSE +0 -0
  22. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/MANIFEST.in +0 -0
  23. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/README.md +0 -0
  24. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/setup.cfg +0 -0
  25. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/__init__.py +0 -0
  26. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/__main__.py +0 -0
  27. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/assets/graph-explorer-template.html +0 -0
  28. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/overlay/template/brand/brand-guide.md +0 -0
  29. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/overlay/template/cos/auto-archive.md +0 -0
  30. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/overlay/template/cos/browser-lane.md +0 -0
  31. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/overlay/template/cos/drafts.md +0 -0
  32. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/overlay/template/cos/ingest.md +0 -0
  33. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/overlay/template/cos/priorities.md +0 -0
  34. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/overlay/template/cos/tenant-domains.md +0 -0
  35. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/overlay/template/keywords/glossary.md +0 -0
  36. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/overlay/template/people/roster.md +0 -0
  37. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/overlay/template/voice/voice-profile.md +0 -0
  38. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/routines/manifest.json +0 -0
  39. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/scripts/brain-brief-mac.plist +0 -0
  40. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/scripts/brain-brief.sh +0 -0
  41. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/scripts/brain-synthesis-mac.plist +0 -0
  42. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/scripts/brain-synthesis.sh +0 -0
  43. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/scripts/brainiac-alerts.sh +0 -0
  44. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/scripts/install-brief-mac.sh +0 -0
  45. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/scripts/install-brief-windows.ps1 +0 -0
  46. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/scripts/register_tasks.py +0 -0
  47. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/scripts/vm-boundary-probe.sh +0 -0
  48. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/scripts/vm-selftest.sh +0 -0
  49. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/templates/company.md +0 -0
  50. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/templates/concept.md +0 -0
  51. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/templates/daily.md +0 -0
  52. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/templates/decision.md +0 -0
  53. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/templates/deliverable.md +0 -0
  54. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/templates/meeting.md +0 -0
  55. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/templates/person.md +0 -0
  56. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/templates/project.md +0 -0
  57. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/templates/state-moc.md +0 -0
  58. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_browser_scan.mjs +0 -0
  59. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_cdp_capture.py +0 -0
  60. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_contract.py +0 -0
  61. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_contract_criteria.py +0 -0
  62. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_contract_criteria_2.py +0 -0
  63. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_contract_ledger_scan.py +0 -0
  64. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_contract_provenance.py +0 -0
  65. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_contract_snapshot_shape.py +0 -0
  66. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_contract_verdict_clauses.py +0 -0
  67. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_driver.py +0 -0
  68. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_driver_accounting.py +0 -0
  69. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_driver_capture.py +0 -0
  70. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_driver_categories.py +0 -0
  71. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_driver_cli.py +0 -0
  72. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_driver_completeness.py +0 -0
  73. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_driver_draw.py +0 -0
  74. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_driver_enumeration.py +0 -0
  75. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_driver_gate.py +0 -0
  76. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_driver_night_records.py +0 -0
  77. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_driver_selfcheck.py +0 -0
  78. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_driver_transport.py +0 -0
  79. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_mutate.py +0 -0
  80. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_mutate_apply.py +0 -0
  81. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_mutate_bridge.py +0 -0
  82. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_mutate_canary.py +0 -0
  83. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_mutate_cli.py +0 -0
  84. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_mutate_evidence.py +0 -0
  85. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_mutate_gates.py +0 -0
  86. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_mutate_ledger.py +0 -0
  87. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_mutate_passes.py +0 -0
  88. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_mutate_plan.py +0 -0
  89. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_mutate_plan_aged.py +0 -0
  90. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_mutate_policy.py +0 -0
  91. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_mutate_rehearsal.py +0 -0
  92. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_mutate_selfcheck.py +0 -0
  93. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_mutate_shapes.py +0 -0
  94. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_mutate_shapestore.py +0 -0
  95. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_reconcile_append.py +0 -0
  96. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_reconcile_guard.py +0 -0
  97. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_reconcile_metrics.py +0 -0
  98. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_reconcile_rows.py +0 -0
  99. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_reconcile_steps.py +0 -0
  100. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_retro.py +0 -0
  101. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_retro_scanners.py +0 -0
  102. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/alerts.py +0 -0
  103. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/alerts_staging.py +0 -0
  104. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/anchor.py +0 -0
  105. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/atomic_temp.py +0 -0
  106. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/attestation.py +0 -0
  107. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/audit.py +0 -0
  108. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/audit_chain.py +0 -0
  109. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/audit_drift.py +0 -0
  110. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/autolink.py +0 -0
  111. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/backup.py +0 -0
  112. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/brief.py +0 -0
  113. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/brief_render.py +0 -0
  114. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/capture.py +0 -0
  115. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/chunk.py +0 -0
  116. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/classification.py +0 -0
  117. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/__init__.py +0 -0
  118. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/common_parser.py +0 -0
  119. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/connect.py +0 -0
  120. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/connect_parser.py +0 -0
  121. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/cos_capture.py +0 -0
  122. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/cos_capture_parser.py +0 -0
  123. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/cos_operations.py +0 -0
  124. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/cos_operations_parser.py +0 -0
  125. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/eval.py +0 -0
  126. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/eval_parser.py +0 -0
  127. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/index_storage.py +0 -0
  128. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/index_storage_parser.py +0 -0
  129. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/ingest_storage.py +0 -0
  130. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/ingest_storage_parser.py +0 -0
  131. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/maintenance_parser.py +0 -0
  132. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/navigation.py +0 -0
  133. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/navigation_parser.py +0 -0
  134. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/owner_workflow.py +0 -0
  135. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/owner_workflow_parser.py +0 -0
  136. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/presentation.py +0 -0
  137. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/presentation_parser.py +0 -0
  138. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/retrieval.py +0 -0
  139. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/retrieval_parser.py +0 -0
  140. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/update.py +0 -0
  141. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/update_parser.py +0 -0
  142. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/versioning.py +0 -0
  143. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/versioning_parser.py +0 -0
  144. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_help.py +0 -0
  145. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_read_record.py +0 -0
  146. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_render.py +0 -0
  147. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/config.py +0 -0
  148. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/config_hostpaths.py +0 -0
  149. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/connect.py +0 -0
  150. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/context.py +0 -0
  151. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/conventions.py +0 -0
  152. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/conventions_scan.py +0 -0
  153. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/core/__init__.py +0 -0
  154. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/core/_audit.py +0 -0
  155. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/core/_briefing.py +0 -0
  156. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/core/_capture.py +0 -0
  157. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/core/_capture_staging.py +0 -0
  158. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/core/_cos_facade.py +0 -0
  159. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/core/_durability.py +0 -0
  160. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/core/_maintenance.py +0 -0
  161. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/core/_maintenance_state.py +0 -0
  162. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/core/_retrieval.py +0 -0
  163. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/core/_shared.py +0 -0
  164. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/core/_supersession.py +0 -0
  165. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/core/_supersession_journal.py +0 -0
  166. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/__init__.py +0 -0
  167. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_answer_consume.py +0 -0
  168. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_approval.py +0 -0
  169. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_approval_cleanup.py +0 -0
  170. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_attachment_acceptance.py +0 -0
  171. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_attachment_anchors.py +0 -0
  172. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_attachment_store.py +0 -0
  173. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_autocap.py +0 -0
  174. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_batch_apply.py +0 -0
  175. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_batches.py +0 -0
  176. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_behavior.py +0 -0
  177. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_claim_binding.py +0 -0
  178. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_claim_evaluation.py +0 -0
  179. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_claim_sweep.py +0 -0
  180. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_claims_state.py +0 -0
  181. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_constants.py +0 -0
  182. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_consumption.py +0 -0
  183. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_corrections.py +0 -0
  184. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_criteria.py +0 -0
  185. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_evidence.py +0 -0
  186. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_facade.py +0 -0
  187. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_guards.py +0 -0
  188. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_hold_release.py +0 -0
  189. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_hold_store.py +0 -0
  190. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_hold_undo.py +0 -0
  191. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_ingest_claim.py +0 -0
  192. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_ingest_sweep.py +0 -0
  193. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_ingress.py +0 -0
  194. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_io.py +0 -0
  195. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_layout.py +0 -0
  196. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_layout_setup.py +0 -0
  197. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_learning_config.py +0 -0
  198. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_learning_ledger.py +0 -0
  199. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_ledger_join.py +0 -0
  200. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_priority.py +0 -0
  201. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_proposal_maintenance.py +0 -0
  202. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_proposal_state.py +0 -0
  203. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_routing.py +0 -0
  204. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_run_manifest_write.py +0 -0
  205. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_run_migration.py +0 -0
  206. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_runs.py +0 -0
  207. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_shared.py +0 -0
  208. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_spine.py +0 -0
  209. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_standing_approval.py +0 -0
  210. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_status.py +0 -0
  211. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_taxonomy.py +0 -0
  212. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_version_apply.py +0 -0
  213. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_version_links.py +0 -0
  214. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_chips.py +0 -0
  215. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_corpus.py +0 -0
  216. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_corpus_retention.py +0 -0
  217. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_corpus_write.py +0 -0
  218. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_deploy.py +0 -0
  219. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_echecks.py +0 -0
  220. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_echecks_answers.py +0 -0
  221. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_echecks_answers_2.py +0 -0
  222. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_echecks_delivery.py +0 -0
  223. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_echecks_runs.py +0 -0
  224. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_runverify.py +0 -0
  225. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_runverify_alerts.py +0 -0
  226. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_runverify_checks.py +0 -0
  227. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_runverify_chipdraw.py +0 -0
  228. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_runverify_chips.py +0 -0
  229. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_runverify_contract.py +0 -0
  230. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_runverify_corpus.py +0 -0
  231. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_runverify_identity.py +0 -0
  232. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_runverify_io.py +0 -0
  233. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_runverify_join.py +0 -0
  234. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_runverify_ledger.py +0 -0
  235. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_runverify_metrics.py +0 -0
  236. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_runverify_planbinding.py +0 -0
  237. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_runverify_preamble.py +0 -0
  238. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_runverify_selfeval.py +0 -0
  239. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_runverify_stamps.py +0 -0
  240. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_runverify_touch.py +0 -0
  241. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cowork_leak_artifact.py +0 -0
  242. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cowork_leak_databases.py +0 -0
  243. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cowork_leak_notes.py +0 -0
  244. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cowork_leak_scan.py +0 -0
  245. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cowork_snapshot_guard.py +0 -0
  246. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cowork_staging.py +0 -0
  247. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/dbretry.py +0 -0
  248. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/deliverables_absorb.py +0 -0
  249. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/deliverables_apply.py +0 -0
  250. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/deliverables_ledger.py +0 -0
  251. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/deliverables_materialize.py +0 -0
  252. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/deliverables_previous.py +0 -0
  253. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/deliverables_readme.py +0 -0
  254. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/deliverables_sync.py +0 -0
  255. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/doctor.py +0 -0
  256. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/doctor_context.py +0 -0
  257. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/doctor_desktop.py +0 -0
  258. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/doctor_health.py +0 -0
  259. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/doctor_mount_leak.py +0 -0
  260. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/doctor_plugins.py +0 -0
  261. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/doctor_staging.py +0 -0
  262. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/doctor_vendor.py +0 -0
  263. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/doctor_vm.py +0 -0
  264. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/doctor_wiring.py +0 -0
  265. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/draft_drain.py +0 -0
  266. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/draft_update.py +0 -0
  267. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/egress.py +0 -0
  268. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/embed.py +0 -0
  269. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/embed_backends.py +0 -0
  270. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/embed_catalog.py +0 -0
  271. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/embed_onnx.py +0 -0
  272. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/embed_qwen.py +0 -0
  273. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/encryption.py +0 -0
  274. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/exceptions_cli.py +0 -0
  275. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/exceptions_page.py +0 -0
  276. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/exceptions_render.py +0 -0
  277. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/exceptions_tray.py +0 -0
  278. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/exceptions_verify.py +0 -0
  279. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/__init__.py +0 -0
  280. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/context.py +0 -0
  281. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/cos.py +0 -0
  282. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/daily.py +0 -0
  283. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/deliverables.py +0 -0
  284. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/golden.py +0 -0
  285. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/graph.py +0 -0
  286. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/intake.py +0 -0
  287. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/invariants.py +0 -0
  288. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/orchestrator.py +0 -0
  289. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/organization.py +0 -0
  290. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/preflight.py +0 -0
  291. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/publish.py +0 -0
  292. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/remediation.py +0 -0
  293. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/remediation_bookkeeping.py +0 -0
  294. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/reporting.py +0 -0
  295. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/retention.py +0 -0
  296. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/watchdogs.py +0 -0
  297. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/frontmatter.py +0 -0
  298. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/golden_ops.py +0 -0
  299. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/golden_probe.py +0 -0
  300. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/golden_probe_cases.py +0 -0
  301. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/graph.py +0 -0
  302. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/graph_hygiene.py +0 -0
  303. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/graph_ops.py +0 -0
  304. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/graphify.py +0 -0
  305. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/graphify_edges.py +0 -0
  306. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/graphreport.py +0 -0
  307. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/graphreport_sections.py +0 -0
  308. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/graphreport_sections_2.py +0 -0
  309. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/healthreport.py +0 -0
  310. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/healthreport_sections.py +0 -0
  311. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/healthreport_unsigned.py +0 -0
  312. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/inbox.py +0 -0
  313. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index/__init__.py +0 -0
  314. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index/_connection.py +0 -0
  315. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index/_families.py +0 -0
  316. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index/_graph.py +0 -0
  317. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index/_identity.py +0 -0
  318. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index/_lifecycle.py +0 -0
  319. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index/_models.py +0 -0
  320. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index/_planning.py +0 -0
  321. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index/_records.py +0 -0
  322. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index/_retirement.py +0 -0
  323. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index/_schema.py +0 -0
  324. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index/_search.py +0 -0
  325. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index/_settings.py +0 -0
  326. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index/_shared.py +0 -0
  327. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index/_tools.py +0 -0
  328. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index/_zones.py +0 -0
  329. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index_stages/__init__.py +0 -0
  330. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index_stages/connection.py +0 -0
  331. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index_stages/diagnostics.py +0 -0
  332. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index_stages/duplicate_families.py +0 -0
  333. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index_stages/near_duplicates.py +0 -0
  334. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index_stages/rebuild.py +0 -0
  335. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index_stages/reranking.py +0 -0
  336. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index_stages/search.py +0 -0
  337. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index_stages/search_finalization.py +0 -0
  338. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index_stages/search_identity.py +0 -0
  339. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index_stages/search_ranking.py +0 -0
  340. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index_stages/sync.py +0 -0
  341. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/__init__.py +0 -0
  342. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/deliverables.py +0 -0
  343. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/handlers/__init__.py +0 -0
  344. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/handlers/base.py +0 -0
  345. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/handlers/docx.py +0 -0
  346. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/handlers/email.py +0 -0
  347. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/handlers/html.py +0 -0
  348. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/handlers/image.py +0 -0
  349. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/handlers/pdf.py +0 -0
  350. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/handlers/pptx.py +0 -0
  351. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/handlers/tables.py +0 -0
  352. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/handlers/text.py +0 -0
  353. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/handlers/xlsx.py +0 -0
  354. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/handlers/zip.py +0 -0
  355. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/pipeline.py +0 -0
  356. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/pipeline_duplicates.py +0 -0
  357. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/pipeline_files.py +0 -0
  358. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/pipeline_injection.py +0 -0
  359. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/pipeline_nested.py +0 -0
  360. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/pipeline_run.py +0 -0
  361. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/pipeline_stages.py +0 -0
  362. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/tierguard.py +0 -0
  363. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/tierguard_verdict.py +0 -0
  364. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/transcript.py +0 -0
  365. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/init.py +0 -0
  366. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/init_import.py +0 -0
  367. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/init_samples.py +0 -0
  368. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/init_seed.py +0 -0
  369. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/init_steps.py +0 -0
  370. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/injection_scan.py +0 -0
  371. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/invariant_coverage.py +0 -0
  372. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/invariant_crosstier.py +0 -0
  373. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/invariant_deliverables.py +0 -0
  374. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/invariant_floors.py +0 -0
  375. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/invariant_ingest_guard.py +0 -0
  376. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/invariant_lane_health.py +0 -0
  377. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/invariant_reachability.py +0 -0
  378. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/invariant_shared.py +0 -0
  379. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/invariant_unsigned_notes.py +0 -0
  380. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/invariants.py +0 -0
  381. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/invariants_metrics.py +0 -0
  382. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/language.py +0 -0
  383. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/link_targets.py +0 -0
  384. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/lock.py +0 -0
  385. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/maintenance.py +0 -0
  386. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/maintenance_escalation.py +0 -0
  387. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/maintenance_folds.py +0 -0
  388. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/maintenance_folds_2.py +0 -0
  389. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/maintenance_folds_3.py +0 -0
  390. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/maintenance_folds_4.py +0 -0
  391. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/maintenance_golden.py +0 -0
  392. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/maintenance_graphfolds.py +0 -0
  393. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/maintenance_healthlog.py +0 -0
  394. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/maintenance_hotmd.py +0 -0
  395. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/maintenance_invariant_fields.py +0 -0
  396. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/maintenance_notify.py +0 -0
  397. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/maintenance_outcomes.py +0 -0
  398. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/maintenance_recommendations.py +0 -0
  399. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/maintenance_retention.py +0 -0
  400. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/maintenance_sweep.py +0 -0
  401. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/maintenance_watchdog.py +0 -0
  402. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/mcp_adapter.py +0 -0
  403. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/mcp_capture_verbs.py +0 -0
  404. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/mcp_housekeeping_verbs.py +0 -0
  405. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/mcp_mediation.py +0 -0
  406. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/mcp_tools/__init__.py +0 -0
  407. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/mcp_tools/capture.py +0 -0
  408. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/mcp_tools/core_read.py +0 -0
  409. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/mcp_tools/housekeeping.py +0 -0
  410. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/mcp_tools/retrieval.py +0 -0
  411. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/mcp_verbs.py +0 -0
  412. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/multihop.py +0 -0
  413. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/notes.py +0 -0
  414. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/originals_resolve.py +0 -0
  415. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/overlay.py +0 -0
  416. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/overlay_rules.py +0 -0
  417. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/pathkey.py +0 -0
  418. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/progress.py +0 -0
  419. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/projection.py +0 -0
  420. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/provenance.py +0 -0
  421. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/provision.py +0 -0
  422. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/provision_mcp.py +0 -0
  423. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/provision_stage.py +0 -0
  424. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/provision_wire.py +0 -0
  425. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/querylog.py +0 -0
  426. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/querylog_digest.py +0 -0
  427. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/querylog_replay.py +0 -0
  428. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/querylog_status.py +0 -0
  429. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/read_log.py +0 -0
  430. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/reexec.py +0 -0
  431. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/remediation.py +0 -0
  432. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/remediation_answers.py +0 -0
  433. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/remediation_branches.py +0 -0
  434. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/remediation_exceptions.py +0 -0
  435. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/remediation_exceptions_store.py +0 -0
  436. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/remediation_extract_retry.py +0 -0
  437. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/remediation_folds.py +0 -0
  438. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/remediation_questions.py +0 -0
  439. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/remediation_routing.py +0 -0
  440. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/remediation_schema.py +0 -0
  441. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/remediation_state.py +0 -0
  442. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/rerank.py +0 -0
  443. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/rerank_onnx.py +0 -0
  444. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/retrieval_ops.py +0 -0
  445. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/retro.py +0 -0
  446. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/snapshot.py +0 -0
  447. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/spine.py +0 -0
  448. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/spine_render.py +0 -0
  449. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/status_ops.py +0 -0
  450. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/sweepdirs.py +0 -0
  451. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/update_channels.py +0 -0
  452. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/update_model.py +0 -0
  453. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/update_ops.py +0 -0
  454. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/update_plugins.py +0 -0
  455. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/update_state.py +0 -0
  456. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/vectors.py +0 -0
  457. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/versionlink.py +0 -0
  458. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/versionlink_stages.py +0 -0
  459. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/vm_ceiling.py +0 -0
  460. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/workspaces.py +0 -0
  461. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brainiac_cli.egg-info/dependency_links.txt +0 -0
  462. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brainiac_cli.egg-info/entry_points.txt +0 -0
  463. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brainiac_cli.egg-info/requires.txt +0 -0
  464. {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brainiac_cli.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: brainiac-cli
3
- Version: 0.20.34
3
+ Version: 0.20.35
4
4
  Summary: Brainiac — local any-LLM second-brain core engine + brain CLI (Markdown truth, derived SQLite index).
5
5
  License-Expression: Apache-2.0
6
6
  Requires-Python: >=3.9
@@ -8,7 +8,7 @@ build-backend = "setuptools.build_meta"
8
8
  # check. The IMPORT package stays `brain` and the console script stays
9
9
  # `brain` — only the distribution/project name changes.
10
10
  name = "brainiac-cli"
11
- version = "0.20.34"
11
+ version = "0.20.35"
12
12
  description = "Brainiac — local any-LLM second-brain core engine + brain CLI (Markdown truth, derived SQLite index)."
13
13
  readme = "README.md"
14
14
  license = "Apache-2.0"
@@ -1034,6 +1034,26 @@ whole plan to rebuild the already-shipped fusion fix (item 10, `d5b2c58`).
1034
1034
  query string itself is the leak. Web search is for terms that are already
1035
1035
  public. When in doubt, treat the topic as internal and stay in the vault.
1036
1036
 
1037
+ **This rule has a MECHANISM now (SEC-07, 2026-09-01), and it is still your
1038
+ rule to follow.** `brain check-egress "<text>"` judges one outbound string
1039
+ against THIS vault's `overlay/keywords/` decoder ring and exits `6` when a
1040
+ term is classified at or above the threshold (default `Confidential`,
1041
+ `$BRAIN_EGRESS_TERM_MIN_TIER` to change it; an unrecognised value fails
1042
+ CLOSED). It is `VM_ALLOWED` — the Cowork leg is the one reading wild content
1043
+ beside a web tool, so it needs the guard most. `scripts/brainiac-egress-guard.sh`
1044
+ is the Claude Code `PreToolUse` wiring; the script's own header carries the
1045
+ settings entry, and on a harness-managed `~/.claude` that entry belongs to
1046
+ the deploy repo, not to this engine.
1047
+
1048
+ **The engine ships NO terms and never will** — they come from the owner's
1049
+ overlay, the same rows that already drive ingest classification, so there is
1050
+ no second list to curate. **Its limits, stated so nobody over-trusts it:** it
1051
+ matches DECLARED terms on word boundaries. It cannot see a paraphrase, a
1052
+ codename nobody wrote down, or a fact you restate in your own words, and a
1053
+ vault with an empty decoder ring protects nothing — `check-egress` says so
1054
+ rather than reporting a clean pass. It is a floor under this rule, not a
1055
+ replacement for it.
1056
+
1037
1057
  This is the substrate's standing retrieval discipline; it replaces the old
1038
1058
  Obsidian "five-step retrieval cascade" rule for any harness reading this file.
1039
1059
 
@@ -1095,7 +1115,7 @@ Obsidian "five-step retrieval cascade" rule for any harness reading this file.
1095
1115
  >
1096
1116
  > **Where the kernel skills live per client:** the ten
1097
1117
  > kernel/extras skills (`kb-curator`, `promote`, `vault-ingestion`,
1098
- > `vault-eval`, `save-conversation`, `voice`, `curation`, `improve`,
1118
+ > `vault-eval`, `save-conversation`, `overlay-style`, `curation`, `improve`,
1099
1119
  > `task-registrar`, `autoresearch`) ship three ways from ONE canonical copy
1100
1120
  > at `.claude/skills/<name>/SKILL.md`
1101
1121
  > (re-synced by `tools/package_clients.py`, never hand-edited in more than one
@@ -1155,6 +1175,18 @@ Obsidian "five-step retrieval cascade" rule for any harness reading this file.
1155
1175
  default: `Internal`** — the untrusted leg keeps the conservative
1156
1176
  deny-by-default cap, and elevation there is the explicit human gate.
1157
1177
  Levels, low→high: `Public < Internal < Confidential < Restricted < MNPI`.
1178
+ **That `--role vm` cap binds the CLI, and since the Cowork cutover the CLI
1179
+ is no longer how a Cowork session reads (2026-09-01).** A Cowork session
1180
+ reaches the vault through the DESK — the host-run `brain-mcp` broker, whose
1181
+ tools Claude Desktop announces into the session — and
1182
+ `mcp_verbs._egress_ceiling_tier()` resolves from `$BRAIN_MAX_EGRESS_TIER`
1183
+ alone. It never sees a role, because `connect.py` writes no `BRAIN_ROLE`
1184
+ into any Desktop stanza. So the untrusted leg reads at the HOST ceiling,
1185
+ which is the full vault. The owner was shown this and DECLINED a per-caller
1186
+ clamp on 2026-08-31 (`docs/security-acceptances.md` A-05): the broker cannot
1187
+ tell a Cowork caller from the owner's own Desktop, so capping one caps both.
1188
+ Do not re-raise it; do not read the `Internal` line above as describing the
1189
+ live Cowork path.
1158
1190
  **Default-deny is a READ rule, never a WRITE target (EXC-01,
1159
1191
  2026-08-22).** `classification.normalize` maps a missing or mis-cased label
1160
1192
  to MNPI, so an unlabelled note is withheld from every capped reader. That is
@@ -1173,6 +1205,19 @@ Obsidian "five-step retrieval cascade" rule for any harness reading this file.
1173
1205
  path; the leg that reads untrusted content must not also hold private data
1174
1206
  + an outbound channel. Surfacing sensitive content and any
1175
1207
  irreversible/outbound action is human-gated.
1208
+ **This is the design, and on the Cowork leg it is no longer met (2026-09-01).**
1209
+ The cutover removed the vault from the mount — a real gain, and it closed an
1210
+ invisible bypass — but it routed the same session through the desk at the
1211
+ host's full-vault ceiling (see the classification bullet above). The Cowork
1212
+ leg therefore holds all three ingredients. What bounds it now is
1213
+ DETECTION, not separation: the SEC-05 ingest quarantine refuses a document
1214
+ carrying a CONCEALED instruction, `egress` marks every `raw/` hit
1215
+ `content_trust: untrusted-source`, the SEC-06 read record is fail-closed,
1216
+ and the Tuesday integrity fold raises a concealed-instruction count and a
1217
+ bulk-read alarm. A plainly VISIBLE injected instruction still enters the
1218
+ vault as `instruction_only` (flagged, not quarantined — presenter notes
1219
+ tripped the stricter rule), so there the model's own compliance is the only
1220
+ control. Accepted, with the reasoning, at A-05.
1176
1221
  - **We hold no model API keys** — the one egress is the desktop app's model call
1177
1222
  under the vendor's enterprise no-train/ZDR terms.
1178
1223
  - **Audit chain.** Every committed write is Ed25519-signed and hash-chained
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env bash
2
+ # SEC-07 — Claude Code PreToolUse guard: refuse a tool call whose arguments
3
+ # carry a term this vault classifies Confidential or above.
4
+ #
5
+ # WHY THIS EXISTS. The classification gate decides what the model may READ. It
6
+ # says nothing about what the model then does. A web search is an outbound
7
+ # channel and nobody reviews a query string, so an injected instruction can put
8
+ # a codename into one and the term has left before any result comes back.
9
+ # `AGENTS.md` retrieval rule 4 has always said so; this script is what enforces
10
+ # it.
11
+ #
12
+ # IT SHIPS NO TERMS. `brain check-egress` reads them from the vault's own
13
+ # `overlay/keywords/` decoder ring. A vault with no ring maps nothing, this
14
+ # guard allows everything, and `brain check-egress` says so in its own words
15
+ # rather than reporting a cheerful pass.
16
+ #
17
+ # WHAT IT DOES NOT DO. It matches DECLARED terms on word boundaries. It cannot
18
+ # see a paraphrase, an undeclared codename, or a fact the model states in its
19
+ # own words. Treat it as a floor, not a seal.
20
+ #
21
+ # INSTALL (Claude Code). Place this file at ~/.claude/hooks/ and register it:
22
+ #
23
+ # "hooks": {
24
+ # "PreToolUse": [
25
+ # { "matcher": "WebSearch|WebFetch",
26
+ # "hooks": [ { "type": "command",
27
+ # "command": "~/.claude/hooks/brainiac-egress-guard.sh",
28
+ # "timeout": 5 } ] }
29
+ # ]
30
+ # }
31
+ #
32
+ # Add your own web-tool names to the matcher — an MCP search tool is matched by
33
+ # its full `mcp__<server>__<tool>` name.
34
+ #
35
+ # ON A HARNESS-MANAGED ~/.claude (one a deploy repo owns, per
36
+ # `brain.session_hook.harness_managed`) the settings.json entry belongs to that
37
+ # repo, not to this engine. Author it there.
38
+
39
+ set -uo pipefail
40
+
41
+ BRAIN_BIN="${BRAIN_BIN:-brain}"
42
+ command -v "$BRAIN_BIN" >/dev/null 2>&1 || exit 0 # no engine here, no opinion
43
+
44
+ payload="$(cat)"
45
+
46
+ # The tool arguments, as one string. `jq` is not assumed: fall back to the raw
47
+ # payload, which is a superset of the arguments and so can only over-refuse,
48
+ # never under-refuse. Over-refusing is the safe direction for a guard.
49
+ if command -v jq >/dev/null 2>&1; then
50
+ text="$(printf '%s' "$payload" | jq -r '.tool_input // empty | tostring' 2>/dev/null)"
51
+ [ -z "$text" ] && text="$payload"
52
+ else
53
+ text="$payload"
54
+ fi
55
+
56
+ verdict="$(printf '%s' "$text" | "$BRAIN_BIN" check-egress 2>/dev/null)"
57
+ status=$?
58
+
59
+ # Exit 6 is the ONLY refusal. Any other non-zero status is this guard failing,
60
+ # not the text failing, and a broken guard must not block the session — it
61
+ # would be indistinguishable from a real refusal and would be switched off
62
+ # within the day. A failure is reported on stderr and allowed through.
63
+ if [ "$status" -eq 6 ]; then
64
+ printf '%s\n' "$verdict" >&2
65
+ exit 2 # Claude Code: block the tool call, show stderr to the model
66
+ fi
67
+ if [ "$status" -ne 0 ]; then
68
+ printf 'brainiac-egress-guard: check failed (exit %s) — ALLOWING, not blocking\n' \
69
+ "$status" >&2
70
+ fi
71
+ exit 0
@@ -9,4 +9,4 @@ pip-installed host, importlib.metadata stays primary; brain/__init__.py reads
9
9
  this file only as the fallback before 0.0.0+unknown.
10
10
  """
11
11
 
12
- __version__ = "0.20.34"
12
+ __version__ = "0.20.35"
@@ -292,6 +292,12 @@ VM_ALLOWED = frozenset(
292
292
  # copy `maintain` already wrote there. It never writes, and
293
293
  # never reaches the host-only full page.
294
294
  "exceptions",
295
+ # SEC-07's outbound term guard. VM_ALLOWED on purpose, and it is the
296
+ # leg that needs it MOST: the Cowork session is the one reading wild
297
+ # content beside a web tool. Pure overlay file reads — no index, key,
298
+ # or network — and it only ever REFUSES, so admitting it to the VM
299
+ # widens nothing.
300
+ "check-egress",
295
301
  "mcp-config", # prints a config string; no index/key/vault read
296
302
  "search",
297
303
  "hybrid-search",
@@ -210,6 +210,39 @@ def _run_exceptions(args, ctx) -> int:
210
210
  return 0
211
211
 
212
212
 
213
+ def _run_check_egress(args, ctx) -> int:
214
+ """SEC-07: judge one OUTBOUND string before a tool call carries it away.
215
+
216
+ Deliberately does NOT require a resolvable vault. The hook that calls this
217
+ fires in whatever directory the session happens to sit in, and a guard that
218
+ exits 3 outside a vault is a guard that is off most of the time. An
219
+ unresolvable vault means an empty decoder ring, which `explain` reports in
220
+ its own words rather than as a cheerful pass.
221
+ """
222
+ from .. import egress_terms
223
+
224
+ text = args.text
225
+ if text is None:
226
+ import sys as _sys
227
+
228
+ text = "" if _sys.stdin.isatty() else _sys.stdin.read()
229
+
230
+ try:
231
+ vault = ctx.config.vault_root(args.vault)
232
+ except Exception:
233
+ vault = None
234
+
235
+ result = egress_terms.check(
236
+ text, vault=vault, threshold=args.min_tier, strict=args.strict,
237
+ )
238
+ result["explanation"] = egress_terms.explain(result)
239
+ if args.json:
240
+ _emit(result, True)
241
+ else:
242
+ _emit(None, False, result["explanation"])
243
+ return 0 if result["allowed"] else egress_terms.EXIT_REFUSED
244
+
245
+
213
246
  def _run_alerts(args, ctx) -> int:
214
247
  role = ctx.role
215
248
  config = ctx.config
@@ -258,16 +291,17 @@ def _run_install_hook(args, ctx) -> int:
258
291
  "Code config); refused on role=vm", file=sys.stderr)
259
292
  return 3
260
293
 
261
- claude_home = Path(args.claude_home).expanduser() if args.claude_home else (
262
- Path.home() / ".claude")
263
- result = session_hook.install(
264
- claude_home, _packaged_script(session_hook.HOOK_SCRIPT))
294
+ from ..update import claude_home_default
295
+
296
+ claude_home = (Path(args.claude_home).expanduser() if args.claude_home
297
+ else claude_home_default())
298
+ results = session_hook.install_all(claude_home, _packaged_script)
265
299
  _emit(
266
- result if args.json else None,
300
+ {"hooks": results} if args.json else None,
267
301
  args.json,
268
- None if args.json else session_hook.render_human(result),
302
+ None if args.json else session_hook.render_all(results),
269
303
  )
270
- return 0 if result["ok"] else 1
304
+ return 0 if all(r["ok"] for r in results) else 1
271
305
 
272
306
 
273
307
  def _run_mcp_config(args, ctx) -> int:
@@ -382,6 +416,7 @@ _HANDLERS = {
382
416
  "init": _run_init,
383
417
  "doctor": _run_doctor,
384
418
  "alerts": _run_alerts,
419
+ "check-egress": _run_check_egress,
385
420
  "exceptions": _run_exceptions,
386
421
  "install-hook": _run_install_hook,
387
422
  "mcp-config": _run_mcp_config,
@@ -105,6 +105,33 @@ def _add_alerts(sub) -> None:
105
105
  )
106
106
 
107
107
 
108
+ def _add_check_egress(sub) -> None:
109
+ sp = sub.add_parser(
110
+ "check-egress",
111
+ help="SEC-07: judge one OUTBOUND string (a web-search query, a URL, a "
112
+ "message body) against this vault's overlay keyword tiers, and "
113
+ "refuse when a term is classified at or above the threshold "
114
+ "(default Confidential). The classification gate protects the READ "
115
+ "side; this is the only check on the write side of a tool call. "
116
+ "Ships NO terms — they come from overlay/keywords/, so a vault "
117
+ "with no decoder ring maps nothing and SAYS SO. Pure file reads: "
118
+ "no index, embedder, network or key. Exit 6 = refused.",
119
+ )
120
+ sp.add_argument("text", nargs="?", help="the outbound string; omit to read stdin")
121
+ sp.add_argument("--json", action="store_true")
122
+ sp.add_argument(
123
+ "--min-tier",
124
+ help="refuse at or above this tier (default: $BRAIN_EGRESS_TERM_MIN_TIER, "
125
+ "else Confidential). An unrecognised value fails CLOSED.",
126
+ )
127
+ sp.add_argument(
128
+ "--strict",
129
+ action="store_true",
130
+ help="also refuse when the overlay maps NO terms — for a deployment that "
131
+ "will not accept an all-clear produced by an empty decoder ring",
132
+ )
133
+
134
+
108
135
  def _add_exceptions(sub) -> None:
109
136
  sp = sub.add_parser(
110
137
  "exceptions",
@@ -217,6 +244,7 @@ def add_parser(sub) -> None:
217
244
  _add_init(sub)
218
245
  _add_doctor(sub)
219
246
  _add_alerts(sub)
247
+ _add_check_egress(sub)
220
248
  _add_exceptions(sub)
221
249
  _add_install_hook(sub)
222
250
  _add_mcp_config(sub)
@@ -121,7 +121,10 @@ def _run_curate(args, ctx) -> int:
121
121
 
122
122
  def _run_integrity(args, ctx) -> int:
123
123
  core = ctx.core
124
- res = core.integrity(min_score=args.min_score, k=args.k)
124
+ scan_injection = bool(getattr(args, "injection", False))
125
+ res = core.integrity(
126
+ min_score=args.min_score, k=args.k, scan_injection=scan_injection,
127
+ )
125
128
  pairs = res["near_dup_pairs"]
126
129
  nodes = {}
127
130
  for p in pairs:
@@ -145,7 +148,12 @@ def _run_integrity(args, ctx) -> int:
145
148
  ]
146
149
  if res.get("audit_issue"):
147
150
  action_required.insert(0, res["audit_issue"])
148
- injection = _injection_rows(args, ctx) if getattr(args, "injection", False) else None
151
+ # Gated for DISPLAY only — a finding names a note, so reporting it to an
152
+ # Internal-capped reader would leak the very thing the gate withholds.
153
+ injection = (
154
+ _filter_dicts(res["injection_rows"], args.max_tier)[0]
155
+ if scan_injection else None
156
+ )
149
157
  if injection:
150
158
  action_required.extend(
151
159
  maint.action_required_item(
@@ -158,7 +166,7 @@ def _run_integrity(args, ctx) -> int:
158
166
  for row in injection
159
167
  if row["verdict"] == "conceal"
160
168
  )
161
- reads = _read_log_status(ctx)
169
+ reads = res["read_log"]
162
170
  if reads.get("bulk_reads"):
163
171
  action_required.append(maint.action_required_item(
164
172
  f"{reads['bulk_reads']} bulk read(s) in the last {reads['days']} days "
@@ -199,30 +207,6 @@ def _run_integrity(args, ctx) -> int:
199
207
  return 0
200
208
 
201
209
 
202
- def _read_log_status(ctx) -> dict:
203
- """SEC-06 access summary. Counts only — the log stores no note ids at all."""
204
- from .. import read_log as _rl
205
-
206
- try:
207
- return _rl.status(getattr(ctx.core, "vault", None))
208
- except Exception:
209
- return {"available": False, "reason": "read-log unreadable"}
210
-
211
-
212
- def _injection_rows(args, ctx) -> list:
213
- """SEC-05 corpus scan, gated at the same chokepoint as the near-dup pairs.
214
-
215
- A finding NAMES a note, so it is egress-gated like any other content
216
- surface — reporting "MNPI-note-x hides an instruction" to an Internal-capped
217
- reader would leak the very thing the gate exists to withhold.
218
- """
219
- from .. import injection_scan as _isc
220
-
221
- rows = _isc.scan_corpus(ctx.core.vault)
222
- surfaced, _report = _filter_dicts(rows, args.max_tier)
223
- return surfaced
224
-
225
-
226
210
  def _run_promote_scan(args, ctx) -> int:
227
211
  core = ctx.core
228
212
  res = core.promote_scan(k=args.k)
@@ -227,7 +227,9 @@ class _CoreHealthMixin:
227
227
  },
228
228
  "auto_fixed": auto_fixed,
229
229
  }
230
- def integrity(self, *, min_score: float = 0.95, k: int = 5) -> dict[str, Any]:
230
+ def integrity(
231
+ self, *, min_score: float = 0.95, k: int = 5, scan_injection: bool = True,
232
+ ) -> dict[str, Any]:
231
233
  """integrity-scan fold (task-disposition.md row 3): audit-chain verify
232
234
  + a corpus-wide near-dup scan directly over the brain vector backend
233
235
  (brain-cli-gaps.md G1 — no SC/MCP round-trip). READ-ONLY. UNFILTERED
@@ -299,12 +301,48 @@ class _CoreHealthMixin:
299
301
  f"{type(exc).__name__}: {exc}",
300
302
  "investigate the embedder/vector-backend, then re-run integrity"))
301
303
 
304
+ # SEC-05 corpus re-scan and the SEC-06 bulk-read alarm. Both lived ONLY
305
+ # in the `brain integrity` CLI body until 2026-09-01, so the Tuesday
306
+ # fold — the one caller that runs with no human present — fired neither
307
+ # of the two detectors built for indirect prompt injection. Moving them
308
+ # here is what arms them; the CLI now reads these keys instead of
309
+ # computing its own. UNFILTERED like `near_dup_pairs`: a finding NAMES a
310
+ # note, so every SURFACING caller egress-gates it.
311
+ injection_rows, reads = self._injection_and_reads(scan_injection, blocked)
312
+
302
313
  return {
303
314
  "ritual": "integrity", "min_score": min_score,
304
315
  "audit": audit_res, "audit_issue": audit_issue,
305
316
  "near_dup_pairs": pairs, # UNFILTERED
317
+ "injection_rows": injection_rows, # UNFILTERED
318
+ "read_log": reads,
306
319
  "blocked": blocked,
307
320
  }
321
+ def _injection_and_reads(
322
+ self, scan_injection: bool, blocked: list[dict[str, Any]],
323
+ ) -> tuple[list[dict[str, Any]], dict[str, Any]]:
324
+ """The two prompt-injection detectors, split out to keep `integrity`
325
+ inside the function-length ratchet. Appends to ``blocked`` in place:
326
+ a scan that raises is a reported gap, never a silent clean sweep."""
327
+ from .. import injection_scan as _isc
328
+ from .. import maintenance as maint
329
+ from .. import read_log as _rl
330
+
331
+ rows: list[dict[str, Any]] = []
332
+ if scan_injection:
333
+ try:
334
+ rows = _isc.scan_corpus(self.vault)
335
+ except Exception as exc:
336
+ blocked.append(maint.blocked_item(
337
+ "concealed-instruction scan raised",
338
+ f"{type(exc).__name__}: {exc}",
339
+ "investigate brain.injection_scan, then re-run integrity"))
340
+ try:
341
+ reads = _rl.status(self.vault)
342
+ except Exception:
343
+ reads = {"available": False, "reason": "read-log unreadable"}
344
+ return rows, reads
345
+
308
346
  def promote_scan(self, *, k: int = 50) -> dict[str, Any]:
309
347
  """promotion-scan fold (task-disposition.md row 5 — ON-INVOKE triage;
310
348
  promotion itself stays a P-10 human gate). Candidates: ``raw/`` zone
@@ -47,6 +47,7 @@ from __future__ import annotations
47
47
 
48
48
  import json
49
49
  import os
50
+ import secrets
50
51
  import threading
51
52
  from dataclasses import dataclass
52
53
  from pathlib import Path
@@ -155,7 +156,11 @@ def _store_bindings(vault: Any, bindings: dict[str, Any]) -> None:
155
156
  path = _bindings_path(vault)
156
157
  path.parent.mkdir(parents=True, exist_ok=True)
157
158
  config.secure_file_permissions(path.parent, 0o700)
158
- tmp = path.with_name(f"{path.name}.{os.getpid()}.tmp")
159
+ # A pid is not unique inside a process. Every caller today holds `_CLAIM_LOCK`
160
+ # plus the writer flock, so nothing races here now -- but a pid-named temp
161
+ # makes the NEXT caller's race silent, and the engine already has a
162
+ # collision-free convention (`cos/_io.py`, `lock.py`).
163
+ tmp = path.with_name(f"{path.name}.{secrets.token_hex(8)}.tmp")
159
164
  tmp.write_text(json.dumps(bindings, indent=2, sort_keys=True), encoding="utf-8")
160
165
  config.secure_file_permissions(tmp, 0o600)
161
166
  tmp.replace(path)
@@ -209,9 +209,9 @@ def _terminal_rows(
209
209
  *checks.mcp_vault_paths()])
210
210
  rows.append(checks.cos_deployed_skill())
211
211
  rows.extend(checks.desktop_plugin_store(context.app_support_dir, ssot))
212
- from .session_hook import doctor_row as _session_hook_row
212
+ from .session_hook import doctor_rows as _session_hook_rows
213
213
 
214
- rows.append(_session_hook_row(context.claude_home))
214
+ rows.extend(_session_hook_rows(context.claude_home))
215
215
  return rows
216
216
 
217
217
 
@@ -0,0 +1,130 @@
1
+ """Outbound term guard (SEC-07) — refuse a protected term leaving in a tool call.
2
+
3
+ **The gap this closes.** The classification gate protects the READ side: it
4
+ decides what a caller may see. It says nothing about what the model then does
5
+ with it. ``AGENTS.md``'s retrieval discipline rule 4 names the consequence —
6
+ "the model's own web-search tool is an *ungated outbound channel*, and the
7
+ query string itself is the leak" — and until this module that rule was PROSE,
8
+ enforced by the model's compliance. A hostile document can carry an instruction
9
+ that puts a codename into a search query; nobody reviews a query string, and by
10
+ the time the results come back the term has already reached a third party.
11
+
12
+ **Why the terms are not in this repo, and never will be.** A shipped list of
13
+ one owner's counterparties is useless to every other owner and a disclosure in
14
+ itself. The terms come from the vault's own **overlay** — ``overlay/keywords/``,
15
+ the per-owner decoder ring whose rows are ``| Term | Expansion | Classification |``
16
+ — read through :func:`brain.overlay.match_keyword_tier`. The engine ships the
17
+ RULE; the vault supplies the DATA. That is the same split the overlay exists for
18
+ (``overlay/README.md``), and those rows already drive ingest classification
19
+ (``provenance.py``) and the COS grounding lane (``cos_ground.py``), so they stay
20
+ current as a side effect of work the owner already does rather than as a second
21
+ list to curate.
22
+
23
+ **An empty decoder ring is REPORTED, never a quiet pass.** A vault that maps no
24
+ terms cannot have a term caught, and a check whose all-clear means "nothing was
25
+ looked at" is worse than no check. :func:`check` always returns
26
+ ``mapped_terms``, every caller surfaces it, and ``--strict`` turns an empty ring
27
+ into a refusal for a deployment that wants fail-closed.
28
+
29
+ **What this does NOT do.** It matches DECLARED terms, on word boundaries, case
30
+ insensitively. It does not catch a paraphrase, a codename nobody wrote down, or
31
+ a fact the model restates in its own words. It raises the bar for the terms an
32
+ owner has declared. It does not close the channel, and no caller should describe
33
+ it as though it did.
34
+ """
35
+ from __future__ import annotations
36
+
37
+ import os
38
+ from typing import Any
39
+
40
+ from . import classification as cls
41
+
42
+ #: Terms at or above this tier refuse. `Internal` is deliberately NOT the
43
+ #: default: an owner's whole vault is Internal by default, so defaulting there
44
+ #: would refuse almost every query and the guard would be switched off within a
45
+ #: day. `Confidential` is the first tier that means "this one is actually
46
+ #: sensitive".
47
+ DEFAULT_MIN_TIER = "Confidential"
48
+
49
+ #: Exit code for a refused text. Distinct from 3 (VM refusal), 4 and
50
+ #: 5 (SEC-06 record failure) so a hook can tell them apart.
51
+ EXIT_REFUSED = 6
52
+
53
+ MIN_TIER_ENV_VAR = "BRAIN_EGRESS_TERM_MIN_TIER"
54
+
55
+
56
+ def min_tier() -> str:
57
+ """The configured refusal threshold, fail-CLOSED on an unrecognised value.
58
+
59
+ A typo must never silently widen what may leave: the only reason to set
60
+ this var is to change the threshold deliberately, so an unreadable value
61
+ falls to the most protective tier rather than the permissive default.
62
+ Same rule, and same reasoning, as ``mcp_verbs._egress_ceiling_tier``.
63
+ """
64
+ raw = os.environ.get(MIN_TIER_ENV_VAR, "").strip()
65
+ if not raw:
66
+ return DEFAULT_MIN_TIER
67
+ return raw if raw in cls.RANK else cls.TIERS[0]
68
+
69
+
70
+ def check(
71
+ text: str,
72
+ *,
73
+ vault: str | os.PathLike[str] | None = None,
74
+ overlay_dir: str | os.PathLike[str] | None = None,
75
+ threshold: str | None = None,
76
+ strict: bool = False,
77
+ ) -> dict[str, Any]:
78
+ """Judge one outbound string against this vault's overlay keyword tiers.
79
+
80
+ Returns ``allowed`` plus everything a caller needs to explain itself:
81
+ the matched ``term`` and its ``tier``, the ``threshold`` applied, and
82
+ ``mapped_terms`` — the size of the decoder ring that was actually
83
+ consulted, so a zero is visible rather than indistinguishable from a pass.
84
+ """
85
+ from . import overlay as ov
86
+
87
+ if threshold is None:
88
+ bar = min_tier()
89
+ elif threshold in cls.RANK:
90
+ bar = threshold
91
+ else:
92
+ bar = cls.TIERS[0] # fail-closed on a typo, as `min_tier` does
93
+ mapped = ov.resolve_keyword_tiers(vault, overlay_dir)
94
+ tier, term = (None, None)
95
+ if mapped and text:
96
+ tier, term = ov.match_keyword_tier(text, vault, overlay_dir)
97
+
98
+ refused = bool(tier) and cls.RANK[tier] >= cls.RANK[bar]
99
+ no_terms = not mapped
100
+ return {
101
+ "allowed": not refused and not (strict and no_terms),
102
+ "refused_for": "term" if refused else ("no-mapped-terms" if strict and no_terms else None),
103
+ "term": term if refused else None,
104
+ "tier": tier if refused else None,
105
+ "threshold": bar,
106
+ "mapped_terms": len(mapped),
107
+ "strict": strict,
108
+ }
109
+
110
+
111
+ def explain(result: dict[str, Any]) -> str:
112
+ """One line a hook can print, saying WHY — including on the allow path.
113
+
114
+ The empty-decoder-ring case gets its own sentence on purpose: a caller
115
+ reading only "allowed" would report a vault that protects nothing as a
116
+ vault that found nothing.
117
+ """
118
+ if result.get("refused_for") == "term":
119
+ return (f"REFUSED: '{result['term']}' is classified {result['tier']} in this "
120
+ f"vault's overlay keywords (threshold {result['threshold']}). "
121
+ f"The query string itself is an egress event — rephrase without the term, "
122
+ f"or stay in the vault.")
123
+ if result.get("refused_for") == "no-mapped-terms":
124
+ return ("REFUSED (--strict): this vault's overlay maps NO keyword tiers, so "
125
+ "nothing could be checked. Fill overlay/keywords/ or drop --strict.")
126
+ if not result.get("mapped_terms"):
127
+ return ("allowed — but this vault's overlay maps NO keyword tiers, so no term "
128
+ "could have been caught. This is not evidence the text is safe.")
129
+ return (f"allowed — {result['mapped_terms']} mapped term(s) checked at "
130
+ f"threshold {result['threshold']}")
@@ -46,6 +46,41 @@ class WeeklyFoldsMixin:
46
46
  "near-dup scan",
47
47
  )
48
48
  )
49
+ # The two prompt-injection detectors. COUNTS ONLY, never note ids:
50
+ # `record_current_findings` persists this text into
51
+ # `.brain/notify-sent/current.json`, which the Cowork VM reads, so a
52
+ # name here would carry a title straight past the egress gate (the
53
+ # same rule `ingest_quarantine_findings` follows, and for the same
54
+ # reason). Each carries its own `notify_key` — without one an item
55
+ # stays in the maintain result and never reaches `brain alerts`.
56
+ conceal = [r for r in (result.get("injection_rows") or [])
57
+ if r.get("verdict") == "conceal"]
58
+ if conceal:
59
+ item = maintenance.action_required_item(
60
+ f"{len(conceal)} vault note(s) hide instructions from a human "
61
+ f"reader (names withheld — run the gated command below)",
62
+ "text concealed from a reader but visible to a model is the "
63
+ "shape of an indirect prompt injection",
64
+ "run `brain integrity --injection --json` for the gated list; "
65
+ "read each note by hand and retire what is not yours",
66
+ "concealed-instruction scan (SEC-05)",
67
+ )
68
+ item["notify_key"] = "injection:conceal"
69
+ run.action_required.append(item)
70
+ reads = result.get("read_log") or {}
71
+ if reads.get("bulk_reads"):
72
+ item = maintenance.action_required_item(
73
+ f"{reads['bulk_reads']} bulk read(s) in the last "
74
+ f"{reads['days']} day(s) (>= {reads['threshold']} notes "
75
+ f"surfaced in one call)",
76
+ "a single call surfacing hundreds of notes is a sweep, not a "
77
+ "question — the shape an injected agent leaves when it "
78
+ "exfiltrates through the gate",
79
+ "review the rows; if none was yours, treat it as an incident",
80
+ reads.get("dir", ""),
81
+ )
82
+ item["notify_key"] = "read-log:bulk"
83
+ run.action_required.append(item)
49
84
  run.mark("integrity", True)
50
85
  except Exception as exc:
51
86
  self._record_weekly_failure(run, "integrity", exc)