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.
- {brainiac_cli-0.20.34/src/brainiac_cli.egg-info → brainiac_cli-0.20.35}/PKG-INFO +1 -1
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/pyproject.toml +1 -1
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/AGENTS.md +46 -1
- brainiac_cli-0.20.35/src/brain/_assets/scripts/brainiac-egress-guard.sh +71 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_version.py +1 -1
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli.py +6 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/lifecycle.py +42 -7
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/lifecycle_parser.py +28 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/maintenance.py +11 -27
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/core/_health.py +39 -1
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/deliverables_shelf.py +6 -1
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/doctor_checks.py +2 -2
- brainiac_cli-0.20.35/src/brain/egress_terms.py +130 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/weekly.py +35 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/session_hook.py +132 -44
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/update.py +26 -3
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/update_channels_2.py +10 -3
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/vmstaging.py +1 -1
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35/src/brainiac_cli.egg-info}/PKG-INFO +1 -1
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brainiac_cli.egg-info/SOURCES.txt +2 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/LICENSE +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/MANIFEST.in +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/README.md +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/setup.cfg +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/__init__.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/__main__.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/assets/graph-explorer-template.html +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/overlay/template/brand/brand-guide.md +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/overlay/template/cos/auto-archive.md +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/overlay/template/cos/browser-lane.md +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/overlay/template/cos/drafts.md +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/overlay/template/cos/ingest.md +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/overlay/template/cos/priorities.md +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/overlay/template/cos/tenant-domains.md +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/overlay/template/keywords/glossary.md +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/overlay/template/people/roster.md +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/overlay/template/voice/voice-profile.md +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/routines/manifest.json +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/scripts/brain-brief-mac.plist +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/scripts/brain-brief.sh +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/scripts/brain-synthesis-mac.plist +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/scripts/brain-synthesis.sh +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/scripts/brainiac-alerts.sh +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/scripts/install-brief-mac.sh +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/scripts/install-brief-windows.ps1 +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/scripts/register_tasks.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/scripts/vm-boundary-probe.sh +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/scripts/vm-selftest.sh +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/templates/company.md +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/templates/concept.md +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/templates/daily.md +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/templates/decision.md +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/templates/deliverable.md +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/templates/meeting.md +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/templates/person.md +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/templates/project.md +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/templates/state-moc.md +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_browser_scan.mjs +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_cdp_capture.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_contract.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_contract_criteria.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_contract_criteria_2.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_contract_ledger_scan.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_contract_provenance.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_contract_snapshot_shape.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_contract_verdict_clauses.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_driver.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_driver_accounting.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_driver_capture.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_driver_categories.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_driver_cli.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_driver_completeness.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_driver_draw.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_driver_enumeration.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_driver_gate.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_driver_night_records.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_driver_selfcheck.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_driver_transport.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_mutate.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_mutate_apply.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_mutate_bridge.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_mutate_canary.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_mutate_cli.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_mutate_evidence.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_mutate_gates.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_mutate_ledger.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_mutate_passes.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_mutate_plan.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_mutate_plan_aged.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_mutate_policy.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_mutate_rehearsal.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_mutate_selfcheck.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_mutate_shapes.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_mutate_shapestore.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_reconcile_append.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_reconcile_guard.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_reconcile_metrics.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_reconcile_rows.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_reconcile_steps.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_retro.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/_assets/tools/cos_retro_scanners.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/alerts.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/alerts_staging.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/anchor.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/atomic_temp.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/attestation.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/audit.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/audit_chain.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/audit_drift.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/autolink.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/backup.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/brief.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/brief_render.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/capture.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/chunk.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/classification.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/__init__.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/common_parser.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/connect.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/connect_parser.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/cos_capture.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/cos_capture_parser.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/cos_operations.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/cos_operations_parser.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/eval.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/eval_parser.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/index_storage.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/index_storage_parser.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/ingest_storage.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/ingest_storage_parser.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/maintenance_parser.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/navigation.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/navigation_parser.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/owner_workflow.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/owner_workflow_parser.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/presentation.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/presentation_parser.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/retrieval.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/retrieval_parser.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/update.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/update_parser.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/versioning.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_cmds/versioning_parser.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_help.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_read_record.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cli_render.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/config.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/config_hostpaths.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/connect.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/context.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/conventions.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/conventions_scan.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/core/__init__.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/core/_audit.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/core/_briefing.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/core/_capture.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/core/_capture_staging.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/core/_cos_facade.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/core/_durability.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/core/_maintenance.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/core/_maintenance_state.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/core/_retrieval.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/core/_shared.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/core/_supersession.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/core/_supersession_journal.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/__init__.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_answer_consume.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_approval.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_approval_cleanup.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_attachment_acceptance.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_attachment_anchors.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_attachment_store.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_autocap.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_batch_apply.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_batches.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_behavior.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_claim_binding.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_claim_evaluation.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_claim_sweep.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_claims_state.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_constants.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_consumption.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_corrections.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_criteria.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_evidence.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_facade.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_guards.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_hold_release.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_hold_store.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_hold_undo.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_ingest_claim.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_ingest_sweep.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_ingress.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_io.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_layout.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_layout_setup.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_learning_config.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_learning_ledger.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_ledger_join.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_priority.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_proposal_maintenance.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_proposal_state.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_routing.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_run_manifest_write.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_run_migration.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_runs.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_shared.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_spine.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_standing_approval.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_status.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_taxonomy.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_version_apply.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos/_version_links.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_chips.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_corpus.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_corpus_retention.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_corpus_write.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_deploy.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_echecks.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_echecks_answers.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_echecks_answers_2.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_echecks_delivery.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_echecks_runs.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_runverify.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_runverify_alerts.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_runverify_checks.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_runverify_chipdraw.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_runverify_chips.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_runverify_contract.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_runverify_corpus.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_runverify_identity.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_runverify_io.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_runverify_join.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_runverify_ledger.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_runverify_metrics.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_runverify_planbinding.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_runverify_preamble.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_runverify_selfeval.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_runverify_stamps.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cos_runverify_touch.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cowork_leak_artifact.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cowork_leak_databases.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cowork_leak_notes.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cowork_leak_scan.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cowork_snapshot_guard.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/cowork_staging.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/dbretry.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/deliverables_absorb.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/deliverables_apply.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/deliverables_ledger.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/deliverables_materialize.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/deliverables_previous.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/deliverables_readme.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/deliverables_sync.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/doctor.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/doctor_context.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/doctor_desktop.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/doctor_health.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/doctor_mount_leak.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/doctor_plugins.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/doctor_staging.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/doctor_vendor.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/doctor_vm.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/doctor_wiring.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/draft_drain.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/draft_update.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/egress.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/embed.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/embed_backends.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/embed_catalog.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/embed_onnx.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/embed_qwen.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/encryption.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/exceptions_cli.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/exceptions_page.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/exceptions_render.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/exceptions_tray.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/exceptions_verify.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/__init__.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/context.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/cos.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/daily.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/deliverables.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/golden.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/graph.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/intake.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/invariants.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/orchestrator.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/organization.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/preflight.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/publish.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/remediation.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/remediation_bookkeeping.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/reporting.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/retention.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/folds/watchdogs.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/frontmatter.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/golden_ops.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/golden_probe.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/golden_probe_cases.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/graph.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/graph_hygiene.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/graph_ops.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/graphify.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/graphify_edges.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/graphreport.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/graphreport_sections.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/graphreport_sections_2.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/healthreport.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/healthreport_sections.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/healthreport_unsigned.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/inbox.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index/__init__.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index/_connection.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index/_families.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index/_graph.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index/_identity.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index/_lifecycle.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index/_models.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index/_planning.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index/_records.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index/_retirement.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index/_schema.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index/_search.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index/_settings.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index/_shared.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index/_tools.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index/_zones.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index_stages/__init__.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index_stages/connection.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index_stages/diagnostics.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index_stages/duplicate_families.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index_stages/near_duplicates.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index_stages/rebuild.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index_stages/reranking.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index_stages/search.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index_stages/search_finalization.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index_stages/search_identity.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index_stages/search_ranking.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/index_stages/sync.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/__init__.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/deliverables.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/handlers/__init__.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/handlers/base.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/handlers/docx.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/handlers/email.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/handlers/html.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/handlers/image.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/handlers/pdf.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/handlers/pptx.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/handlers/tables.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/handlers/text.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/handlers/xlsx.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/handlers/zip.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/pipeline.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/pipeline_duplicates.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/pipeline_files.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/pipeline_injection.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/pipeline_nested.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/pipeline_run.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/pipeline_stages.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/tierguard.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/tierguard_verdict.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/ingest/transcript.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/init.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/init_import.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/init_samples.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/init_seed.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/init_steps.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/injection_scan.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/invariant_coverage.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/invariant_crosstier.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/invariant_deliverables.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/invariant_floors.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/invariant_ingest_guard.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/invariant_lane_health.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/invariant_reachability.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/invariant_shared.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/invariant_unsigned_notes.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/invariants.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/invariants_metrics.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/language.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/link_targets.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/lock.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/maintenance.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/maintenance_escalation.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/maintenance_folds.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/maintenance_folds_2.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/maintenance_folds_3.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/maintenance_folds_4.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/maintenance_golden.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/maintenance_graphfolds.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/maintenance_healthlog.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/maintenance_hotmd.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/maintenance_invariant_fields.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/maintenance_notify.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/maintenance_outcomes.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/maintenance_recommendations.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/maintenance_retention.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/maintenance_sweep.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/maintenance_watchdog.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/mcp_adapter.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/mcp_capture_verbs.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/mcp_housekeeping_verbs.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/mcp_mediation.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/mcp_tools/__init__.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/mcp_tools/capture.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/mcp_tools/core_read.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/mcp_tools/housekeeping.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/mcp_tools/retrieval.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/mcp_verbs.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/multihop.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/notes.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/originals_resolve.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/overlay.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/overlay_rules.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/pathkey.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/progress.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/projection.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/provenance.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/provision.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/provision_mcp.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/provision_stage.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/provision_wire.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/querylog.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/querylog_digest.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/querylog_replay.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/querylog_status.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/read_log.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/reexec.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/remediation.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/remediation_answers.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/remediation_branches.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/remediation_exceptions.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/remediation_exceptions_store.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/remediation_extract_retry.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/remediation_folds.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/remediation_questions.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/remediation_routing.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/remediation_schema.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/remediation_state.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/rerank.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/rerank_onnx.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/retrieval_ops.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/retro.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/snapshot.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/spine.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/spine_render.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/status_ops.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/sweepdirs.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/update_channels.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/update_model.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/update_ops.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/update_plugins.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/update_state.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/vectors.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/versionlink.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/versionlink_stages.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/vm_ceiling.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brain/workspaces.py +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brainiac_cli.egg-info/dependency_links.txt +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brainiac_cli.egg-info/entry_points.txt +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brainiac_cli.egg-info/requires.txt +0 -0
- {brainiac_cli-0.20.34 → brainiac_cli-0.20.35}/src/brainiac_cli.egg-info/top_level.txt +0 -0
|
@@ -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.
|
|
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`, `
|
|
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
|
|
@@ -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
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
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
|
-
|
|
300
|
+
{"hooks": results} if args.json else None,
|
|
267
301
|
args.json,
|
|
268
|
-
None if args.json else session_hook.
|
|
302
|
+
None if args.json else session_hook.render_all(results),
|
|
269
303
|
)
|
|
270
|
-
return 0 if
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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(
|
|
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
|
-
|
|
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
|
|
212
|
+
from .session_hook import doctor_rows as _session_hook_rows
|
|
213
213
|
|
|
214
|
-
rows.
|
|
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)
|