textus 0.54.2 → 0.55.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 +4 -4
- data/CHANGELOG.md +37 -0
- data/README.md +8 -1
- data/SPEC.md +27 -0
- data/docs/architecture/README.md +20 -8
- data/docs/reference/conventions.md +1 -1
- data/exe/textus +1 -1
- data/lib/textus/action/accept.rb +23 -21
- data/lib/textus/action/audit.rb +24 -61
- data/lib/textus/action/base.rb +9 -9
- data/lib/textus/action/blame.rb +18 -36
- data/lib/textus/action/boot.rb +2 -4
- data/lib/textus/action/data_mv.rb +20 -31
- data/lib/textus/action/deps.rb +3 -18
- data/lib/textus/action/doctor.rb +2 -9
- data/lib/textus/action/drain.rb +11 -19
- data/lib/textus/action/enqueue.rb +14 -30
- data/lib/textus/action/get.rb +12 -56
- data/lib/textus/action/ingest.rb +74 -78
- data/lib/textus/action/jobs.rb +6 -15
- data/lib/textus/action/key_delete.rb +6 -16
- data/lib/textus/action/key_delete_prefix.rb +8 -17
- data/lib/textus/action/key_mv.rb +54 -61
- data/lib/textus/action/key_mv_prefix.rb +13 -22
- data/lib/textus/action/list.rb +7 -21
- data/lib/textus/action/propose.rb +16 -26
- data/lib/textus/action/published.rb +3 -5
- data/lib/textus/action/pulse.rb +19 -26
- data/lib/textus/action/put.rb +15 -29
- data/lib/textus/action/rdeps.rb +3 -18
- data/lib/textus/action/reject.rb +12 -21
- data/lib/textus/action/rule_explain.rb +12 -22
- data/lib/textus/action/rule_lint.rb +10 -16
- data/lib/textus/action/rule_list.rb +5 -9
- data/lib/textus/action/schema_envelope.rb +3 -10
- data/lib/textus/action/uid.rb +3 -17
- data/lib/textus/action/where.rb +3 -18
- data/lib/textus/boot.rb +7 -15
- data/lib/textus/contract/arg.rb +10 -0
- data/lib/textus/contract/dsl.rb +88 -0
- data/lib/textus/contract/spec.rb +25 -0
- data/lib/textus/contract.rb +0 -162
- data/lib/textus/doctor/check/audit_log.rb +2 -2
- data/lib/textus/doctor/check/generator_drift.rb +2 -2
- data/lib/textus/doctor/check/orphaned_publish_targets.rb +3 -3
- data/lib/textus/doctor/check/protocol_version.rb +2 -2
- data/lib/textus/doctor/check/raw_asset_paths.rb +1 -1
- data/lib/textus/doctor/check/schema_parse_error.rb +1 -1
- data/lib/textus/doctor/check/schema_violations.rb +2 -2
- data/lib/textus/doctor/check/schemas.rb +1 -1
- data/lib/textus/doctor/check/sentinels.rb +4 -4
- data/lib/textus/doctor/check/templates.rb +1 -1
- data/lib/textus/doctor/check/unowned_schema_fields.rb +1 -1
- data/lib/textus/doctor/check.rb +4 -7
- data/lib/textus/doctor.rb +1 -1
- data/lib/textus/errors.rb +6 -0
- data/lib/textus/format/base.rb +0 -4
- data/lib/textus/format/json.rb +5 -6
- data/lib/textus/format/markdown.rb +5 -6
- data/lib/textus/format/shared.rb +17 -0
- data/lib/textus/format/text.rb +5 -4
- data/lib/textus/format/yaml.rb +30 -6
- data/lib/textus/format.rb +6 -0
- data/lib/textus/gate/auth.rb +2 -17
- data/lib/textus/gate/binder.rb +50 -0
- data/lib/textus/gate.rb +64 -88
- data/lib/textus/init.rb +2 -4
- data/lib/textus/jobs.rb +3 -9
- data/lib/textus/manifest/capabilities.rb +3 -3
- data/lib/textus/manifest/entry/base.rb +1 -1
- data/lib/textus/manifest/entry/publish/subtree_mirror.rb +2 -2
- data/lib/textus/manifest/entry/publish/to_paths.rb +1 -1
- data/lib/textus/manifest/schema/semantics/cross_field.rb +53 -0
- data/lib/textus/manifest/schema/semantics/invariants.rb +125 -0
- data/lib/textus/manifest/schema/semantics/migration.rb +83 -0
- data/lib/textus/manifest/schema/semantics.rb +11 -216
- data/lib/textus/meta.rb +54 -0
- data/lib/textus/{ports → port}/audit_log.rb +44 -4
- data/lib/textus/{ports → port}/build_lock.rb +2 -2
- data/lib/textus/{ports → port}/clock.rb +1 -1
- data/lib/textus/{ports → port}/publisher.rb +5 -5
- data/lib/textus/{ports → port}/sentinel_store.rb +3 -3
- data/lib/textus/{ports → port}/storage/file_stat.rb +1 -1
- data/lib/textus/{ports → port}/storage/file_store.rb +2 -2
- data/lib/textus/port/store.rb +93 -0
- data/lib/textus/{ports → port}/watcher_lock.rb +3 -3
- data/lib/textus/produce/engine.rb +1 -1
- data/lib/textus/schema/tools.rb +11 -7
- data/lib/textus/store/compositor.rb +34 -0
- data/lib/textus/store/container.rb +43 -0
- data/lib/textus/store/cursor.rb +26 -0
- data/lib/textus/store/envelope/reader.rb +43 -0
- data/lib/textus/store/envelope/writer.rb +195 -0
- data/lib/textus/store/geometry.rb +81 -0
- data/lib/textus/store/index/builder.rb +74 -0
- data/lib/textus/store/index/lookup.rb +60 -0
- data/lib/textus/store/jobs/base.rb +13 -0
- data/lib/textus/store/jobs/index.rb +15 -0
- data/lib/textus/store/jobs/materialize.rb +15 -0
- data/lib/textus/store/jobs/plan.rb +11 -0
- data/lib/textus/store/jobs/planner.rb +104 -0
- data/lib/textus/store/jobs/queue.rb +154 -0
- data/lib/textus/store/jobs/registry.rb +19 -0
- data/lib/textus/store/jobs/retention.rb +50 -0
- data/lib/textus/store/jobs/sweep.rb +21 -0
- data/lib/textus/store/jobs/worker.rb +64 -0
- data/lib/textus/store/session.rb +37 -0
- data/lib/textus/store.rb +21 -13
- data/lib/textus/{surfaces → surface}/cli/group/data.rb +1 -1
- data/lib/textus/{surfaces → surface}/cli/group/key.rb +1 -1
- data/lib/textus/{surfaces → surface}/cli/group/mcp.rb +1 -1
- data/lib/textus/{surfaces → surface}/cli/group/rule.rb +1 -1
- data/lib/textus/{surfaces → surface}/cli/group/schema.rb +1 -1
- data/lib/textus/{surfaces → surface}/cli/group.rb +2 -2
- data/lib/textus/{surfaces → surface}/cli/runner.rb +10 -63
- data/lib/textus/surface/cli/sources.rb +41 -0
- data/lib/textus/{surfaces → surface}/cli/verb/doctor.rb +4 -6
- data/lib/textus/{surfaces → surface}/cli/verb/get.rb +4 -4
- data/lib/textus/{surfaces → surface}/cli/verb/init.rb +1 -1
- data/lib/textus/{surfaces → surface}/cli/verb/mcp_serve.rb +3 -3
- data/lib/textus/{surfaces → surface}/cli/verb/put.rb +6 -11
- data/lib/textus/{surfaces → surface}/cli/verb/schema_diff.rb +1 -1
- data/lib/textus/{surfaces → surface}/cli/verb/schema_init.rb +1 -1
- data/lib/textus/{surfaces → surface}/cli/verb/schema_migrate.rb +1 -1
- data/lib/textus/{surfaces → surface}/cli/verb/watch.rb +2 -2
- data/lib/textus/{surfaces → surface}/cli/verb.rb +3 -8
- data/lib/textus/{surfaces → surface}/cli.rb +1 -1
- data/lib/textus/{surfaces → surface}/mcp/catalog.rb +9 -26
- data/lib/textus/{surfaces → surface}/mcp/errors.rb +1 -1
- data/lib/textus/{surfaces → surface}/mcp/server.rb +5 -5
- data/lib/textus/{surfaces → surface}/mcp.rb +2 -2
- data/lib/textus/surface/projector.rb +27 -0
- data/lib/textus/{surfaces → surface}/role_scope.rb +1 -1
- data/lib/textus/{surfaces → surface}/watcher.rb +8 -8
- data/lib/textus/value/call.rb +30 -0
- data/lib/textus/value/command.rb +16 -0
- data/lib/textus/value/envelope.rb +89 -0
- data/lib/textus/value/etag.rb +39 -0
- data/lib/textus/value/result.rb +26 -0
- data/lib/textus/value/role.rb +38 -0
- data/lib/textus/value/types.rb +13 -0
- data/lib/textus/{uid.rb → value/uid.rb} +9 -7
- data/lib/textus/version.rb +1 -1
- data/lib/textus/workflow/loader.rb +4 -4
- data/lib/textus/workflow/runner.rb +4 -18
- data/lib/textus.rb +9 -10
- metadata +100 -63
- data/lib/textus/action/write_verb.rb +0 -44
- data/lib/textus/call.rb +0 -28
- data/lib/textus/command.rb +0 -41
- data/lib/textus/container.rb +0 -26
- data/lib/textus/contract/around.rb +0 -29
- data/lib/textus/contract/binder.rb +0 -88
- data/lib/textus/contract/resources/build_lock.rb +0 -17
- data/lib/textus/contract/resources/cursor.rb +0 -26
- data/lib/textus/contract/sources.rb +0 -39
- data/lib/textus/contract/view.rb +0 -15
- data/lib/textus/cursor_store.rb +0 -24
- data/lib/textus/envelope/reader.rb +0 -46
- data/lib/textus/envelope/writer.rb +0 -209
- data/lib/textus/envelope.rb +0 -79
- data/lib/textus/etag.rb +0 -36
- data/lib/textus/jobs/base.rb +0 -23
- data/lib/textus/jobs/materialize.rb +0 -20
- data/lib/textus/jobs/plan.rb +0 -9
- data/lib/textus/jobs/planner.rb +0 -101
- data/lib/textus/jobs/retention.rb +0 -48
- data/lib/textus/jobs/sweep.rb +0 -27
- data/lib/textus/jobs/worker.rb +0 -67
- data/lib/textus/layout.rb +0 -91
- data/lib/textus/ports/job_store/job.rb +0 -65
- data/lib/textus/ports/job_store.rb +0 -123
- data/lib/textus/ports/raw_index.rb +0 -61
- data/lib/textus/role.rb +0 -36
- data/lib/textus/session.rb +0 -35
- data/lib/textus/types.rb +0 -15
|
@@ -57,12 +57,12 @@ module Textus
|
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
def built_in_publish(key, data, ctx)
|
|
60
|
-
normalized =
|
|
60
|
+
normalized = Textus::Format.data_to_payload(data, ctx.entry.format)
|
|
61
61
|
Gate::Auth.new(@container).check_action!(action: :converge, actor: @call.role, key: key)
|
|
62
|
-
Envelope::Writer.from(container: @container, call: @call).put(
|
|
62
|
+
Textus::Store::Envelope::Writer.from(container: @container, call: @call).put(
|
|
63
63
|
key,
|
|
64
64
|
mentry: ctx.entry,
|
|
65
|
-
payload: Envelope::Writer::Payload.new(**normalized),
|
|
65
|
+
payload: Textus::Store::Envelope::Writer::Payload.new(**normalized),
|
|
66
66
|
)
|
|
67
67
|
publish_external(key, ctx)
|
|
68
68
|
end
|
|
@@ -74,26 +74,12 @@ module Textus
|
|
|
74
74
|
entry_path = @container.manifest.resolver.resolve(key).path
|
|
75
75
|
return unless entry.publish_tree || File.exist?(entry_path)
|
|
76
76
|
|
|
77
|
-
reader = Textus::Envelope::Reader.from(container: @container)
|
|
77
|
+
reader = Textus::Store::Envelope::Reader.from(container: @container)
|
|
78
78
|
pctx = Textus::Manifest::Entry::Base::PublishContext.new(
|
|
79
79
|
container: @container, call: @call, reader: reader.method(:read),
|
|
80
80
|
)
|
|
81
81
|
entry.publish_via(pctx)
|
|
82
82
|
end
|
|
83
|
-
|
|
84
|
-
def normalize(data, format)
|
|
85
|
-
return { meta: {}, body: "", content: nil } if data.nil?
|
|
86
|
-
|
|
87
|
-
data = data.transform_keys(&:to_s) if data.is_a?(Hash)
|
|
88
|
-
case format.to_s
|
|
89
|
-
when "markdown", "text"
|
|
90
|
-
{ meta: data["_meta"] || {}, body: (data["body"] || "").to_s, content: nil }
|
|
91
|
-
when "json", "yaml"
|
|
92
|
-
{ meta: data["_meta"] || {}, body: nil, content: data["content"] || data }
|
|
93
|
-
else
|
|
94
|
-
{ meta: {}, body: data.to_s, content: nil }
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
83
|
end
|
|
98
84
|
end
|
|
99
85
|
end
|
data/lib/textus.rb
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
require "zeitwerk"
|
|
2
|
+
require "dry-monads"
|
|
2
3
|
require_relative "textus/version"
|
|
3
4
|
require_relative "textus/errors"
|
|
4
|
-
require_relative "textus/
|
|
5
|
-
require_relative "textus/
|
|
5
|
+
require_relative "textus/surface/mcp"
|
|
6
|
+
require_relative "textus/surface/mcp/errors"
|
|
6
7
|
|
|
7
8
|
loader = Zeitwerk::Loader.for_gem
|
|
8
9
|
loader.inflector.inflect(
|
|
@@ -14,8 +15,8 @@ loader.inflector.inflect(
|
|
|
14
15
|
"dsl" => "DSL",
|
|
15
16
|
)
|
|
16
17
|
loader.ignore(File.expand_path("textus/errors.rb", __dir__))
|
|
17
|
-
loader.ignore(File.expand_path("textus/
|
|
18
|
-
loader.ignore(File.expand_path("textus/
|
|
18
|
+
loader.ignore(File.expand_path("textus/surface/mcp.rb", __dir__))
|
|
19
|
+
loader.ignore(File.expand_path("textus/surface/mcp/errors.rb", __dir__))
|
|
19
20
|
loader.ignore(File.expand_path("textus/workflow/errors.rb", __dir__))
|
|
20
21
|
# Scaffold sources copied verbatim into user stores by `textus init`. They are
|
|
21
22
|
# templates for user-owned step classes, not gem constants — Zeitwerk must not
|
|
@@ -64,15 +65,14 @@ Textus::Boot::CLI_VERBS = Textus::Boot.build_cli_verbs.freeze
|
|
|
64
65
|
|
|
65
66
|
# Dynamic verb methods on Store (deferred after VERBS is defined).
|
|
66
67
|
Textus::Action::VERBS.each_key do |verb|
|
|
67
|
-
Textus::Store.define_method(verb) do |*args, role: Textus::Role::DEFAULT, **kwargs|
|
|
68
|
+
Textus::Store.define_method(verb) do |*args, role: Textus::Value::Role::DEFAULT, **kwargs|
|
|
68
69
|
as(role).public_send(verb, *args, **kwargs)
|
|
69
70
|
end
|
|
70
71
|
|
|
71
|
-
Textus::
|
|
72
|
+
Textus::Surface::RoleScope.define_method(verb) do |*args, **kwargs|
|
|
72
73
|
klass = Textus::Action::VERBS[verb]
|
|
73
|
-
cmd_class = Textus::Gate::VERB_COMMAND[verb]
|
|
74
74
|
inputs = if klass.respond_to?(:contract?) && klass.contract?
|
|
75
|
-
Textus::
|
|
75
|
+
Textus::Gate::Binder.inputs_from_ordered(klass.contract, args, kwargs)
|
|
76
76
|
else
|
|
77
77
|
kwargs.transform_keys(&:to_sym)
|
|
78
78
|
end
|
|
@@ -84,8 +84,7 @@ Textus::Action::VERBS.each_key do |verb|
|
|
|
84
84
|
@role
|
|
85
85
|
end
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
@container.gate.dispatch(cmd_class.new(**filled), correlation_id: @correlation_id)
|
|
87
|
+
@container.gate.dispatch(spec: klass.contract, inputs: inputs, role: role_value, correlation_id: @correlation_id)
|
|
89
88
|
end
|
|
90
89
|
end
|
|
91
90
|
|
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.55.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Patrick
|
|
@@ -23,6 +23,20 @@ dependencies:
|
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '3.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: dry-monads
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '1.6'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '1.6'
|
|
26
40
|
- !ruby/object:Gem::Dependency
|
|
27
41
|
name: dry-schema
|
|
28
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -93,6 +107,20 @@ dependencies:
|
|
|
93
107
|
- - ">="
|
|
94
108
|
- !ruby/object:Gem::Version
|
|
95
109
|
version: '3.2'
|
|
110
|
+
- !ruby/object:Gem::Dependency
|
|
111
|
+
name: sqlite3
|
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
|
113
|
+
requirements:
|
|
114
|
+
- - "~>"
|
|
115
|
+
- !ruby/object:Gem::Version
|
|
116
|
+
version: '2.0'
|
|
117
|
+
type: :runtime
|
|
118
|
+
prerelease: false
|
|
119
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
120
|
+
requirements:
|
|
121
|
+
- - "~>"
|
|
122
|
+
- !ruby/object:Gem::Version
|
|
123
|
+
version: '2.0'
|
|
96
124
|
- !ruby/object:Gem::Dependency
|
|
97
125
|
name: zeitwerk
|
|
98
126
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -182,18 +210,11 @@ files:
|
|
|
182
210
|
- lib/textus/action/schema_envelope.rb
|
|
183
211
|
- lib/textus/action/uid.rb
|
|
184
212
|
- lib/textus/action/where.rb
|
|
185
|
-
- lib/textus/action/write_verb.rb
|
|
186
213
|
- lib/textus/boot.rb
|
|
187
|
-
- lib/textus/call.rb
|
|
188
|
-
- lib/textus/command.rb
|
|
189
|
-
- lib/textus/container.rb
|
|
190
214
|
- lib/textus/contract.rb
|
|
191
|
-
- lib/textus/contract/
|
|
192
|
-
- lib/textus/contract/
|
|
193
|
-
- lib/textus/contract/
|
|
194
|
-
- lib/textus/contract/resources/cursor.rb
|
|
195
|
-
- lib/textus/contract/sources.rb
|
|
196
|
-
- lib/textus/contract/view.rb
|
|
215
|
+
- lib/textus/contract/arg.rb
|
|
216
|
+
- lib/textus/contract/dsl.rb
|
|
217
|
+
- lib/textus/contract/spec.rb
|
|
197
218
|
- lib/textus/core/duration.rb
|
|
198
219
|
- lib/textus/core/freshness.rb
|
|
199
220
|
- lib/textus/core/freshness/evaluator.rb
|
|
@@ -201,7 +222,6 @@ files:
|
|
|
201
222
|
- lib/textus/core/retention.rb
|
|
202
223
|
- lib/textus/core/retention/sweep.rb
|
|
203
224
|
- lib/textus/core/sentinel.rb
|
|
204
|
-
- lib/textus/cursor_store.rb
|
|
205
225
|
- lib/textus/doctor.rb
|
|
206
226
|
- lib/textus/doctor/check.rb
|
|
207
227
|
- lib/textus/doctor/check/audit_log.rb
|
|
@@ -223,33 +243,23 @@ files:
|
|
|
223
243
|
- lib/textus/doctor/check/templates.rb
|
|
224
244
|
- lib/textus/doctor/check/unowned_schema_fields.rb
|
|
225
245
|
- lib/textus/doctor/validator.rb
|
|
226
|
-
- lib/textus/envelope.rb
|
|
227
|
-
- lib/textus/envelope/reader.rb
|
|
228
|
-
- lib/textus/envelope/writer.rb
|
|
229
246
|
- lib/textus/errors.rb
|
|
230
|
-
- lib/textus/etag.rb
|
|
231
247
|
- lib/textus/format.rb
|
|
232
248
|
- lib/textus/format/base.rb
|
|
233
249
|
- lib/textus/format/json.rb
|
|
234
250
|
- lib/textus/format/markdown.rb
|
|
251
|
+
- lib/textus/format/shared.rb
|
|
235
252
|
- lib/textus/format/text.rb
|
|
236
253
|
- lib/textus/format/yaml.rb
|
|
237
254
|
- lib/textus/gate.rb
|
|
238
255
|
- lib/textus/gate/auth.rb
|
|
256
|
+
- lib/textus/gate/binder.rb
|
|
239
257
|
- lib/textus/init.rb
|
|
240
258
|
- lib/textus/jobs.rb
|
|
241
|
-
- lib/textus/jobs/base.rb
|
|
242
|
-
- lib/textus/jobs/materialize.rb
|
|
243
|
-
- lib/textus/jobs/plan.rb
|
|
244
|
-
- lib/textus/jobs/planner.rb
|
|
245
|
-
- lib/textus/jobs/retention.rb
|
|
246
|
-
- lib/textus/jobs/sweep.rb
|
|
247
|
-
- lib/textus/jobs/worker.rb
|
|
248
259
|
- lib/textus/key/distance.rb
|
|
249
260
|
- lib/textus/key/grammar.rb
|
|
250
261
|
- lib/textus/key/matching.rb
|
|
251
262
|
- lib/textus/key/path.rb
|
|
252
|
-
- lib/textus/layout.rb
|
|
253
263
|
- lib/textus/manifest.rb
|
|
254
264
|
- lib/textus/manifest/capabilities.rb
|
|
255
265
|
- lib/textus/manifest/data.rb
|
|
@@ -283,53 +293,80 @@ files:
|
|
|
283
293
|
- lib/textus/manifest/schema/contract.rb
|
|
284
294
|
- lib/textus/manifest/schema/keys.rb
|
|
285
295
|
- lib/textus/manifest/schema/semantics.rb
|
|
296
|
+
- lib/textus/manifest/schema/semantics/cross_field.rb
|
|
297
|
+
- lib/textus/manifest/schema/semantics/invariants.rb
|
|
298
|
+
- lib/textus/manifest/schema/semantics/migration.rb
|
|
286
299
|
- lib/textus/manifest/schema/validator.rb
|
|
287
300
|
- lib/textus/manifest/schema/vocabulary.rb
|
|
288
|
-
- lib/textus/
|
|
289
|
-
- lib/textus/
|
|
290
|
-
- lib/textus/
|
|
291
|
-
- lib/textus/
|
|
292
|
-
- lib/textus/
|
|
293
|
-
- lib/textus/
|
|
294
|
-
- lib/textus/
|
|
295
|
-
- lib/textus/
|
|
296
|
-
- lib/textus/
|
|
297
|
-
- lib/textus/
|
|
298
|
-
- lib/textus/ports/watcher_lock.rb
|
|
301
|
+
- lib/textus/meta.rb
|
|
302
|
+
- lib/textus/port/audit_log.rb
|
|
303
|
+
- lib/textus/port/build_lock.rb
|
|
304
|
+
- lib/textus/port/clock.rb
|
|
305
|
+
- lib/textus/port/publisher.rb
|
|
306
|
+
- lib/textus/port/sentinel_store.rb
|
|
307
|
+
- lib/textus/port/storage/file_stat.rb
|
|
308
|
+
- lib/textus/port/storage/file_store.rb
|
|
309
|
+
- lib/textus/port/store.rb
|
|
310
|
+
- lib/textus/port/watcher_lock.rb
|
|
299
311
|
- lib/textus/produce/engine.rb
|
|
300
312
|
- lib/textus/produce/render.rb
|
|
301
|
-
- lib/textus/role.rb
|
|
302
313
|
- lib/textus/schema.rb
|
|
303
314
|
- lib/textus/schema/tools.rb
|
|
304
315
|
- lib/textus/schemas.rb
|
|
305
|
-
- lib/textus/session.rb
|
|
306
316
|
- lib/textus/store.rb
|
|
307
|
-
- lib/textus/
|
|
308
|
-
- lib/textus/
|
|
309
|
-
- lib/textus/
|
|
310
|
-
- lib/textus/
|
|
311
|
-
- lib/textus/
|
|
312
|
-
- lib/textus/
|
|
313
|
-
- lib/textus/
|
|
314
|
-
- lib/textus/
|
|
315
|
-
- lib/textus/
|
|
316
|
-
- lib/textus/
|
|
317
|
-
- lib/textus/
|
|
318
|
-
- lib/textus/
|
|
319
|
-
- lib/textus/
|
|
320
|
-
- lib/textus/
|
|
321
|
-
- lib/textus/
|
|
322
|
-
- lib/textus/
|
|
323
|
-
- lib/textus/
|
|
324
|
-
- lib/textus/
|
|
325
|
-
- lib/textus/
|
|
326
|
-
- lib/textus/
|
|
327
|
-
- lib/textus/
|
|
328
|
-
- lib/textus/
|
|
329
|
-
- lib/textus/
|
|
330
|
-
- lib/textus/
|
|
331
|
-
- lib/textus/
|
|
332
|
-
- lib/textus/
|
|
317
|
+
- lib/textus/store/compositor.rb
|
|
318
|
+
- lib/textus/store/container.rb
|
|
319
|
+
- lib/textus/store/cursor.rb
|
|
320
|
+
- lib/textus/store/envelope/reader.rb
|
|
321
|
+
- lib/textus/store/envelope/writer.rb
|
|
322
|
+
- lib/textus/store/geometry.rb
|
|
323
|
+
- lib/textus/store/index/builder.rb
|
|
324
|
+
- lib/textus/store/index/lookup.rb
|
|
325
|
+
- lib/textus/store/jobs/base.rb
|
|
326
|
+
- lib/textus/store/jobs/index.rb
|
|
327
|
+
- lib/textus/store/jobs/materialize.rb
|
|
328
|
+
- lib/textus/store/jobs/plan.rb
|
|
329
|
+
- lib/textus/store/jobs/planner.rb
|
|
330
|
+
- lib/textus/store/jobs/queue.rb
|
|
331
|
+
- lib/textus/store/jobs/registry.rb
|
|
332
|
+
- lib/textus/store/jobs/retention.rb
|
|
333
|
+
- lib/textus/store/jobs/sweep.rb
|
|
334
|
+
- lib/textus/store/jobs/worker.rb
|
|
335
|
+
- lib/textus/store/session.rb
|
|
336
|
+
- lib/textus/surface/cli.rb
|
|
337
|
+
- lib/textus/surface/cli/group.rb
|
|
338
|
+
- lib/textus/surface/cli/group/data.rb
|
|
339
|
+
- lib/textus/surface/cli/group/key.rb
|
|
340
|
+
- lib/textus/surface/cli/group/mcp.rb
|
|
341
|
+
- lib/textus/surface/cli/group/rule.rb
|
|
342
|
+
- lib/textus/surface/cli/group/schema.rb
|
|
343
|
+
- lib/textus/surface/cli/runner.rb
|
|
344
|
+
- lib/textus/surface/cli/sources.rb
|
|
345
|
+
- lib/textus/surface/cli/verb.rb
|
|
346
|
+
- lib/textus/surface/cli/verb/doctor.rb
|
|
347
|
+
- lib/textus/surface/cli/verb/get.rb
|
|
348
|
+
- lib/textus/surface/cli/verb/init.rb
|
|
349
|
+
- lib/textus/surface/cli/verb/mcp_serve.rb
|
|
350
|
+
- lib/textus/surface/cli/verb/put.rb
|
|
351
|
+
- lib/textus/surface/cli/verb/schema_diff.rb
|
|
352
|
+
- lib/textus/surface/cli/verb/schema_init.rb
|
|
353
|
+
- lib/textus/surface/cli/verb/schema_migrate.rb
|
|
354
|
+
- lib/textus/surface/cli/verb/watch.rb
|
|
355
|
+
- lib/textus/surface/mcp.rb
|
|
356
|
+
- lib/textus/surface/mcp/catalog.rb
|
|
357
|
+
- lib/textus/surface/mcp/errors.rb
|
|
358
|
+
- lib/textus/surface/mcp/server.rb
|
|
359
|
+
- lib/textus/surface/projector.rb
|
|
360
|
+
- lib/textus/surface/role_scope.rb
|
|
361
|
+
- lib/textus/surface/watcher.rb
|
|
362
|
+
- lib/textus/value/call.rb
|
|
363
|
+
- lib/textus/value/command.rb
|
|
364
|
+
- lib/textus/value/envelope.rb
|
|
365
|
+
- lib/textus/value/etag.rb
|
|
366
|
+
- lib/textus/value/result.rb
|
|
367
|
+
- lib/textus/value/role.rb
|
|
368
|
+
- lib/textus/value/types.rb
|
|
369
|
+
- lib/textus/value/uid.rb
|
|
333
370
|
- lib/textus/version.rb
|
|
334
371
|
- lib/textus/workflow.rb
|
|
335
372
|
- lib/textus/workflow/collector.rb
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Textus
|
|
4
|
-
module Action
|
|
5
|
-
class WriteVerb < Base
|
|
6
|
-
private
|
|
7
|
-
|
|
8
|
-
def auth(container)
|
|
9
|
-
Textus::Gate::Auth.new(container)
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def writer(container, call)
|
|
13
|
-
Textus::Envelope::Writer.from(container: container, call: call)
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def reader(container)
|
|
17
|
-
Textus::Envelope::Reader.from(container: container)
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def run_with_cascade(target_key, container:, call:)
|
|
21
|
-
result = yield
|
|
22
|
-
cascade_to_rdeps(target_key, container, call) if target_key
|
|
23
|
-
result
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def cascade_to_rdeps(key, container, call)
|
|
27
|
-
rdeps = Textus::Action::Rdeps.new(key: key).call(container: container, call: call).fetch("rdeps", [])
|
|
28
|
-
producible = rdeps.select { |dep_key| producible?(dep_key, container) }
|
|
29
|
-
return if producible.empty?
|
|
30
|
-
|
|
31
|
-
producible.each do |dep_key|
|
|
32
|
-
Textus::Jobs::Materialize.new(key: dep_key).call(container:, call:)
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def producible?(key, container)
|
|
37
|
-
entry = container.manifest.resolver.resolve(key).entry
|
|
38
|
-
!entry.publish_tree.nil?
|
|
39
|
-
rescue Textus::Error
|
|
40
|
-
false
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|
data/lib/textus/call.rb
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
require "securerandom"
|
|
2
|
-
|
|
3
|
-
module Textus
|
|
4
|
-
# Immutable per-invocation value. Carries who is acting (role), the
|
|
5
|
-
# request correlation id, the wall clock, and the dry_run flag — the
|
|
6
|
-
# bits Use Cases need that are not part of the Container.
|
|
7
|
-
Call = Data.define(:role, :correlation_id, :now, :dry_run) do
|
|
8
|
-
def self.build(role:, correlation_id: nil, now: nil, dry_run: false)
|
|
9
|
-
new(
|
|
10
|
-
role: role.to_s,
|
|
11
|
-
correlation_id: correlation_id || SecureRandom.uuid,
|
|
12
|
-
now: now || Textus::Ports::Clock.new.now,
|
|
13
|
-
dry_run: dry_run,
|
|
14
|
-
)
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def dry_run? = dry_run
|
|
18
|
-
|
|
19
|
-
def with_role(new_role)
|
|
20
|
-
self.class.new(
|
|
21
|
-
role: new_role.to_s,
|
|
22
|
-
correlation_id: correlation_id,
|
|
23
|
-
now: now,
|
|
24
|
-
dry_run: dry_run,
|
|
25
|
-
)
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
data/lib/textus/command.rb
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
module Textus
|
|
2
|
-
module Command
|
|
3
|
-
Get = Data.define(:key, :role)
|
|
4
|
-
Put = Data.define(:key, :meta, :body, :content, :if_etag, :role)
|
|
5
|
-
Propose = Data.define(:key, :meta, :body, :content, :role, :pending_key) do
|
|
6
|
-
def initialize(key:, role:, meta: nil, body: nil, content: nil, pending_key: nil)
|
|
7
|
-
super
|
|
8
|
-
end
|
|
9
|
-
end
|
|
10
|
-
KeyDelete = Data.define(:key, :if_etag, :role)
|
|
11
|
-
KeyMv = Data.define(:old_key, :new_key, :dry_run, :role)
|
|
12
|
-
Accept = Data.define(:pending_key, :role)
|
|
13
|
-
Reject = Data.define(:pending_key, :role)
|
|
14
|
-
Enqueue = Data.define(:type, :args, :role)
|
|
15
|
-
Ingest = Data.define(:kind, :slug, :url, :path, :zone, :label, :role) do
|
|
16
|
-
def initialize(kind:, slug:, role:, url: nil, path: nil, zone: nil, label: nil)
|
|
17
|
-
super
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
List = Data.define(:prefix, :lane, :role)
|
|
21
|
-
Where = Data.define(:key, :role)
|
|
22
|
-
Uid = Data.define(:key, :role)
|
|
23
|
-
Blame = Data.define(:key, :limit, :role)
|
|
24
|
-
Audit = Data.define(:key, :lane, :verb, :since, :seq_since, :correlation_id, :limit, :role)
|
|
25
|
-
Deps = Data.define(:key, :role)
|
|
26
|
-
Rdeps = Data.define(:key, :role)
|
|
27
|
-
Pulse = Data.define(:since, :role)
|
|
28
|
-
RuleExplain = Data.define(:key, :detail, :role)
|
|
29
|
-
RuleList = Data.define(:role)
|
|
30
|
-
RuleLint = Data.define(:candidate_yaml, :role)
|
|
31
|
-
Published = Data.define(:role)
|
|
32
|
-
SchemaShow = Data.define(:key, :role)
|
|
33
|
-
Doctor = Data.define(:checks, :role)
|
|
34
|
-
Boot = Data.define(:role)
|
|
35
|
-
Jobs = Data.define(:state, :action, :job_id, :role)
|
|
36
|
-
DataMv = Data.define(:from, :to, :dry_run, :role)
|
|
37
|
-
KeyMvPrefix = Data.define(:from_prefix, :to_prefix, :dry_run, :role)
|
|
38
|
-
KeyDeletePrefix = Data.define(:prefix, :dry_run, :role)
|
|
39
|
-
Drain = Data.define(:prefix, :lane, :role)
|
|
40
|
-
end
|
|
41
|
-
end
|
data/lib/textus/container.rb
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "dry-struct"
|
|
4
|
-
|
|
5
|
-
module Textus
|
|
6
|
-
# Single capability record handed to every use case. Replaces the
|
|
7
|
-
# ReadCaps/WriteCaps/HookCaps trio from 0.26.x. Built once per Store
|
|
8
|
-
# (see Store#initialize); Store delegates its readers to this record,
|
|
9
|
-
# so this struct is the single source of truth for the field set.
|
|
10
|
-
class Container < Dry::Struct
|
|
11
|
-
attribute :manifest, Types::Any
|
|
12
|
-
attribute :file_store, Types::Any
|
|
13
|
-
attribute :schemas, Types::Any
|
|
14
|
-
attribute :root, Types::String
|
|
15
|
-
attribute :audit_log, Types::Any
|
|
16
|
-
attribute :workflows, Types::Any
|
|
17
|
-
attribute :gate, Types::Any
|
|
18
|
-
|
|
19
|
-
def with(**attrs) = self.class.new(to_h.merge(attrs))
|
|
20
|
-
|
|
21
|
-
def initialize(*)
|
|
22
|
-
super
|
|
23
|
-
freeze
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
module Textus
|
|
2
|
-
module Contract
|
|
3
|
-
# Registry of named, stateful wrappers a verb may declare via `around :name`.
|
|
4
|
-
# A resource implements
|
|
5
|
-
# `wrap(scope:, inputs:, session:) { |effective_inputs| ... }`:
|
|
6
|
-
# it may adjust the inputs before the call and post-process the result after
|
|
7
|
-
# — exactly what build's lock and pulse's cursor need, without a hand-authored
|
|
8
|
-
# CLI class (ADR 0068). `session:` is the dispatching session (nil for the
|
|
9
|
-
# sessionless CLI/Ruby surfaces, present for MCP), so a session-aware resource
|
|
10
|
-
# like the cursor can defer to the session's own state instead of its file.
|
|
11
|
-
module Around
|
|
12
|
-
@registry = {}
|
|
13
|
-
|
|
14
|
-
module_function
|
|
15
|
-
|
|
16
|
-
def register(name, resource)
|
|
17
|
-
@registry[name] = resource
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def fetch(name)
|
|
21
|
-
@registry.fetch(name) { raise "no around resource registered: #{name.inspect}" }
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def with(name, scope:, inputs:, session: nil, &call)
|
|
25
|
-
fetch(name).wrap(scope: scope, inputs: inputs, session: session, &call)
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
module Textus
|
|
2
|
-
module Contract
|
|
3
|
-
# Raised when a required arg is absent from the bound input. Surface
|
|
4
|
-
# adapters translate this to their native error (MCP ToolError, CLI
|
|
5
|
-
# UsageError); a direct Ruby call lets it surface as-is.
|
|
6
|
-
class MissingArgs < Textus::Error
|
|
7
|
-
attr_reader :spec, :missing
|
|
8
|
-
|
|
9
|
-
def initialize(spec, missing)
|
|
10
|
-
@spec = spec
|
|
11
|
-
@missing = missing
|
|
12
|
-
super("missing_args", "#{spec.verb}: missing #{missing.map(&:wire).join(", ")}")
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
# The single argument binder for every surface (spike: collapses the three
|
|
17
|
-
# historical implementations — MCP::Catalog.map_args, CLI::Runner.call_args,
|
|
18
|
-
# and RoleScope's default-injection loop — into one algorithm).
|
|
19
|
-
#
|
|
20
|
-
# Input is a uniform `inputs` hash keyed by arg NAME (the use-case kwarg
|
|
21
|
-
# name, never the wire name): each surface normalizes its own raw transport
|
|
22
|
-
# shape (MCP JSON keyed by wire-name, CLI argv+flags, Ruby args+kwargs) into
|
|
23
|
-
# this hash. Binder owns the shared algorithm and nothing transport-specific:
|
|
24
|
-
#
|
|
25
|
-
# 1. validate every required arg is present in `inputs`;
|
|
26
|
-
# 2. for absentees, fall back to session_default (when a session is given)
|
|
27
|
-
# then to the literal default; otherwise omit the arg entirely;
|
|
28
|
-
# 3. split into the (positional, keyword) pair to splat into the use-case,
|
|
29
|
-
# routing by `arg.positional`.
|
|
30
|
-
#
|
|
31
|
-
# Returns `[positional_array, keyword_hash]` — exactly what
|
|
32
|
-
# `RoleScope#<verb>(*pos, **kw)` expects.
|
|
33
|
-
module Binder
|
|
34
|
-
module_function
|
|
35
|
-
|
|
36
|
-
# Validation is unconditional: a `required:` arg absent from `inputs` is a
|
|
37
|
-
# contract violation on every surface (ADR 0069). `required:` is now an
|
|
38
|
-
# honest contract invariant, not a surface policy — args the use-case
|
|
39
|
-
# treats as optional (e.g. `meta`, whose real requiredness lives in schema
|
|
40
|
-
# validation downstream) are declared `required: false`, so this check
|
|
41
|
-
# never fires spuriously and never needs an opt-out.
|
|
42
|
-
def bind(spec, inputs, session: nil)
|
|
43
|
-
missing = spec.required_args.reject { |a| inputs.key?(a.name) }
|
|
44
|
-
raise MissingArgs.new(spec, missing) unless missing.empty?
|
|
45
|
-
|
|
46
|
-
pos = []
|
|
47
|
-
kw = {}
|
|
48
|
-
spec.args.each do |a|
|
|
49
|
-
if inputs.key?(a.name)
|
|
50
|
-
value = inputs[a.name]
|
|
51
|
-
elsif a.session_default && session
|
|
52
|
-
value = session.public_send(a.session_default)
|
|
53
|
-
elsif !a.default.nil?
|
|
54
|
-
value = a.default
|
|
55
|
-
else
|
|
56
|
-
next
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
if a.positional
|
|
60
|
-
pos << value
|
|
61
|
-
else
|
|
62
|
-
kw[a.name] = value
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
[pos, kw]
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
# Normalize an ordered positional list + a by-name keyword hash (the shape
|
|
69
|
-
# CLI argv+flags and Ruby args+kwargs both arrive in) into the uniform
|
|
70
|
-
# by-name `inputs` hash bind expects. Positionals beyond what was supplied
|
|
71
|
-
# are dropped so bind's required-check sees them as absent.
|
|
72
|
-
def inputs_from_ordered(spec, ordered_positionals, by_name_keywords)
|
|
73
|
-
names = spec.args.select(&:positional).map(&:name)
|
|
74
|
-
names.zip(ordered_positionals).to_h.compact.merge(by_name_keywords)
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
# Normalize a raw transport hash keyed by WIRE name (the shape MCP JSON
|
|
78
|
-
# arrives in) into the uniform by-name `inputs` hash bind expects. Keys
|
|
79
|
-
# not declared on the contract are ignored.
|
|
80
|
-
def inputs_from_wire(spec, raw)
|
|
81
|
-
raw ||= {}
|
|
82
|
-
spec.args.each_with_object({}) do |a, h|
|
|
83
|
-
h[a.name] = raw[a.wire.to_s] if raw.key?(a.wire.to_s)
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
end
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
module Textus
|
|
2
|
-
module Contract
|
|
3
|
-
module Resources
|
|
4
|
-
# Serializes builds across every surface (CLI, MCP, Ruby). Previously the
|
|
5
|
-
# CLI verb wrapped each build in a BuildLock by hand; lifting it into the
|
|
6
|
-
# contract means the MCP surface inherits the single-writer guarantee and
|
|
7
|
-
# cannot collide with a concurrent CLI or background build.
|
|
8
|
-
class BuildLock
|
|
9
|
-
def wrap(scope:, inputs:, session: nil) # rubocop:disable Lint/UnusedMethodArgument
|
|
10
|
-
Textus::Ports::BuildLock.with(root: scope.container.root) { yield(inputs) }
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
Textus::Contract::Around.register(:build_lock, Textus::Contract::Resources::BuildLock.new)
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
module Textus
|
|
2
|
-
module Contract
|
|
3
|
-
module Resources
|
|
4
|
-
# Reads the persisted file cursor as the `since` default when the caller
|
|
5
|
-
# did not supply one, runs pulse, then persists the returned cursor.
|
|
6
|
-
# Replaces CLI::Verb::Pulse's hand-coded CursorStore read/write (ADR 0068).
|
|
7
|
-
#
|
|
8
|
-
# A session-bearing surface (MCP) carries its own cursor via the contract's
|
|
9
|
-
# `session_default: :cursor`, so this defers entirely when a session is
|
|
10
|
-
# present — it is the sessionless CLI/Ruby surfaces that need the file.
|
|
11
|
-
class Cursor
|
|
12
|
-
def wrap(scope:, inputs:, session:)
|
|
13
|
-
return yield(inputs) if session
|
|
14
|
-
|
|
15
|
-
store = Textus::CursorStore.new(root: scope.container.root, role: scope.role)
|
|
16
|
-
effective = inputs.key?(:since) ? inputs : inputs.merge(since: store.read)
|
|
17
|
-
result = yield(effective)
|
|
18
|
-
store.write(result["cursor"]) if result.is_a?(Hash) && result["cursor"]
|
|
19
|
-
result
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
Textus::Contract::Around.register(:cursor, Textus::Contract::Resources::Cursor.new)
|