Add outcome-aware preflight contracts for v0.9 (#6)

Add VF010-VF013, structural outcome contracts, adversarial fixtures, documentation, and Friday release materials.
This commit is contained in:
Felipe Domingues 2026-07-22 13:58:46 -03:00 committed by GitHub
parent b312f18251
commit f62a78faaf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 1107 additions and 95 deletions

View file

@ -0,0 +1,14 @@
{
"$schema": "../schemas/vibeflow-config.schema.json",
"outcomeContracts": {
"Issue refund": {
"impact": "money",
"approvalNode": "Approve refund",
"auditNode": "Record refund audit",
"failureNotificationNode": "Notify refund failure",
"amountGuard": { "node": "Limit refund amount", "maximum": 500, "currency": "USD" },
"counterpartyGuard": { "node": "Allow refund account", "allowed": ["merchant-primary"] },
"recovery": { "strategy": "compensate", "node": "Compensate transaction" }
}
}
}

View file

@ -0,0 +1,85 @@
{
"name": "Safe refund with outcome contract",
"nodes": [
{
"id": "webhook",
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"parameters": { "authentication": "headerAuth", "path": "refund" },
"credentials": { "httpHeaderAuth": { "id": "credential-reference", "name": "Webhook Header Auth" } }
},
{
"id": "idempotency",
"name": "Claim idempotency event",
"type": "n8n-nodes-base.postgres",
"parameters": {
"operation": "executeQuery",
"query": "INSERT INTO event_ledger (event_id) VALUES ($1) ON CONFLICT (event_id) DO NOTHING RETURNING event_id"
}
},
{
"id": "audit",
"name": "Record refund audit",
"type": "n8n-nodes-base.postgres",
"parameters": { "operation": "insert", "table": "refund_audit" }
},
{
"id": "approval",
"name": "Approve refund",
"type": "n8n-nodes-base.if",
"parameters": { "conditions": { "boolean": [{ "value1": "={{ $json.approved }}", "value2": true }] } }
},
{
"id": "amount",
"name": "Limit refund amount",
"type": "n8n-nodes-base.if",
"parameters": {
"conditions": { "number": [{ "value1": "={{ $json.amount }}", "operation": "smallerEqual", "value2": 500 }] },
"currency": "USD"
}
},
{
"id": "counterparty",
"name": "Allow refund account",
"type": "n8n-nodes-base.switch",
"parameters": { "value": "={{ $json.counterparty }}", "rules": [{ "value": "merchant-primary" }] }
},
{
"id": "refund",
"name": "Issue refund",
"type": "n8n-nodes-base.httpRequest",
"onError": "continueErrorOutput",
"parameters": { "method": "POST", "url": "https://payments.invalid/refund" }
},
{
"id": "failure-alert",
"name": "Notify refund failure",
"type": "n8n-nodes-base.slack",
"parameters": { "channel": "operations", "text": "Notify failure for refund {{ $json.event_id }}" }
},
{
"id": "compensation",
"name": "Compensate transaction",
"type": "n8n-nodes-base.httpRequest",
"parameters": { "method": "POST", "url": "https://payments.invalid/reverse", "operation": "compensate" }
}
],
"connections": {
"Webhook": { "main": [[{ "node": "Claim idempotency event", "type": "main", "index": 0 }]] },
"Claim idempotency event": { "main": [[{ "node": "Record refund audit", "type": "main", "index": 0 }]] },
"Record refund audit": { "main": [[{ "node": "Approve refund", "type": "main", "index": 0 }]] },
"Approve refund": { "main": [[{ "node": "Limit refund amount", "type": "main", "index": 0 }], []] },
"Limit refund amount": { "main": [[{ "node": "Allow refund account", "type": "main", "index": 0 }], []] },
"Allow refund account": { "main": [[{ "node": "Issue refund", "type": "main", "index": 0 }], []] },
"Issue refund": {
"main": [
[],
[
{ "node": "Notify refund failure", "type": "main", "index": 0 },
{ "node": "Compensate transaction", "type": "main", "index": 0 }
]
]
}
},
"settings": { "executionTimeout": 120, "errorWorkflow": "global-error-handler" }
}

View file

@ -64,6 +64,13 @@
"type": "n8n-nodes-base.noOp",
"parameters": {}
},
{
"id": "failure-alert",
"name": "Operator error alert",
"type": "n8n-nodes-base.slack",
"onError": "continueErrorOutput",
"parameters": { "channel": "operations", "text": "Failure alert: {{ $json.error.message }}" }
},
{
"id": "response",
"name": "Respond to Webhook",
@ -76,7 +83,7 @@
"Claim idempotency event": {
"main": [
[{ "node": "Agent enabled kill switch", "type": "main", "index": 0 }],
[{ "node": "Ledger failure stop", "type": "main", "index": 0 }]
[{ "node": "Operator error alert", "type": "main", "index": 0 }]
]
},
"Agent enabled kill switch": { "main": [[{ "node": "AI Agent", "type": "main", "index": 0 }], []] },
@ -89,6 +96,12 @@
]
},
"Human handoff ticket": {
"main": [
[],
[{ "node": "Operator error alert", "type": "main", "index": 0 }]
]
},
"Operator error alert": {
"main": [
[],
[{ "node": "Handoff failure stop", "type": "main", "index": 0 }]

View file

@ -0,0 +1,29 @@
{
"name": "Unsafe refund without outcome contract",
"nodes": [
{
"id": "webhook",
"name": "Public refund webhook",
"type": "n8n-nodes-base.webhook",
"parameters": { "authentication": "none", "path": "refund" }
},
{
"id": "refund",
"name": "Issue refund",
"type": "n8n-nodes-base.httpRequest",
"onError": "continueErrorOutput",
"parameters": { "method": "POST", "url": "https://payments.invalid/refund" }
},
{
"id": "swallow",
"name": "Swallow refund failure",
"type": "n8n-nodes-base.noOp",
"parameters": {}
}
],
"connections": {
"Public refund webhook": { "main": [[{ "node": "Issue refund", "type": "main", "index": 0 }]] },
"Issue refund": { "main": [[], [{ "node": "Swallow refund failure", "type": "main", "index": 0 }]] }
},
"settings": {}
}