BAT Protocol: deterministic governance layer and security hardening#3
BAT Protocol: deterministic governance layer and security hardening#3Knapp-Kevin wants to merge 12 commits intossdavidai:masterfrom
Conversation
…bution, Ledger Sync, Compliance
There was a problem hiding this comment.
Pull request overview
This PR introduces a full deterministic governance layer (“BAT Protocol”) for agent operations, including proposal normalization, deterministic risk classification, policy enforcement, integrity-protected audit logging, and operator tooling.
Changes:
- Adds core BAT runtime modules: proposal schema, risk engine + default rules, enforcement engine, interceptor, and HMAC-linked governance ledger.
- Adds security hardening modules: path validation, policy integrity guard + startup gate, resource governor, delegation, break-glass, and optional ledger encryption.
- Adds DSL tooling (YAML rule parser/loader) plus CLI commands and documentation updates.
Reviewed changes
Copilot reviewed 43 out of 66 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| src/alfred/bat/rules/init.py | Adds baseline deterministic risk rules and helper matchers. |
| src/alfred/bat/risk.py | Implements deterministic risk engine, rules, and explain output. |
| src/alfred/bat/resource_governor.py | Adds rate limits, metadata caps, queue depth checks, rotation, and safe deserialization helpers. |
| src/alfred/bat/proposal.py | Adds canonical OperationProposal schema and validation/hash utilities. |
| src/alfred/bat/profiles.py | Adds personal/secure/enterprise profiles and profile utilities. |
| src/alfred/bat/policy_integrity.py | Adds signed manifest verification and immutable-root protections with a startup gate. |
| src/alfred/bat/path_security.py | Adds path traversal/UNC/ADS/null-byte blocking and optional symlink restrictions. |
| src/alfred/bat/ledger.py | Adds append-only hash-chained HMAC ledger with verification and export/stats. |
| src/alfred/bat/interceptor.py | Adds main governance interception + execution scaffold with invariant re-check hook. |
| src/alfred/bat/enforcement.py | Adds policy-driven enforcement + ledger recording of decisions. |
| src/alfred/bat/encryption.py | Adds optional at-rest ledger encryption support. |
| src/alfred/bat/dsl/parser.py | Adds YAML DSL parser converting rules into executable RiskRules. |
| src/alfred/bat/dsl/loader.py | Adds ruleset loader with checksum-based hot reload and default rules file generator. |
| src/alfred/bat/dsl/init.py | Exposes DSL public API. |
| src/alfred/bat/delegation.py | Adds capability-set delegation with chain provenance and intersection enforcement. |
| src/alfred/bat/cli.py | Adds operator CLI commands (status/audit/test-policy/verify-ledger/explain). |
| src/alfred/bat/break_glass.py | Adds emergency override + break-glass session support with separate logging. |
| src/alfred/bat/init.py | Exposes BAT Protocol public surface and updates version to 1.5.0. |
| src/alfred/_bundled/config.yaml.example | Documents semantic drift feature flags. |
| pyproject.toml | Adds optional dependencies for bat and dev; updates “all” extras. |
| docs/bat-protocol/_Sidebar.md | Adds BAT documentation sidebar and quick start. |
| docs/bat-protocol/PROJECT_PLAN.md | Adds project plan and phase roadmap. |
| docs/bat-protocol/CANONICAL_DESIGN.md | Adds canonical BAT architecture specification. |
| docs/Surveyor.md | Documents semantic drift configuration + artifacts. |
| docs/CLI-Commands.md | Documents new drift CLI commands. |
| config.yaml.example | Documents semantic drift feature flags. |
| SECURITY_REVIEW.md | Adds an automated security review document. |
| SECURITY_ELEVATION.md | Adds BAT security elevation plan and acceptance gates. |
| README-BATPROTOCOL.md | Adds BAT implementation-focused README for contributors. |
| INITIAL_SECURITY_REVIEW.md | Adds drift monitor security review and remediation log. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def is_within_vault(path: str, vault_root: str = None) -> bool: | ||
| """Check if path is within the vault directory. | ||
|
|
||
| Args: | ||
| path: File path to check | ||
| vault_root: Root directory of the vault (default: ~/vault) | ||
|
|
||
| Returns: | ||
| True if path is within vault, False otherwise | ||
| """ | ||
| if not path: | ||
| return False | ||
|
|
||
| if vault_root is None: | ||
| vault_root = os.path.expanduser("~/vault") | ||
| else: | ||
| vault_root = os.path.expanduser(vault_root) | ||
|
|
||
| expanded_path = os.path.expanduser(path) | ||
| normalized_path = os.path.normpath(expanded_path) | ||
| normalized_vault = os.path.normpath(vault_root) | ||
|
|
||
| return normalized_path.startswith(normalized_vault) |
There was a problem hiding this comment.
startswith() is not a safe containment check (e.g., /home/user/vault2/... will be treated as within /home/user/vault). Use Path(...).expanduser().resolve() and a relative_to() containment check (or equivalent) to ensure the target is truly inside the vault root.
Security fixes:
- Fix path traversal in is_within_vault using Path.resolve()/relative_to()
- Fail-closed when PyNaCl missing instead of bypassing signature verification
- Remove insecure XOR fallback cipher when cryptography library unavailable
- Exclude manifest.json from self-referential hashing
Correctness fixes:
- Fix f-string bug: "JSON parsing failed: e" -> "JSON parsing failed: {e}"
- Fix rate limit windows to match documented limits (1s/60s/3600s)
- Fix get_usage to aggregate per-second count across agent_id:* keys
- Fix ProfileName enum handling for invalid/missing values
- Fix create_custom_profile to handle non-standard profile names
- Record BLOCK entry to ledger when invariant check fails (audit consistency)
- Fix lambda closure bug in DSL parser (pats vs patterns)
Performance fix:
- Stream ledger with bounded deque in bat audit for constant memory usage
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Code fixes: - Move ClusterState and datetime imports to module scope (daemon.py) - Use .get() with defaults for drift report fields to prevent KeyError (drift_cli.py) - Add _setup_logging_from_config() call to cmd_drift (cli.py) Documentation fixes: - Clarify that features/semantic_drift are top-level config sections (Surveyor.md) - Fix inaccurate statement about drift commands availability (CLI-Commands.md) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
All concerns fully addressed, ready for merge. |
* feat: close Curator ↔ alfred-learn filing loop Wire the routing brain (alfred-learn) to the processing muscles (Curator): - New endpoint: POST /api/v1/curator/route-and-process - Accepts routing context (destination, instinct, confidence) - Injects routing metadata as frontmatter into inbox file - Triggers Curator for structured processing (notes, entities, wikilinks) - Returns enriched result (note_path, entities_created, entities_linked) - Falls back to raw fs.rename if Curator unavailable - Updated POST /api/v1/learning/route (human routing) - Now also triggers Curator for structured processing - Observations enriched with Curator output - Updated alfred-learn judge.py - execute_route calls new curator endpoint (120s timeout) - attempt_judgment (inline) uses Curator via _execute_route_inline - Both fall back gracefully if Curator is down - Updated alfred-learn judgment.py - Passes full routing context (instinct name, confidence, assignee) - Observation records include Curator results - Avoids duplicate observations when endpoint writes its own The loop: inputs → classify → score → route (Curator processes with routing context) → structured records + enriched observation → reflection learns from richer data → instincts improve. Brain and muscles connected. * fix: address Copilot review — security, robustness, YAML safety Fixes all 11 Copilot review comments: 1. YAML escaping: Added yamlEscape() helper for frontmatter values containing colons/quotes/newlines. All observation and routing frontmatter now properly escaped. 2. Path traversal: Added assertSafePath() — validates vault-relative paths have no '..' or absolute components. Called in both endpoints. 3. Inbox filename collision: Unique suffix (timestamp + random) on inbox filenames prevents concurrent request overwrites. 4. Curator file verification: After Curator runs, checks if inbox file was consumed. If not (processed different file), returns error instead of silently deleting original. 5. Fallback error handling: Raw move failures now captured and reported. Returns 500 with error details when both Curator AND fallback fail (instead of silent 200). 6. observation_path normalization: Legacy /learning/route returns both 'observation' and 'observation_path' keys. Python fallbacks normalize the key. judgment.py checks both keys to prevent duplicate writes. 7. Route failure escalation: judgment.py now escalates to user when routing completely fails (both Curator and fallback) instead of silently counting as routed. 8. attempt_judgment truthfulness: Only reports routed=True when routing actually succeeded (checks processed/fallback/observation). --------- Co-authored-by: ssd <administrator@administrators-Mac-mini.local>
…ray mutation log) Fifth and final commit for Voice Stage 2a-wk2. Team-lead decision to bundle the three small polish fixes together (plan open question ssdavidai#4). (a) Transcript timestamps - session.append_turn stamps _ts on every turn and _kind on user turns. New kind="text" kwarg. Wk1 transcripts collapsed every turn to the session start time because _ts was never stamped. (b) Voice counter cleanup - conversation.run_turn accepts user_kind="text" and threads it into append_turn as kind=. - bot._open_session_with_stash no longer initialises state-dict voice_messages / text_messages counters. handle_message no longer increments them. _count_message_kinds (already shipped) is the one source of truth, deriving from per-turn _kind. (c) Stray mutation log (plan open question ssdavidai#3) - conversation._execute_tool drops both mutation_log.log_mutation( session.session_id, ...) calls. That API expects a JSONL FILE PATH, not a UUID — wk1 passing session.session_id caused open(<uuid>, "a") to create a file literally named after the UUID at the repo root (286921d8-* artifact cleaned manually). Session vault_ops are already tracked via append_vault_op → session-record frontmatter. data/vault_audit.log wiring will pick up the same events later. mutation_log import dropped from conversation.py. Tests: 7 new tests across three files, one per bug class: - test_session_body.py (2 tests — _ts stamped, body renders distinct HH:MM) - test_voice_counter.py (3 tests — count function, run_turn threads kind, stash doesn't seed redundant counters) - test_no_orphan_mutation_log.py (2 tests — call-signature guard, end-to-end _execute_tool leaves no stray UUID file at CWD) pytest: 38/38 passing (31 prior + 7 new). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When KAL-LE hits 404 record_not_found on GET /canonical/person/{name},
it now POSTs a proposal to Salem's new /canonical/person/propose
endpoint instead of silent fallback. Handles 409 race (retry GET),
logs structured events for audit. Proposer correlation ID shape:
kal-le-propose-person-<hex6>.
This closes the ssdavidai#3 task design from project_kalle_propose_person.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…, worked-example redirect, near-miss bad example, scholar-tone, mode-name consistency Targeted fixes from vault-reviewer pass on commit 39a852a: P0-1: Phase 2 leak removed from vault_edit core instructions. The published_url clause was sitting in the main use-it list; moved to a clearly-marked "Phase 2 forward-compat note" block at the bottom of the section, with explicit "do not act on this in Phase 1" gating and "draft/essay/ is out of scope — don't touch it." P0-2: propose-person flow now has the explicit transport CLI form (mirrors KAL-LE's pattern): alfred transport propose-person salem "<Full Name>" [--alias ...] [--note ...] The earlier in-flow reference (Mode 1 step 3) cross-references the expanded peer-protocol section instead of saying "escalate via propose-person" with no command shape. P1-1: Conversation-mode "good question" ssdavidai#3 — the soft-redirect "if you wrote that as a one-pager..." example pulled Andrew from "is this the marketing angle?" into "let's operationalize the one-pager." Replaced with a question that stays inside the marketing-angle thesis: "What separates the stories that would land for a stranger from the ones that only land for the driver who saw them?" P1-2: Added one near-miss bad example to the bad-questions list: "Is the story really the marketing, or is the reliability what people are buying?" — frame-replacement dressed as a clarifying question. Annotated as the hardest failure mode to catch in oneself, with the heuristic shape: "is it really X, or is it Y?" where Y wasn't on the table = reframing, not deepening. P1-3: Hard guardrails preamble rewritten in Hypatia's target voice as self-demonstration (scholar-first per feedback_practitioner_scholar_calibration.md). The preamble and the first guardrail's lead now read in a careful, evidence- respecting register; the procedural detail of the four guardrails themselves stays imperative. P1-4: Mode-name consistency — the Tone section bullets used "Business drafting / Conversation / Capture extraction" while the rest of the SKILL uses "business mode / conversation mode / capture mode" with numbered headers ("Mode 1 — ..."). Swept the Tone section to match. Same pattern as KAL-LE's SKILL. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
BAT Protocol: Deterministic Governance Layer (End-to-End)
Summary
This PR delivers the full BAT Protocol governance surface in
alfred.bat, including deterministic risk classification, policy enforcement, integrity-protected audit logging, security elevation tracks, enterprise governance modules, and operator tooling.Core invariant: no agent decides its own risk.
Why
Alfred agents can execute commands, mutate files, and access sensitive runtime capabilities. BAT introduces a proposal-first governance path so every operation is classified, enforced, and auditable under deterministic policy.
What�s Included
Foundation and Hardening
bat statusbat auditbat test-policybat verify-ledgerbat explainSecurity Elevation Tracks
personal,secure, andenterprisemodes.Enterprise Governance
Security Fixes Included in This PR
entry_idvalidation.status; deprecate/revoke paths re-sign updated records.Documentation and Hygiene
README-BATPROTOCOL.md1.5.0..hypothesis/to.gitignore.Testing
pytest tests/bat -qBackward Compatibility and Risk
Source of Truth
src/alfred/bat/__init__.py(__version__ = "1.5.0")README-BATPROTOCOL.mddocs/bat-protocol/PROJECT_PLAN.mdStatus Snapshot
PROJECT_PLAN.md.Commit
3e0d676-Harden BAT verification paths and close signature coverage gaps