You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When operating in a multi-agent or team environment, a single approver_role: human flag is far too coarse.
Today every proposal — whether it is a trivial fact Claim filed by a well-known trusted agent or a new Entity proposed by an unknown actor — goes into the same pending queue and waits for a human to act on it.
In practice, three patterns come up constantly:
Trusted-agent fast-path. A well-configured agent (e.g. claude-code) files a fact Claim that cites an existing, verified Source. There is no reason to block it; requiring a human round-trip actively slows the agent down.
Escalation by kind or confidence. A decision Claim with confidence < 0.7, or any Claim with supersedes set, is high-stakes and should always require explicit human sign-off regardless of who filed it.
Redaction / quarantine. An anonymous or untrusted actor MUST NOT be able to land any durable artifact without explicit approval.
Right now there is no way to express these rules; every deployment uses the same binary human | trusted-agent knob.
What you've tried
review.approver_role: trusted-agent in config.yaml — this auto-approves everything for the configured agent, with no conditions on claim type, confidence, or evidence quality.
PolicyDecision carries action, matched_rule_name | None, and reason.
3. Model changes — src/vouch/models.py
Config gains a policy: ReviewPolicy | None field (optional; absence keeps current behaviour).
AuditEvent gains policy_rule: str | None — the name of the rule that fired, or None for manual decisions.
Proposal gains auto_approved: bool and policy_rule: str | None on the decided record so the review-gate decision is traceable.
4. src/vouch/proposals.py — integrate policy at write time
file_proposal() calls policy.evaluate() immediately after validation:
block → raise PolicyBlockedError (new domain error), the proposal is never written to proposed/.
auto_approve → call approve() inline with actor="policy-engine", write directly to durable storage and decided/.
require_human → current behaviour (write to proposed/, surface in vouch pending).
5. src/vouch/audit.py — new event types
proposal.auto_approved — fired when policy engine auto-approves
proposal.policy_blocked — fired when policy engine blocks at file time
Both events carry policy_rule in their data dict.
6. CLI changes — src/vouch/cli.py
vouch policy check <proposal-yaml>
Dry-run evaluation: given a YAML file in proposal format (or a proposal id), prints which rule would fire and what action would result. Exit code 0 = would pass, 2 = would block.
vouch policy lint
Validates the review.policy stanza in config.yaml — detects unreachable rules, conflicting conditions, missing reason on block actions, etc. Integrates into vouch lint.
vouch pending --show-rule
Adds a policy_rule column to vouch pending output (and --json equivalent) so humans can see why each proposal ended up in the queue.
vouch status
Adds auto_approved_today, blocked_today counts alongside existing pending count.
7. MCP / JSONL server — src/vouch/server.py, src/vouch/jsonl_server.py
kb.capabilities response gains a policy capability descriptor:
kb.propose_* tools surface PolicyBlockedError as a structured error response (code: "policy_blocked", rule, reason) so agents can handle it programmatically rather than receiving an unstructured exception.
New read-only tool kb.policy_check — evaluates a prospective proposal payload against the current policy without filing it. Returns {action, rule, reason}. Useful for agents to pre-flight before committing a proposal.
Show policy config for a multi-agent deployment with mixed trust levels
docs/object-model.md
Document auto_approved and policy_rule fields on Proposal and AuditEvent
SPEC.md
Extend §1.1 (config.yaml), §3 (review-gate state machine), §4 (audit log)
A new VEP-0006-review-gate-policy-engine.md is needed in proposals/ before this lands (surface change: new config stanza, new method kb.policy_check, new fields on Proposal and AuditEvent).
Compatibility considerations
This is additive at the config level: if no review.policy stanza is present, behaviour is identical to today. Existing KBs need no migration.
It is a surface change in two ways:
New kb.policy_check method on the kb.* surface → VEP required before implementation.
New fields on Proposal (auto_approved, policy_rule) and AuditEvent (policy_rule) → schema version bump; old readers ignore unknown fields (pydantic extra = "ignore"), so backwards-compatible.
No on-disk layout changes — decided/ YAML files gain optional fields; proposed/ files are unaffected.
The vouch migrate infrastructure (PR #108) would need to carry a migration step that backfills auto_approved: false on existing decided records (cosmetic; not required for correctness).
Alternatives
Shell-script wrapper around vouch approve — logic lives outside the KB, not audited, breaks on concurrent agents.
Extend approver_role to a list of trusted agents — covers the fast-path case only; cannot express condition-based escalation or blocking.
Per-proposal annotations in rationale — agents can self-declare trustworthiness in the proposal text, but there is no enforcement.
What you're trying to do
When operating in a multi-agent or team environment, a single
approver_role: humanflag is far too coarse.Today every proposal — whether it is a trivial
factClaim filed by a well-known trusted agent or a newEntityproposed by an unknown actor — goes into the same pending queue and waits for a human to act on it.In practice, three patterns come up constantly:
claude-code) files afactClaim that cites an existing, verified Source. There is no reason to block it; requiring a human round-trip actively slows the agent down.decisionClaim withconfidence < 0.7, or any Claim withsupersedesset, is high-stakes and should always require explicit human sign-off regardless of who filed it.Right now there is no way to express these rules; every deployment uses the same binary
human | trusted-agentknob.What you've tried
review.approver_role: trusted-agentinconfig.yaml— this auto-approves everything for the configured agent, with no conditions on claim type, confidence, or evidence quality.vouch approve --batch(PR Feat/batch approve proposals #111) — speeds up human review but does not eliminate the queue.vouch pending --json+ shell scripting — possible but fragile; the logic lives outside the KB and does not appear in the audit log.Suggested shape
1.
config.yaml— newreview.policystanzaRules are evaluated top-to-bottom; first match wins.
defaultfires when no rule matches.2.
src/vouch/policy.py— new modulePolicyDecisioncarriesaction,matched_rule_name | None, andreason.3. Model changes —
src/vouch/models.pyConfiggains apolicy: ReviewPolicy | Nonefield (optional; absence keeps current behaviour).AuditEventgainspolicy_rule: str | None— the name of the rule that fired, orNonefor manual decisions.Proposalgainsauto_approved: boolandpolicy_rule: str | Noneon the decided record so the review-gate decision is traceable.4.
src/vouch/proposals.py— integrate policy at write timefile_proposal()callspolicy.evaluate()immediately after validation:block→ raisePolicyBlockedError(new domain error), the proposal is never written toproposed/.auto_approve→ callapprove()inline withactor="policy-engine", write directly to durable storage anddecided/.require_human→ current behaviour (write toproposed/, surface invouch pending).5.
src/vouch/audit.py— new event typesBoth events carry
policy_rulein theirdatadict.6. CLI changes —
src/vouch/cli.pyvouch policy check <proposal-yaml>Dry-run evaluation: given a YAML file in proposal format (or a proposal id), prints which rule would fire and what action would result. Exit code 0 = would pass, 2 = would block.
vouch policy lintValidates the
review.policystanza inconfig.yaml— detects unreachable rules, conflicting conditions, missingreasononblockactions, etc. Integrates intovouch lint.vouch pending --show-ruleAdds a
policy_rulecolumn tovouch pendingoutput (and--jsonequivalent) so humans can see why each proposal ended up in the queue.vouch statusAdds
auto_approved_today,blocked_todaycounts alongside existingpendingcount.7. MCP / JSONL server —
src/vouch/server.py,src/vouch/jsonl_server.pykb.capabilitiesresponse gains apolicycapability descriptor:kb.propose_*tools surfacePolicyBlockedErroras a structured error response (code: "policy_blocked",rule,reason) so agents can handle it programmatically rather than receiving an unstructured exception.kb.policy_check— evaluates a prospective proposal payload against the current policy without filing it. Returns{action, rule, reason}. Useful for agents to pre-flight before committing a proposal.8. Schemas —
schemas/Four schema files need updating or creation:
schemas/config.schema.jsonreview.policywithPolicyRule[]shapeschemas/proposal.schema.jsonauto_approved: bool,policy_rule: string|nullschemas/audit-event.schema.jsonpolicy_rule: string|nulltodataschemas/capabilities.schema.jsonpolicycapability descriptorschemas/policy-rule.schema.jsonPolicyRuleschemas/policy-decision.schema.jsonPolicyDecision9. Tests —
tests/tests/test_policy.pyPolicyRule.evaluate(), condition matching, rule ordering, default fallback,blockraisestests/test_proposals.py(extend)auto_approvepath lands in durable storage;blocknever writesproposed/; audit events firetests/test_cli.py(extend)vouch policy check,vouch policy lint,--show-ruleflagstests/test_server.py(extend)kb.policy_checktool;kb.propose_claimreturns structuredpolicy_blockederrortests/test_jsonl_server.py(extend)policy_blockederror shape,kb.policy_checkround-triptests/test_audit.py(extend)proposal.auto_approvedandproposal.policy_blockedevents written correctly10. Documentation
docs/review-gate.mdauto_approve/block/require_humansemanticsdocs/multi-agent.mddocs/object-model.mdauto_approvedandpolicy_rulefields onProposalandAuditEventSPEC.mdA new
VEP-0006-review-gate-policy-engine.mdis needed inproposals/before this lands (surface change: new config stanza, new methodkb.policy_check, new fields onProposalandAuditEvent).Compatibility considerations
This is additive at the config level: if no
review.policystanza is present, behaviour is identical to today. Existing KBs need no migration.It is a surface change in two ways:
kb.policy_checkmethod on thekb.*surface → VEP required before implementation.Proposal(auto_approved,policy_rule) andAuditEvent(policy_rule) → schema version bump; old readers ignore unknown fields (pydanticextra = "ignore"), so backwards-compatible.No on-disk layout changes —
decided/YAML files gain optional fields;proposed/files are unaffected.The
vouch migrateinfrastructure (PR #108) would need to carry a migration step that backfillsauto_approved: falseon existing decided records (cosmetic; not required for correctness).Alternatives
vouch approve— logic lives outside the KB, not audited, breaks on concurrent agents.approver_roleto a list of trusted agents — covers the fast-path case only; cannot express condition-based escalation or blocking.rationale— agents can self-declare trustworthiness in the proposal text, but there is no enforcement.kb.propose_*but not what kinds of proposals auto-land vs. require review.