Class: MandateClaw::Registry::TaskContract
- Inherits:
-
Object
- Object
- MandateClaw::Registry::TaskContract
- Defined in:
- lib/mandate_claw/registry/task_contract.rb
Overview
A TaskContract is an instantiated, signable contract for a specific scope.
Usage:
tc = MandateClaw::Registry::TaskContract.new(
template: InvoiceContract,
scope: invoice,
parties: { buyer: current_user, seller: merchant, ai_agent: agent },
validity: 24.hours
)
tc.sign_as(:buyer, key: user.signing_key_hex)
tc.sign_as(:ai_agent, key: agent.capability_key_hex,
attests: agent.capability_manifest)
tc.anchor! # persists to MandateClaw::Registry::SignedContract
Instance Attribute Summary collapse
-
#contract_id ⇒ Object
readonly
Returns the value of attribute contract_id.
-
#created_at ⇒ Object
readonly
Returns the value of attribute created_at.
-
#parties ⇒ Object
readonly
Returns the value of attribute parties.
-
#scope ⇒ Object
readonly
Returns the value of attribute scope.
-
#signatures ⇒ Object
readonly
Returns the value of attribute signatures.
-
#template ⇒ Object
readonly
Returns the value of attribute template.
-
#validity ⇒ Object
readonly
Returns the value of attribute validity.
Instance Method Summary collapse
-
#anchor! ⇒ Object
Persists this contract to the registry database.
-
#digest ⇒ Object
The canonical digest is the authoritative fingerprint of this contract instance.
- #expired? ⇒ Boolean
- #fully_signed? ⇒ Boolean
-
#initialize(template:, scope:, parties:, validity: 24.hours) ⇒ TaskContract
constructor
A new instance of TaskContract.
- #sign_as(party_name, key:, attests: nil, algorithm: :ed25519) ⇒ Object
Constructor Details
#initialize(template:, scope:, parties:, validity: 24.hours) ⇒ TaskContract
Returns a new instance of TaskContract.
28 29 30 31 32 33 34 35 36 |
# File 'lib/mandate_claw/registry/task_contract.rb', line 28 def initialize(template:, scope:, parties:, validity: 24.hours) @template = template @scope = scope @parties = parties @validity = validity @signatures = {} @created_at = Time.current @contract_id = nil end |
Instance Attribute Details
#contract_id ⇒ Object (readonly)
Returns the value of attribute contract_id.
25 26 27 |
# File 'lib/mandate_claw/registry/task_contract.rb', line 25 def contract_id @contract_id end |
#created_at ⇒ Object (readonly)
Returns the value of attribute created_at.
25 26 27 |
# File 'lib/mandate_claw/registry/task_contract.rb', line 25 def created_at @created_at end |
#parties ⇒ Object (readonly)
Returns the value of attribute parties.
25 26 27 |
# File 'lib/mandate_claw/registry/task_contract.rb', line 25 def parties @parties end |
#scope ⇒ Object (readonly)
Returns the value of attribute scope.
25 26 27 |
# File 'lib/mandate_claw/registry/task_contract.rb', line 25 def scope @scope end |
#signatures ⇒ Object (readonly)
Returns the value of attribute signatures.
25 26 27 |
# File 'lib/mandate_claw/registry/task_contract.rb', line 25 def signatures @signatures end |
#template ⇒ Object (readonly)
Returns the value of attribute template.
25 26 27 |
# File 'lib/mandate_claw/registry/task_contract.rb', line 25 def template @template end |
#validity ⇒ Object (readonly)
Returns the value of attribute validity.
25 26 27 |
# File 'lib/mandate_claw/registry/task_contract.rb', line 25 def validity @validity end |
Instance Method Details
#anchor! ⇒ Object
Persists this contract to the registry database. Raises unless fully signed.
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/mandate_claw/registry/task_contract.rb', line 71 def anchor! raise Registry::SignatureError, "Contract is not fully signed" unless fully_signed? record = SignedContract.create!( contract_digest: digest, template_name: template._contract_name.to_s, scope_type: scope.class.name, scope_id: scope.id.to_s, parties_json: parties.transform_values(&:to_s).to_json, signatures_json: signatures.to_json, rendered_markdown: template.to_markdown, expires_at: created_at + validity, status: :active ) @contract_id = record.id record end |
#digest ⇒ Object
The canonical digest is the authoritative fingerprint of this contract instance. All parties sign this exact string.
40 41 42 |
# File 'lib/mandate_claw/registry/task_contract.rb', line 40 def digest @digest ||= Digest::SHA256.hexdigest(canonical_payload.to_json) end |
#expired? ⇒ Boolean
90 91 92 |
# File 'lib/mandate_claw/registry/task_contract.rb', line 90 def expired? created_at + validity < Time.current end |
#fully_signed? ⇒ Boolean
64 65 66 67 |
# File 'lib/mandate_claw/registry/task_contract.rb', line 64 def fully_signed? required = template._attestation&.required_signatories || [] required.all? { |p| signatures.key?(p) } end |
#sign_as(party_name, key:, attests: nil, algorithm: :ed25519) ⇒ Object
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/mandate_claw/registry/task_contract.rb', line 44 def sign_as(party_name, key:, attests: nil, algorithm: :ed25519) raise ArgumentError, "Unknown party: #{party_name}" unless parties.key?(party_name) sig = case algorithm when :ed25519 Signing::Ed25519Signer.sign(digest, key) when :hmac_sha256 Signing::HmacSigner.sign(digest, key) else raise ArgumentError, "Unsupported algorithm: #{algorithm}" end @signatures[party_name] = { signature: sig, algorithm: algorithm, attests: attests, signed_at: Time.current } end |