mandateclaw 0.0.1
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +19 -0
- data/LICENSE +21 -0
- data/README.md +262 -0
- data/lib/mandate_claw/dsl/agent_bounds.rb +38 -0
- data/lib/mandate_claw/dsl/attestation.rb +36 -0
- data/lib/mandate_claw/dsl/contract.rb +109 -0
- data/lib/mandate_claw/dsl/decision_rule.rb +39 -0
- data/lib/mandate_claw/dsl/obligation.rb +12 -0
- data/lib/mandate_claw/dsl/party.rb +17 -0
- data/lib/mandate_claw/dsl/permission.rb +12 -0
- data/lib/mandate_claw/dsl/prohibition.rb +12 -0
- data/lib/mandate_claw/dsl/temporal.rb +21 -0
- data/lib/mandate_claw/renderers/markdown_renderer.rb +147 -0
- data/lib/mandate_claw/validators/contract_validator.rb +83 -0
- data/lib/mandate_claw/version.rb +5 -0
- data/lib/mandate_claw.rb +36 -0
- metadata +120 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 8cfb305a7b88ab0dfda330d81c15d38efaa5c85a86f98aef71b45b894a268edb
|
|
4
|
+
data.tar.gz: 798ea557d19afbb122a3b32b57a0e9aa99481f486a84f08c5e7adbb3433c1566
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 0aa4cf6c878a5d379cbb07da2464939b69261978275d75b7f093be6cb1593a447e7a75ca1e76830029561e42abdde70dbe4dc654c4bf62ca7379687d6b59b079
|
|
7
|
+
data.tar.gz: 5622779131e0c3372d7e8332cbddeca676e8977f6a956cc480259d66c89dce578a37d7e80db707cb4903a0f5a0417da127be5b84a8afdf800ab9318dcba89f6e
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to MandateClaw-DSL are documented here.
|
|
4
|
+
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
5
|
+
|
|
6
|
+
## [0.0.1] - 2026-05-21
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
- `MandateClaw::DSL::Contract` — base class for contract definitions
|
|
10
|
+
- `party` DSL: declare human, organisation, and autonomous agent parties
|
|
11
|
+
- `obligation` DSL: positive duties with temporal constraints and breach consequences
|
|
12
|
+
- `permission` DSL: rights a party may exercise
|
|
13
|
+
- `prohibition` DSL: explicit denials; attempts are breach events
|
|
14
|
+
- `agent_bounds` DSL: capability envelope for autonomous agent parties (`may`, `must_not`, `must_log_to`)
|
|
15
|
+
- `attestation` DSL: signing requirements (Ed25519, HMAC-SHA256)
|
|
16
|
+
- `rule` DSL: named decision rules with ternary truth (true / false / `:unknown`)
|
|
17
|
+
- `MandateClaw::Validators::ContractValidator` — validates contract structure at definition time
|
|
18
|
+
- `MandateClaw::Renderers::MarkdownRenderer` — renders contract as human-readable Markdown
|
|
19
|
+
- `MandateClaw.define` convenience method for inline contract definition
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Abhishek Parolkar
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# MandateClaw-DSL
|
|
2
|
+
|
|
3
|
+
> **The Employment Contract Between You and Your Agent.**
|
|
4
|
+
|
|
5
|
+
MandateClaw-DSL is a Ruby gem for defining programmatic, human-readable contracts that govern what an AI agent is — and is not — allowed to do on behalf of a human or organisation.
|
|
6
|
+
|
|
7
|
+
It gives your agent a signed scope. Not a system prompt. A contract.
|
|
8
|
+
|
|
9
|
+
[](https://rubygems.org/gems/mandateclaw-dsl)
|
|
10
|
+
[](LICENSE)
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## The Problem
|
|
15
|
+
|
|
16
|
+
Every serious deployment of an AI agent in a consequential workflow — financial, legal, medical, regulatory — hits the same wall:
|
|
17
|
+
|
|
18
|
+
- **System prompts are unenforceable.** The model may ignore them.
|
|
19
|
+
- **Tool allowlists are opaque.** They are deployer-side, invisible to counterparties and regulators.
|
|
20
|
+
- **Audit logs are held by the deployer.** Exactly the wrong party.
|
|
21
|
+
- **Smart contracts are unreadable** to the lawyers and compliance officers who need to sign off.
|
|
22
|
+
|
|
23
|
+
What is missing is a layer that is:
|
|
24
|
+
- Signed **before** work begins
|
|
25
|
+
- Deterministic in what it permits
|
|
26
|
+
- Readable by humans
|
|
27
|
+
- Enforceable at runtime
|
|
28
|
+
- Auditable by regulators
|
|
29
|
+
|
|
30
|
+
That is MandateClaw.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
```ruby
|
|
37
|
+
# Gemfile
|
|
38
|
+
gem "mandateclaw-dsl"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
bundle install
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Or install directly:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
gem install mandateclaw-dsl
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Quick Start
|
|
54
|
+
|
|
55
|
+
```ruby
|
|
56
|
+
require "mandate_claw"
|
|
57
|
+
|
|
58
|
+
class InvoiceContract < MandateClaw::DSL::Contract
|
|
59
|
+
contract :invoice do
|
|
60
|
+
# ── Parties ──────────────────────────────────────────────────────────
|
|
61
|
+
party :buyer, identifies_by: :customer_id
|
|
62
|
+
party :seller, identifies_by: :merchant_id
|
|
63
|
+
party :ai_agent, identifies_by: :agent_did, kind: :autonomous
|
|
64
|
+
|
|
65
|
+
# ── Obligations: what a party MUST do ────────────────────────────────
|
|
66
|
+
obligation :pay_invoice,
|
|
67
|
+
on: :buyer,
|
|
68
|
+
within: 30.days,
|
|
69
|
+
breach: :late_payment_penalty
|
|
70
|
+
|
|
71
|
+
# ── Permissions: what a party MAY do ─────────────────────────────────
|
|
72
|
+
permission :dispute,
|
|
73
|
+
on: :buyer,
|
|
74
|
+
within: 7.days,
|
|
75
|
+
event: :raise_dispute
|
|
76
|
+
|
|
77
|
+
# ── Prohibitions: what a party MUST NOT do ───────────────────────────
|
|
78
|
+
prohibition :unilateral_amend,
|
|
79
|
+
on: :ai_agent,
|
|
80
|
+
breach: :void_transition
|
|
81
|
+
|
|
82
|
+
# ── Agent capability envelope ─────────────────────────────────────────
|
|
83
|
+
agent_bounds :ai_agent do
|
|
84
|
+
may :issue_invoice, :send_reminder
|
|
85
|
+
must_not :modify_amount, :waive_penalty
|
|
86
|
+
must_log_to :contract_registry
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# ── Decision rules (ternary: true / false / :unknown) ─────────────────
|
|
90
|
+
rule :payment_overdue do
|
|
91
|
+
decide :overdue,
|
|
92
|
+
when: ->(ctx) { ctx.days_since_issued > 30 },
|
|
93
|
+
unknown_when: ->(ctx) { ctx.issued_at.nil? }
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# ── Attestation: who must sign before the contract is in force ────────
|
|
97
|
+
attestation do
|
|
98
|
+
require_signature_from :buyer, :seller, :ai_agent
|
|
99
|
+
sign_with :ed25519
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Validate the contract definition
|
|
106
|
+
|
|
107
|
+
```ruby
|
|
108
|
+
InvoiceContract.validate!
|
|
109
|
+
# => true (raises MandateClaw::ValidationError if malformed)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Render as human-readable Markdown
|
|
113
|
+
|
|
114
|
+
```ruby
|
|
115
|
+
puts InvoiceContract.to_markdown
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Output:
|
|
119
|
+
|
|
120
|
+
```markdown
|
|
121
|
+
# Contract: Invoice
|
|
122
|
+
|
|
123
|
+
> Generated by MandateClaw-DSL v0.0.1
|
|
124
|
+
|
|
125
|
+
## Parties
|
|
126
|
+
|
|
127
|
+
| Name | Kind | Identified By |
|
|
128
|
+
|----------|--------------|---------------|
|
|
129
|
+
| buyer | Human | customer_id |
|
|
130
|
+
| seller | Human | merchant_id |
|
|
131
|
+
| ai_agent | Autonomous | agent_did |
|
|
132
|
+
|
|
133
|
+
## Obligations
|
|
134
|
+
|
|
135
|
+
- **Buyer** MUST `pay_invoice` within **30 days**. Breach: `late_payment_penalty`.
|
|
136
|
+
|
|
137
|
+
## Permissions
|
|
138
|
+
|
|
139
|
+
- **Buyer** MAY `dispute` within **7 days**.
|
|
140
|
+
|
|
141
|
+
## Prohibitions
|
|
142
|
+
|
|
143
|
+
- **Ai_agent** MUST NOT `unilateral_amend`. Breach: `void_transition`.
|
|
144
|
+
|
|
145
|
+
## Agent Capability Bounds
|
|
146
|
+
|
|
147
|
+
### Agent: `ai_agent`
|
|
148
|
+
|
|
149
|
+
- **May do:** `issue_invoice`, `send_reminder`
|
|
150
|
+
- **Must not do:** `modify_amount`, `waive_penalty`
|
|
151
|
+
- **Must log to:** `contract_registry`
|
|
152
|
+
|
|
153
|
+
## Attestation
|
|
154
|
+
|
|
155
|
+
This contract comes into force only when all required parties have signed.
|
|
156
|
+
|
|
157
|
+
**Required signatories:**
|
|
158
|
+
- `buyer`
|
|
159
|
+
- `seller`
|
|
160
|
+
- `ai_agent`
|
|
161
|
+
|
|
162
|
+
**Signing algorithm:** `ed25519`
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Core Concepts
|
|
168
|
+
|
|
169
|
+
### Parties
|
|
170
|
+
|
|
171
|
+
```ruby
|
|
172
|
+
party :buyer, identifies_by: :customer_id # human (default)
|
|
173
|
+
party :seller, identifies_by: :merchant_id, kind: :organisation
|
|
174
|
+
party :ai_agent, identifies_by: :agent_did, kind: :autonomous
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Party kinds: `:human`, `:organisation`, `:autonomous`.
|
|
178
|
+
|
|
179
|
+
### Obligations, Permissions, Prohibitions
|
|
180
|
+
|
|
181
|
+
The three deontic modalities of contract law:
|
|
182
|
+
|
|
183
|
+
| DSL method | Meaning | Breach on failure? |
|
|
184
|
+
|---------------|----------------------|--------------------|
|
|
185
|
+
| `obligation` | Party MUST do this | Yes |
|
|
186
|
+
| `permission` | Party MAY do this | No |
|
|
187
|
+
| `prohibition` | Party MUST NOT do this | Yes (on attempt) |
|
|
188
|
+
|
|
189
|
+
### Agent Bounds
|
|
190
|
+
|
|
191
|
+
The `agent_bounds` block is the contract clause that directly constrains an autonomous agent. The agent is given a capability envelope it cannot exceed:
|
|
192
|
+
|
|
193
|
+
```ruby
|
|
194
|
+
agent_bounds :ai_agent do
|
|
195
|
+
may :read_invoice, :send_reminder # explicit allowlist
|
|
196
|
+
must_not :delete_invoice, :modify_amount # explicit denylist
|
|
197
|
+
must_log_to :contract_registry # mandatory audit targets
|
|
198
|
+
end
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Decision Rules (Ternary Truth)
|
|
202
|
+
|
|
203
|
+
Borrowed from [SMU's L4 language](https://l4-documentation.readthedocs.io/): a rule evaluates to `true`, `false`, or `:unknown` — never silently collapses an unknown value to false:
|
|
204
|
+
|
|
205
|
+
```ruby
|
|
206
|
+
rule :payment_overdue do
|
|
207
|
+
decide :overdue,
|
|
208
|
+
when: ->(ctx) { ctx.days_since_issued > 30 },
|
|
209
|
+
unknown_when: ->(ctx) { ctx.issued_at.nil? }
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
result = InvoiceContract._rules.first.evaluate(my_context)
|
|
213
|
+
# => true | false | :unknown
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Inline Definition
|
|
217
|
+
|
|
218
|
+
```ruby
|
|
219
|
+
PaymentContract = MandateClaw.define(:payment) do
|
|
220
|
+
party :payer, identifies_by: :user_id
|
|
221
|
+
party :payee, identifies_by: :merchant_id
|
|
222
|
+
|
|
223
|
+
obligation :transfer_funds, on: :payer, within: 1.day, breach: :payment_failed
|
|
224
|
+
end
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## Architecture
|
|
230
|
+
|
|
231
|
+
MandateClaw is intentionally split into focused components:
|
|
232
|
+
|
|
233
|
+
| Gem | Role | License |
|
|
234
|
+
|-----|------|---------|
|
|
235
|
+
| **mandateclaw-dsl** (this gem) | Contract definition DSL + Markdown renderer | MIT |
|
|
236
|
+
| [mandateclaw-registry](https://github.com/parolkar/MandateClaw-Registry) | Signing, storage, audit ledger | FSL-1.1-Apache-2.0 |
|
|
237
|
+
| [MandateClaw Cloud](https://mandateclaw.com) | Hosted registry, regulator webhooks, compliance dashboards | Commercial |
|
|
238
|
+
|
|
239
|
+
The DSL has no runtime enforcement — that is the registry's job. The DSL's output is a validated, human-readable contract definition that the registry stores, signs, and monitors.
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## Roadmap
|
|
244
|
+
|
|
245
|
+
- [x] `0.0.1` — Core DSL: parties, obligations, permissions, prohibitions, agent bounds, attestation, decision rules, Markdown renderer
|
|
246
|
+
- [ ] `0.1.0` — `fosm-rails` integration: lifecycle events validated against contract definitions
|
|
247
|
+
- [ ] `0.2.0` — JSON Schema renderer for machine-readable contract exchange
|
|
248
|
+
- [ ] `0.3.0` — Contract diffing: show what changed between two versions of a contract
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## Contributing
|
|
253
|
+
|
|
254
|
+
Bug reports and pull requests are welcome at [github.com/parolkar/MandateClaw-DSL](https://github.com/parolkar/MandateClaw-DSL).
|
|
255
|
+
|
|
256
|
+
Please read [CONTRIBUTING.md](CONTRIBUTING.md) before opening a PR. All contributions are expected to have specs.
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## License
|
|
261
|
+
|
|
262
|
+
[MIT](LICENSE) — Copyright (c) 2026 Abhishek Parolkar
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MandateClaw
|
|
4
|
+
module DSL
|
|
5
|
+
# Defines the capability envelope for an autonomous agent party.
|
|
6
|
+
# The agent is constrained to only the actions listed under +may+
|
|
7
|
+
# and is explicitly barred from those under +must_not+.
|
|
8
|
+
#
|
|
9
|
+
# agent_bounds :ai_agent do
|
|
10
|
+
# may :issue_invoice, :send_reminder
|
|
11
|
+
# must_not :modify_amount, :waive_penalty
|
|
12
|
+
# must_log_to :contract_registry
|
|
13
|
+
# end
|
|
14
|
+
#
|
|
15
|
+
class AgentBounds
|
|
16
|
+
attr_reader :party, :permitted_actions, :prohibited_actions, :log_targets
|
|
17
|
+
|
|
18
|
+
def initialize(party)
|
|
19
|
+
@party = party
|
|
20
|
+
@permitted_actions = []
|
|
21
|
+
@prohibited_actions = []
|
|
22
|
+
@log_targets = []
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def may(*actions)
|
|
26
|
+
@permitted_actions.concat(actions.flatten)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def must_not(*actions)
|
|
30
|
+
@prohibited_actions.concat(actions.flatten)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def must_log_to(*targets)
|
|
34
|
+
@log_targets.concat(targets.flatten)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MandateClaw
|
|
4
|
+
module DSL
|
|
5
|
+
# Defines signing requirements for the contract.
|
|
6
|
+
# A contract is not in force until all required parties have signed.
|
|
7
|
+
#
|
|
8
|
+
# attestation do
|
|
9
|
+
# require_signature_from :buyer, :seller, :ai_agent
|
|
10
|
+
# sign_with :ed25519
|
|
11
|
+
# end
|
|
12
|
+
#
|
|
13
|
+
class Attestation
|
|
14
|
+
SUPPORTED_ALGORITHMS = %i[ed25519 hmac_sha256].freeze
|
|
15
|
+
|
|
16
|
+
attr_reader :required_signatories, :algorithm
|
|
17
|
+
|
|
18
|
+
def initialize
|
|
19
|
+
@required_signatories = []
|
|
20
|
+
@algorithm = :ed25519
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def require_signature_from(*parties)
|
|
24
|
+
@required_signatories.concat(parties.flatten)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def sign_with(algorithm)
|
|
28
|
+
unless SUPPORTED_ALGORITHMS.include?(algorithm)
|
|
29
|
+
raise ArgumentError, "Unsupported signing algorithm: #{algorithm}. " \
|
|
30
|
+
"Supported: #{SUPPORTED_ALGORITHMS.join(', ')}"
|
|
31
|
+
end
|
|
32
|
+
@algorithm = algorithm
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "securerandom"
|
|
4
|
+
|
|
5
|
+
module MandateClaw
|
|
6
|
+
module DSL
|
|
7
|
+
# Base class for all MandateClaw contracts.
|
|
8
|
+
#
|
|
9
|
+
# Usage:
|
|
10
|
+
#
|
|
11
|
+
# class InvoiceContract < MandateClaw::DSL::Contract
|
|
12
|
+
# contract do
|
|
13
|
+
# party :buyer, identifies_by: :customer_id
|
|
14
|
+
# party :seller, identifies_by: :merchant_id
|
|
15
|
+
# party :ai_agent, identifies_by: :agent_did, kind: :autonomous
|
|
16
|
+
#
|
|
17
|
+
# obligation :pay_invoice, on: :buyer, within: 30.days, breach: :late_payment_penalty
|
|
18
|
+
# permission :dispute, on: :buyer, within: 7.days, event: :raise_dispute
|
|
19
|
+
# prohibition :unilateral_amend, on: :ai_agent, breach: :void_transition
|
|
20
|
+
#
|
|
21
|
+
# agent_bounds :ai_agent do
|
|
22
|
+
# may :issue_invoice, :send_reminder
|
|
23
|
+
# must_not :modify_amount, :waive_penalty
|
|
24
|
+
# must_log_to :contract_registry
|
|
25
|
+
# end
|
|
26
|
+
#
|
|
27
|
+
# attestation do
|
|
28
|
+
# require_signature_from :buyer, :seller, :ai_agent
|
|
29
|
+
# sign_with :ed25519
|
|
30
|
+
# end
|
|
31
|
+
# end
|
|
32
|
+
# end
|
|
33
|
+
#
|
|
34
|
+
class Contract
|
|
35
|
+
class << self
|
|
36
|
+
attr_reader :_parties, :_obligations, :_permissions,
|
|
37
|
+
:_prohibitions, :_agent_bounds, :_attestation,
|
|
38
|
+
:_rules, :_contract_name
|
|
39
|
+
|
|
40
|
+
def contract(name = nil, &block)
|
|
41
|
+
@_contract_name = name || self.name&.underscore&.to_sym || :unnamed
|
|
42
|
+
@_parties = {}
|
|
43
|
+
@_obligations = []
|
|
44
|
+
@_permissions = []
|
|
45
|
+
@_prohibitions = []
|
|
46
|
+
@_agent_bounds = {}
|
|
47
|
+
@_attestation = nil
|
|
48
|
+
@_rules = []
|
|
49
|
+
|
|
50
|
+
DSL::ContractBuilder.new(self).instance_eval(&block) if block_given?
|
|
51
|
+
self
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def validate!
|
|
55
|
+
MandateClaw::Validators::ContractValidator.new(self).validate!
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def to_markdown
|
|
59
|
+
MandateClaw::Renderers::MarkdownRenderer.new(self).render
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def autonomous_parties
|
|
63
|
+
_parties.select { |_, p| p.kind == :autonomous }.keys
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Internal builder — keeps the Contract class clean
|
|
69
|
+
class ContractBuilder
|
|
70
|
+
def initialize(target)
|
|
71
|
+
@target = target
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def party(name, identifies_by:, kind: :human)
|
|
75
|
+
@target._parties[name] = DSL::Party.new(name, identifies_by: identifies_by, kind: kind)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def obligation(name, on:, within: nil, breach: nil)
|
|
79
|
+
@target._obligations << DSL::Obligation.new(name, on: on, within: within, breach: breach)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def permission(name, on:, within: nil, event: nil)
|
|
83
|
+
@target._permissions << DSL::Permission.new(name, on: on, within: within, event: event)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def prohibition(name, on:, breach: nil)
|
|
87
|
+
@target._prohibitions << DSL::Prohibition.new(name, on: on, breach: breach)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def agent_bounds(party_name, &block)
|
|
91
|
+
bounds = DSL::AgentBounds.new(party_name)
|
|
92
|
+
bounds.instance_eval(&block) if block_given?
|
|
93
|
+
@target._agent_bounds[party_name] = bounds
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def rule(name, &block)
|
|
97
|
+
dr = DSL::DecisionRule.new(name)
|
|
98
|
+
dr.instance_eval(&block) if block_given?
|
|
99
|
+
@target._rules << dr
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def attestation(&block)
|
|
103
|
+
att = DSL::Attestation.new
|
|
104
|
+
att.instance_eval(&block) if block_given?
|
|
105
|
+
@target._attestation = att
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MandateClaw
|
|
4
|
+
module DSL
|
|
5
|
+
# A named decision rule with ternary truth: true / false / :unknown.
|
|
6
|
+
# Borrowed from L4's model: an unknown value propagates uncertainty
|
|
7
|
+
# rather than defaulting to false.
|
|
8
|
+
#
|
|
9
|
+
# rule :payment_due_today do
|
|
10
|
+
# decide :due, when: ->(ctx) { ctx.issued_at + ctx.net_terms <= Date.today }
|
|
11
|
+
# unknown_when: ->(ctx) { ctx.issued_at.nil? }
|
|
12
|
+
# end
|
|
13
|
+
#
|
|
14
|
+
class DecisionRule
|
|
15
|
+
TRUTH_VALUES = [true, false, :unknown].freeze
|
|
16
|
+
|
|
17
|
+
attr_reader :name, :outcome, :condition, :unknown_condition
|
|
18
|
+
|
|
19
|
+
def initialize(name)
|
|
20
|
+
@name = name
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def decide(outcome, when: nil, unknown_when: nil)
|
|
24
|
+
@outcome = outcome
|
|
25
|
+
@condition = binding.local_variable_get(:when)
|
|
26
|
+
@unknown_condition = unknown_when
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Evaluate against a context object.
|
|
30
|
+
# Returns true, false, or :unknown — never nil.
|
|
31
|
+
def evaluate(context)
|
|
32
|
+
return :unknown if @unknown_condition&.call(context)
|
|
33
|
+
return @condition.call(context) if @condition
|
|
34
|
+
|
|
35
|
+
:unknown
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MandateClaw
|
|
4
|
+
module DSL
|
|
5
|
+
# A positive duty: the named party MUST perform this action.
|
|
6
|
+
# Failure to perform within +within+ triggers the +breach+ consequence.
|
|
7
|
+
#
|
|
8
|
+
# obligation :pay_invoice, on: :buyer, within: 30.days, breach: :late_payment_penalty
|
|
9
|
+
#
|
|
10
|
+
Obligation = Struct.new(:name, :on, :within, :breach, keyword_init: true)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MandateClaw
|
|
4
|
+
module DSL
|
|
5
|
+
Party = Struct.new(:name, :identifies_by, :kind, keyword_init: true) do
|
|
6
|
+
KINDS = %i[human organisation autonomous].freeze
|
|
7
|
+
|
|
8
|
+
def autonomous?
|
|
9
|
+
kind == :autonomous
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def valid?
|
|
13
|
+
KINDS.include?(kind) && name.is_a?(Symbol) && identifies_by.is_a?(Symbol)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MandateClaw
|
|
4
|
+
module DSL
|
|
5
|
+
# A right: the named party MAY perform this action.
|
|
6
|
+
# Not performing is not a breach.
|
|
7
|
+
#
|
|
8
|
+
# permission :dispute, on: :buyer, within: 7.days, event: :raise_dispute
|
|
9
|
+
#
|
|
10
|
+
Permission = Struct.new(:name, :on, :within, :event, keyword_init: true)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MandateClaw
|
|
4
|
+
module DSL
|
|
5
|
+
# An explicit denial: the named party MUST NOT perform this action.
|
|
6
|
+
# The attempt itself is a breach event, even if the action is blocked.
|
|
7
|
+
#
|
|
8
|
+
# prohibition :unilateral_amend, on: :ai_agent, breach: :void_transition
|
|
9
|
+
#
|
|
10
|
+
Prohibition = Struct.new(:name, :on, :breach, keyword_init: true)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MandateClaw
|
|
4
|
+
module DSL
|
|
5
|
+
# Represents a temporal constraint on an obligation or permission.
|
|
6
|
+
# Anchored to a named event or an absolute duration.
|
|
7
|
+
#
|
|
8
|
+
# Used internally; surface API is:
|
|
9
|
+
# obligation :pay, on: :buyer, within: 30.days
|
|
10
|
+
#
|
|
11
|
+
TemporalConstraint = Struct.new(:duration, :anchor, keyword_init: true) do
|
|
12
|
+
def deadline_from(base_time)
|
|
13
|
+
base_time + duration
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def breached?(base_time, now = Time.current)
|
|
17
|
+
now > deadline_from(base_time)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MandateClaw
|
|
4
|
+
module Renderers
|
|
5
|
+
# Renders a contract class as a human-readable Markdown document.
|
|
6
|
+
# This is the "bidirectional transpilation" — the same DSL that
|
|
7
|
+
# the Ruby runtime enforces can be read by lawyers and regulators.
|
|
8
|
+
class MarkdownRenderer
|
|
9
|
+
def initialize(contract_class)
|
|
10
|
+
@c = contract_class
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def render
|
|
14
|
+
sections = []
|
|
15
|
+
sections << header
|
|
16
|
+
sections << parties_section
|
|
17
|
+
sections << obligations_section if @c._obligations.any?
|
|
18
|
+
sections << permissions_section if @c._permissions.any?
|
|
19
|
+
sections << prohibitions_section if @c._prohibitions.any?
|
|
20
|
+
sections << agent_bounds_section if @c._agent_bounds.any?
|
|
21
|
+
sections << rules_section if @c._rules.any?
|
|
22
|
+
sections << attestation_section if @c._attestation
|
|
23
|
+
sections << footer
|
|
24
|
+
sections.compact.join("\n\n")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def header
|
|
30
|
+
<<~MD
|
|
31
|
+
# Contract: #{contract_title}
|
|
32
|
+
|
|
33
|
+
> Generated by [MandateClaw-DSL](https://mandateclaw.com) v#{MandateClaw::VERSION}
|
|
34
|
+
> This document is the human-readable rendering of a programmatic contract.
|
|
35
|
+
> The Ruby definition is the authoritative source of enforcement.
|
|
36
|
+
MD
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def contract_title
|
|
40
|
+
@c._contract_name.to_s.humanize.titleize
|
|
41
|
+
rescue StandardError
|
|
42
|
+
@c._contract_name.to_s
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def parties_section
|
|
46
|
+
rows = @c._parties.map do |name, party|
|
|
47
|
+
"| `#{name}` | #{party.kind.to_s.capitalize} | `#{party.identifies_by}` |"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
<<~MD
|
|
51
|
+
## Parties
|
|
52
|
+
|
|
53
|
+
| Name | Kind | Identified By |
|
|
54
|
+
|------|------|---------------|
|
|
55
|
+
#{rows.join("\n")}
|
|
56
|
+
MD
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def obligations_section
|
|
60
|
+
items = @c._obligations.map do |ob|
|
|
61
|
+
deadline = ob.within ? " within **#{humanize_duration(ob.within)}**" : ""
|
|
62
|
+
breach = ob.breach ? " Breach: `#{ob.breach}`." : ""
|
|
63
|
+
"- **#{ob.on.to_s.capitalize}** MUST `#{ob.name}`#{deadline}.#{breach}"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
"## Obligations\n\n#{items.join("\n")}"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def permissions_section
|
|
70
|
+
items = @c._permissions.map do |pr|
|
|
71
|
+
deadline = pr.within ? " within **#{humanize_duration(pr.within)}**" : ""
|
|
72
|
+
"- **#{pr.on.to_s.capitalize}** MAY `#{pr.name}`#{deadline}."
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
"## Permissions\n\n#{items.join("\n")}"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def prohibitions_section
|
|
79
|
+
items = @c._prohibitions.map do |pr|
|
|
80
|
+
breach = pr.breach ? " Breach: `#{pr.breach}`." : ""
|
|
81
|
+
"- **#{pr.on.to_s.capitalize}** MUST NOT `#{pr.name}`.#{breach}"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
"## Prohibitions\n\n#{items.join("\n")}"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def agent_bounds_section
|
|
88
|
+
sections = @c._agent_bounds.map do |name, bounds|
|
|
89
|
+
permitted = bounds.permitted_actions.map { |a| "`#{a}`" }.join(", ")
|
|
90
|
+
prohibited = bounds.prohibited_actions.map { |a| "`#{a}`" }.join(", ")
|
|
91
|
+
log_targets = bounds.log_targets.map { |t| "`#{t}`" }.join(", ")
|
|
92
|
+
|
|
93
|
+
<<~MD
|
|
94
|
+
### Agent: `#{name}`
|
|
95
|
+
|
|
96
|
+
- **May do:** #{permitted.empty? ? "_none specified_" : permitted}
|
|
97
|
+
- **Must not do:** #{prohibited.empty? ? "_none specified_" : prohibited}
|
|
98
|
+
- **Must log to:** #{log_targets.empty? ? "_none specified_" : log_targets}
|
|
99
|
+
MD
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
"## Agent Capability Bounds\n\n#{sections.join("\n")}"
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def rules_section
|
|
106
|
+
items = @c._rules.map do |rule|
|
|
107
|
+
"- **#{rule.name}**: evaluates to `true`, `false`, or `:unknown` (ternary)"
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
"## Decision Rules\n\n#{items.join("\n")}"
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def attestation_section
|
|
114
|
+
att = @c._attestation
|
|
115
|
+
sigs = att.required_signatories.map { |s| "- `#{s}`" }.join("\n")
|
|
116
|
+
|
|
117
|
+
<<~MD
|
|
118
|
+
## Attestation
|
|
119
|
+
|
|
120
|
+
This contract comes into force only when all required parties have signed.
|
|
121
|
+
|
|
122
|
+
**Required signatories:**
|
|
123
|
+
#{sigs}
|
|
124
|
+
|
|
125
|
+
**Signing algorithm:** `#{att.algorithm}`
|
|
126
|
+
MD
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def footer
|
|
130
|
+
"---\n_MandateClaw — mandateclaw.com_"
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def humanize_duration(duration)
|
|
134
|
+
return duration.inspect unless duration.respond_to?(:to_i)
|
|
135
|
+
|
|
136
|
+
seconds = duration.to_i
|
|
137
|
+
if seconds >= 86_400
|
|
138
|
+
"#{seconds / 86_400} days"
|
|
139
|
+
elsif seconds >= 3_600
|
|
140
|
+
"#{seconds / 3_600} hours"
|
|
141
|
+
else
|
|
142
|
+
"#{seconds} seconds"
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MandateClaw
|
|
4
|
+
module Validators
|
|
5
|
+
class ContractValidator
|
|
6
|
+
def initialize(contract_class)
|
|
7
|
+
@contract = contract_class
|
|
8
|
+
@errors = []
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def validate!
|
|
12
|
+
validate_parties
|
|
13
|
+
validate_obligations
|
|
14
|
+
validate_prohibitions
|
|
15
|
+
validate_agent_bounds
|
|
16
|
+
validate_attestation
|
|
17
|
+
|
|
18
|
+
raise MandateClaw::ValidationError, @errors.join("; ") if @errors.any?
|
|
19
|
+
|
|
20
|
+
true
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def validate_parties
|
|
26
|
+
if @contract._parties.empty?
|
|
27
|
+
@errors << "Contract must define at least one party"
|
|
28
|
+
return
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
@contract._parties.each do |name, party|
|
|
32
|
+
@errors << "Party #{name} has invalid kind: #{party.kind}" unless party.valid?
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def validate_obligations
|
|
37
|
+
@contract._obligations.each do |ob|
|
|
38
|
+
unless @contract._parties.key?(ob.on)
|
|
39
|
+
@errors << "Obligation #{ob.name}: unknown party #{ob.on}"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def validate_prohibitions
|
|
45
|
+
@contract._prohibitions.each do |pr|
|
|
46
|
+
unless @contract._parties.key?(pr.on)
|
|
47
|
+
@errors << "Prohibition #{pr.name}: unknown party #{pr.on}"
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def validate_agent_bounds
|
|
53
|
+
@contract._agent_bounds.each do |party_name, bounds|
|
|
54
|
+
unless @contract._parties.key?(party_name)
|
|
55
|
+
@errors << "agent_bounds references unknown party: #{party_name}"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
party = @contract._parties[party_name]
|
|
59
|
+
unless party&.autonomous?
|
|
60
|
+
@errors << "agent_bounds can only be applied to autonomous parties; " \
|
|
61
|
+
"#{party_name} has kind: #{party&.kind}"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
overlap = bounds.permitted_actions & bounds.prohibited_actions
|
|
65
|
+
if overlap.any?
|
|
66
|
+
@errors << "agent_bounds for #{party_name}: actions #{overlap.join(', ')} " \
|
|
67
|
+
"appear in both may and must_not"
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def validate_attestation
|
|
73
|
+
return unless @contract._attestation
|
|
74
|
+
|
|
75
|
+
@contract._attestation.required_signatories.each do |sig|
|
|
76
|
+
unless @contract._parties.key?(sig)
|
|
77
|
+
@errors << "Attestation requires signature from unknown party: #{sig}"
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
data/lib/mandate_claw.rb
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support"
|
|
4
|
+
require "active_support/core_ext"
|
|
5
|
+
|
|
6
|
+
require "mandate_claw/version"
|
|
7
|
+
require "mandate_claw/dsl/contract"
|
|
8
|
+
require "mandate_claw/dsl/party"
|
|
9
|
+
require "mandate_claw/dsl/obligation"
|
|
10
|
+
require "mandate_claw/dsl/permission"
|
|
11
|
+
require "mandate_claw/dsl/prohibition"
|
|
12
|
+
require "mandate_claw/dsl/agent_bounds"
|
|
13
|
+
require "mandate_claw/dsl/attestation"
|
|
14
|
+
require "mandate_claw/dsl/temporal"
|
|
15
|
+
require "mandate_claw/dsl/decision_rule"
|
|
16
|
+
require "mandate_claw/validators/contract_validator"
|
|
17
|
+
require "mandate_claw/renderers/markdown_renderer"
|
|
18
|
+
|
|
19
|
+
module MandateClaw
|
|
20
|
+
class Error < StandardError; end
|
|
21
|
+
class ValidationError < Error; end
|
|
22
|
+
class BreachError < Error; end
|
|
23
|
+
|
|
24
|
+
# Convenience: define a contract class inline
|
|
25
|
+
#
|
|
26
|
+
# MandateClaw.define(:invoice_contract) do
|
|
27
|
+
# party :buyer, identifies_by: :customer_id
|
|
28
|
+
# ...
|
|
29
|
+
# end
|
|
30
|
+
#
|
|
31
|
+
def self.define(name, &block)
|
|
32
|
+
contract = Class.new(MandateClaw::DSL::Contract)
|
|
33
|
+
contract.contract(&block)
|
|
34
|
+
contract
|
|
35
|
+
end
|
|
36
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: mandateclaw
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Abhishek Parolkar
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-05-21 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: activesupport
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '7.0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '7.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rspec
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '3.12'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '3.12'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: rubocop
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '1.60'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '1.60'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rubocop-rspec
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '2.25'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '2.25'
|
|
69
|
+
description: MandateClaw-DSL is a Ruby DSL for defining programmatic, signed, and
|
|
70
|
+
auditable contracts that govern what an AI agent is and isn't allowed to do on behalf
|
|
71
|
+
of a human or organisation.
|
|
72
|
+
email:
|
|
73
|
+
- abhishek@parolkar.com
|
|
74
|
+
executables: []
|
|
75
|
+
extensions: []
|
|
76
|
+
extra_rdoc_files: []
|
|
77
|
+
files:
|
|
78
|
+
- CHANGELOG.md
|
|
79
|
+
- LICENSE
|
|
80
|
+
- README.md
|
|
81
|
+
- lib/mandate_claw.rb
|
|
82
|
+
- lib/mandate_claw/dsl/agent_bounds.rb
|
|
83
|
+
- lib/mandate_claw/dsl/attestation.rb
|
|
84
|
+
- lib/mandate_claw/dsl/contract.rb
|
|
85
|
+
- lib/mandate_claw/dsl/decision_rule.rb
|
|
86
|
+
- lib/mandate_claw/dsl/obligation.rb
|
|
87
|
+
- lib/mandate_claw/dsl/party.rb
|
|
88
|
+
- lib/mandate_claw/dsl/permission.rb
|
|
89
|
+
- lib/mandate_claw/dsl/prohibition.rb
|
|
90
|
+
- lib/mandate_claw/dsl/temporal.rb
|
|
91
|
+
- lib/mandate_claw/renderers/markdown_renderer.rb
|
|
92
|
+
- lib/mandate_claw/validators/contract_validator.rb
|
|
93
|
+
- lib/mandate_claw/version.rb
|
|
94
|
+
homepage: https://mandateclaw.com
|
|
95
|
+
licenses:
|
|
96
|
+
- MIT
|
|
97
|
+
metadata:
|
|
98
|
+
homepage_uri: https://mandateclaw.com
|
|
99
|
+
source_code_uri: https://github.com/parolkar/MandateClaw-DSL
|
|
100
|
+
changelog_uri: https://github.com/parolkar/MandateClaw-DSL/blob/main/CHANGELOG.md
|
|
101
|
+
post_install_message:
|
|
102
|
+
rdoc_options: []
|
|
103
|
+
require_paths:
|
|
104
|
+
- lib
|
|
105
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - ">="
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: 3.1.0
|
|
110
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
111
|
+
requirements:
|
|
112
|
+
- - ">="
|
|
113
|
+
- !ruby/object:Gem::Version
|
|
114
|
+
version: '0'
|
|
115
|
+
requirements: []
|
|
116
|
+
rubygems_version: 3.5.22
|
|
117
|
+
signing_key:
|
|
118
|
+
specification_version: 4
|
|
119
|
+
summary: The Employment Contract Between You and Your Agent
|
|
120
|
+
test_files: []
|