textus 0.30.0 → 0.38.0

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.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/ARCHITECTURE.md +2 -241
  3. data/CHANGELOG.md +221 -0
  4. data/README.md +89 -69
  5. data/SPEC.md +359 -212
  6. data/docs/conventions.md +42 -37
  7. data/lib/textus/boot.rb +122 -87
  8. data/lib/textus/cli/group/{refresh.rb → fetch.rb} +4 -4
  9. data/lib/textus/cli/verb/build.rb +1 -1
  10. data/lib/textus/cli/verb/fetch.rb +14 -0
  11. data/lib/textus/cli/verb/{refresh_stale.rb → fetch_stale.rb} +3 -3
  12. data/lib/textus/cli/verb/get.rb +1 -1
  13. data/lib/textus/cli/verb/hooks.rb +1 -1
  14. data/lib/textus/cli/verb/mcp_serve.rb +8 -3
  15. data/lib/textus/cli/verb/propose.rb +28 -0
  16. data/lib/textus/cli/verb/pulse.rb +12 -3
  17. data/lib/textus/cli/verb/put.rb +1 -1
  18. data/lib/textus/cli/verb/rule_list.rb +7 -7
  19. data/lib/textus/cli/verb/schema.rb +1 -1
  20. data/lib/textus/cli/verb.rb +3 -2
  21. data/lib/textus/cli.rb +2 -2
  22. data/lib/textus/container.rb +1 -2
  23. data/lib/textus/contract.rb +106 -0
  24. data/lib/textus/cursor_store.rb +24 -0
  25. data/lib/textus/dispatcher.rb +6 -4
  26. data/lib/textus/doctor/check/audit_log.rb +1 -1
  27. data/lib/textus/doctor/check/{refresh_locks.rb → fetch_locks.rb} +8 -8
  28. data/lib/textus/doctor/check/proposal_targets.rb +45 -0
  29. data/lib/textus/doctor/check/rule_ambiguity.rb +3 -3
  30. data/lib/textus/doctor.rb +2 -1
  31. data/lib/textus/domain/action.rb +3 -3
  32. data/lib/textus/domain/freshness/evaluator.rb +3 -3
  33. data/lib/textus/domain/freshness/policy.rb +2 -2
  34. data/lib/textus/domain/freshness.rb +7 -7
  35. data/lib/textus/domain/outcome.rb +2 -2
  36. data/lib/textus/domain/permission.rb +2 -10
  37. data/lib/textus/domain/policy/base_guards.rb +25 -0
  38. data/lib/textus/domain/policy/evaluation.rb +15 -0
  39. data/lib/textus/domain/policy/{refresh.rb → fetch.rb} +1 -1
  40. data/lib/textus/domain/policy/guard.rb +35 -0
  41. data/lib/textus/domain/policy/guard_factory.rb +40 -0
  42. data/lib/textus/domain/policy/predicates/author_held.rb +33 -0
  43. data/lib/textus/domain/policy/predicates/etag_match.rb +32 -0
  44. data/lib/textus/domain/policy/predicates/fresh_within.rb +58 -0
  45. data/lib/textus/domain/policy/predicates/registry.rb +39 -0
  46. data/lib/textus/domain/policy/predicates/schema_valid.rb +30 -19
  47. data/lib/textus/domain/policy/predicates/target_is_canon.rb +33 -0
  48. data/lib/textus/domain/policy/predicates/zone_writable_by.rb +39 -0
  49. data/lib/textus/domain/staleness/intake_check.rb +6 -6
  50. data/lib/textus/envelope.rb +2 -2
  51. data/lib/textus/errors.rb +25 -28
  52. data/lib/textus/hooks/event_bus.rb +4 -4
  53. data/lib/textus/init.rb +27 -18
  54. data/lib/textus/layout.rb +41 -0
  55. data/lib/textus/maintenance/key_delete_prefix.rb +9 -0
  56. data/lib/textus/maintenance/key_mv_prefix.rb +10 -0
  57. data/lib/textus/maintenance/migrate.rb +9 -0
  58. data/lib/textus/maintenance/rule_lint.rb +8 -0
  59. data/lib/textus/maintenance/zone_mv.rb +11 -1
  60. data/lib/textus/manifest/capabilities.rb +29 -0
  61. data/lib/textus/manifest/data.rb +14 -10
  62. data/lib/textus/manifest/policy.rb +37 -21
  63. data/lib/textus/manifest/rules.rb +16 -14
  64. data/lib/textus/manifest/schema.rb +48 -58
  65. data/lib/textus/manifest.rb +3 -3
  66. data/lib/textus/mcp/catalog.rb +72 -0
  67. data/lib/textus/mcp/server.rb +8 -5
  68. data/lib/textus/mcp/session.rb +3 -20
  69. data/lib/textus/mcp/tool_schemas.rb +6 -62
  70. data/lib/textus/mcp/tools.rb +4 -119
  71. data/lib/textus/ports/audit_log.rb +17 -15
  72. data/lib/textus/ports/audit_subscriber.rb +1 -1
  73. data/lib/textus/ports/build_lock.rb +1 -2
  74. data/lib/textus/ports/{refresh → fetch}/detached.rb +4 -4
  75. data/lib/textus/ports/{refresh → fetch}/lock.rb +2 -2
  76. data/lib/textus/projection.rb +1 -1
  77. data/lib/textus/read/audit.rb +3 -3
  78. data/lib/textus/read/boot.rb +6 -0
  79. data/lib/textus/read/freshness.rb +9 -9
  80. data/lib/textus/read/get.rb +16 -8
  81. data/lib/textus/read/{get_or_refresh.rb → get_or_fetch.rb} +11 -11
  82. data/lib/textus/read/list.rb +8 -0
  83. data/lib/textus/read/policy_explain.rb +14 -10
  84. data/lib/textus/read/pulse.rb +12 -4
  85. data/lib/textus/read/rules.rb +24 -0
  86. data/lib/textus/read/schema_envelope.rb +7 -0
  87. data/lib/textus/read/validator.rb +1 -1
  88. data/lib/textus/role.rb +6 -2
  89. data/lib/textus/schema/tools.rb +5 -5
  90. data/lib/textus/session.rb +24 -0
  91. data/lib/textus/store.rb +11 -0
  92. data/lib/textus/version.rb +1 -1
  93. data/lib/textus/write/accept.rb +19 -55
  94. data/lib/textus/write/delete.rb +14 -2
  95. data/lib/textus/write/{refresh_all.rb → fetch_all.rb} +14 -6
  96. data/lib/textus/write/{refresh_orchestrator.rb → fetch_orchestrator.rb} +14 -14
  97. data/lib/textus/write/{refresh_worker.rb → fetch_worker.rb} +29 -14
  98. data/lib/textus/write/mv.rb +15 -3
  99. data/lib/textus/write/propose.rb +46 -0
  100. data/lib/textus/write/put.rb +26 -2
  101. data/lib/textus/write/reject.rb +11 -5
  102. data/lib/textus.rb +4 -0
  103. metadata +36 -21
  104. data/lib/textus/cli/verb/refresh.rb +0 -14
  105. data/lib/textus/domain/authorizer.rb +0 -37
  106. data/lib/textus/domain/policy/predicates/accept_authority_signed.rb +0 -33
  107. data/lib/textus/domain/policy/promote.rb +0 -26
  108. data/lib/textus/domain/policy/promotion.rb +0 -57
  109. data/lib/textus/manifest/role_kinds.rb +0 -21
  110. data/lib/textus/write/authority_gate.rb +0 -24
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: textus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.30.0
4
+ version: 0.38.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patrick
@@ -93,8 +93,9 @@ dependencies:
93
93
  - - "~>"
94
94
  - !ruby/object:Gem::Version
95
95
  version: '3.13'
96
- description: Storage convention and JSON wire protocol for agent-readable project
97
- memory.
96
+ description: A coordination space for humans, AI, and automation. Durable, multi-writer
97
+ project memory where each actor writes into its own lane, proposals cross a review
98
+ queue, and every change is audited.
98
99
  email:
99
100
  - patrick204nqh@gmail.com
100
101
  executables:
@@ -119,10 +120,10 @@ files:
119
120
  - lib/textus/call.rb
120
121
  - lib/textus/cli.rb
121
122
  - lib/textus/cli/group.rb
123
+ - lib/textus/cli/group/fetch.rb
122
124
  - lib/textus/cli/group/hook.rb
123
125
  - lib/textus/cli/group/key.rb
124
126
  - lib/textus/cli/group/mcp.rb
125
- - lib/textus/cli/group/refresh.rb
126
127
  - lib/textus/cli/group/rule.rb
127
128
  - lib/textus/cli/group/schema.rb
128
129
  - lib/textus/cli/group/zone.rb
@@ -135,6 +136,8 @@ files:
135
136
  - lib/textus/cli/verb/delete.rb
136
137
  - lib/textus/cli/verb/deps.rb
137
138
  - lib/textus/cli/verb/doctor.rb
139
+ - lib/textus/cli/verb/fetch.rb
140
+ - lib/textus/cli/verb/fetch_stale.rb
138
141
  - lib/textus/cli/verb/freshness.rb
139
142
  - lib/textus/cli/verb/get.rb
140
143
  - lib/textus/cli/verb/hook_run.rb
@@ -145,12 +148,11 @@ files:
145
148
  - lib/textus/cli/verb/mcp_serve.rb
146
149
  - lib/textus/cli/verb/migrate.rb
147
150
  - lib/textus/cli/verb/mv.rb
151
+ - lib/textus/cli/verb/propose.rb
148
152
  - lib/textus/cli/verb/published.rb
149
153
  - lib/textus/cli/verb/pulse.rb
150
154
  - lib/textus/cli/verb/put.rb
151
155
  - lib/textus/cli/verb/rdeps.rb
152
- - lib/textus/cli/verb/refresh.rb
153
- - lib/textus/cli/verb/refresh_stale.rb
154
156
  - lib/textus/cli/verb/reject.rb
155
157
  - lib/textus/cli/verb/retain.rb
156
158
  - lib/textus/cli/verb/rule_explain.rb
@@ -164,17 +166,20 @@ files:
164
166
  - lib/textus/cli/verb/where.rb
165
167
  - lib/textus/cli/verb/zone_mv.rb
166
168
  - lib/textus/container.rb
169
+ - lib/textus/contract.rb
170
+ - lib/textus/cursor_store.rb
167
171
  - lib/textus/dispatcher.rb
168
172
  - lib/textus/doctor.rb
169
173
  - lib/textus/doctor/check.rb
170
174
  - lib/textus/doctor/check/audit_log.rb
175
+ - lib/textus/doctor/check/fetch_locks.rb
171
176
  - lib/textus/doctor/check/handler_allowlist.rb
172
177
  - lib/textus/doctor/check/hooks.rb
173
178
  - lib/textus/doctor/check/illegal_keys.rb
174
179
  - lib/textus/doctor/check/intake_registration.rb
175
180
  - lib/textus/doctor/check/manifest_files.rb
181
+ - lib/textus/doctor/check/proposal_targets.rb
176
182
  - lib/textus/doctor/check/protocol_version.rb
177
- - lib/textus/doctor/check/refresh_locks.rb
178
183
  - lib/textus/doctor/check/rule_ambiguity.rb
179
184
  - lib/textus/doctor/check/schema_parse_error.rb
180
185
  - lib/textus/doctor/check/schema_violations.rb
@@ -183,7 +188,6 @@ files:
183
188
  - lib/textus/doctor/check/templates.rb
184
189
  - lib/textus/doctor/check/unowned_schema_fields.rb
185
190
  - lib/textus/domain/action.rb
186
- - lib/textus/domain/authorizer.rb
187
191
  - lib/textus/domain/duration.rb
188
192
  - lib/textus/domain/freshness.rb
189
193
  - lib/textus/domain/freshness/evaluator.rb
@@ -191,13 +195,20 @@ files:
191
195
  - lib/textus/domain/freshness/verdict.rb
192
196
  - lib/textus/domain/outcome.rb
193
197
  - lib/textus/domain/permission.rb
198
+ - lib/textus/domain/policy/base_guards.rb
199
+ - lib/textus/domain/policy/evaluation.rb
200
+ - lib/textus/domain/policy/fetch.rb
201
+ - lib/textus/domain/policy/guard.rb
202
+ - lib/textus/domain/policy/guard_factory.rb
194
203
  - lib/textus/domain/policy/handler_allowlist.rb
195
204
  - lib/textus/domain/policy/matcher.rb
196
- - lib/textus/domain/policy/predicates/accept_authority_signed.rb
205
+ - lib/textus/domain/policy/predicates/author_held.rb
206
+ - lib/textus/domain/policy/predicates/etag_match.rb
207
+ - lib/textus/domain/policy/predicates/fresh_within.rb
208
+ - lib/textus/domain/policy/predicates/registry.rb
197
209
  - lib/textus/domain/policy/predicates/schema_valid.rb
198
- - lib/textus/domain/policy/promote.rb
199
- - lib/textus/domain/policy/promotion.rb
200
- - lib/textus/domain/policy/refresh.rb
210
+ - lib/textus/domain/policy/predicates/target_is_canon.rb
211
+ - lib/textus/domain/policy/predicates/zone_writable_by.rb
201
212
  - lib/textus/domain/policy/retention.rb
202
213
  - lib/textus/domain/retention.rb
203
214
  - lib/textus/domain/sentinel.rb
@@ -227,6 +238,7 @@ files:
227
238
  - lib/textus/key/distance.rb
228
239
  - lib/textus/key/grammar.rb
229
240
  - lib/textus/key/path.rb
241
+ - lib/textus/layout.rb
230
242
  - lib/textus/maintenance.rb
231
243
  - lib/textus/maintenance/key_delete_prefix.rb
232
244
  - lib/textus/maintenance/key_mv_prefix.rb
@@ -234,6 +246,7 @@ files:
234
246
  - lib/textus/maintenance/rule_lint.rb
235
247
  - lib/textus/maintenance/zone_mv.rb
236
248
  - lib/textus/manifest.rb
249
+ - lib/textus/manifest/capabilities.rb
237
250
  - lib/textus/manifest/data.rb
238
251
  - lib/textus/manifest/entry.rb
239
252
  - lib/textus/manifest/entry/base.rb
@@ -250,10 +263,10 @@ files:
250
263
  - lib/textus/manifest/entry/validators/publish_each.rb
251
264
  - lib/textus/manifest/policy.rb
252
265
  - lib/textus/manifest/resolver.rb
253
- - lib/textus/manifest/role_kinds.rb
254
266
  - lib/textus/manifest/rules.rb
255
267
  - lib/textus/manifest/schema.rb
256
268
  - lib/textus/mcp.rb
269
+ - lib/textus/mcp/catalog.rb
257
270
  - lib/textus/mcp/errors.rb
258
271
  - lib/textus/mcp/server.rb
259
272
  - lib/textus/mcp/session.rb
@@ -264,9 +277,9 @@ files:
264
277
  - lib/textus/ports/audit_subscriber.rb
265
278
  - lib/textus/ports/build_lock.rb
266
279
  - lib/textus/ports/clock.rb
280
+ - lib/textus/ports/fetch/detached.rb
281
+ - lib/textus/ports/fetch/lock.rb
267
282
  - lib/textus/ports/publisher.rb
268
- - lib/textus/ports/refresh/detached.rb
269
- - lib/textus/ports/refresh/lock.rb
270
283
  - lib/textus/ports/sentinel_store.rb
271
284
  - lib/textus/ports/storage/file_stat.rb
272
285
  - lib/textus/ports/storage/file_store.rb
@@ -278,13 +291,14 @@ files:
278
291
  - lib/textus/read/doctor.rb
279
292
  - lib/textus/read/freshness.rb
280
293
  - lib/textus/read/get.rb
281
- - lib/textus/read/get_or_refresh.rb
294
+ - lib/textus/read/get_or_fetch.rb
282
295
  - lib/textus/read/list.rb
283
296
  - lib/textus/read/policy_explain.rb
284
297
  - lib/textus/read/published.rb
285
298
  - lib/textus/read/pulse.rb
286
299
  - lib/textus/read/rdeps.rb
287
300
  - lib/textus/read/retainable.rb
301
+ - lib/textus/read/rules.rb
288
302
  - lib/textus/read/schema_envelope.rb
289
303
  - lib/textus/read/stale.rb
290
304
  - lib/textus/read/uid.rb
@@ -296,20 +310,21 @@ files:
296
310
  - lib/textus/schema.rb
297
311
  - lib/textus/schema/tools.rb
298
312
  - lib/textus/schemas.rb
313
+ - lib/textus/session.rb
299
314
  - lib/textus/store.rb
300
315
  - lib/textus/uid.rb
301
316
  - lib/textus/version.rb
302
317
  - lib/textus/write/accept.rb
303
- - lib/textus/write/authority_gate.rb
304
318
  - lib/textus/write/delete.rb
319
+ - lib/textus/write/fetch_all.rb
320
+ - lib/textus/write/fetch_orchestrator.rb
321
+ - lib/textus/write/fetch_worker.rb
305
322
  - lib/textus/write/intake_fetch.rb
306
323
  - lib/textus/write/materializer.rb
307
324
  - lib/textus/write/mv.rb
325
+ - lib/textus/write/propose.rb
308
326
  - lib/textus/write/publish.rb
309
327
  - lib/textus/write/put.rb
310
- - lib/textus/write/refresh_all.rb
311
- - lib/textus/write/refresh_orchestrator.rb
312
- - lib/textus/write/refresh_worker.rb
313
328
  - lib/textus/write/reject.rb
314
329
  - lib/textus/write/retention_sweep.rb
315
330
  homepage: https://github.com/patrick204nqh/textus
@@ -338,5 +353,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
338
353
  requirements: []
339
354
  rubygems_version: 3.6.9
340
355
  specification_version: 4
341
- summary: Reference implementation of the textus/1 protocol.
356
+ summary: Reference implementation of the textus/3 protocol.
342
357
  test_files: []
@@ -1,14 +0,0 @@
1
- module Textus
2
- class CLI
3
- class Verb
4
- class Refresh < Verb
5
- option :as_flag, "--as=ROLE"
6
-
7
- def call(store)
8
- key = positional.shift or raise UsageError.new("refresh requires a key")
9
- emit(session_for(store).refresh(key).to_h_for_wire)
10
- end
11
- end
12
- end
13
- end
14
- end
@@ -1,37 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Textus
4
- module Domain
5
- # Authorization service. Single source of truth for "given a manifest
6
- # entry and a role, may this caller read/write?". Lives in Domain
7
- # alongside Permission.
8
- class Authorizer
9
- def initialize(manifest:)
10
- @manifest = manifest
11
- end
12
-
13
- def can_write?(zone, role:)
14
- @manifest.policy.permission_for(zone.to_s).allows_write?(role)
15
- end
16
-
17
- def can_read?(zone, role:)
18
- @manifest.policy.permission_for(zone.to_s).allows_read?(role)
19
- end
20
-
21
- def authorize_write!(mentry, role:)
22
- return if can_write?(mentry.zone, role: role)
23
-
24
- writers = @manifest.policy.zone_writers(mentry.zone)
25
- raise WriteForbidden.new(mentry.key, mentry.zone, writers: writers)
26
- end
27
-
28
- def authorize_read!(mentry, role:)
29
- return if can_read?(mentry.zone, role: role)
30
-
31
- readers = @manifest.policy.zone_readers[mentry.zone]
32
- readers = nil if readers == :all
33
- raise ReadForbidden.new(mentry.key, mentry.zone, readers: readers)
34
- end
35
- end
36
- end
37
- end
@@ -1,33 +0,0 @@
1
- module Textus
2
- module Domain
3
- module Policy
4
- module Predicates
5
- # Promotion predicate: the role driving the promotion must have
6
- # role_kind == :accept_authority in the active manifest.
7
- #
8
- # Accept/Reject already gate on this kind before reaching the
9
- # promotion policy, so in the default control-flow this predicate
10
- # trivially passes. It is kept so manifests can express the
11
- # requirement explicitly in `rules[].promotion.requires`.
12
- class AcceptAuthoritySigned
13
- attr_reader :reason
14
-
15
- def name
16
- "accept_authority_signed"
17
- end
18
-
19
- def call(role:, manifest:, entry: nil) # rubocop:disable Lint/UnusedMethodArgument
20
- role_str = role&.to_s
21
- return true if role_str.nil? || role_str.empty?
22
-
23
- kind = manifest.policy.role_kind(role_str)
24
- return true if kind == :accept_authority
25
-
26
- @reason = "role '#{role_str}' has kind '#{kind.inspect}', expected ':accept_authority'"
27
- false
28
- end
29
- end
30
- end
31
- end
32
- end
33
- end
@@ -1,26 +0,0 @@
1
- module Textus
2
- module Domain
3
- module Policy
4
- class Promote
5
- KNOWN = %i[schema_valid accept_authority_signed].freeze
6
- attr_reader :requires
7
-
8
- def initialize(requires:)
9
- syms = Array(requires).map { |r| r.to_s.to_sym }
10
- unknown = syms - KNOWN
11
- unless unknown.empty?
12
- raise Textus::UsageError.new(
13
- "unknown promote requirement: #{unknown.first.inspect} (known: #{KNOWN.join(", ")})",
14
- )
15
- end
16
-
17
- @requires = syms
18
- end
19
-
20
- def demands?(req)
21
- @requires.include?(req)
22
- end
23
- end
24
- end
25
- end
26
- end
@@ -1,57 +0,0 @@
1
- require_relative "predicates/schema_valid"
2
- require_relative "predicates/accept_authority_signed"
3
-
4
- module Textus
5
- module Domain
6
- module Policy
7
- class Promotion
8
- Result = Struct.new(:ok?, :reasons, keyword_init: true)
9
-
10
- REGISTRY = {
11
- "schema_valid" => -> { Predicates::SchemaValid.new },
12
- "accept_authority_signed" => -> { Predicates::AcceptAuthoritySigned.new },
13
- }.freeze
14
-
15
- def self.from_names(names)
16
- predicates = Array(names).map do |n|
17
- ctor = REGISTRY[n.to_s] or raise Textus::UsageError.new(
18
- "unknown promotion predicate: '#{n}' (known: #{REGISTRY.keys.join(", ")})",
19
- )
20
- ctor.call
21
- end
22
- new(predicates: predicates)
23
- end
24
-
25
- attr_reader :predicates
26
-
27
- def initialize(predicates:)
28
- @predicates = predicates
29
- end
30
-
31
- def predicate_names
32
- @predicates.map(&:name)
33
- end
34
-
35
- def evaluate(entry:, schemas:, manifest:, role:)
36
- reasons = []
37
- @predicates.each do |pred|
38
- ok = invoke(pred, entry: entry, schemas: schemas, manifest: manifest, role: role)
39
- reasons << "#{pred.name}: #{pred.reason || "predicate failed"}" unless ok
40
- end
41
- Result.new(ok?: reasons.empty?, reasons: reasons)
42
- end
43
-
44
- private
45
-
46
- def invoke(pred, entry:, schemas:, manifest:, role:)
47
- case pred.name
48
- when "accept_authority_signed"
49
- pred.call(role: role, manifest: manifest, entry: entry)
50
- else
51
- pred.call(entry: entry, schemas: schemas, manifest: manifest)
52
- end
53
- end
54
- end
55
- end
56
- end
57
- end
@@ -1,21 +0,0 @@
1
- module Textus
2
- class Manifest
3
- module RoleKinds
4
- DEFAULT_MAPPING = {
5
- "human" => :accept_authority,
6
- "agent" => :proposer,
7
- "builder" => :generator,
8
- "runner" => :runner,
9
- }.freeze
10
-
11
- # Returns { role_name => kind_symbol }. When `roles:` is declared we use
12
- # exactly that; defaults are *not* layered in (declaring roles is an opt-in
13
- # to a fully user-defined vocabulary).
14
- def self.resolve(raw_roles)
15
- return DEFAULT_MAPPING if raw_roles.nil?
16
-
17
- raw_roles.to_h { |r| [r["name"], r["kind"].to_sym] }.freeze
18
- end
19
- end
20
- end
21
- end
@@ -1,24 +0,0 @@
1
- module Textus
2
- module Write
3
- # Shared gate for write verbs that require the caller to hold the
4
- # manifest's accept_authority role. Provides one method, expressed
5
- # as two early-returns rather than a ternary, so each failure mode
6
- # reads on its own line.
7
- module AuthorityGate
8
- def assert_accept_authority!(verb)
9
- return if @manifest.policy.role_kind(@call.role) == :accept_authority
10
-
11
- authority = @manifest.policy.roles_with_kind(:accept_authority).first
12
- if authority.nil?
13
- raise ProposalError.new(
14
- "no role with accept_authority kind is declared in this manifest; #{verb} is disabled",
15
- )
16
- end
17
-
18
- raise ProposalError.new(
19
- "only #{authority} role can #{verb} proposals; got '#{@call.role}'",
20
- )
21
- end
22
- end
23
- end
24
- end