Skip to content

Commit 958ca96

Browse files
authored
feat: financial_governance spec block — Phase 1 (schema + example) (open-gitagent#79)
* Refine descriptions in agent-yaml.schema.json schema: add financial_governance block to compliance_config * examples: add financial-agent with financial_governance block Addresses RFC open-gitagent#38 feedback: - financial_governance nested under compliance (not top-level) - firewall field is a named identifier, not an endpoint - blocked_categories evaluated before allowed_categories - auto_deny_on_timeout defaults to true (timeout = DENIED) - audit handled via compliance.recordkeeping (no duplicate fields) * spec: add financial_governance to compliance section and validation rules Addresses RFC open-gitagent#38 feedback — adds financial_governance to compliance section and validation rules in SPECIFICATION.md * validate: add financial_governance checks to compliance validation Addresses RFC open-gitagent#38 feedback — warns when high/critical risk agents have financial tools but no financial_governance block, and validates firewall field is a named identifier not an endpoint URL * adapters: surface financial_governance caps in buildComplianceSection Addresses RFC open-gitagent#38 feedback — exposes spending caps, category controls, and approval threshold in the shared compliance section surfaced by all adapters
1 parent 1762a72 commit 958ca96

5 files changed

Lines changed: 223 additions & 28 deletions

File tree

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# financial-agent — example agent.yaml
2+
#
3+
# Demonstrates the financial_governance block for a payment-capable agent.
4+
# This agent is authorised to purchase software, compute, and API services
5+
# up to $50 per transaction, with human approval required above $20.
6+
7+
spec_version: "0.1.0"
8+
9+
name: financial-agent
10+
version: "1.0.0"
11+
description: >
12+
Autonomous purchasing agent for software, compute, and API service procurement.
13+
Operates within defined spending limits with human approval for higher-value transactions.
14+
15+
compliance:
16+
risk_tier: high
17+
supervision:
18+
human_in_the_loop: conditional
19+
recordkeeping:
20+
audit_logging: true
21+
retention_period: 7y
22+
immutable: true
23+
financial_governance:
24+
enabled: true
25+
firewall: valkurai # named identifier — valkurai, stripe-radar, or local-script
26+
spending:
27+
max_per_transaction_cents: 5000 # $50.00 hard cap per transaction
28+
max_monthly_cents: 100000 # $1,000.00 monthly cumulative cap
29+
currency: AUD
30+
allowed_categories:
31+
- software
32+
- compute
33+
- api_services
34+
blocked_categories:
35+
- gambling
36+
- crypto
37+
- unknown
38+
approval:
39+
require_above_cents: 2000 # human approval required above $20.00
40+
timeout_minutes: 60
41+
auto_deny_on_timeout: true

spec/SPECIFICATION.md

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,27 @@ compliance:
237237
approval_required: true
238238

239239
enforcement: strict # strict | advisory
240+
241+
# Financial governance (runtime spending controls for payment-capable agents)
242+
financial_governance:
243+
enabled: true # false = declared but not enforced
244+
firewall: valkurai # named identifier: valkurai, stripe-radar, local-script
245+
spending:
246+
max_per_transaction_cents: 5000 # $50.00 hard cap per transaction
247+
max_monthly_cents: 100000 # $1,000.00 monthly cumulative cap
248+
currency: AUD # ISO 4217 default currency
249+
allowed_categories:
250+
- software
251+
- compute
252+
- api_services
253+
blocked_categories:
254+
- gambling
255+
- crypto
256+
- unknown
257+
approval:
258+
require_above_cents: 2000 # human approval required above $20.00
259+
timeout_minutes: 60
260+
auto_deny_on_timeout: true # timeout = DENIED
240261
```
241262
242263
### Example Minimal agent.yaml
@@ -938,7 +959,12 @@ A valid gitagent repository must:
938959
- No agent in `assignments` may hold roles that appear together in `conflicts`
939960
- `handoffs.required_roles` must reference defined role IDs and include at least 2
940961
- Assigned agents should exist in the `agents` section
941-
962+
9. If `compliance.financial_governance` is present:
963+
- `enabled` must be specified
964+
- If `enabled` is `true` and `spending` is present, `max_per_transaction_cents` must be a positive integer
965+
- `approval.auto_deny_on_timeout` should default to `true` if not specified
966+
- `firewall` references a named integration identifier, not an endpoint URL
967+
942968
## 19. CLI Commands
943969

944970
### Implemented (v0.1.0)

spec/schemas/agent-yaml.schema.json

Lines changed: 93 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@
308308
"triggers": {
309309
"type": "array",
310310
"items": { "type": "string" },
311-
"description": "Conditions that activate this sub-agent (e.g., factual_claim_detected, confidence_low, domain_mismatch)"
311+
"description": "Conditions that activate this sub-agent"
312312
}
313313
},
314314
"additionalProperties": false
@@ -340,7 +340,7 @@
340340
"properties": {
341341
"action_type": {
342342
"type": "string",
343-
"description": "Escalate for specific action types (e.g., customer_communication, trade_execution, credit_decision, regulatory_filing)"
343+
"description": "Escalate for specific action types"
344344
}
345345
},
346346
"additionalProperties": false
@@ -432,6 +432,9 @@
432432
},
433433
"segregation_of_duties": {
434434
"$ref": "#/$defs/segregation_of_duties_config"
435+
},
436+
"financial_governance": {
437+
"$ref": "#/$defs/financial_governance_config"
435438
}
436439
},
437440
"additionalProperties": false,
@@ -478,7 +481,7 @@
478481
"human_in_the_loop": {
479482
"type": "string",
480483
"enum": ["always", "conditional", "advisory", "none"],
481-
"description": "Level of human involvement. 'always': every decision. 'conditional': per escalation_triggers. 'advisory': human notified but not blocking. 'none': fully autonomous."
484+
"description": "Level of human involvement."
482485
},
483486
"escalation_triggers": {
484487
"type": "array",
@@ -513,7 +516,7 @@
513516
"retention_period": {
514517
"type": "string",
515518
"pattern": "^\\d+[ymd]$",
516-
"description": "Minimum retention period (e.g., 6y = 6 years, 90d = 90 days, 18m = 18 months)"
519+
"description": "Minimum retention period (e.g., 6y = 6 years, 90d = 90 days)"
517520
},
518521
"log_contents": {
519522
"type": "array",
@@ -528,7 +531,7 @@
528531
]
529532
},
530533
"uniqueItems": true,
531-
"description": "Categories of data to log. Any subset is valid."
534+
"description": "Categories of data to log."
532535
},
533536
"immutable": {
534537
"type": "boolean",
@@ -554,7 +557,7 @@
554557
"validation_type": {
555558
"type": "string",
556559
"enum": ["full", "targeted", "change_based"],
557-
"description": "Type of validation. 'full': three-pillar SR 11-7. 'targeted': specific area. 'change_based': triggered by changes."
560+
"description": "Type of validation."
558561
},
559562
"conceptual_soundness": {
560563
"type": ["string", "null"],
@@ -566,7 +569,7 @@
566569
},
567570
"outcomes_analysis": {
568571
"type": "boolean",
569-
"description": "Whether model outputs are compared to actual results (back-testing)"
572+
"description": "Whether model outputs are compared to actual results"
570573
},
571574
"drift_detection": {
572575
"type": "boolean",
@@ -587,7 +590,7 @@
587590
"pii_handling": {
588591
"type": "string",
589592
"enum": ["redact", "encrypt", "prohibit", "allow"],
590-
"description": "'redact': strip PII from outputs. 'encrypt': encrypt at rest. 'prohibit': reject PII input. 'allow': no restrictions."
593+
"description": "How PII is handled"
591594
},
592595
"data_classification": {
593596
"type": "string",
@@ -608,7 +611,7 @@
608611
},
609612
"lda_search": {
610613
"type": "boolean",
611-
"description": "Whether Less Discriminatory Alternative search is required (CFPB Circular 2022-03)"
614+
"description": "Whether Less Discriminatory Alternative search is required"
612615
}
613616
},
614617
"additionalProperties": false
@@ -621,7 +624,7 @@
621624
"type": {
622625
"type": "string",
623626
"enum": ["correspondence", "retail", "institutional"],
624-
"description": "'correspondence': <=25 retail investors/30 days. 'retail': >25 retail investors/30 days. 'institutional': institutional investors only."
627+
"description": "Communication type classification"
625628
},
626629
"pre_review_required": {
627630
"type": "boolean",
@@ -633,7 +636,7 @@
633636
},
634637
"no_misleading": {
635638
"type": "boolean",
636-
"description": "Whether misleading, exaggerated, or promissory statements are prohibited"
639+
"description": "Whether misleading statements are prohibited"
637640
},
638641
"disclosures_required": {
639642
"type": "boolean",
@@ -661,15 +664,15 @@
661664
},
662665
"subcontractor_assessment": {
663666
"type": "boolean",
664-
"description": "Whether fourth-party (subcontractor) risk has been assessed"
667+
"description": "Whether fourth-party risk has been assessed"
665668
}
666669
},
667670
"additionalProperties": false
668671
},
669672

670673
"segregation_of_duties_config": {
671674
"type": "object",
672-
"description": "Segregation of duties configuration for multi-agent systems. Ensures no single agent has complete control over critical processes.",
675+
"description": "Segregation of duties configuration for multi-agent systems.",
673676
"properties": {
674677
"roles": {
675678
"type": "array",
@@ -703,12 +706,10 @@
703706
},
704707
"conflicts": {
705708
"type": "array",
706-
"description": "Pairs of role IDs that cannot be held by the same agent (SOD matrix)",
709+
"description": "Pairs of role IDs that cannot be held by the same agent",
707710
"items": {
708711
"type": "array",
709-
"items": {
710-
"type": "string"
711-
},
712+
"items": { "type": "string" },
712713
"minItems": 2,
713714
"maxItems": 2
714715
}
@@ -718,9 +719,7 @@
718719
"description": "Maps agent names to their assigned roles",
719720
"additionalProperties": {
720721
"type": "array",
721-
"items": {
722-
"type": "string"
723-
},
722+
"items": { "type": "string" },
724723
"minItems": 1
725724
}
726725
},
@@ -730,13 +729,11 @@
730729
"properties": {
731730
"state": {
732731
"type": "string",
733-
"enum": ["full", "shared", "none"],
734-
"description": "'full': agents cannot access each other's state. 'shared': read-only cross-access. 'none': no isolation."
732+
"enum": ["full", "shared", "none"]
735733
},
736734
"credentials": {
737735
"type": "string",
738-
"enum": ["separate", "shared"],
739-
"description": "'separate': each role has its own credential scope. 'shared': agents share credentials."
736+
"enum": ["separate", "shared"]
740737
}
741738
},
742739
"additionalProperties": false
@@ -750,7 +747,7 @@
750747
"properties": {
751748
"action": {
752749
"type": "string",
753-
"description": "Action type requiring handoff (e.g., credit_decision, loan_disbursement)"
750+
"description": "Action type requiring handoff"
754751
},
755752
"required_roles": {
756753
"type": "array",
@@ -775,6 +772,77 @@
775772
}
776773
},
777774
"additionalProperties": false
775+
},
776+
777+
"financial_governance_config": {
778+
"type": "object",
779+
"description": "Runtime financial controls for payment-capable agents. Declarative — enforcement is handled by a pre_tool_use hook calling a compliant financial firewall. A block with no compliant enforcement layer is advisory only.",
780+
"properties": {
781+
"enabled": {
782+
"type": "boolean",
783+
"description": "If false, block is declared but not enforced. Default: false."
784+
},
785+
"firewall": {
786+
"type": "string",
787+
"description": "Named identifier of the financial firewall implementation (e.g. valkurai, stripe-radar, local-script). Not an endpoint — endpoint config belongs in runtime environment config."
788+
},
789+
"spending": {
790+
"type": "object",
791+
"description": "Spending cap and category controls",
792+
"properties": {
793+
"max_per_transaction_cents": {
794+
"type": "integer",
795+
"minimum": 1,
796+
"description": "Hard cap per transaction in smallest currency unit (cents for AUD/USD, pence for GBP). No floats."
797+
},
798+
"max_monthly_cents": {
799+
"type": "integer",
800+
"minimum": 1,
801+
"description": "Cumulative monthly cap in smallest currency unit. Omit to disable."
802+
},
803+
"currency": {
804+
"type": "string",
805+
"description": "Default ISO 4217 currency code (e.g. AUD, USD, GBP)."
806+
},
807+
"allowed_categories": {
808+
"type": "array",
809+
"items": { "type": "string" },
810+
"description": "Permitted spending categories (e.g. software, compute, api_services). Empty array = all categories permitted."
811+
},
812+
"blocked_categories": {
813+
"type": "array",
814+
"items": { "type": "string" },
815+
"description": "Explicitly blocked spending categories. Evaluated before allowed_categories."
816+
}
817+
},
818+
"required": ["max_per_transaction_cents"],
819+
"additionalProperties": false
820+
},
821+
"approval": {
822+
"type": "object",
823+
"description": "Human approval threshold and timeout controls",
824+
"properties": {
825+
"require_above_cents": {
826+
"type": "integer",
827+
"minimum": 0,
828+
"description": "Transactions above this amount require human approval before execution. Set to 0 to require approval on all transactions."
829+
},
830+
"timeout_minutes": {
831+
"type": "integer",
832+
"minimum": 1,
833+
"description": "Minutes before an unanswered approval request times out."
834+
},
835+
"auto_deny_on_timeout": {
836+
"type": "boolean",
837+
"description": "If true, timeout = DENIED. If false, timeout = APPROVED. Recommended default: true."
838+
}
839+
},
840+
"required": ["require_above_cents", "timeout_minutes", "auto_deny_on_timeout"],
841+
"additionalProperties": false
842+
}
843+
},
844+
"required": ["enabled"],
845+
"additionalProperties": false
778846
}
779847
}
780848
}

src/adapters/shared.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,38 @@ export function buildComplianceSection(compliance: NonNullable<ReturnType<typeof
5858
if (sod.isolation?.credentials === 'separate') {
5959
constraints.push('- Credentials are segregated per role');
6060
}
61-
if (sod.enforcement === 'strict') {
61+
if (sod.enforcement === 'strict') {
6262
constraints.push('- SOD enforcement is STRICT — violations will block execution');
6363
}
6464
}
6565

66+
// Financial governance constraints
67+
if (c.financial_governance?.enabled) {
68+
const fg = c.financial_governance;
69+
constraints.push('- Financial governance is enforced:');
70+
if (fg.spending?.max_per_transaction_cents) {
71+
constraints.push(` - Maximum per transaction: ${fg.spending.max_per_transaction_cents} cents`);
72+
}
73+
if (fg.spending?.max_monthly_cents) {
74+
constraints.push(` - Maximum monthly spend: ${fg.spending.max_monthly_cents} cents`);
75+
}
76+
if (fg.spending?.allowed_categories && fg.spending.allowed_categories.length > 0) {
77+
constraints.push(` - Allowed categories: ${fg.spending.allowed_categories.join(', ')}`);
78+
}
79+
if (fg.spending?.blocked_categories && fg.spending.blocked_categories.length > 0) {
80+
constraints.push(` - Blocked categories: ${fg.spending.blocked_categories.join(', ')}`);
81+
}
82+
if (fg.approval?.require_above_cents !== undefined) {
83+
constraints.push(` - Human approval required above: ${fg.approval.require_above_cents} cents`);
84+
}
85+
if (fg.approval?.auto_deny_on_timeout) {
86+
constraints.push(' - Unanswered approval requests are automatically DENIED');
87+
}
88+
if (fg.firewall) {
89+
constraints.push(` - Financial firewall: ${fg.firewall}`);
90+
}
91+
}
92+
6693
if (constraints.length === 0) return '';
6794
return `## Compliance Constraints\n\n${constraints.join('\n')}`;
6895
}

0 commit comments

Comments
 (0)