textus 0.52.0 → 0.54.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 (261) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +59 -0
  3. data/README.md +62 -54
  4. data/SPEC.md +64 -192
  5. data/docs/architecture/README.md +157 -156
  6. data/exe/textus +1 -1
  7. data/lib/textus/action/accept.rb +44 -0
  8. data/lib/textus/action/audit.rb +131 -0
  9. data/lib/textus/action/base.rb +42 -0
  10. data/lib/textus/{read → action}/blame.rb +28 -22
  11. data/lib/textus/action/boot.rb +17 -0
  12. data/lib/textus/action/data_mv.rb +69 -0
  13. data/lib/textus/action/deps.rb +34 -0
  14. data/lib/textus/action/doctor.rb +24 -0
  15. data/lib/textus/action/drain.rb +39 -0
  16. data/lib/textus/action/enqueue.rb +53 -0
  17. data/lib/textus/action/get.rb +78 -0
  18. data/lib/textus/action/ingest.rb +135 -0
  19. data/lib/textus/action/jobs.rb +36 -0
  20. data/lib/textus/action/key_delete.rb +36 -0
  21. data/lib/textus/action/key_delete_prefix.rb +44 -0
  22. data/lib/textus/action/key_mv.rb +129 -0
  23. data/lib/textus/action/key_mv_prefix.rb +57 -0
  24. data/lib/textus/action/list.rb +42 -0
  25. data/lib/textus/action/propose.rb +52 -0
  26. data/lib/textus/action/published.rb +24 -0
  27. data/lib/textus/action/pulse.rb +56 -0
  28. data/lib/textus/action/put.rb +52 -0
  29. data/lib/textus/action/rdeps.rb +39 -0
  30. data/lib/textus/action/reject.rb +37 -0
  31. data/lib/textus/action/rule_explain.rb +91 -0
  32. data/lib/textus/action/rule_lint.rb +68 -0
  33. data/lib/textus/action/rule_list.rb +42 -0
  34. data/lib/textus/action/schema_envelope.rb +29 -0
  35. data/lib/textus/action/uid.rb +33 -0
  36. data/lib/textus/action/where.rb +36 -0
  37. data/lib/textus/action/write_verb.rb +44 -0
  38. data/lib/textus/boot.rb +64 -132
  39. data/lib/textus/command.rb +41 -0
  40. data/lib/textus/container.rb +1 -1
  41. data/lib/textus/{domain → core}/duration.rb +1 -1
  42. data/lib/textus/{domain → core}/freshness/evaluator.rb +25 -41
  43. data/lib/textus/{domain → core}/freshness/verdict.rb +2 -2
  44. data/lib/textus/{domain → core}/freshness.rb +2 -2
  45. data/lib/textus/{domain → core}/retention/sweep.rb +7 -7
  46. data/lib/textus/{domain → core}/retention.rb +2 -2
  47. data/lib/textus/{domain → core}/sentinel.rb +1 -1
  48. data/lib/textus/doctor/check/generator_drift.rb +1 -1
  49. data/lib/textus/doctor/check/illegal_keys.rb +1 -1
  50. data/lib/textus/doctor/check/notebook_sources.rb +50 -0
  51. data/lib/textus/doctor/check/proposal_targets.rb +3 -3
  52. data/lib/textus/doctor/check/raw_asset_paths.rb +50 -0
  53. data/lib/textus/doctor/check/rule_ambiguity.rb +2 -2
  54. data/lib/textus/doctor/check/schema_violations.rb +8 -2
  55. data/lib/textus/doctor/check.rb +11 -9
  56. data/lib/textus/{read → doctor}/validator.rb +22 -13
  57. data/lib/textus/doctor.rb +4 -44
  58. data/lib/textus/envelope/reader.rb +46 -0
  59. data/lib/textus/envelope/writer.rb +209 -0
  60. data/lib/textus/envelope.rb +5 -3
  61. data/lib/textus/errors.rb +25 -13
  62. data/lib/textus/{entry → format}/base.rb +1 -6
  63. data/lib/textus/{entry → format}/json.rb +1 -4
  64. data/lib/textus/{entry → format}/markdown.rb +2 -6
  65. data/lib/textus/{entry → format}/text.rb +2 -6
  66. data/lib/textus/{entry → format}/yaml.rb +1 -4
  67. data/lib/textus/{entry.rb → format.rb} +9 -10
  68. data/lib/textus/gate/auth.rb +227 -0
  69. data/lib/textus/gate.rb +116 -0
  70. data/lib/textus/init.rb +59 -105
  71. data/lib/textus/jobs/base.rb +23 -0
  72. data/lib/textus/jobs/materialize.rb +20 -0
  73. data/lib/textus/jobs/plan.rb +9 -0
  74. data/lib/textus/jobs/planner.rb +101 -0
  75. data/lib/textus/jobs/retention.rb +48 -0
  76. data/lib/textus/jobs/sweep.rb +27 -0
  77. data/lib/textus/jobs/worker.rb +67 -0
  78. data/lib/textus/jobs.rb +15 -0
  79. data/lib/textus/key/path.rb +10 -3
  80. data/lib/textus/layout.rb +13 -0
  81. data/lib/textus/manifest/data.rb +14 -14
  82. data/lib/textus/manifest/entry/base.rb +14 -25
  83. data/lib/textus/manifest/entry/parser.rb +31 -14
  84. data/lib/textus/manifest/entry/produced.rb +13 -33
  85. data/lib/textus/manifest/entry/publish/mode.rb +1 -1
  86. data/lib/textus/manifest/entry/publish/to_paths.rb +4 -3
  87. data/lib/textus/manifest/entry/publish.rb +0 -12
  88. data/lib/textus/manifest/entry/validators/format_matrix.rb +1 -1
  89. data/lib/textus/manifest/entry/validators.rb +0 -1
  90. data/lib/textus/{domain → manifest}/policy/matcher.rb +2 -2
  91. data/lib/textus/{domain → manifest}/policy/publish_target.rb +2 -2
  92. data/lib/textus/manifest/policy/react.rb +30 -0
  93. data/lib/textus/{domain → manifest}/policy/retention.rb +3 -3
  94. data/lib/textus/manifest/policy/source.rb +30 -0
  95. data/lib/textus/manifest/policy.rb +38 -53
  96. data/lib/textus/manifest/resolver.rb +5 -4
  97. data/lib/textus/manifest/rules.rb +4 -4
  98. data/lib/textus/manifest/schema/keys.rb +20 -28
  99. data/lib/textus/manifest/schema/validator.rb +27 -33
  100. data/lib/textus/manifest/schema/vocabulary.rb +2 -1
  101. data/lib/textus/manifest/schema.rb +2 -2
  102. data/lib/textus/manifest.rb +2 -2
  103. data/lib/textus/ports/audit_log.rb +31 -5
  104. data/lib/textus/{domain/jobs → ports/job_store}/job.rb +19 -12
  105. data/lib/textus/ports/{queue.rb → job_store.rb} +7 -14
  106. data/lib/textus/ports/sentinel_store.rb +2 -2
  107. data/lib/textus/ports/watcher_lock.rb +48 -0
  108. data/lib/textus/produce/engine.rb +25 -70
  109. data/lib/textus/produce/render.rb +2 -7
  110. data/lib/textus/schema/tools.rb +4 -3
  111. data/lib/textus/session.rb +6 -3
  112. data/lib/textus/store.rb +12 -19
  113. data/lib/textus/surfaces/cli/group/data.rb +11 -0
  114. data/lib/textus/surfaces/cli/group/key.rb +11 -0
  115. data/lib/textus/surfaces/cli/group/mcp.rb +11 -0
  116. data/lib/textus/surfaces/cli/group/rule.rb +11 -0
  117. data/lib/textus/surfaces/cli/group/schema.rb +11 -0
  118. data/lib/textus/surfaces/cli/group.rb +50 -0
  119. data/lib/textus/surfaces/cli/runner.rb +236 -0
  120. data/lib/textus/surfaces/cli/verb/doctor.rb +21 -0
  121. data/lib/textus/surfaces/cli/verb/get.rb +21 -0
  122. data/lib/textus/surfaces/cli/verb/init.rb +20 -0
  123. data/lib/textus/surfaces/cli/verb/mcp_serve.rb +24 -0
  124. data/lib/textus/surfaces/cli/verb/put.rb +30 -0
  125. data/lib/textus/surfaces/cli/verb/schema_diff.rb +17 -0
  126. data/lib/textus/surfaces/cli/verb/schema_init.rb +21 -0
  127. data/lib/textus/surfaces/cli/verb/schema_migrate.rb +21 -0
  128. data/lib/textus/surfaces/cli/verb/watch.rb +19 -0
  129. data/lib/textus/surfaces/cli/verb.rb +111 -0
  130. data/lib/textus/surfaces/cli.rb +148 -0
  131. data/lib/textus/surfaces/mcp/catalog.rb +99 -0
  132. data/lib/textus/surfaces/mcp/errors.rb +25 -0
  133. data/lib/textus/surfaces/mcp/routing.rb +51 -0
  134. data/lib/textus/surfaces/mcp/server.rb +184 -0
  135. data/lib/textus/surfaces/mcp/session.rb +9 -0
  136. data/lib/textus/surfaces/mcp/tool_schemas.rb +17 -0
  137. data/lib/textus/surfaces/mcp.rb +8 -0
  138. data/lib/textus/surfaces/role_scope.rb +34 -0
  139. data/lib/textus/surfaces/watcher.rb +38 -0
  140. data/lib/textus/version.rb +1 -1
  141. data/lib/textus/workflow/collector.rb +27 -0
  142. data/lib/textus/workflow/context.rb +5 -0
  143. data/lib/textus/workflow/dsl.rb +44 -0
  144. data/lib/textus/workflow/loader.rb +17 -0
  145. data/lib/textus/workflow/pattern.rb +18 -0
  146. data/lib/textus/workflow/registry.rb +21 -0
  147. data/lib/textus/workflow/runner.rb +84 -0
  148. data/lib/textus/workflow.rb +28 -0
  149. data/lib/textus.rb +75 -22
  150. metadata +118 -132
  151. data/lib/textus/cli/group/hook.rb +0 -9
  152. data/lib/textus/cli/group/key.rb +0 -9
  153. data/lib/textus/cli/group/mcp.rb +0 -9
  154. data/lib/textus/cli/group/rule.rb +0 -9
  155. data/lib/textus/cli/group/schema.rb +0 -9
  156. data/lib/textus/cli/group/zone.rb +0 -9
  157. data/lib/textus/cli/group.rb +0 -48
  158. data/lib/textus/cli/runner.rb +0 -193
  159. data/lib/textus/cli/verb/doctor.rb +0 -17
  160. data/lib/textus/cli/verb/get.rb +0 -18
  161. data/lib/textus/cli/verb/hook_run.rb +0 -48
  162. data/lib/textus/cli/verb/hooks.rb +0 -50
  163. data/lib/textus/cli/verb/init.rb +0 -18
  164. data/lib/textus/cli/verb/mcp_serve.rb +0 -22
  165. data/lib/textus/cli/verb/put.rb +0 -30
  166. data/lib/textus/cli/verb/schema_diff.rb +0 -15
  167. data/lib/textus/cli/verb/schema_init.rb +0 -19
  168. data/lib/textus/cli/verb/schema_migrate.rb +0 -19
  169. data/lib/textus/cli/verb/serve.rb +0 -19
  170. data/lib/textus/cli/verb.rb +0 -116
  171. data/lib/textus/cli.rb +0 -138
  172. data/lib/textus/dispatcher.rb +0 -54
  173. data/lib/textus/doctor/check/handler_allowlist.rb +0 -34
  174. data/lib/textus/doctor/check/hooks.rb +0 -32
  175. data/lib/textus/doctor/check/intake_registration.rb +0 -46
  176. data/lib/textus/domain/action.rb +0 -9
  177. data/lib/textus/domain/jobs/registry.rb +0 -37
  178. data/lib/textus/domain/permission.rb +0 -7
  179. data/lib/textus/domain/policy/base_guards.rb +0 -25
  180. data/lib/textus/domain/policy/evaluation.rb +0 -15
  181. data/lib/textus/domain/policy/guard.rb +0 -35
  182. data/lib/textus/domain/policy/guard_factory.rb +0 -40
  183. data/lib/textus/domain/policy/handler_allowlist.rb +0 -17
  184. data/lib/textus/domain/policy/predicates/author_held.rb +0 -33
  185. data/lib/textus/domain/policy/predicates/etag_match.rb +0 -32
  186. data/lib/textus/domain/policy/predicates/fresh_within.rb +0 -59
  187. data/lib/textus/domain/policy/predicates/registry.rb +0 -39
  188. data/lib/textus/domain/policy/predicates/schema_valid.rb +0 -61
  189. data/lib/textus/domain/policy/predicates/target_is_canon.rb +0 -33
  190. data/lib/textus/domain/policy/predicates/zone_writable_by.rb +0 -39
  191. data/lib/textus/domain/policy/source.rb +0 -73
  192. data/lib/textus/envelope/io/reader.rb +0 -48
  193. data/lib/textus/envelope/io/writer.rb +0 -182
  194. data/lib/textus/hooks/builtin.rb +0 -70
  195. data/lib/textus/hooks/catalog.rb +0 -37
  196. data/lib/textus/hooks/context.rb +0 -72
  197. data/lib/textus/hooks/error_log.rb +0 -32
  198. data/lib/textus/hooks/event_bus.rb +0 -114
  199. data/lib/textus/hooks/fire_report.rb +0 -23
  200. data/lib/textus/hooks/loader.rb +0 -54
  201. data/lib/textus/hooks/rpc_registry.rb +0 -43
  202. data/lib/textus/hooks/signature.rb +0 -31
  203. data/lib/textus/init/templates/machine_intake.rb +0 -45
  204. data/lib/textus/init/templates/orientation_reducer.rb +0 -17
  205. data/lib/textus/jobs/handlers.rb +0 -62
  206. data/lib/textus/jobs/scheduler.rb +0 -36
  207. data/lib/textus/jobs/seeder.rb +0 -57
  208. data/lib/textus/maintenance/drain.rb +0 -42
  209. data/lib/textus/maintenance/key_delete_prefix.rb +0 -48
  210. data/lib/textus/maintenance/key_mv_prefix.rb +0 -68
  211. data/lib/textus/maintenance/retention/apply.rb +0 -52
  212. data/lib/textus/maintenance/rule_lint.rb +0 -66
  213. data/lib/textus/maintenance/serve.rb +0 -30
  214. data/lib/textus/maintenance/worker.rb +0 -74
  215. data/lib/textus/maintenance/zone_mv.rb +0 -64
  216. data/lib/textus/maintenance.rb +0 -15
  217. data/lib/textus/manifest/entry/validators/events.rb +0 -22
  218. data/lib/textus/mcp/catalog.rb +0 -70
  219. data/lib/textus/mcp/errors.rb +0 -32
  220. data/lib/textus/mcp/server.rb +0 -138
  221. data/lib/textus/mcp/session.rb +0 -7
  222. data/lib/textus/mcp/tool_schemas.rb +0 -15
  223. data/lib/textus/mcp.rb +0 -6
  224. data/lib/textus/mustache.rb +0 -117
  225. data/lib/textus/ports/audit_subscriber.rb +0 -42
  226. data/lib/textus/ports/produce_on_write_subscriber.rb +0 -73
  227. data/lib/textus/produce/acquire/handler.rb +0 -29
  228. data/lib/textus/produce/acquire/intake.rb +0 -130
  229. data/lib/textus/produce/acquire/projection.rb +0 -127
  230. data/lib/textus/produce/acquire/serializer/json.rb +0 -31
  231. data/lib/textus/produce/acquire/serializer/text.rb +0 -16
  232. data/lib/textus/produce/acquire/serializer/yaml.rb +0 -31
  233. data/lib/textus/produce/acquire/serializer.rb +0 -17
  234. data/lib/textus/produce/events.rb +0 -36
  235. data/lib/textus/projection.rb +0 -103
  236. data/lib/textus/read/audit.rb +0 -130
  237. data/lib/textus/read/boot.rb +0 -26
  238. data/lib/textus/read/capabilities.rb +0 -70
  239. data/lib/textus/read/deps.rb +0 -38
  240. data/lib/textus/read/doctor.rb +0 -27
  241. data/lib/textus/read/freshness.rb +0 -152
  242. data/lib/textus/read/get.rb +0 -73
  243. data/lib/textus/read/jobs.rb +0 -31
  244. data/lib/textus/read/list.rb +0 -24
  245. data/lib/textus/read/published.rb +0 -22
  246. data/lib/textus/read/pulse.rb +0 -98
  247. data/lib/textus/read/rdeps.rb +0 -39
  248. data/lib/textus/read/rule_explain.rb +0 -96
  249. data/lib/textus/read/rule_list.rb +0 -54
  250. data/lib/textus/read/schema_envelope.rb +0 -25
  251. data/lib/textus/read/uid.rb +0 -29
  252. data/lib/textus/read/validate_all.rb +0 -36
  253. data/lib/textus/read/where.rb +0 -24
  254. data/lib/textus/role_scope.rb +0 -78
  255. data/lib/textus/write/accept.rb +0 -58
  256. data/lib/textus/write/enqueue.rb +0 -50
  257. data/lib/textus/write/key_delete.rb +0 -65
  258. data/lib/textus/write/key_mv.rb +0 -141
  259. data/lib/textus/write/propose.rb +0 -54
  260. data/lib/textus/write/put.rb +0 -74
  261. data/lib/textus/write/reject.rb +0 -68
@@ -0,0 +1,44 @@
1
+ module Textus
2
+ module Workflow
3
+ module DSL
4
+ Step = Data.define(:name, :callable, :timeout)
5
+
6
+ class Definition
7
+ attr_reader :name, :steps, :match_pattern, :publish_block
8
+
9
+ def initialize(name)
10
+ @name = name
11
+ @steps = []
12
+ @match_pattern = nil
13
+ @publish_block = nil
14
+ end
15
+
16
+ def match(pattern)
17
+ @match_pattern = pattern
18
+ end
19
+
20
+ def step(name, callable_or_opt = nil, timeout: nil, &block)
21
+ callable = if callable_or_opt.respond_to?(:call)
22
+ callable_or_opt
23
+ elsif block
24
+ block
25
+ else
26
+ raise ArgumentError.new("step :#{name} requires a block or a callable (got neither)")
27
+ end
28
+ t = callable_or_opt.is_a?(Hash) ? callable_or_opt[:timeout] : timeout
29
+ @steps << Step.new(name: name, callable: callable, timeout: t)
30
+ end
31
+
32
+ def publish(&block)
33
+ @publish_block = block || :default
34
+ end
35
+
36
+ def match?(key)
37
+ return false unless @match_pattern
38
+
39
+ Pattern.match?(@match_pattern, key)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,17 @@
1
+ module Textus
2
+ module Workflow
3
+ class Loader
4
+ def self.load_all(root)
5
+ registry = Registry.new
6
+ workflows_dir = File.join(root, "workflows")
7
+ return registry unless File.directory?(workflows_dir)
8
+
9
+ collector = Collector.new(registry)
10
+ Collector.with(collector) do
11
+ Dir.glob(File.join(workflows_dir, "**", "*.rb")).each { |path| load path }
12
+ end
13
+ registry
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ module Textus
2
+ module Workflow
3
+ module Pattern
4
+ def self.match?(pattern, key)
5
+ if pattern.end_with?(".**")
6
+ prefix = pattern.delete_suffix(".**")
7
+ key.start_with?("#{prefix}.")
8
+ elsif pattern.end_with?(".*")
9
+ prefix = pattern.delete_suffix(".*")
10
+ suffix = key.delete_prefix("#{prefix}.")
11
+ key != suffix && !suffix.include?(".")
12
+ else
13
+ key == pattern
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,21 @@
1
+ module Textus
2
+ module Workflow
3
+ class Registry
4
+ def initialize
5
+ @definitions = []
6
+ end
7
+
8
+ def register(definition)
9
+ @definitions << definition
10
+ end
11
+
12
+ def for(key)
13
+ @definitions.find { |d| d.match?(key) }
14
+ end
15
+
16
+ def all
17
+ @definitions.dup
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,84 @@
1
+ require "timeout"
2
+
3
+ module Textus
4
+ module Workflow
5
+ class Runner
6
+ DEFAULT_TIMEOUT = 30
7
+
8
+ def initialize(definition, container:, call:)
9
+ @definition = definition
10
+ @container = container
11
+ @call = call
12
+ end
13
+
14
+ def run(key)
15
+ ctx = build_context(key)
16
+ data = execute_steps(ctx)
17
+ publish(key, data, ctx)
18
+ data
19
+ end
20
+
21
+ private
22
+
23
+ def build_context(key)
24
+ res = @container.manifest.resolver.resolve(key)
25
+ Context.new(
26
+ key: key,
27
+ entry: res.entry,
28
+ config: {}.freeze,
29
+ lane: res.entry.lane.to_s,
30
+ container: @container,
31
+ call: @call,
32
+ )
33
+ end
34
+
35
+ def execute_steps(ctx)
36
+ data = nil
37
+ @definition.steps.each { |step| data = execute_one(step, data, ctx) }
38
+ data
39
+ end
40
+
41
+ def execute_one(step, data, ctx)
42
+ timeout = step.timeout || DEFAULT_TIMEOUT
43
+ Timeout.timeout(timeout) { step.callable.call(data, ctx) }
44
+ rescue Timeout::Error => e
45
+ raise StepFailed.new(step.name, e)
46
+ rescue Textus::Error
47
+ raise
48
+ rescue StandardError => e
49
+ raise StepFailed.new(step.name, e)
50
+ end
51
+
52
+ def publish(key, data, ctx)
53
+ blk = @definition.publish_block
54
+ return blk.call(data, ctx) if blk && blk != :default
55
+
56
+ built_in_publish(key, data, ctx)
57
+ end
58
+
59
+ def built_in_publish(key, data, ctx)
60
+ normalized = normalize(data, ctx.entry.format)
61
+ Gate::Auth.new(@container).check_action!(action: :converge, actor: @call.role, key: key)
62
+ Envelope::Writer.from(container: @container, call: @call).put(
63
+ key,
64
+ mentry: ctx.entry,
65
+ payload: Envelope::Writer::Payload.new(**normalized),
66
+ )
67
+ end
68
+
69
+ def normalize(data, format)
70
+ return { meta: {}, body: "", content: nil } if data.nil?
71
+
72
+ data = data.transform_keys(&:to_s) if data.is_a?(Hash)
73
+ case format.to_s
74
+ when "markdown", "text"
75
+ { meta: data["_meta"] || {}, body: (data["body"] || "").to_s, content: nil }
76
+ when "json", "yaml"
77
+ { meta: data["_meta"] || {}, body: nil, content: data["content"] || data }
78
+ else
79
+ { meta: {}, body: data.to_s, content: nil }
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,28 @@
1
+ module Textus
2
+ module Workflow
3
+ class StepFailed < Textus::Error
4
+ attr_reader :step_name, :cause
5
+
6
+ def initialize(step_name, cause)
7
+ @step_name = step_name
8
+ @cause = cause
9
+ super(:workflow_step_failed, "workflow step '#{step_name}' failed: #{cause.message}")
10
+ end
11
+ end
12
+
13
+ class NotFound < Textus::Error
14
+ def initialize(key)
15
+ super(:workflow_not_found, "no workflow matches key '#{key}'; add a .textus/workflows/*.rb file with match: '#{key}'")
16
+ end
17
+ end
18
+ end
19
+
20
+ def self.workflow(name, &)
21
+ collector = Workflow::Collector.current
22
+ raise "Textus.workflow called outside Workflow::Loader.load_all context" unless collector
23
+
24
+ defn = Workflow::DSL::Definition.new(name)
25
+ defn.instance_eval(&)
26
+ collector.register(defn)
27
+ end
28
+ end
data/lib/textus.rb CHANGED
@@ -1,48 +1,101 @@
1
1
  require "zeitwerk"
2
2
  require_relative "textus/version"
3
3
  require_relative "textus/errors"
4
- require_relative "textus/mcp"
5
- require_relative "textus/mcp/errors"
4
+ require_relative "textus/surfaces/mcp"
5
+ require_relative "textus/surfaces/mcp/errors"
6
6
 
7
7
  loader = Zeitwerk::Loader.for_gem
8
8
  loader.inflector.inflect(
9
9
  "cli" => "CLI",
10
10
  "json" => "Json",
11
11
  "yaml" => "Yaml",
12
- "hook_dsl_scanner" => "HookDSLScanner",
13
- "io" => "IO",
14
12
  "mcp" => "MCP",
15
13
  "mcp_serve" => "MCPServe",
14
+ "dsl" => "DSL",
16
15
  )
17
16
  loader.ignore(File.expand_path("textus/errors.rb", __dir__))
18
- loader.ignore(File.expand_path("textus/mcp.rb", __dir__))
19
- loader.ignore(File.expand_path("textus/mcp/errors.rb", __dir__))
17
+ loader.ignore(File.expand_path("textus/surfaces/mcp.rb", __dir__))
18
+ loader.ignore(File.expand_path("textus/surfaces/mcp/errors.rb", __dir__))
19
+ loader.ignore(File.expand_path("textus/workflow/errors.rb", __dir__))
20
20
  # Scaffold sources copied verbatim into user stores by `textus init`. They are
21
- # file templates (one calls `Textus.hook` at load time), not gem constants —
22
- # Zeitwerk must not manage or eager-load them (ADR 0043).
21
+ # templates for user-owned step classes, not gem constants — Zeitwerk must not
22
+ # manage or eager-load them.
23
23
  loader.ignore(File.expand_path("textus/init/templates", __dir__))
24
+ loader.ignore(File.expand_path("textus/produce/acquire", __dir__))
24
25
  loader.setup
25
26
  loader.eager_load
26
27
 
27
- # Derive CLI_VERBS after eager_load so all contract-declaring files are present
28
- # (boot.rb loads first alphabetically; Dispatcher contracts are declared later).
28
+ # Verb symbol Action class mapping. Replaces Textus::Dispatcher::VERBS.
29
+ Textus::Action::VERBS = {
30
+ put: Textus::Action::Put,
31
+ propose: Textus::Action::Propose,
32
+ key_delete: Textus::Action::KeyDelete,
33
+ key_mv: Textus::Action::KeyMv,
34
+ accept: Textus::Action::Accept,
35
+ reject: Textus::Action::Reject,
36
+ enqueue: Textus::Action::Enqueue,
37
+ get: Textus::Action::Get,
38
+ ingest: Textus::Action::Ingest,
39
+ list: Textus::Action::List,
40
+ where: Textus::Action::Where,
41
+ uid: Textus::Action::Uid,
42
+ blame: Textus::Action::Blame,
43
+ audit: Textus::Action::Audit,
44
+ # materialize, refresh, sweep are Worker-only — not in VERBS
45
+ deps: Textus::Action::Deps,
46
+ rdeps: Textus::Action::Rdeps,
47
+ pulse: Textus::Action::Pulse,
48
+ rule_explain: Textus::Action::RuleExplain,
49
+ rule_list: Textus::Action::RuleList,
50
+ published: Textus::Action::Published,
51
+ schema_show: Textus::Action::SchemaEnvelope,
52
+ doctor: Textus::Action::Doctor,
53
+ boot: Textus::Action::Boot,
54
+ jobs: Textus::Action::Jobs,
55
+ data_mv: Textus::Action::DataMv,
56
+ key_mv_prefix: Textus::Action::KeyMvPrefix,
57
+ key_delete_prefix: Textus::Action::KeyDeletePrefix,
58
+ drain: Textus::Action::Drain,
59
+ rule_lint: Textus::Action::RuleLint,
60
+ }.freeze
61
+
62
+ # Derive CLI_VERBS after VERBS is available.
29
63
  Textus::Boot::CLI_VERBS = Textus::Boot.build_cli_verbs.freeze
30
64
 
31
- module Textus
32
- @hook_mutex = Mutex.new
33
- @hook_blocks = []
65
+ # Dynamic verb methods on Store (deferred after VERBS is defined).
66
+ Textus::Action::VERBS.each_key do |verb|
67
+ Textus::Store.define_method(verb) do |*args, role: Textus::Role::DEFAULT, **kwargs|
68
+ as(role).public_send(verb, *args, **kwargs)
69
+ end
70
+
71
+ Textus::Surfaces::RoleScope.define_method(verb) do |*args, **kwargs|
72
+ klass = Textus::Action::VERBS[verb]
73
+ cmd_class = Textus::Gate::VERB_COMMAND[verb]
74
+ inputs = if klass.respond_to?(:contract?) && klass.contract?
75
+ Textus::Contract::Binder.inputs_from_ordered(klass.contract, args, kwargs)
76
+ else
77
+ kwargs.transform_keys(&:to_sym)
78
+ end
34
79
 
35
- def self.hook(&blk)
36
- raise UsageError.new("hook block required") unless blk
80
+ role_value = if klass.respond_to?(:contract?) && klass.contract? &&
81
+ klass.contract.args.any? { |a| a.name == :role }
82
+ inputs[:role]
83
+ else
84
+ @role
85
+ end
37
86
 
38
- @hook_mutex.synchronize { @hook_blocks << blk }
87
+ filled = cmd_class.members.to_h { |m| [m, inputs.merge(role: role_value)[m]] }
88
+ @container.gate.dispatch(cmd_class.new(**filled), correlation_id: @correlation_id)
39
89
  end
90
+ end
91
+
92
+ module Textus
93
+ def self.workflow(name, &)
94
+ collector = Workflow::Collector.current
95
+ raise "Textus.workflow called outside Workflow::Loader.load_all context" unless collector
40
96
 
41
- def self.drain_hook_blocks
42
- @hook_mutex.synchronize do
43
- blocks = @hook_blocks
44
- @hook_blocks = []
45
- blocks
46
- end
97
+ defn = Workflow::DSL::Definition.new(name)
98
+ defn.instance_eval(&)
99
+ collector.register(defn)
47
100
  end
48
101
  end
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.52.0
4
+ version: 0.54.0
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: mustache
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '1.1'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.1'
26
40
  - !ruby/object:Gem::Dependency
27
41
  name: psych
28
42
  requirement: !ruby/object:Gem::Requirement
@@ -110,29 +124,40 @@ files:
110
124
  - docs/reference/conventions.md
111
125
  - exe/textus
112
126
  - lib/textus.rb
127
+ - lib/textus/action/accept.rb
128
+ - lib/textus/action/audit.rb
129
+ - lib/textus/action/base.rb
130
+ - lib/textus/action/blame.rb
131
+ - lib/textus/action/boot.rb
132
+ - lib/textus/action/data_mv.rb
133
+ - lib/textus/action/deps.rb
134
+ - lib/textus/action/doctor.rb
135
+ - lib/textus/action/drain.rb
136
+ - lib/textus/action/enqueue.rb
137
+ - lib/textus/action/get.rb
138
+ - lib/textus/action/ingest.rb
139
+ - lib/textus/action/jobs.rb
140
+ - lib/textus/action/key_delete.rb
141
+ - lib/textus/action/key_delete_prefix.rb
142
+ - lib/textus/action/key_mv.rb
143
+ - lib/textus/action/key_mv_prefix.rb
144
+ - lib/textus/action/list.rb
145
+ - lib/textus/action/propose.rb
146
+ - lib/textus/action/published.rb
147
+ - lib/textus/action/pulse.rb
148
+ - lib/textus/action/put.rb
149
+ - lib/textus/action/rdeps.rb
150
+ - lib/textus/action/reject.rb
151
+ - lib/textus/action/rule_explain.rb
152
+ - lib/textus/action/rule_lint.rb
153
+ - lib/textus/action/rule_list.rb
154
+ - lib/textus/action/schema_envelope.rb
155
+ - lib/textus/action/uid.rb
156
+ - lib/textus/action/where.rb
157
+ - lib/textus/action/write_verb.rb
113
158
  - lib/textus/boot.rb
114
159
  - lib/textus/call.rb
115
- - lib/textus/cli.rb
116
- - lib/textus/cli/group.rb
117
- - lib/textus/cli/group/hook.rb
118
- - lib/textus/cli/group/key.rb
119
- - lib/textus/cli/group/mcp.rb
120
- - lib/textus/cli/group/rule.rb
121
- - lib/textus/cli/group/schema.rb
122
- - lib/textus/cli/group/zone.rb
123
- - lib/textus/cli/runner.rb
124
- - lib/textus/cli/verb.rb
125
- - lib/textus/cli/verb/doctor.rb
126
- - lib/textus/cli/verb/get.rb
127
- - lib/textus/cli/verb/hook_run.rb
128
- - lib/textus/cli/verb/hooks.rb
129
- - lib/textus/cli/verb/init.rb
130
- - lib/textus/cli/verb/mcp_serve.rb
131
- - lib/textus/cli/verb/put.rb
132
- - lib/textus/cli/verb/schema_diff.rb
133
- - lib/textus/cli/verb/schema_init.rb
134
- - lib/textus/cli/verb/schema_migrate.rb
135
- - lib/textus/cli/verb/serve.rb
160
+ - lib/textus/command.rb
136
161
  - lib/textus/container.rb
137
162
  - lib/textus/contract.rb
138
163
  - lib/textus/contract/around.rb
@@ -141,21 +166,26 @@ files:
141
166
  - lib/textus/contract/resources/cursor.rb
142
167
  - lib/textus/contract/sources.rb
143
168
  - lib/textus/contract/view.rb
169
+ - lib/textus/core/duration.rb
170
+ - lib/textus/core/freshness.rb
171
+ - lib/textus/core/freshness/evaluator.rb
172
+ - lib/textus/core/freshness/verdict.rb
173
+ - lib/textus/core/retention.rb
174
+ - lib/textus/core/retention/sweep.rb
175
+ - lib/textus/core/sentinel.rb
144
176
  - lib/textus/cursor_store.rb
145
- - lib/textus/dispatcher.rb
146
177
  - lib/textus/doctor.rb
147
178
  - lib/textus/doctor/check.rb
148
179
  - lib/textus/doctor/check/audit_log.rb
149
180
  - lib/textus/doctor/check/generator_drift.rb
150
- - lib/textus/doctor/check/handler_allowlist.rb
151
- - lib/textus/doctor/check/hooks.rb
152
181
  - lib/textus/doctor/check/illegal_keys.rb
153
- - lib/textus/doctor/check/intake_registration.rb
154
182
  - lib/textus/doctor/check/manifest_files.rb
183
+ - lib/textus/doctor/check/notebook_sources.rb
155
184
  - lib/textus/doctor/check/orphaned_publish_targets.rb
156
185
  - lib/textus/doctor/check/proposal_targets.rb
157
186
  - lib/textus/doctor/check/protocol_version.rb
158
187
  - lib/textus/doctor/check/publish_tree_index_overlap.rb
188
+ - lib/textus/doctor/check/raw_asset_paths.rb
159
189
  - lib/textus/doctor/check/rule_ambiguity.rb
160
190
  - lib/textus/doctor/check/schema_parse_error.rb
161
191
  - lib/textus/doctor/check/schema_violations.rb
@@ -163,73 +193,34 @@ files:
163
193
  - lib/textus/doctor/check/sentinels.rb
164
194
  - lib/textus/doctor/check/templates.rb
165
195
  - lib/textus/doctor/check/unowned_schema_fields.rb
166
- - lib/textus/domain/action.rb
167
- - lib/textus/domain/duration.rb
168
- - lib/textus/domain/freshness.rb
169
- - lib/textus/domain/freshness/evaluator.rb
170
- - lib/textus/domain/freshness/verdict.rb
171
- - lib/textus/domain/jobs/job.rb
172
- - lib/textus/domain/jobs/registry.rb
173
- - lib/textus/domain/permission.rb
174
- - lib/textus/domain/policy/base_guards.rb
175
- - lib/textus/domain/policy/evaluation.rb
176
- - lib/textus/domain/policy/guard.rb
177
- - lib/textus/domain/policy/guard_factory.rb
178
- - lib/textus/domain/policy/handler_allowlist.rb
179
- - lib/textus/domain/policy/matcher.rb
180
- - lib/textus/domain/policy/predicates/author_held.rb
181
- - lib/textus/domain/policy/predicates/etag_match.rb
182
- - lib/textus/domain/policy/predicates/fresh_within.rb
183
- - lib/textus/domain/policy/predicates/registry.rb
184
- - lib/textus/domain/policy/predicates/schema_valid.rb
185
- - lib/textus/domain/policy/predicates/target_is_canon.rb
186
- - lib/textus/domain/policy/predicates/zone_writable_by.rb
187
- - lib/textus/domain/policy/publish_target.rb
188
- - lib/textus/domain/policy/retention.rb
189
- - lib/textus/domain/policy/source.rb
190
- - lib/textus/domain/retention.rb
191
- - lib/textus/domain/retention/sweep.rb
192
- - lib/textus/domain/sentinel.rb
193
- - lib/textus/entry.rb
194
- - lib/textus/entry/base.rb
195
- - lib/textus/entry/json.rb
196
- - lib/textus/entry/markdown.rb
197
- - lib/textus/entry/text.rb
198
- - lib/textus/entry/yaml.rb
196
+ - lib/textus/doctor/validator.rb
199
197
  - lib/textus/envelope.rb
200
- - lib/textus/envelope/io/reader.rb
201
- - lib/textus/envelope/io/writer.rb
198
+ - lib/textus/envelope/reader.rb
199
+ - lib/textus/envelope/writer.rb
202
200
  - lib/textus/errors.rb
203
201
  - lib/textus/etag.rb
204
- - lib/textus/hooks/builtin.rb
205
- - lib/textus/hooks/catalog.rb
206
- - lib/textus/hooks/context.rb
207
- - lib/textus/hooks/error_log.rb
208
- - lib/textus/hooks/event_bus.rb
209
- - lib/textus/hooks/fire_report.rb
210
- - lib/textus/hooks/loader.rb
211
- - lib/textus/hooks/rpc_registry.rb
212
- - lib/textus/hooks/signature.rb
202
+ - lib/textus/format.rb
203
+ - lib/textus/format/base.rb
204
+ - lib/textus/format/json.rb
205
+ - lib/textus/format/markdown.rb
206
+ - lib/textus/format/text.rb
207
+ - lib/textus/format/yaml.rb
208
+ - lib/textus/gate.rb
209
+ - lib/textus/gate/auth.rb
213
210
  - lib/textus/init.rb
214
- - lib/textus/init/templates/machine_intake.rb
215
- - lib/textus/init/templates/orientation_reducer.rb
216
- - lib/textus/jobs/handlers.rb
217
- - lib/textus/jobs/scheduler.rb
218
- - lib/textus/jobs/seeder.rb
211
+ - lib/textus/jobs.rb
212
+ - lib/textus/jobs/base.rb
213
+ - lib/textus/jobs/materialize.rb
214
+ - lib/textus/jobs/plan.rb
215
+ - lib/textus/jobs/planner.rb
216
+ - lib/textus/jobs/retention.rb
217
+ - lib/textus/jobs/sweep.rb
218
+ - lib/textus/jobs/worker.rb
219
219
  - lib/textus/key/distance.rb
220
220
  - lib/textus/key/grammar.rb
221
221
  - lib/textus/key/matching.rb
222
222
  - lib/textus/key/path.rb
223
223
  - lib/textus/layout.rb
224
- - lib/textus/maintenance.rb
225
- - lib/textus/maintenance/drain.rb
226
- - lib/textus/maintenance/key_delete_prefix.rb
227
- - lib/textus/maintenance/key_mv_prefix.rb
228
- - lib/textus/maintenance/retention/apply.rb
229
- - lib/textus/maintenance/rule_lint.rb
230
- - lib/textus/maintenance/serve.rb
231
- - lib/textus/maintenance/worker.rb
232
- - lib/textus/maintenance/zone_mv.rb
233
224
  - lib/textus/manifest.rb
234
225
  - lib/textus/manifest/capabilities.rb
235
226
  - lib/textus/manifest/data.rb
@@ -248,81 +239,76 @@ files:
248
239
  - lib/textus/manifest/entry/publish/to_paths.rb
249
240
  - lib/textus/manifest/entry/publish/tree.rb
250
241
  - lib/textus/manifest/entry/validators.rb
251
- - lib/textus/manifest/entry/validators/events.rb
252
242
  - lib/textus/manifest/entry/validators/format_matrix.rb
253
243
  - lib/textus/manifest/entry/validators/ignore.rb
254
244
  - lib/textus/manifest/entry/validators/publish.rb
255
245
  - lib/textus/manifest/policy.rb
246
+ - lib/textus/manifest/policy/matcher.rb
247
+ - lib/textus/manifest/policy/publish_target.rb
248
+ - lib/textus/manifest/policy/react.rb
249
+ - lib/textus/manifest/policy/retention.rb
250
+ - lib/textus/manifest/policy/source.rb
256
251
  - lib/textus/manifest/resolver.rb
257
252
  - lib/textus/manifest/rules.rb
258
253
  - lib/textus/manifest/schema.rb
259
254
  - lib/textus/manifest/schema/keys.rb
260
255
  - lib/textus/manifest/schema/validator.rb
261
256
  - lib/textus/manifest/schema/vocabulary.rb
262
- - lib/textus/mcp.rb
263
- - lib/textus/mcp/catalog.rb
264
- - lib/textus/mcp/errors.rb
265
- - lib/textus/mcp/server.rb
266
- - lib/textus/mcp/session.rb
267
- - lib/textus/mcp/tool_schemas.rb
268
- - lib/textus/mustache.rb
269
257
  - lib/textus/ports/audit_log.rb
270
- - lib/textus/ports/audit_subscriber.rb
271
258
  - lib/textus/ports/build_lock.rb
272
259
  - lib/textus/ports/clock.rb
273
- - lib/textus/ports/produce_on_write_subscriber.rb
260
+ - lib/textus/ports/job_store.rb
261
+ - lib/textus/ports/job_store/job.rb
274
262
  - lib/textus/ports/publisher.rb
275
- - lib/textus/ports/queue.rb
276
263
  - lib/textus/ports/sentinel_store.rb
277
264
  - lib/textus/ports/storage/file_stat.rb
278
265
  - lib/textus/ports/storage/file_store.rb
279
- - lib/textus/produce/acquire/handler.rb
280
- - lib/textus/produce/acquire/intake.rb
281
- - lib/textus/produce/acquire/projection.rb
282
- - lib/textus/produce/acquire/serializer.rb
283
- - lib/textus/produce/acquire/serializer/json.rb
284
- - lib/textus/produce/acquire/serializer/text.rb
285
- - lib/textus/produce/acquire/serializer/yaml.rb
266
+ - lib/textus/ports/watcher_lock.rb
286
267
  - lib/textus/produce/engine.rb
287
- - lib/textus/produce/events.rb
288
268
  - lib/textus/produce/render.rb
289
- - lib/textus/projection.rb
290
- - lib/textus/read/audit.rb
291
- - lib/textus/read/blame.rb
292
- - lib/textus/read/boot.rb
293
- - lib/textus/read/capabilities.rb
294
- - lib/textus/read/deps.rb
295
- - lib/textus/read/doctor.rb
296
- - lib/textus/read/freshness.rb
297
- - lib/textus/read/get.rb
298
- - lib/textus/read/jobs.rb
299
- - lib/textus/read/list.rb
300
- - lib/textus/read/published.rb
301
- - lib/textus/read/pulse.rb
302
- - lib/textus/read/rdeps.rb
303
- - lib/textus/read/rule_explain.rb
304
- - lib/textus/read/rule_list.rb
305
- - lib/textus/read/schema_envelope.rb
306
- - lib/textus/read/uid.rb
307
- - lib/textus/read/validate_all.rb
308
- - lib/textus/read/validator.rb
309
- - lib/textus/read/where.rb
310
269
  - lib/textus/role.rb
311
- - lib/textus/role_scope.rb
312
270
  - lib/textus/schema.rb
313
271
  - lib/textus/schema/tools.rb
314
272
  - lib/textus/schemas.rb
315
273
  - lib/textus/session.rb
316
274
  - lib/textus/store.rb
275
+ - lib/textus/surfaces/cli.rb
276
+ - lib/textus/surfaces/cli/group.rb
277
+ - lib/textus/surfaces/cli/group/data.rb
278
+ - lib/textus/surfaces/cli/group/key.rb
279
+ - lib/textus/surfaces/cli/group/mcp.rb
280
+ - lib/textus/surfaces/cli/group/rule.rb
281
+ - lib/textus/surfaces/cli/group/schema.rb
282
+ - lib/textus/surfaces/cli/runner.rb
283
+ - lib/textus/surfaces/cli/verb.rb
284
+ - lib/textus/surfaces/cli/verb/doctor.rb
285
+ - lib/textus/surfaces/cli/verb/get.rb
286
+ - lib/textus/surfaces/cli/verb/init.rb
287
+ - lib/textus/surfaces/cli/verb/mcp_serve.rb
288
+ - lib/textus/surfaces/cli/verb/put.rb
289
+ - lib/textus/surfaces/cli/verb/schema_diff.rb
290
+ - lib/textus/surfaces/cli/verb/schema_init.rb
291
+ - lib/textus/surfaces/cli/verb/schema_migrate.rb
292
+ - lib/textus/surfaces/cli/verb/watch.rb
293
+ - lib/textus/surfaces/mcp.rb
294
+ - lib/textus/surfaces/mcp/catalog.rb
295
+ - lib/textus/surfaces/mcp/errors.rb
296
+ - lib/textus/surfaces/mcp/routing.rb
297
+ - lib/textus/surfaces/mcp/server.rb
298
+ - lib/textus/surfaces/mcp/session.rb
299
+ - lib/textus/surfaces/mcp/tool_schemas.rb
300
+ - lib/textus/surfaces/role_scope.rb
301
+ - lib/textus/surfaces/watcher.rb
317
302
  - lib/textus/uid.rb
318
303
  - lib/textus/version.rb
319
- - lib/textus/write/accept.rb
320
- - lib/textus/write/enqueue.rb
321
- - lib/textus/write/key_delete.rb
322
- - lib/textus/write/key_mv.rb
323
- - lib/textus/write/propose.rb
324
- - lib/textus/write/put.rb
325
- - lib/textus/write/reject.rb
304
+ - lib/textus/workflow.rb
305
+ - lib/textus/workflow/collector.rb
306
+ - lib/textus/workflow/context.rb
307
+ - lib/textus/workflow/dsl.rb
308
+ - lib/textus/workflow/loader.rb
309
+ - lib/textus/workflow/pattern.rb
310
+ - lib/textus/workflow/registry.rb
311
+ - lib/textus/workflow/runner.rb
326
312
  homepage: https://github.com/patrick204nqh/textus
327
313
  licenses:
328
314
  - MIT