brainiac-cli 0.20.46__tar.gz → 0.20.48__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.46/src/brainiac_cli.egg-info → brainiac_cli-0.20.48}/PKG-INFO +1 -1
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/pyproject.toml +1 -1
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/_assets/AGENTS.md +11 -4
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/_assets/scripts/brain-synthesis.sh +13 -1
- brainiac_cli-0.20.48/src/brain/_assets/scripts/shorten_long_note_ids.py +100 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/_version.py +1 -1
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/alerts.py +5 -5
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/alerts_staging.py +36 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cli_cmds/lifecycle_parser.py +1 -1
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/ingest/handlers/concealment.py +32 -9
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/ingest/handlers/concealment_office.py +3 -3
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/injection_scan.py +32 -3
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/mcp_housekeeping_verbs.py +1 -1
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/remediation.py +12 -83
- brainiac_cli-0.20.48/src/brain/remediation_invariants.py +93 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48/src/brainiac_cli.egg-info}/PKG-INFO +1 -1
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brainiac_cli.egg-info/SOURCES.txt +2 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/LICENSE +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/MANIFEST.in +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/README.md +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/setup.cfg +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/__init__.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/__main__.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/_assets/assets/graph-explorer-template.html +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/_assets/overlay/template/brand/brand-guide.md +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/_assets/overlay/template/keywords/glossary.md +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/_assets/overlay/template/people/roster.md +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/_assets/overlay/template/voice/voice-profile.md +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/_assets/routines/manifest.json +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/_assets/scripts/brain-brief-mac.plist +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/_assets/scripts/brain-brief.sh +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/_assets/scripts/brain-synthesis-mac.plist +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/_assets/scripts/brain_synthesis_codex.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/_assets/scripts/brainiac-alerts.sh +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/_assets/scripts/brainiac-egress-guard.sh +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/_assets/scripts/install-brief-mac.sh +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/_assets/scripts/install-brief-windows.ps1 +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/_assets/scripts/register_tasks.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/_assets/scripts/vm-boundary-probe.sh +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/_assets/scripts/vm-selftest.sh +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/_assets/templates/company.md +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/_assets/templates/concept.md +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/_assets/templates/daily.md +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/_assets/templates/decision.md +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/_assets/templates/deliverable.md +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/_assets/templates/meeting.md +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/_assets/templates/person.md +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/_assets/templates/project.md +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/_assets/templates/state-moc.md +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/_optional.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/alerts_render.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/anchor.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/atomic_temp.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/attestation.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/audit.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/audit_chain.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/audit_drift.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/autolink.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/backup.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/brief.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/brief_render.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/capture.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/chunk.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/classification.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cli.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cli_cmds/__init__.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cli_cmds/common_parser.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cli_cmds/connect.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cli_cmds/connect_parser.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cli_cmds/eval.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cli_cmds/eval_parser.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cli_cmds/index_storage.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cli_cmds/index_storage_parser.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cli_cmds/ingest_storage.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cli_cmds/ingest_storage_parser.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cli_cmds/lifecycle.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cli_cmds/maintenance.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cli_cmds/maintenance_parser.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cli_cmds/navigation.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cli_cmds/navigation_parser.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cli_cmds/owner_workflow.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cli_cmds/owner_workflow_parser.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cli_cmds/presentation.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cli_cmds/presentation_parser.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cli_cmds/retrieval.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cli_cmds/retrieval_parser.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cli_cmds/update.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cli_cmds/update_parser.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cli_cmds/versioning.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cli_cmds/versioning_parser.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cli_help.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cli_read_record.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cli_render.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/config.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/config_hostpaths.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/connect.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/context.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/conventions.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/conventions_scan.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/core/__init__.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/core/_audit.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/core/_briefing.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/core/_capture.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/core/_capture_staging.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/core/_durability.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/core/_health.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/core/_maintenance.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/core/_maintenance_state.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/core/_retrieval.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/core/_shared.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/core/_supersede_batch.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/core/_supersession.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/core/_supersession_journal.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cowork_leak_artifact.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cowork_leak_databases.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cowork_leak_notes.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cowork_leak_scan.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cowork_snapshot_guard.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/cowork_staging.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/dbretry.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/deliverables_absorb.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/deliverables_apply.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/deliverables_ledger.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/deliverables_materialize.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/deliverables_previous.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/deliverables_readme.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/deliverables_shelf.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/deliverables_sync.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/doctor.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/doctor_checks.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/doctor_context.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/doctor_desktop.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/doctor_health.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/doctor_mount_leak.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/doctor_plugins.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/doctor_staging.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/doctor_vendor.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/doctor_vm.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/doctor_wiring.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/draft_drain.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/draft_update.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/egress.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/egress_terms.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/embed.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/embed_backends.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/embed_catalog.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/embed_onnx.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/embed_qwen.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/encryption.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/exceptions_cli.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/exceptions_page.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/exceptions_render.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/exceptions_tray.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/exceptions_verify.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/folds/__init__.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/folds/context.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/folds/daily.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/folds/deliverables.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/folds/golden.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/folds/graph.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/folds/intake.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/folds/invariants.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/folds/orchestrator.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/folds/organization.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/folds/preflight.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/folds/publish.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/folds/remediation.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/folds/remediation_bookkeeping.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/folds/reporting.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/folds/retention.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/folds/watchdogs.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/folds/weekly.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/frontmatter.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/golden_ops.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/golden_probe.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/golden_probe_cases.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/graph.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/graph_hygiene.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/graph_ops.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/graphify.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/graphify_edges.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/graphreport.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/graphreport_sections.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/graphreport_sections_2.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/guard_settings.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/healthreport.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/healthreport_sections.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/healthreport_unsigned.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/inbox.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/index/__init__.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/index/_connection.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/index/_families.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/index/_graph.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/index/_identity.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/index/_lifecycle.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/index/_models.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/index/_planning.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/index/_records.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/index/_retirement.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/index/_schema.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/index/_search.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/index/_settings.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/index/_shared.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/index/_tools.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/index/_zones.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/index_stages/__init__.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/index_stages/connection.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/index_stages/diagnostics.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/index_stages/duplicate_families.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/index_stages/near_duplicates.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/index_stages/rebuild.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/index_stages/reranking.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/index_stages/search.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/index_stages/search_finalization.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/index_stages/search_identity.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/index_stages/search_ranking.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/index_stages/sync.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/ingest/__init__.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/ingest/deliverables.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/ingest/handlers/__init__.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/ingest/handlers/base.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/ingest/handlers/concealment_gate.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/ingest/handlers/concealment_html.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/ingest/handlers/concealment_ledger.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/ingest/handlers/concealment_pdf.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/ingest/handlers/docx.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/ingest/handlers/email.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/ingest/handlers/html.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/ingest/handlers/image.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/ingest/handlers/ole.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/ingest/handlers/pdf.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/ingest/handlers/pptx.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/ingest/handlers/tables.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/ingest/handlers/text.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/ingest/handlers/xlsx.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/ingest/handlers/zip.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/ingest/pipeline.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/ingest/pipeline_duplicates.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/ingest/pipeline_files.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/ingest/pipeline_injection.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/ingest/pipeline_nested.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/ingest/pipeline_run.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/ingest/pipeline_stages.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/ingest/tierguard.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/ingest/tierguard_verdict.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/ingest/transcript.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/init.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/init_import.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/init_samples.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/init_seed.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/init_steps.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/injection_fold.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/interview.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/interview_apply.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/interview_detect.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/invariant_coverage.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/invariant_crosstier.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/invariant_deliverables.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/invariant_floors.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/invariant_ingest_guard.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/invariant_lane_health.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/invariant_reachability.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/invariant_shared.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/invariant_unsigned_notes.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/invariants.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/invariants_metrics.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/language.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/link_targets.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/lock.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/maintenance.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/maintenance_escalation.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/maintenance_folds.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/maintenance_folds_2.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/maintenance_folds_3.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/maintenance_folds_4.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/maintenance_golden.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/maintenance_graphfolds.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/maintenance_healthlog.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/maintenance_hotmd.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/maintenance_invariant_fields.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/maintenance_notify.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/maintenance_outcomes.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/maintenance_para.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/maintenance_recommendations.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/maintenance_retention.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/maintenance_sweep.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/maintenance_watchdog.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/mcp_adapter.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/mcp_capture_verbs.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/mcp_mediation.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/mcp_supersede_verbs.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/mcp_tools/__init__.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/mcp_tools/capture.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/mcp_tools/core_read.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/mcp_tools/housekeeping.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/mcp_tools/retrieval.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/mcp_tools/supersession.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/mcp_verbs.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/model_slot.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/multihop.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/notes.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/originals_resolve.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/overlay.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/overlay_core.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/overlay_generated.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/overlay_keywords.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/overlay_rules.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/pathkey.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/progress.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/projection.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/provenance.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/provision.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/provision_mcp.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/provision_stage.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/provision_wire.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/querylog.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/querylog_digest.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/querylog_replay.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/querylog_status.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/read_log.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/reexec.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/remediation_answers.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/remediation_branches.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/remediation_converged.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/remediation_exceptions.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/remediation_exceptions_store.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/remediation_extract_retry.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/remediation_folds.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/remediation_questions.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/remediation_routing.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/remediation_schema.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/remediation_state.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/rerank.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/rerank_onnx.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/retrieval_ops.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/retro.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/session_hook.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/session_hook_settings.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/session_hook_spec.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/snapshot.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/status_ops.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/supersede_declared.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/sweepdirs.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/update.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/update_channels.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/update_channels_2.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/update_cos_guard.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/update_model.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/update_ops.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/update_plugins.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/update_state.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/update_venv.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/vectors.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/versionlink.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/versionlink_stages.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/vm_ceiling.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/vmstaging.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/workspaces.py +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brainiac_cli.egg-info/dependency_links.txt +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brainiac_cli.egg-info/entry_points.txt +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brainiac_cli.egg-info/requires.txt +0 -0
- {brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/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.
|
|
3
|
+
Version: 0.20.48
|
|
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
|
Project-URL: Homepage, https://github.com/Autopsias/brainiac
|
|
@@ -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.48"
|
|
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"
|
|
@@ -1028,6 +1028,12 @@ rules, in order:
|
|
|
1028
1028
|
env-exported `$BRAIN_VM_MAX_EGRESS_TIER` is IGNORED (the session's own
|
|
1029
1029
|
shell can set it; VULN-3386). Raising a VM's ceiling is a host-operator
|
|
1030
1030
|
action (`brain vm-egress-tier <TIER>`, signed), never the model's own.
|
|
1031
|
+
That clamp binds the VM's own `brain --role vm` CLI only. A Cowork session
|
|
1032
|
+
that reads through the host broker (`brain-mcp`, the Desktop MCP entry)
|
|
1033
|
+
gets the BROKER's ceiling — the full vault unless `$BRAIN_MAX_EGRESS_TIER`
|
|
1034
|
+
narrows that entry — because the broker receives no caller role (owner
|
|
1035
|
+
ruling, `docs/security-acceptances.md` A-01; pentest VULN-3419). When
|
|
1036
|
+
asked for your cap, report the broker's, never the VM file's.
|
|
1031
1037
|
|
|
1032
1038
|
3. **Ask it in every language the vault holds — the VARIANT CONTRACT
|
|
1033
1039
|
(CON-01) — and paraphrase within your own.** The cross-language half is a
|
|
@@ -1850,8 +1856,9 @@ entry formats in `docs/session-memory.md`. Rules an agent needs at a glance:
|
|
|
1850
1856
|
count match the host's; there is nothing enforcing that a session actually
|
|
1851
1857
|
does it. When `N > 0`, do not tell the owner to go run `/brain-inbox` —
|
|
1852
1858
|
open the exceptions page yourself and walk them through what is open using
|
|
1853
|
-
`AskUserQuestion`, one at a time. The
|
|
1854
|
-
(`~/.brainiac/update-state.json`, `~/.brain/synthesis-state.json
|
|
1859
|
+
`AskUserQuestion`, one at a time. The three host-home sources
|
|
1860
|
+
(`~/.brainiac/update-state.json`, `~/.brain/synthesis-state.json`,
|
|
1861
|
+
`~/.brain/retrieval-watch-state.json`) stay out
|
|
1855
1862
|
of the VM's reach and are listed under `unreachable` rather than skipped.
|
|
1856
1863
|
Claude Code and Codex both fire this from a SessionStart hook; run it first
|
|
1857
1864
|
regardless of harness.
|
|
@@ -1928,8 +1935,8 @@ summary is the one authority for open owner questions on the VM leg now.
|
|
|
1928
1935
|
That is the whole point of the first bullet above — until 2026-08-14 this
|
|
1929
1936
|
logic lived only in a Claude Code hook, so a Cowork session worked for days
|
|
1930
1937
|
against a vault whose invariants had regressed with no surface that could
|
|
1931
|
-
tell it. Only the
|
|
1932
|
-
`~/.brain/synthesis-state.json`) are out of the VM's reach, and `alerts`
|
|
1938
|
+
tell it. Only the three HOST-HOME sources (`~/.brainiac/update-state.json`,
|
|
1939
|
+
`~/.brain/synthesis-state.json`, `~/.brain/retrieval-watch-state.json`) are out of the VM's reach, and `alerts`
|
|
1933
1940
|
reports those under `unreachable` rather than skipping them. The reads are
|
|
1934
1941
|
file-reads of counts and two findings files; the VM still never WRITES
|
|
1935
1942
|
anything here,
|
|
@@ -82,7 +82,9 @@ note and will be rejected; cite the source in the BODY as [[<bare-id>]], never
|
|
|
82
82
|
edge, so it inflates the metric while linking nothing); put the [[raw/<id>]]
|
|
83
83
|
form in the note's `source:` frontmatter as usual; set the note's
|
|
84
84
|
classification to at least the HIGHEST classification among the sources it
|
|
85
|
-
cites, read live off each source;
|
|
85
|
+
cites, read live off each source; give each new note a SHORT kebab-case
|
|
86
|
+
id of at most 80 characters, the same as its file name (a sentence-long id
|
|
87
|
+
is refused at signing and the note stays unsigned); and NEVER append these links into an
|
|
86
88
|
existing hand-curated note — always a new note. Work the list in order; in
|
|
87
89
|
your final summary state HOW MANY of the file's candidates you covered (the
|
|
88
90
|
file's own "consumed_today" field is the fold's count — quote yours beside
|
|
@@ -335,6 +337,16 @@ doctor: $DOCTOR_DIAG"
|
|
|
335
337
|
find "$VAULT/brain" -type f -name '*.md' \
|
|
336
338
|
! -name 'backlinks.md' ! -name 'catalog.md' \
|
|
337
339
|
-newer "$SIGN_MARKER" > "$SIGN_LIST" 2>/dev/null || true
|
|
340
|
+
# A sentence-long note name is refused by the drain's safe-id check
|
|
341
|
+
# (2026-10-25: 20 of 45 notes unsigned, rc=75). Give those a short id
|
|
342
|
+
# first; anything it cannot shorten safely is left for the drain to refuse.
|
|
343
|
+
SHORT_LIST="$(mktemp -t brain-synthesis-shortlist)"
|
|
344
|
+
if python3 "$(dirname "$0")/shorten_long_note_ids.py" "$SIGN_LIST" \
|
|
345
|
+
> "$SHORT_LIST" 2>>"$LOG"; then
|
|
346
|
+
mv -f -- "$SHORT_LIST" "$SIGN_LIST"
|
|
347
|
+
else
|
|
348
|
+
rm -f -- "$SHORT_LIST"
|
|
349
|
+
fi
|
|
338
350
|
SIGNED=0; SIGN_FAIL=0
|
|
339
351
|
# fd 3, not stdin: `brain write` reads the note body FROM STDIN, so a
|
|
340
352
|
# plain `while read` loop would feed it the file list instead.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""Give a too-long note a short id before the synthesis sign-drain signs it.
|
|
2
|
+
|
|
3
|
+
2026-10-25: the confined session named 20 of its 45 notes after a whole
|
|
4
|
+
sentence (up to 208 characters). `notes.safe_slug` refuses an id over
|
|
5
|
+
MAX_SLUG_BYTES, so all 20 stayed unsigned and the run failed rc=75.
|
|
6
|
+
|
|
7
|
+
Reads the drain's file list (one path per line), and for each note whose
|
|
8
|
+
file stem is over the limit AND equals its frontmatter `id:`, moves it to
|
|
9
|
+
`<first words>-<8 hex of the old id>.md` and rewrites that one `id:` line.
|
|
10
|
+
Anything else is left exactly as it was, so the drain still refuses it.
|
|
11
|
+
Then rewrites `[[old-id]]` links to `[[new-id]]` in every listed note: the
|
|
12
|
+
same session wrote the notes that link to these, and on 2026-10-26 a rename
|
|
13
|
+
without this left 18 links pointing at files that no longer existed.
|
|
14
|
+
Prints the list back with the new paths. Stdlib only: the script runs it
|
|
15
|
+
with the host's `python3`, outside the engine's venv.
|
|
16
|
+
"""
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import hashlib
|
|
20
|
+
import re
|
|
21
|
+
import sys
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
#: Same number as `brain.notes.MAX_SLUG_BYTES` (a test holds them equal).
|
|
25
|
+
MAX_SLUG_BYTES = 180
|
|
26
|
+
#: Short enough to read, long enough to keep the subject.
|
|
27
|
+
KEEP_BYTES = 80
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def short_id(old: str) -> str:
|
|
31
|
+
head = old.encode("utf-8")[:KEEP_BYTES].decode("utf-8", "ignore")
|
|
32
|
+
if "-" in head:
|
|
33
|
+
head = head.rsplit("-", 1)[0]
|
|
34
|
+
return f"{head.rstrip('-.')}-{hashlib.sha256(old.encode('utf-8')).hexdigest()[:8]}"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def shorten(path: Path) -> Path:
|
|
38
|
+
old = path.stem
|
|
39
|
+
if len(old.encode("utf-8")) <= MAX_SLUG_BYTES:
|
|
40
|
+
return path
|
|
41
|
+
lines = path.read_text(encoding="utf-8").split("\n")
|
|
42
|
+
if not lines or lines[0].strip() != "---":
|
|
43
|
+
return path
|
|
44
|
+
for i, line in enumerate(lines[1:], start=1):
|
|
45
|
+
if line.strip() == "---":
|
|
46
|
+
return path # no `id:` line: leave it to the drain
|
|
47
|
+
if line.startswith("id:"):
|
|
48
|
+
if line[3:].strip().strip("\"'") != old:
|
|
49
|
+
return path # the id is not the file name: leave it
|
|
50
|
+
new = short_id(old)
|
|
51
|
+
target = path.with_name(f"{new}.md")
|
|
52
|
+
if target.exists():
|
|
53
|
+
return path
|
|
54
|
+
lines[i] = f"id: {new}"
|
|
55
|
+
target.write_text("\n".join(lines), encoding="utf-8")
|
|
56
|
+
path.unlink()
|
|
57
|
+
return target
|
|
58
|
+
return path
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def relink(paths: list[str], renames: dict[str, str]) -> None:
|
|
62
|
+
"""Point `[[old]]`, `[[old|..]]` and `[[old#..]]` at the new id."""
|
|
63
|
+
if not renames:
|
|
64
|
+
return
|
|
65
|
+
pattern = re.compile(r"\[\[(" + "|".join(map(re.escape, renames)) + r")(?=[\]|#])")
|
|
66
|
+
for raw in paths:
|
|
67
|
+
path = Path(raw)
|
|
68
|
+
try:
|
|
69
|
+
text = path.read_text(encoding="utf-8")
|
|
70
|
+
except (OSError, UnicodeDecodeError):
|
|
71
|
+
continue
|
|
72
|
+
new = pattern.sub(lambda m: "[[" + renames[m.group(1)], text)
|
|
73
|
+
if new != text:
|
|
74
|
+
path.write_text(new, encoding="utf-8")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def main(argv: list[str]) -> int:
|
|
78
|
+
# "\n" only, as the shell's `read` splits: splitlines() also breaks on
|
|
79
|
+
# \r, \x1c, \u2028 ..., so a crafted file name could add a list entry.
|
|
80
|
+
with open(argv[1], encoding="utf-8", newline="") as fh: # no \r translation
|
|
81
|
+
entries = fh.read().split("\n")
|
|
82
|
+
out: list[str] = []
|
|
83
|
+
renames: dict[str, str] = {}
|
|
84
|
+
for raw in entries:
|
|
85
|
+
if raw:
|
|
86
|
+
try:
|
|
87
|
+
new = shorten(Path(raw))
|
|
88
|
+
except (OSError, UnicodeDecodeError):
|
|
89
|
+
new = Path(raw)
|
|
90
|
+
if str(new) != raw:
|
|
91
|
+
renames[Path(raw).stem] = new.stem
|
|
92
|
+
out.append(str(new))
|
|
93
|
+
relink(out, renames)
|
|
94
|
+
for line in out:
|
|
95
|
+
print(line)
|
|
96
|
+
return 0
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
if __name__ == "__main__":
|
|
100
|
+
raise SystemExit(main(sys.argv))
|
|
@@ -12,7 +12,7 @@ identical list.
|
|
|
12
12
|
|
|
13
13
|
Two design rules, both learned from earlier failures in this repo:
|
|
14
14
|
|
|
15
|
-
* **Silence must never be able to mean "could not look."**
|
|
15
|
+
* **Silence must never be able to mean "could not look."** Some of the
|
|
16
16
|
inputs live in the HOST home (``~/.brainiac``, ``~/.brain``) and the Cowork
|
|
17
17
|
VM cannot reach them. On ``role=vm`` they are reported in ``unreachable``
|
|
18
18
|
rather than quietly contributing nothing — the same posture ``doctor`` takes
|
|
@@ -42,7 +42,7 @@ from .alerts_staging import ( # noqa: F401 — re-exported for callers/tests
|
|
|
42
42
|
_untriaged,
|
|
43
43
|
_untriaged_key,
|
|
44
44
|
_unwrap_untriaged,
|
|
45
|
-
host_vaults,
|
|
45
|
+
host_vaults, retrieval_watch_alerts, RETRIEVAL_WATCH_UNREACHABLE,
|
|
46
46
|
staging_alerts,
|
|
47
47
|
)
|
|
48
48
|
|
|
@@ -443,7 +443,7 @@ def collect(
|
|
|
443
443
|
"""Every alert this role can see, plus what it could not look at.
|
|
444
444
|
|
|
445
445
|
``host`` sweeps the workspace registry; ``vm`` reads only its own vault and
|
|
446
|
-
reports the
|
|
446
|
+
reports the host-home sources as unreachable."""
|
|
447
447
|
home = home or Path.home()
|
|
448
448
|
today = today or datetime.date.today()
|
|
449
449
|
alerts: list[dict[str, str]] = []
|
|
@@ -454,7 +454,7 @@ def collect(
|
|
|
454
454
|
"auto-update state (~/.brainiac/update-state.json) — host-only, "
|
|
455
455
|
"run `brain alerts` on the host Mac",
|
|
456
456
|
"weekly synthesis task health (~/.brain/synthesis-state.json) — "
|
|
457
|
-
"host-only, run `brain alerts` on the host Mac",
|
|
457
|
+
"host-only, run `brain alerts` on the host Mac", RETRIEVAL_WATCH_UNREACHABLE,
|
|
458
458
|
]
|
|
459
459
|
vaults = [Path(vault)] if vault else []
|
|
460
460
|
if not vaults:
|
|
@@ -467,7 +467,7 @@ def collect(
|
|
|
467
467
|
else:
|
|
468
468
|
alerts += update_alerts(home, today)
|
|
469
469
|
alerts += staging_alerts(home)
|
|
470
|
-
alerts += synthesis_alerts(home, today)
|
|
470
|
+
alerts += synthesis_alerts(home, today) + retrieval_watch_alerts(home, today)
|
|
471
471
|
alerts += wiring_alerts()
|
|
472
472
|
vaults = host_vaults(home)
|
|
473
473
|
if not vaults and vault:
|
|
@@ -186,3 +186,39 @@ def host_vaults(home: Path) -> list[Path]:
|
|
|
186
186
|
if path not in seen:
|
|
187
187
|
seen.append(path)
|
|
188
188
|
return seen
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
#: The monthly retrieval watch fires on day 1; past this its silence is the news.
|
|
192
|
+
RETRIEVAL_WATCH_STALE_DAYS = 35
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def retrieval_watch_alerts(home: Path, today: Any) -> list[dict[str, str]]:
|
|
196
|
+
"""The monthly retrieval-quality watch (``~/.brain/retrieval-watch-state.json``,
|
|
197
|
+
written by ``scripts/brain-retrieval-watch.sh``). Until 2026-10-27 a FAIL
|
|
198
|
+
went only to ``hot.md``, a log nobody has to read, and it failed unread on
|
|
199
|
+
three months running against a baseline from a retired embedder."""
|
|
200
|
+
from .alerts import _alert, _date, _read_json
|
|
201
|
+
|
|
202
|
+
state = _read_json(home / ".brain" / "retrieval-watch-state.json")
|
|
203
|
+
if not isinstance(state, dict):
|
|
204
|
+
return [] # never installed on this host — nothing to watch
|
|
205
|
+
day = _date(state.get("at"))
|
|
206
|
+
if state.get("verdict") == "FAIL":
|
|
207
|
+
return [_alert(
|
|
208
|
+
"retrieval-watch:fail",
|
|
209
|
+
f"monthly retrieval check FAILED on {state.get('at')} — run /autoresearch, "
|
|
210
|
+
"or re-baseline if the engine or embedder changed")]
|
|
211
|
+
if state.get("verdict") == "ERROR":
|
|
212
|
+
return [_alert(
|
|
213
|
+
"retrieval-watch:error",
|
|
214
|
+
f"monthly retrieval check could not run on {state.get('at')} — "
|
|
215
|
+
"read ~/.brain/logs/launchd-retrievalwatch-stderr.log")]
|
|
216
|
+
if day is None or (today - day).days > RETRIEVAL_WATCH_STALE_DAYS:
|
|
217
|
+
return [_alert(
|
|
218
|
+
"retrieval-watch:stale",
|
|
219
|
+
f"monthly retrieval check has not run since {state.get('at') or 'never'}")]
|
|
220
|
+
return []
|
|
221
|
+
|
|
222
|
+
RETRIEVAL_WATCH_UNREACHABLE = (
|
|
223
|
+
"monthly retrieval check (~/.brain/retrieval-watch-state.json) — host-only, "
|
|
224
|
+
"run `brain alerts` on the host Mac")
|
|
@@ -95,7 +95,7 @@ def _add_doctor(sub) -> None:
|
|
|
95
95
|
def _add_alerts(sub) -> None:
|
|
96
96
|
sp = sub.add_parser(
|
|
97
97
|
"alerts",
|
|
98
|
-
help="READ-ONLY degradation digest every harness can call: auto-update state, weekly-synthesis task health, engine-feedback backlog, the owner-decision queue, and whatever is degraded RIGHT NOW per the findings feed `brain maintain` rewrites each run. Pure file reads — no index, embedder, network or key. role=vm reads only its own vault and REPORTS the
|
|
98
|
+
help="READ-ONLY degradation digest every harness can call: auto-update state, weekly-synthesis task health, engine-feedback backlog, the owner-decision queue, and whatever is degraded RIGHT NOW per the findings feed `brain maintain` rewrites each run. Pure file reads — no index, embedder, network or key. role=vm reads only its own vault and REPORTS the host-home sources it cannot reach, so silence never means 'could not look'.",
|
|
99
99
|
)
|
|
100
100
|
sp.add_argument("--json", action="store_true")
|
|
101
101
|
sp.add_argument(
|
|
@@ -178,9 +178,9 @@ from .concealment_gate import (AUTHORED, CHROME, COVERED_SOURCES, # noqa: F401
|
|
|
178
178
|
|
|
179
179
|
#: Every marker this module can emit. A run record carries exactly one.
|
|
180
180
|
MARKERS = (
|
|
181
|
-
"hidden_white_text", # text
|
|
181
|
+
"hidden_white_text", # text too close to its ground to read
|
|
182
182
|
"hidden_transparent_text", # zero-alpha colour: invisible on any ground
|
|
183
|
-
"hidden_tiny_text", # resolved font size below
|
|
183
|
+
"hidden_tiny_text", # resolved font size below TINY_PT
|
|
184
184
|
"hidden_offscreen_text", # parked far off the page / outside the slide
|
|
185
185
|
"hidden_vanish_run", # Word w:vanish / w:webHidden
|
|
186
186
|
"hidden_render_mode", # PDF text render mode 3 or 7 (invisible)
|
|
@@ -195,8 +195,18 @@ MARKERS = (
|
|
|
195
195
|
NEAR = 0xF0
|
|
196
196
|
#: Per-channel distance at which text is "the same colour as its ground".
|
|
197
197
|
GROUND_DELTA = 0x10
|
|
198
|
-
#:
|
|
199
|
-
|
|
198
|
+
#: Below this WCAG contrast ratio against its ground, text is hidden too. A
|
|
199
|
+
#: coloured near-white walked past ``GROUND_DELTA`` alone: ``#ffffdd`` on
|
|
200
|
+
#: white is 1.02:1, fainter than the ``#f0f0f0`` (1.14:1) it caught (pentest
|
|
201
|
+
#: VULN-3418, 2026-10-28). Measured on 1,055 real originals that day: 0
|
|
202
|
+
#: convictions at 1.2, 1.3 and 1.5; new labels in 32 documents at 1.5 (mostly
|
|
203
|
+
#: headings and slide labels), 25 at 1.3, 10 at 1.2.
|
|
204
|
+
MIN_CONTRAST = 1.2
|
|
205
|
+
#: Text below this size is hidden. Was 2pt, and 2.7px (2.03pt) text passed it
|
|
206
|
+
#: (VULN-3418). 4pt added 1,805 runs of real 3-4pt slide labels; 3pt added 1.
|
|
207
|
+
#: One value for every lane: the Office walkers take it in points.
|
|
208
|
+
TINY_PT = 3
|
|
209
|
+
TINY_PX = TINY_PT * 4 / 3
|
|
200
210
|
#: An offset at least this far negative is off the page, not a layout nudge.
|
|
201
211
|
OFFSCREEN_PX = 1000.0
|
|
202
212
|
|
|
@@ -306,21 +316,34 @@ def colour_marker(colour: tuple[int, int, int, float] | None,
|
|
|
306
316
|
"""The marker a resolved text colour earns against its resolved ground.
|
|
307
317
|
|
|
308
318
|
``hidden_transparent_text`` for a zero alpha (invisible whatever is
|
|
309
|
-
behind it) and ``hidden_white_text`` when the text
|
|
310
|
-
``GROUND_DELTA`` of
|
|
311
|
-
|
|
312
|
-
practice; the test is "
|
|
319
|
+
behind it) and ``hidden_white_text`` when the text, blended over its
|
|
320
|
+
ground by its alpha, is within ``GROUND_DELTA`` of it on every channel or
|
|
321
|
+
below ``MIN_CONTRAST``. Named for white-on-white because that is what it
|
|
322
|
+
catches in practice; the test is "too close to what is behind it to read".
|
|
313
323
|
"""
|
|
314
324
|
if colour is None:
|
|
315
325
|
return None
|
|
316
326
|
if colour[3] <= 0.0:
|
|
317
327
|
return "hidden_transparent_text"
|
|
318
328
|
bg = ground if ground is not None and ground[3] > 0.0 else (*WHITE, 1.0)
|
|
319
|
-
|
|
329
|
+
a = min(colour[3], 1.0)
|
|
330
|
+
seen = tuple(a * colour[i] + (1 - a) * bg[i] for i in range(3))
|
|
331
|
+
if (all(abs(seen[i] - bg[i]) <= GROUND_DELTA for i in range(3))
|
|
332
|
+
or contrast_ratio(seen, bg) < MIN_CONTRAST):
|
|
320
333
|
return "hidden_white_text"
|
|
321
334
|
return None
|
|
322
335
|
|
|
323
336
|
|
|
337
|
+
def contrast_ratio(fg: tuple[float, ...], bg: tuple[float, ...]) -> float:
|
|
338
|
+
"""WCAG 2 contrast ratio of two sRGB colours, from 1.0 (same) to 21.0."""
|
|
339
|
+
def luminance(rgb: tuple[float, ...]) -> float:
|
|
340
|
+
lin = [c / 255 / 12.92 if c / 255 <= 0.04045
|
|
341
|
+
else ((c / 255 + 0.055) / 1.055) ** 2.4 for c in rgb[:3]]
|
|
342
|
+
return 0.2126 * lin[0] + 0.7152 * lin[1] + 0.0722 * lin[2]
|
|
343
|
+
hi, lo = sorted((luminance(fg), luminance(bg)), reverse=True)
|
|
344
|
+
return (hi + 0.05) / (lo + 0.05)
|
|
345
|
+
|
|
346
|
+
|
|
324
347
|
def to_px(value: str) -> float | None:
|
|
325
348
|
"""A CSS length in pixels, or ``None`` when the value carries no unit."""
|
|
326
349
|
m = _SIZE.search(value or "")
|
{brainiac_cli-0.20.46 → brainiac_cli-0.20.48}/src/brain/ingest/handlers/concealment_office.py
RENAMED
|
@@ -21,7 +21,7 @@ from __future__ import annotations
|
|
|
21
21
|
|
|
22
22
|
from typing import Any, Callable
|
|
23
23
|
|
|
24
|
-
from .concealment import colour_marker, normalise, note_degraded, run_record
|
|
24
|
+
from .concealment import TINY_PT, colour_marker, normalise, note_degraded, run_record
|
|
25
25
|
|
|
26
26
|
#: Theme slots that are white in every stock Office theme. ``pptx.py`` reads no
|
|
27
27
|
#: theme part, so this is a NAME test, not a resolved colour — see the gap list
|
|
@@ -170,7 +170,7 @@ def docx_runs(document: Any,
|
|
|
170
170
|
from docx.shared import Pt
|
|
171
171
|
from docx.text.run import Run
|
|
172
172
|
|
|
173
|
-
rgb_type, tiny, run_tag = MSO_COLOR_TYPE.RGB, Pt(
|
|
173
|
+
rgb_type, tiny, run_tag = MSO_COLOR_TYPE.RGB, Pt(TINY_PT), qn("w:r")
|
|
174
174
|
runs: list[dict[str, Any]] = []
|
|
175
175
|
for index, paragraph in enumerate(_docx_paragraphs(document), start=1):
|
|
176
176
|
container = f"docx:p{index}"
|
|
@@ -253,7 +253,7 @@ def pptx_runs(presentation: Any,
|
|
|
253
253
|
|
|
254
254
|
runs: list[dict[str, Any]] = []
|
|
255
255
|
width, height = presentation.slide_width, presentation.slide_height
|
|
256
|
-
style = (MSO_COLOR_TYPE.RGB, Pt(
|
|
256
|
+
style = (MSO_COLOR_TYPE.RGB, Pt(TINY_PT))
|
|
257
257
|
for si, slide in enumerate(presentation.slides, start=1):
|
|
258
258
|
for k, shape in enumerate(slide.shapes):
|
|
259
259
|
offscreen = _shape_offscreen(shape, width, height)
|
|
@@ -87,6 +87,35 @@ _HIDDEN_STYLE = re.compile(
|
|
|
87
87
|
r"""color\s*:\s*#?(fff(fff)?|white)\b)""",
|
|
88
88
|
re.I,
|
|
89
89
|
)
|
|
90
|
+
#: An inline ``style`` value: double-quoted, single-quoted or bare, one group
|
|
91
|
+
#: each, so a quote of the OTHER kind inside the value cannot end it early.
|
|
92
|
+
_STYLE_ATTR = re.compile(
|
|
93
|
+
r"""\bstyle\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>"']+))""", re.I)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _hidden_style_offsets(text: str) -> list[int]:
|
|
97
|
+
"""Where hiding styling starts: the literal patterns above, plus any inline
|
|
98
|
+
``style=""`` the handler walkers would call hidden — too little contrast on
|
|
99
|
+
a white page, or below the size floor. ``color:#ffffdd;font-size:2.7px``
|
|
100
|
+
matched no literal (pentest VULN-3418, 2026-10-28)."""
|
|
101
|
+
found = {m.start() for m in _HIDDEN_STYLE.finditer(text)}
|
|
102
|
+
if _STYLE_ATTR.search(text):
|
|
103
|
+
from .ingest.handlers import concealment as c
|
|
104
|
+
|
|
105
|
+
for m in _STYLE_ATTR.finditer(text):
|
|
106
|
+
if any(m.start() <= f < m.end() for f in found):
|
|
107
|
+
continue # a literal already marked this attribute
|
|
108
|
+
decl = c.declarations("".join(g or "" for g in m.groups()))
|
|
109
|
+
try:
|
|
110
|
+
hidden = bool(
|
|
111
|
+
c.colour_marker(c.parse_colour(decl.get("color", "")), None)
|
|
112
|
+
or c.is_tiny(c.to_px(decl.get("font-size", ""))))
|
|
113
|
+
except (ValueError, OverflowError):
|
|
114
|
+
hidden = True # unparseable: fail closed, the instruction check decides
|
|
115
|
+
if hidden:
|
|
116
|
+
found.add(m.start())
|
|
117
|
+
return sorted(found)
|
|
118
|
+
|
|
90
119
|
|
|
91
120
|
# --------------------------------------------------------------------------
|
|
92
121
|
# Instruction: imperative phrasing in the clear. Flagged, never quarantined.
|
|
@@ -243,14 +272,14 @@ def _concealment_hits(text: str) -> list[dict[str, Any]]:
|
|
|
243
272
|
"offset": m.start(),
|
|
244
273
|
"excerpt": " ".join(body.split())[:160],
|
|
245
274
|
})
|
|
246
|
-
for
|
|
247
|
-
window = text[
|
|
275
|
+
for start in _hidden_style_offsets(text):
|
|
276
|
+
window = text[start: start + 400]
|
|
248
277
|
found = _instruction_hits(window)
|
|
249
278
|
if found:
|
|
250
279
|
out.append({
|
|
251
280
|
"marker": "hidden_styled_instruction",
|
|
252
281
|
"revealed": found[0]["marker"],
|
|
253
|
-
"offset":
|
|
282
|
+
"offset": start,
|
|
254
283
|
"excerpt": " ".join(window.split())[:160],
|
|
255
284
|
})
|
|
256
285
|
return out
|
|
@@ -26,7 +26,7 @@ UNCHANGED and keep real paths. Both already branch correctly on
|
|
|
26
26
|
pinned workspace identity, schema, freshness — ``exceptions_verify.verify``,
|
|
27
27
|
called from inside
|
|
28
28
|
``alerts.exceptions_alerts``/``exceptions_cli.vault_row``) before anything in
|
|
29
|
-
it is trusted, and the
|
|
29
|
+
it is trusted, and the host-home sources
|
|
30
30
|
(``~/.brainiac/update-state.json``, ``~/.brain/synthesis-state.json``) come
|
|
31
31
|
back under ``unreachable`` rather than being silently skipped. That
|
|
32
32
|
verification is NOT reimplemented here — it is the same call the CLI makes,
|
|
@@ -72,6 +72,8 @@ from .remediation_schema import ( # noqa: F401 (facade re-export)
|
|
|
72
72
|
UNTRIAGED_PREFIX as UNTRIAGED_PREFIX,
|
|
73
73
|
)
|
|
74
74
|
from . import remediation_schema as _schema
|
|
75
|
+
from .remediation_invariants import ( # the invariant:* rows, split out by size
|
|
76
|
+
_AUTO_INVARIANTS, _OWNER_INVARIANTS, _SHELF_INVARIANTS)
|
|
75
77
|
|
|
76
78
|
# ---------------------------------------------------------------------------
|
|
77
79
|
# Owning branches and their cadences (in days). The scheduler reads THIS, so a
|
|
@@ -131,89 +133,6 @@ def validate_mechanical_allow_list(
|
|
|
131
133
|
validate_mechanical_allow_list()
|
|
132
134
|
MECHANICAL_QUARANTINE_REASONS = ALLOWED_MECHANICAL_QUARANTINE_REASONS
|
|
133
135
|
|
|
134
|
-
_AUTO_INVARIANTS = {
|
|
135
|
-
"invariant:unsigned_notes": Remedy(
|
|
136
|
-
AUTO, "sign_repair", 3, "additive", shadow_nights=3,
|
|
137
|
-
note="signs ONLY notes with host-side provenance of an interrupted "
|
|
138
|
-
"host write; an unsigned note without it is a TAMPER exception"),
|
|
139
|
-
"invariant:unguarded_ingests": Remedy(
|
|
140
|
-
AUTO, "reguard", 3, "raising", shadow_nights=3,
|
|
141
|
-
note="ENF-04 re-run; only ever raises, undecided fails closed high"),
|
|
142
|
-
"invariant:subfloor_families": Remedy(
|
|
143
|
-
AUTO, "extract_retry", 3, "additive",
|
|
144
|
-
note="heals only when re-extraction clears the ENF-01 floor, never by "
|
|
145
|
-
"merging stubs"),
|
|
146
|
-
"invariant:unlinked_sources": Remedy(
|
|
147
|
-
AUTO, "link_lane", 2, "additive",
|
|
148
|
-
note="fix-05: keyed to BAK-04 lane health, not the raw count; "
|
|
149
|
-
"escalate_after is 2 MISSED WEEKS at the link_lane cadence"),
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
_OWNER_INVARIANTS = {
|
|
153
|
-
"invariant:cross_tier_duplicates": Remedy(
|
|
154
|
-
OWNER, note="CUR-01-style proposal batch; re-tiering cascades"),
|
|
155
|
-
"invariant:cross_tier_candidates": Remedy(
|
|
156
|
-
OWNER, note="the UNDECIDED band — a guess here is the failure mode"),
|
|
157
|
-
"invariant:cross_tier_twins": Remedy(
|
|
158
|
-
OWNER, note="proposal batch, same store as the other two"),
|
|
159
|
-
"invariant:unreachable_gold": Remedy(
|
|
160
|
-
OWNER, note="usually a retirement mistake; reinstating is an owner act"),
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
#: DLV-05's three shelf counters. BANNER, and deliberately neither of the two
|
|
164
|
-
#: dispositions its neighbours in this block carry.
|
|
165
|
-
#:
|
|
166
|
-
#: NOT ``auto``: an auto row is a promise about the REMEDIATION apparatus — an
|
|
167
|
-
#: owning branch that writes a `_remediation.branches.<name>` row every night,
|
|
168
|
-
#: which `branch_liveness`, `coverage` and `thrashing_targets` then read to
|
|
169
|
-
#: decide whether to suppress the finding. The nightly fold that actually
|
|
170
|
-
#: converges these three (`deliverables_shelf_fold`, and the drop lane's
|
|
171
|
-
#: recovery pass at the top of every drain) is an ordinary maintain fold, not a
|
|
172
|
-
#: remediation branch, and writes no such row — so declaring `auto` here would
|
|
173
|
-
#: claim an escalation apparatus that does not exist and park a permanently
|
|
174
|
-
#: empty branch on the exceptions page.
|
|
175
|
-
#:
|
|
176
|
-
#: NOT ``owner``: an owner row must be ONE decidable question with options and
|
|
177
|
-
#: a stated default. A count that stayed above its floor is not that — and each
|
|
178
|
-
#: CAUSE already has its own named, answerable surface (`shelf:move-cap`,
|
|
179
|
-
#: `shelf:refused`, `shelf:diverged`, `shelf:sole-copy`, the five
|
|
180
|
-
#: `deliverables:*` resolver refusals). Asking a second time here would be the
|
|
181
|
-
#: same finding twice.
|
|
182
|
-
#:
|
|
183
|
-
#: So: the same "something stopped" class as those nine keys, and structurally
|
|
184
|
-
#: visible for as long as it is true.
|
|
185
|
-
_SHELF_INVARIANTS = {
|
|
186
|
-
"invariant:unshelved_deliverables": Remedy(
|
|
187
|
-
BANNER,
|
|
188
|
-
note="marked deliverables the shelf ledger claims no copy of. The "
|
|
189
|
-
"nightly shelf fold copies every censused deliverable, so a count "
|
|
190
|
-
"above the floor means that fold declined or could not finish — "
|
|
191
|
-
"and the reason it declined carries its own key"),
|
|
192
|
-
"invariant:stale_shelf_entries": Remedy(
|
|
193
|
-
BANNER,
|
|
194
|
-
note="shelf ledger rows whose note has left the census. Nothing is "
|
|
195
|
-
"lost (the shelf never deletes; displaced copies go to "
|
|
196
|
-
"_previous/), but the shelf has stopped telling the truth about "
|
|
197
|
-
"what the vault holds"),
|
|
198
|
-
"invariant:unanchored_deliverable_payloads": Remedy(
|
|
199
|
-
BANNER,
|
|
200
|
-
note="drop-lane payloads with no anchor note. The lane's own recovery "
|
|
201
|
-
"pass retries the anchor at the top of every drain, so a count "
|
|
202
|
-
"that stays up means that write keeps failing — the one metric "
|
|
203
|
-
"that can see the shelf mechanism having stopped entirely, since "
|
|
204
|
-
"the other two read 0 of 0 when nothing is being marked at all"),
|
|
205
|
-
"invariant:supersede_declared_failed": Remedy(
|
|
206
|
-
BANNER,
|
|
207
|
-
note="live notes whose `replaces:` names a note that is NOT retired "
|
|
208
|
-
"under them (SUP-01) — the old version is still live in search. "
|
|
209
|
-
"`brain health-report` samples the pairs; `brain supersede <old> "
|
|
210
|
-
"<new>` on the host clears each"),
|
|
211
|
-
"supersede_declared_failed": Remedy(
|
|
212
|
-
BANNER,
|
|
213
|
-
note="this run's owner-declared supersession(s) that could not be "
|
|
214
|
-
"applied; the corpus count above keeps reporting them"),
|
|
215
|
-
}
|
|
216
|
-
|
|
217
136
|
_EXACT: dict[str, Remedy] = {
|
|
218
137
|
**_AUTO_INVARIANTS,
|
|
219
138
|
**_OWNER_INVARIANTS,
|
|
@@ -253,6 +172,16 @@ _EXACT: dict[str, Remedy] = {
|
|
|
253
172
|
AUTO, "synthesis_retry", 1, "process",
|
|
254
173
|
note="retried at most once per night — it costs real money"),
|
|
255
174
|
"synthesis:stale": Remedy(AUTO, "synthesis_retry", 1, "process"),
|
|
175
|
+
# Monthly retrieval watch (alerts_staging.retrieval_watch_alerts). BANNER,
|
|
176
|
+
# all three: no branch can rerun or re-baseline it, and LOG would put it
|
|
177
|
+
# back in hot.md alone, where three false FAILs went unread (974cf75b).
|
|
178
|
+
"retrieval-watch:fail": Remedy(
|
|
179
|
+
BANNER, note="quality dropped below the baseline; the owner runs "
|
|
180
|
+
"/autoresearch, or re-baselines after an engine change"),
|
|
181
|
+
"retrieval-watch:error": Remedy(
|
|
182
|
+
BANNER, note="the watch could not run; its launchd stderr log says why"),
|
|
183
|
+
"retrieval-watch:stale": Remedy(
|
|
184
|
+
BANNER, note="no monthly run in 35 days: the job itself stopped"),
|
|
256
185
|
# Mechanical quarantine: an operator action on THIS host, retryable.
|
|
257
186
|
**{f"quarantine:{r}": Remedy(AUTO, "extract_retry", 3, "process")
|
|
258
187
|
for r in MECHANICAL_QUARANTINE_REASONS},
|