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.
- checksums.yaml +4 -4
- data/ARCHITECTURE.md +2 -241
- data/CHANGELOG.md +221 -0
- data/README.md +89 -69
- data/SPEC.md +359 -212
- data/docs/conventions.md +42 -37
- data/lib/textus/boot.rb +122 -87
- data/lib/textus/cli/group/{refresh.rb → fetch.rb} +4 -4
- data/lib/textus/cli/verb/build.rb +1 -1
- data/lib/textus/cli/verb/fetch.rb +14 -0
- data/lib/textus/cli/verb/{refresh_stale.rb → fetch_stale.rb} +3 -3
- data/lib/textus/cli/verb/get.rb +1 -1
- data/lib/textus/cli/verb/hooks.rb +1 -1
- data/lib/textus/cli/verb/mcp_serve.rb +8 -3
- data/lib/textus/cli/verb/propose.rb +28 -0
- data/lib/textus/cli/verb/pulse.rb +12 -3
- data/lib/textus/cli/verb/put.rb +1 -1
- data/lib/textus/cli/verb/rule_list.rb +7 -7
- data/lib/textus/cli/verb/schema.rb +1 -1
- data/lib/textus/cli/verb.rb +3 -2
- data/lib/textus/cli.rb +2 -2
- data/lib/textus/container.rb +1 -2
- data/lib/textus/contract.rb +106 -0
- data/lib/textus/cursor_store.rb +24 -0
- data/lib/textus/dispatcher.rb +6 -4
- data/lib/textus/doctor/check/audit_log.rb +1 -1
- data/lib/textus/doctor/check/{refresh_locks.rb → fetch_locks.rb} +8 -8
- data/lib/textus/doctor/check/proposal_targets.rb +45 -0
- data/lib/textus/doctor/check/rule_ambiguity.rb +3 -3
- data/lib/textus/doctor.rb +2 -1
- data/lib/textus/domain/action.rb +3 -3
- data/lib/textus/domain/freshness/evaluator.rb +3 -3
- data/lib/textus/domain/freshness/policy.rb +2 -2
- data/lib/textus/domain/freshness.rb +7 -7
- data/lib/textus/domain/outcome.rb +2 -2
- data/lib/textus/domain/permission.rb +2 -10
- data/lib/textus/domain/policy/base_guards.rb +25 -0
- data/lib/textus/domain/policy/evaluation.rb +15 -0
- data/lib/textus/domain/policy/{refresh.rb → fetch.rb} +1 -1
- data/lib/textus/domain/policy/guard.rb +35 -0
- data/lib/textus/domain/policy/guard_factory.rb +40 -0
- data/lib/textus/domain/policy/predicates/author_held.rb +33 -0
- data/lib/textus/domain/policy/predicates/etag_match.rb +32 -0
- data/lib/textus/domain/policy/predicates/fresh_within.rb +58 -0
- data/lib/textus/domain/policy/predicates/registry.rb +39 -0
- data/lib/textus/domain/policy/predicates/schema_valid.rb +30 -19
- data/lib/textus/domain/policy/predicates/target_is_canon.rb +33 -0
- data/lib/textus/domain/policy/predicates/zone_writable_by.rb +39 -0
- data/lib/textus/domain/staleness/intake_check.rb +6 -6
- data/lib/textus/envelope.rb +2 -2
- data/lib/textus/errors.rb +25 -28
- data/lib/textus/hooks/event_bus.rb +4 -4
- data/lib/textus/init.rb +27 -18
- data/lib/textus/layout.rb +41 -0
- data/lib/textus/maintenance/key_delete_prefix.rb +9 -0
- data/lib/textus/maintenance/key_mv_prefix.rb +10 -0
- data/lib/textus/maintenance/migrate.rb +9 -0
- data/lib/textus/maintenance/rule_lint.rb +8 -0
- data/lib/textus/maintenance/zone_mv.rb +11 -1
- data/lib/textus/manifest/capabilities.rb +29 -0
- data/lib/textus/manifest/data.rb +14 -10
- data/lib/textus/manifest/policy.rb +37 -21
- data/lib/textus/manifest/rules.rb +16 -14
- data/lib/textus/manifest/schema.rb +48 -58
- data/lib/textus/manifest.rb +3 -3
- data/lib/textus/mcp/catalog.rb +72 -0
- data/lib/textus/mcp/server.rb +8 -5
- data/lib/textus/mcp/session.rb +3 -20
- data/lib/textus/mcp/tool_schemas.rb +6 -62
- data/lib/textus/mcp/tools.rb +4 -119
- data/lib/textus/ports/audit_log.rb +17 -15
- data/lib/textus/ports/audit_subscriber.rb +1 -1
- data/lib/textus/ports/build_lock.rb +1 -2
- data/lib/textus/ports/{refresh → fetch}/detached.rb +4 -4
- data/lib/textus/ports/{refresh → fetch}/lock.rb +2 -2
- data/lib/textus/projection.rb +1 -1
- data/lib/textus/read/audit.rb +3 -3
- data/lib/textus/read/boot.rb +6 -0
- data/lib/textus/read/freshness.rb +9 -9
- data/lib/textus/read/get.rb +16 -8
- data/lib/textus/read/{get_or_refresh.rb → get_or_fetch.rb} +11 -11
- data/lib/textus/read/list.rb +8 -0
- data/lib/textus/read/policy_explain.rb +14 -10
- data/lib/textus/read/pulse.rb +12 -4
- data/lib/textus/read/rules.rb +24 -0
- data/lib/textus/read/schema_envelope.rb +7 -0
- data/lib/textus/read/validator.rb +1 -1
- data/lib/textus/role.rb +6 -2
- data/lib/textus/schema/tools.rb +5 -5
- data/lib/textus/session.rb +24 -0
- data/lib/textus/store.rb +11 -0
- data/lib/textus/version.rb +1 -1
- data/lib/textus/write/accept.rb +19 -55
- data/lib/textus/write/delete.rb +14 -2
- data/lib/textus/write/{refresh_all.rb → fetch_all.rb} +14 -6
- data/lib/textus/write/{refresh_orchestrator.rb → fetch_orchestrator.rb} +14 -14
- data/lib/textus/write/{refresh_worker.rb → fetch_worker.rb} +29 -14
- data/lib/textus/write/mv.rb +15 -3
- data/lib/textus/write/propose.rb +46 -0
- data/lib/textus/write/put.rb +26 -2
- data/lib/textus/write/reject.rb +11 -5
- data/lib/textus.rb +4 -0
- metadata +36 -21
- data/lib/textus/cli/verb/refresh.rb +0 -14
- data/lib/textus/domain/authorizer.rb +0 -37
- data/lib/textus/domain/policy/predicates/accept_authority_signed.rb +0 -33
- data/lib/textus/domain/policy/promote.rb +0 -26
- data/lib/textus/domain/policy/promotion.rb +0 -57
- data/lib/textus/manifest/role_kinds.rb +0 -21
- 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.
|
|
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:
|
|
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/
|
|
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/
|
|
199
|
-
- lib/textus/domain/policy/
|
|
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/
|
|
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/
|
|
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
|