textus 0.55.1 → 0.56.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 (329) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +175 -2768
  3. data/README.md +17 -18
  4. data/lib/textus/artifact.rb +8 -0
  5. data/lib/textus/errors.rb +2 -39
  6. data/lib/textus/human.rb +7 -0
  7. data/lib/textus/infra/adapters/concurrency_adapter.rb +15 -0
  8. data/lib/textus/infra/clock.rb +7 -0
  9. data/lib/textus/infra/database.rb +127 -0
  10. data/lib/textus/infra/file_system.rb +54 -0
  11. data/lib/textus/infra/locks.rb +50 -0
  12. data/lib/textus/infra/port/clock.rb +18 -0
  13. data/lib/textus/infra/port/publisher.rb +71 -0
  14. data/lib/textus/infra/port/sentinel.rb +25 -0
  15. data/lib/textus/infra/port/sentinel_store.rb +93 -0
  16. data/lib/textus/infra/store/audit_log.rb +42 -0
  17. data/lib/textus/infra/store/base.rb +25 -0
  18. data/lib/textus/infra/store/cursor_store.rb +27 -0
  19. data/lib/textus/infra/store/entry_index.rb +31 -0
  20. data/lib/textus/infra/store/envelope/meta.rb +63 -0
  21. data/lib/textus/infra/store/file_store.rb +23 -0
  22. data/lib/textus/infra/store/index/builder.rb +28 -0
  23. data/lib/textus/infra/store/index/lookup.rb +23 -0
  24. data/lib/textus/infra/store/link_store.rb +43 -0
  25. data/lib/textus/infra/store/workflow_queue.rb +54 -0
  26. data/lib/textus/infra/store.rb +29 -0
  27. data/lib/textus/ingest.rb +8 -0
  28. data/lib/textus/lanes/artifact/cache.rb +35 -0
  29. data/lib/textus/lanes/artifact/handlers.rb +29 -0
  30. data/lib/textus/lanes/artifact/lifecycle.rb +23 -0
  31. data/lib/textus/lanes/artifact/verbs.rb +18 -0
  32. data/lib/textus/lanes/ingest/dedup.rb +17 -0
  33. data/lib/textus/lanes/ingest/entry_types/asset.rb +45 -0
  34. data/lib/textus/lanes/ingest/entry_types/link.rb +16 -0
  35. data/lib/textus/lanes/ingest/entry_types/text.rb +17 -0
  36. data/lib/textus/lanes/ingest/entry_types.rb +25 -0
  37. data/lib/textus/lanes/ingest/handlers.rb +54 -0
  38. data/lib/textus/lanes/ingest/index_rebuilder.rb +13 -0
  39. data/lib/textus/lanes/ingest/key_builder.rb +33 -0
  40. data/lib/textus/lanes/ingest/resolver.rb +21 -0
  41. data/lib/textus/lanes/knowledge/freeform.rb +15 -0
  42. data/lib/textus/lanes/knowledge/handlers.rb +53 -0
  43. data/lib/textus/lanes/knowledge/runner.rb +45 -0
  44. data/lib/textus/lanes/knowledge/verbs.rb +16 -0
  45. data/lib/textus/lanes/scratchpad/handlers.rb +164 -0
  46. data/lib/textus/lanes/scratchpad/verbs.rb +73 -0
  47. data/lib/textus/{gate → protocol}/binder.rb +2 -10
  48. data/lib/textus/protocol/boot/catalog.rb +34 -0
  49. data/lib/textus/protocol/boot/context.rb +124 -0
  50. data/lib/textus/protocol/boot/protocol.rb +33 -0
  51. data/lib/textus/protocol/boot.rb +42 -0
  52. data/lib/textus/protocol/builder/domain.rb +17 -0
  53. data/lib/textus/protocol/builder/infrastructure.rb +17 -0
  54. data/lib/textus/protocol/builder/runtime.rb +46 -0
  55. data/lib/textus/protocol/builder.rb +11 -0
  56. data/lib/textus/protocol/container.rb +22 -0
  57. data/lib/textus/protocol/diff.rb +170 -0
  58. data/lib/textus/protocol/entry_constraint.rb +54 -0
  59. data/lib/textus/protocol/envelope.rb +70 -0
  60. data/lib/textus/protocol/format/base.rb +80 -0
  61. data/lib/textus/protocol/format/bash.rb +10 -0
  62. data/lib/textus/protocol/format/javascript.rb +10 -0
  63. data/lib/textus/protocol/format/json.rb +82 -0
  64. data/lib/textus/protocol/format/markdown.rb +53 -0
  65. data/lib/textus/protocol/format/python.rb +10 -0
  66. data/lib/textus/protocol/format/ruby.rb +10 -0
  67. data/lib/textus/protocol/format/script.rb +55 -0
  68. data/lib/textus/protocol/format/text.rb +48 -0
  69. data/lib/textus/protocol/format/yaml.rb +105 -0
  70. data/lib/textus/protocol/format.rb +73 -0
  71. data/lib/textus/protocol/freshness/ttl_evaluator.rb +61 -0
  72. data/lib/textus/protocol/gate.rb +61 -0
  73. data/lib/textus/protocol/handlers/command_context.rb +19 -0
  74. data/lib/textus/protocol/handlers/concern.rb +15 -0
  75. data/lib/textus/protocol/handlers/ops/audit_filter.rb +37 -0
  76. data/lib/textus/protocol/handlers/ops/bulk_planner.rb +38 -0
  77. data/lib/textus/protocol/handlers/ops/data_mv.rb +49 -0
  78. data/lib/textus/protocol/handlers/ops/git_blame.rb +65 -0
  79. data/lib/textus/protocol/handlers/ops/pulse_coordinator.rb +37 -0
  80. data/lib/textus/protocol/handlers/ops/source_expander.rb +41 -0
  81. data/lib/textus/protocol/handlers/query_context.rb +41 -0
  82. data/lib/textus/protocol/handlers/read.rb +67 -0
  83. data/lib/textus/protocol/handlers/system.rb +93 -0
  84. data/lib/textus/protocol/handlers/write.rb +57 -0
  85. data/lib/textus/protocol/init.rb +143 -0
  86. data/lib/textus/protocol/key/grammar.rb +35 -0
  87. data/lib/textus/protocol/key/matching.rb +23 -0
  88. data/lib/textus/protocol/key/path.rb +23 -0
  89. data/lib/textus/protocol/key.rb +25 -0
  90. data/lib/textus/protocol/lane.rb +12 -0
  91. data/lib/textus/{store/geometry.rb → protocol/layout.rb} +32 -22
  92. data/lib/textus/protocol/links/resolver.rb +33 -0
  93. data/lib/textus/protocol/links/uri_rewriter.rb +30 -0
  94. data/lib/textus/protocol/links.rb +10 -0
  95. data/lib/textus/protocol/manifest/data.rb +90 -0
  96. data/lib/textus/protocol/manifest/entry/base.rb +95 -0
  97. data/lib/textus/protocol/manifest/entry/leaf.rb +19 -0
  98. data/lib/textus/protocol/manifest/entry/nested.rb +19 -0
  99. data/lib/textus/protocol/manifest/entry/parser.rb +71 -0
  100. data/lib/textus/protocol/manifest/entry/publish.rb +209 -0
  101. data/lib/textus/protocol/manifest/entry.rb +10 -0
  102. data/lib/textus/protocol/manifest/policy/publish_target.rb +36 -0
  103. data/lib/textus/protocol/manifest/policy.rb +69 -0
  104. data/lib/textus/protocol/manifest/resolver.rb +156 -0
  105. data/lib/textus/protocol/manifest/schema/contract.rb +53 -0
  106. data/lib/textus/protocol/manifest/schema/validator.rb +52 -0
  107. data/lib/textus/protocol/manifest/schema.rb +40 -0
  108. data/lib/textus/protocol/manifest.rb +62 -0
  109. data/lib/textus/protocol/produce/render/context.rb +43 -0
  110. data/lib/textus/protocol/produce/render.rb +44 -0
  111. data/lib/textus/protocol/schema/registry.rb +44 -0
  112. data/lib/textus/protocol/schema/tools.rb +104 -0
  113. data/lib/textus/protocol/schema.rb +117 -0
  114. data/lib/textus/protocol/session/bridge.rb +11 -0
  115. data/lib/textus/protocol/session/context.rb +5 -0
  116. data/lib/textus/protocol/session.rb +87 -0
  117. data/lib/textus/protocol/store_engine/delete.rb +37 -0
  118. data/lib/textus/protocol/store_engine/move.rb +109 -0
  119. data/lib/textus/protocol/store_engine/pipeline.rb +61 -0
  120. data/lib/textus/protocol/store_engine/put.rb +104 -0
  121. data/lib/textus/protocol/store_engine.rb +86 -0
  122. data/lib/textus/protocol/v4.rb +25 -0
  123. data/lib/textus/protocol/verb_registry/arg_spec.rb +19 -0
  124. data/lib/textus/protocol/verb_registry/core_verbs.rb +164 -0
  125. data/lib/textus/protocol/verb_registry/verb_spec.rb +42 -0
  126. data/lib/textus/protocol/verb_registry.rb +42 -0
  127. data/lib/textus/surface/cli/group/{rule.rb → web.rb} +2 -2
  128. data/lib/textus/surface/cli/runner.rb +25 -25
  129. data/lib/textus/surface/cli/sources.rb +1 -3
  130. data/lib/textus/surface/cli/verb/get.rb +3 -6
  131. data/lib/textus/surface/cli/verb/init.rb +1 -1
  132. data/lib/textus/surface/cli/verb/put.rb +8 -6
  133. data/lib/textus/surface/cli/verb/web_serve.rb +22 -0
  134. data/lib/textus/surface/cli.rb +25 -6
  135. data/lib/textus/surface/dispatch.rb +17 -0
  136. data/lib/textus/surface/mcp/adapters/mcp_adapter.rb +28 -0
  137. data/lib/textus/surface/mcp/catalog.rb +26 -62
  138. data/lib/textus/surface/mcp/cursor_manager.rb +22 -0
  139. data/lib/textus/surface/mcp/drift_annotation.rb +19 -0
  140. data/lib/textus/surface/mcp/errors.rb +0 -10
  141. data/lib/textus/surface/mcp/server.rb +46 -46
  142. data/lib/textus/surface/watcher.rb +18 -12
  143. data/lib/textus/surface/web.rb +50 -0
  144. data/lib/textus/value/call.rb +1 -1
  145. data/lib/textus/{core → value}/duration.rb +1 -4
  146. data/lib/textus/value/etag.rb +7 -7
  147. data/lib/textus/value/payload.rb +7 -0
  148. data/lib/textus/version.rb +1 -1
  149. data/lib/textus/workflow/consumer.rb +39 -0
  150. data/lib/textus/workflow/dsl/lifecycle.rb +32 -0
  151. data/lib/textus/workflow/dsl/pattern_matcher.rb +20 -0
  152. data/lib/textus/workflow/dsl/step_builder.rb +56 -0
  153. data/lib/textus/workflow/dsl.rb +111 -21
  154. data/lib/textus/workflow/event_emitter.rb +21 -0
  155. data/lib/textus/workflow/publisher.rb +59 -0
  156. data/lib/textus/workflow/registry.rb +31 -8
  157. data/lib/textus/workflow/runner.rb +25 -61
  158. data/lib/textus/workflow/scheduler.rb +45 -0
  159. data/lib/textus/workflow/step_executor.rb +78 -0
  160. data/lib/textus/workflow/step_helpers.rb +102 -0
  161. data/lib/textus/workflow.rb +2 -5
  162. data/lib/textus.rb +16 -80
  163. metadata +167 -183
  164. data/SPEC.md +0 -908
  165. data/docs/architecture/README.md +0 -273
  166. data/docs/reference/conventions.md +0 -138
  167. data/lib/textus/action/accept.rb +0 -46
  168. data/lib/textus/action/audit.rb +0 -94
  169. data/lib/textus/action/base.rb +0 -42
  170. data/lib/textus/action/blame.rb +0 -79
  171. data/lib/textus/action/boot.rb +0 -15
  172. data/lib/textus/action/data_mv.rb +0 -58
  173. data/lib/textus/action/deps.rb +0 -19
  174. data/lib/textus/action/doctor.rb +0 -17
  175. data/lib/textus/action/drain.rb +0 -31
  176. data/lib/textus/action/enqueue.rb +0 -37
  177. data/lib/textus/action/get.rb +0 -34
  178. data/lib/textus/action/ingest.rb +0 -199
  179. data/lib/textus/action/jobs.rb +0 -27
  180. data/lib/textus/action/key_delete.rb +0 -26
  181. data/lib/textus/action/key_delete_prefix.rb +0 -35
  182. data/lib/textus/action/key_mv.rb +0 -122
  183. data/lib/textus/action/key_mv_prefix.rb +0 -48
  184. data/lib/textus/action/list.rb +0 -28
  185. data/lib/textus/action/propose.rb +0 -42
  186. data/lib/textus/action/published.rb +0 -22
  187. data/lib/textus/action/pulse.rb +0 -49
  188. data/lib/textus/action/put.rb +0 -38
  189. data/lib/textus/action/rdeps.rb +0 -24
  190. data/lib/textus/action/reject.rb +0 -28
  191. data/lib/textus/action/rule_explain.rb +0 -81
  192. data/lib/textus/action/rule_lint.rb +0 -62
  193. data/lib/textus/action/rule_list.rb +0 -38
  194. data/lib/textus/action/schema_envelope.rb +0 -22
  195. data/lib/textus/action/uid.rb +0 -19
  196. data/lib/textus/action/where.rb +0 -21
  197. data/lib/textus/boot.rb +0 -202
  198. data/lib/textus/contract/arg.rb +0 -10
  199. data/lib/textus/contract/dsl.rb +0 -88
  200. data/lib/textus/contract/spec.rb +0 -25
  201. data/lib/textus/contract.rb +0 -12
  202. data/lib/textus/core/freshness/evaluator.rb +0 -150
  203. data/lib/textus/core/freshness/verdict.rb +0 -34
  204. data/lib/textus/core/freshness.rb +0 -11
  205. data/lib/textus/core/retention/sweep.rb +0 -57
  206. data/lib/textus/core/retention.rb +0 -11
  207. data/lib/textus/core/sentinel.rb +0 -30
  208. data/lib/textus/doctor/check/audit_log.rb +0 -34
  209. data/lib/textus/doctor/check/generator_drift.rb +0 -29
  210. data/lib/textus/doctor/check/illegal_keys.rb +0 -60
  211. data/lib/textus/doctor/check/manifest_files.rb +0 -25
  212. data/lib/textus/doctor/check/notebook_sources.rb +0 -50
  213. data/lib/textus/doctor/check/orphaned_publish_targets.rb +0 -35
  214. data/lib/textus/doctor/check/proposal_targets.rb +0 -45
  215. data/lib/textus/doctor/check/protocol_version.rb +0 -47
  216. data/lib/textus/doctor/check/publish_tree_index_overlap.rb +0 -48
  217. data/lib/textus/doctor/check/raw_asset_paths.rb +0 -50
  218. data/lib/textus/doctor/check/rule_ambiguity.rb +0 -49
  219. data/lib/textus/doctor/check/schema_parse_error.rb +0 -28
  220. data/lib/textus/doctor/check/schema_violations.rb +0 -28
  221. data/lib/textus/doctor/check/schemas.rb +0 -26
  222. data/lib/textus/doctor/check/sentinels.rb +0 -60
  223. data/lib/textus/doctor/check/stale_reviewed_stamp.rb +0 -54
  224. data/lib/textus/doctor/check/templates.rb +0 -29
  225. data/lib/textus/doctor/check/unowned_schema_fields.rb +0 -40
  226. data/lib/textus/doctor/check.rb +0 -41
  227. data/lib/textus/doctor/validator.rb +0 -93
  228. data/lib/textus/doctor.rb +0 -54
  229. data/lib/textus/format/base.rb +0 -49
  230. data/lib/textus/format/json.rb +0 -102
  231. data/lib/textus/format/markdown.rb +0 -80
  232. data/lib/textus/format/shared.rb +0 -17
  233. data/lib/textus/format/text.rb +0 -55
  234. data/lib/textus/format/yaml.rb +0 -125
  235. data/lib/textus/format.rb +0 -46
  236. data/lib/textus/gate/auth.rb +0 -212
  237. data/lib/textus/gate.rb +0 -92
  238. data/lib/textus/init.rb +0 -157
  239. data/lib/textus/jobs.rb +0 -9
  240. data/lib/textus/key/distance.rb +0 -55
  241. data/lib/textus/key/grammar.rb +0 -33
  242. data/lib/textus/key/matching.rb +0 -24
  243. data/lib/textus/key/path.rb +0 -28
  244. data/lib/textus/manifest/capabilities.rb +0 -29
  245. data/lib/textus/manifest/data.rb +0 -102
  246. data/lib/textus/manifest/entry/base.rb +0 -105
  247. data/lib/textus/manifest/entry/ignore_matcher.rb +0 -46
  248. data/lib/textus/manifest/entry/leaf.rb +0 -17
  249. data/lib/textus/manifest/entry/nested.rb +0 -37
  250. data/lib/textus/manifest/entry/parser.rb +0 -99
  251. data/lib/textus/manifest/entry/produced.rb +0 -36
  252. data/lib/textus/manifest/entry/publish/mode.rb +0 -45
  253. data/lib/textus/manifest/entry/publish/none.rb +0 -14
  254. data/lib/textus/manifest/entry/publish/subtree_mirror.rb +0 -73
  255. data/lib/textus/manifest/entry/publish/template.rb +0 -16
  256. data/lib/textus/manifest/entry/publish/to_paths.rb +0 -76
  257. data/lib/textus/manifest/entry/publish/tree.rb +0 -50
  258. data/lib/textus/manifest/entry/publish.rb +0 -45
  259. data/lib/textus/manifest/entry/validators/format_matrix.rb +0 -21
  260. data/lib/textus/manifest/entry/validators/ignore.rb +0 -28
  261. data/lib/textus/manifest/entry/validators/publish.rb +0 -30
  262. data/lib/textus/manifest/entry/validators.rb +0 -18
  263. data/lib/textus/manifest/entry.rb +0 -8
  264. data/lib/textus/manifest/policy/matcher.rb +0 -51
  265. data/lib/textus/manifest/policy/publish_target.rb +0 -34
  266. data/lib/textus/manifest/policy/react.rb +0 -30
  267. data/lib/textus/manifest/policy/retention.rb +0 -29
  268. data/lib/textus/manifest/policy/source.rb +0 -30
  269. data/lib/textus/manifest/policy.rb +0 -95
  270. data/lib/textus/manifest/resolver.rb +0 -118
  271. data/lib/textus/manifest/rules.rb +0 -86
  272. data/lib/textus/manifest/schema/contract.rb +0 -61
  273. data/lib/textus/manifest/schema/keys.rb +0 -90
  274. data/lib/textus/manifest/schema/semantics/cross_field.rb +0 -53
  275. data/lib/textus/manifest/schema/semantics/invariants.rb +0 -125
  276. data/lib/textus/manifest/schema/semantics/migration.rb +0 -83
  277. data/lib/textus/manifest/schema/semantics.rb +0 -27
  278. data/lib/textus/manifest/schema/validator.rb +0 -48
  279. data/lib/textus/manifest/schema/vocabulary.rb +0 -25
  280. data/lib/textus/manifest/schema.rb +0 -30
  281. data/lib/textus/manifest.rb +0 -65
  282. data/lib/textus/meta.rb +0 -54
  283. data/lib/textus/port/audit_log.rb +0 -268
  284. data/lib/textus/port/build_lock.rb +0 -64
  285. data/lib/textus/port/clock.rb +0 -10
  286. data/lib/textus/port/publisher.rb +0 -60
  287. data/lib/textus/port/sentinel_store.rb +0 -83
  288. data/lib/textus/port/storage/file_stat.rb +0 -19
  289. data/lib/textus/port/storage/file_store.rb +0 -26
  290. data/lib/textus/port/store.rb +0 -93
  291. data/lib/textus/port/watcher_lock.rb +0 -48
  292. data/lib/textus/produce/engine.rb +0 -50
  293. data/lib/textus/produce/render.rb +0 -20
  294. data/lib/textus/schema/tools.rb +0 -115
  295. data/lib/textus/schema.rb +0 -100
  296. data/lib/textus/schemas.rb +0 -54
  297. data/lib/textus/store/compositor.rb +0 -34
  298. data/lib/textus/store/container.rb +0 -43
  299. data/lib/textus/store/cursor.rb +0 -26
  300. data/lib/textus/store/envelope/reader.rb +0 -43
  301. data/lib/textus/store/envelope/writer.rb +0 -195
  302. data/lib/textus/store/index/builder.rb +0 -74
  303. data/lib/textus/store/index/lookup.rb +0 -60
  304. data/lib/textus/store/jobs/base.rb +0 -13
  305. data/lib/textus/store/jobs/index.rb +0 -15
  306. data/lib/textus/store/jobs/materialize.rb +0 -15
  307. data/lib/textus/store/jobs/plan.rb +0 -11
  308. data/lib/textus/store/jobs/planner.rb +0 -104
  309. data/lib/textus/store/jobs/queue.rb +0 -154
  310. data/lib/textus/store/jobs/registry.rb +0 -19
  311. data/lib/textus/store/jobs/retention.rb +0 -50
  312. data/lib/textus/store/jobs/sweep.rb +0 -21
  313. data/lib/textus/store/jobs/worker.rb +0 -64
  314. data/lib/textus/store/session.rb +0 -37
  315. data/lib/textus/store.rb +0 -99
  316. data/lib/textus/surface/cli/verb/doctor.rb +0 -19
  317. data/lib/textus/surface/cli/verb/schema_diff.rb +0 -17
  318. data/lib/textus/surface/cli/verb/schema_init.rb +0 -21
  319. data/lib/textus/surface/cli/verb/schema_migrate.rb +0 -21
  320. data/lib/textus/surface/mcp.rb +0 -8
  321. data/lib/textus/surface/projector.rb +0 -27
  322. data/lib/textus/surface/role_scope.rb +0 -34
  323. data/lib/textus/value/command.rb +0 -16
  324. data/lib/textus/value/envelope.rb +0 -89
  325. data/lib/textus/value/result.rb +0 -26
  326. data/lib/textus/workflow/collector.rb +0 -27
  327. data/lib/textus/workflow/context.rb +0 -5
  328. data/lib/textus/workflow/loader.rb +0 -17
  329. data/lib/textus/workflow/pattern.rb +0 -18
@@ -1,50 +0,0 @@
1
- require "fileutils"
2
-
3
- module Textus
4
- class Store
5
- module Jobs
6
- class Retention
7
- def initialize(container:, call:)
8
- @container = container
9
- @call = call
10
- end
11
-
12
- def call(rows)
13
- out = { dropped: [], archived: [], failed: [] }
14
- rows.each do |row|
15
- key = row["key"]
16
- begin
17
- case row["action"]
18
- when "drop"
19
- delete(key)
20
- out[:dropped] << key
21
- when "archive"
22
- archive_leaf(row)
23
- delete(key)
24
- out[:archived] << key
25
- end
26
- rescue Textus::Error => e
27
- out[:failed] << { "key" => key, "error" => e.message }
28
- end
29
- end
30
- out
31
- end
32
-
33
- private
34
-
35
- def archive_leaf(row)
36
- src = row["path"]
37
- root = @container.root.to_s
38
- rel = src.delete_prefix("#{root}/")
39
- dest = File.join(root, "archive", rel)
40
- FileUtils.mkdir_p(File.dirname(dest))
41
- FileUtils.cp(src, dest)
42
- end
43
-
44
- def delete(key)
45
- Textus::Action::KeyDelete.call(container: @container, call: @call, key: key)
46
- end
47
- end
48
- end
49
- end
50
- end
@@ -1,21 +0,0 @@
1
- module Textus
2
- class Store
3
- module Jobs
4
- class Sweep < Base
5
- REQUIRED_ROLE = Textus::Value::Role::AUTOMATION
6
- TYPE = "sweep"
7
-
8
- def self.call(container:, call:, scope: {}, key: nil)
9
- prefix = key || (scope.is_a?(Hash) ? scope["prefix"] : nil)
10
- lane = scope.is_a?(Hash) ? scope["lane"] : nil
11
- rows = Textus::Core::Retention::Sweep.new(
12
- manifest: container.manifest,
13
- file_stat: Textus::Port::Storage::FileStat.new,
14
- clock: Textus::Port::Clock.new,
15
- ).call(prefix: prefix, lane: lane)
16
- Textus::Store::Jobs::Retention.new(container: container, call: call).call(rows)
17
- end
18
- end
19
- end
20
- end
21
- end
@@ -1,64 +0,0 @@
1
- module Textus
2
- class Store
3
- module Jobs
4
- class Worker
5
- Summary = Struct.new(:completed, :failed, keyword_init: true)
6
-
7
- def self.for(container:, queue:)
8
- new(queue: queue, container: container,
9
- lease_ttl: container.manifest.data.worker_config[:lease_ttl])
10
- end
11
-
12
- def initialize(queue:, container:, lease_ttl: 60)
13
- @queue = queue
14
- @container = container
15
- @lease_ttl = lease_ttl
16
- end
17
-
18
- def drain(worker_id: "drain-#{Process.pid}")
19
- completed = 0
20
- failed = 0
21
- loop do
22
- leased = @queue.lease(worker_id: worker_id, lease_ttl: @lease_ttl)
23
- break unless leased
24
-
25
- case run_one(leased)
26
- when :completed then completed += 1
27
- when :dead_lettered then failed += 1
28
- end
29
- end
30
- Summary.new(completed: completed, failed: failed)
31
- end
32
-
33
- def drain_pool(pool: 4)
34
- summaries = []
35
- mutex = Mutex.new
36
- threads = Array.new(pool) do |i|
37
- Thread.new do
38
- s = drain(worker_id: "pool-#{Process.pid}-#{i}")
39
- mutex.synchronize { summaries << s }
40
- end
41
- end
42
- threads.each(&:join)
43
- Summary.new(completed: summaries.sum(&:completed), failed: summaries.sum(&:failed))
44
- end
45
-
46
- private
47
-
48
- def run_one(leased)
49
- job = leased.job
50
- klass = Textus::Jobs.fetch(job.type)
51
- call = Textus::Value::Call.build(
52
- role: job.role || Textus::Value::Role::AUTOMATION,
53
- correlation_id: SecureRandom.uuid,
54
- )
55
- klass.call(container: @container, call: call, **job.args.transform_keys(&:to_sym))
56
- @queue.ack(leased)
57
- :completed
58
- rescue StandardError => e
59
- @queue.fail(leased, error: e.message)
60
- end
61
- end
62
- end
63
- end
64
- end
@@ -1,37 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "dry-struct"
4
-
5
- module Textus
6
- # The agent session: per-connection (MCP), per-process (CLI), or per-loop
7
- # (Ruby) orientation state — the audit cursor plus the contract etag and
8
- # propose_lane captured at boot. Immutable Dry::Struct::Value; advance_cursor
9
- # and with return new instances. ADR 0036; contract_etag widened in ADR 0074.
10
- class Store
11
- class Session < Dry::Struct
12
- attribute :role, Value::Types::RoleName
13
- attribute :cursor, Value::Types::Cursor
14
- attribute :propose_lane, Value::Types::String.optional
15
- attribute :contract_etag, Value::Types::String
16
-
17
- def with(**attrs) = self.class.new(to_h.merge(attrs))
18
-
19
- def advance_cursor(new_cursor) = with(cursor: new_cursor)
20
-
21
- def check_etag!(observed_etag)
22
- return if observed_etag == contract_etag
23
-
24
- raise Textus::ContractDrift.new(
25
- "contract changed (manifest/hooks/schemas were #{short_etag(contract_etag)}, " \
26
- "now #{short_etag(observed_etag)}); re-run boot",
27
- )
28
- end
29
-
30
- private
31
-
32
- # First 8 hex chars after the "sha256:" prefix — a stable short id for
33
- # the drift diagnostic.
34
- def short_etag(etag) = etag.to_s.delete_prefix("sha256:")[0, 8]
35
- end
36
- end
37
- end
data/lib/textus/store.rb DELETED
@@ -1,99 +0,0 @@
1
- require "fileutils"
2
-
3
- module Textus
4
- class Store
5
- attr_reader :container
6
-
7
- # Readers are derived from the Container's schema, so the field set lives
8
- # in exactly one place (Container). A new capability added there is
9
- # automatically exposed on the Store.
10
- Textus::Store::Container.attribute_names.each do |field|
11
- define_method(field) { @container.public_send(field) }
12
- end
13
-
14
- def self.discover(start_dir = Dir.pwd, root: nil)
15
- explicit = root || ENV.fetch("TEXTUS_ROOT", nil)
16
- return discover_explicit(explicit) if explicit
17
-
18
- ascend_for_store(File.expand_path(start_dir)) ||
19
- raise(IoError.new("no .textus directory found from #{start_dir}"))
20
- end
21
-
22
- private_class_method def self.ascend_for_store(dir)
23
- loop do
24
- candidate = File.join(dir, ".textus")
25
- return new(candidate) if store_dir?(candidate)
26
-
27
- parent = File.dirname(dir)
28
- return nil if parent == dir
29
-
30
- dir = parent
31
- end
32
- end
33
-
34
- private_class_method def self.discover_explicit(root_arg)
35
- abs = File.expand_path(root_arg)
36
- raise IoError.new("no textus store at #{abs}") unless store_dir?(abs)
37
-
38
- new(abs)
39
- end
40
-
41
- private_class_method def self.store_dir?(dir)
42
- File.directory?(dir) && File.exist?(File.join(dir, "manifest.yaml"))
43
- end
44
-
45
- def initialize(root)
46
- @container = build_container(File.expand_path(root))
47
- end
48
-
49
- # Build an agent Session oriented at the current cursor/manifest — the
50
- # Ruby equivalent of an MCP `initialize`. ADR 0036.
51
- def session(role:)
52
- Textus::Store::Session.new(
53
- role: role.to_s,
54
- cursor: audit_log.latest_seq,
55
- propose_lane: manifest.policy.propose_lane_for(role),
56
- contract_etag: Textus::Value::Etag.for_contract(root),
57
- )
58
- end
59
-
60
- def gate
61
- @container.gate
62
- end
63
-
64
- def as(role, dry_run: false, correlation_id: nil)
65
- Textus::Surface::RoleScope.new(container: container, role: role, dry_run: dry_run, correlation_id: correlation_id)
66
- end
67
-
68
- private
69
-
70
- def build_container(root)
71
- manifest = Manifest.load(root)
72
- job_store = Port::Store.new(root: root).setup!
73
- geometry = Store::Geometry.new(root)
74
- infra = Container::Infrastructure.new(
75
- file_store: Port::Storage::FileStore.new,
76
- schemas: Schemas.new(geometry.schemas_dir),
77
- audit_log: Port::AuditLog.new(
78
- root,
79
- max_size: manifest.data.audit_config[:max_size],
80
- keep: manifest.data.audit_config[:keep],
81
- ),
82
- job_store:,
83
- geometry:,
84
- )
85
-
86
- coord = Container::Coordination.new(
87
- manifest:,
88
- workflows: Workflow::Loader.load_all(root),
89
- gate: nil,
90
- compositor: nil,
91
- )
92
-
93
- container = Container.new(infra, coord)
94
- compositor = Store::Compositor.new(container)
95
- gate = Textus::Gate.new(container)
96
- container.wire_gate!(gate, compositor)
97
- end
98
- end
99
- end
@@ -1,19 +0,0 @@
1
- module Textus
2
- module Surface
3
- class CLI
4
- class Verb
5
- class Doctor < Verb
6
- command_name "doctor"
7
- option :checks, "--check=NAME"
8
-
9
- def call(store)
10
- spec = Textus::Action::Doctor.contract
11
- inputs = { checks: checks&.split(",")&.map(&:strip) }
12
- res = store.gate.dispatch(spec: spec, inputs: inputs, role: resolved_role(store))
13
- emit(res, exit_code: res["ok"] ? 0 : 1)
14
- end
15
- end
16
- end
17
- end
18
- end
19
- end
@@ -1,17 +0,0 @@
1
- module Textus
2
- module Surface
3
- class CLI
4
- class Verb
5
- class SchemaDiff < Verb
6
- command_name "diff"
7
- parent_group Group::Schema
8
-
9
- def call(store)
10
- name = positional.shift or raise UsageError.new("schema diff NAME")
11
- emit(Textus::Schema::Tools.diff(store, name: name))
12
- end
13
- end
14
- end
15
- end
16
- end
17
- end
@@ -1,21 +0,0 @@
1
- module Textus
2
- module Surface
3
- class CLI
4
- class Verb
5
- class SchemaInit < Verb
6
- command_name "init"
7
- parent_group Group::Schema
8
-
9
- option :from_key, "--from=KEY"
10
-
11
- def call(store)
12
- name = positional.shift or raise UsageError.new("schema init NAME")
13
- raise UsageError.new("schema init requires --from=KEY") unless from_key
14
-
15
- emit(Textus::Schema::Tools.init(store, name: name, from: from_key))
16
- end
17
- end
18
- end
19
- end
20
- end
21
- end
@@ -1,21 +0,0 @@
1
- module Textus
2
- module Surface
3
- class CLI
4
- class Verb
5
- class SchemaMigrate < Verb
6
- command_name "migrate"
7
- parent_group Group::Schema
8
-
9
- option :rename, "--rename=O:N"
10
-
11
- def call(store)
12
- name = positional.shift or raise UsageError.new("schema migrate NAME")
13
- raise UsageError.new("schema migrate requires --rename=OLD:NEW") unless rename
14
-
15
- emit(Textus::Schema::Tools.migrate(store, name: name, rename: rename))
16
- end
17
- end
18
- end
19
- end
20
- end
21
- end
@@ -1,8 +0,0 @@
1
- module Textus
2
- module Surface
3
- # The agent gate. Stdio JSON-RPC 2.0 server speaking MCP draft 2024-11-05.
4
- # Wraps Textus::Surface::RoleScope as auto-derived tools. See ADR 0015.
5
- module MCP
6
- end
7
- end
8
- end
@@ -1,27 +0,0 @@
1
- module Textus
2
- module Surface
3
- class Projector
4
- def initialize(view_key: :default, binder_method: :inputs_from_wire)
5
- @view_key = view_key
6
- @binder_method = binder_method
7
- end
8
-
9
- def verbs(action_verbs = Textus::Action::VERBS)
10
- action_verbs.select do |_verb, klass|
11
- klass.respond_to?(:contract?) && klass.contract?
12
- end
13
- end
14
-
15
- def names(action_verbs = Textus::Action::VERBS)
16
- verbs(action_verbs).keys.map(&:to_s)
17
- end
18
-
19
- def dispatch(verb_name, inputs:, store:, role:, session: nil)
20
- klass = Textus::Action::VERBS.fetch(verb_name.to_sym)
21
- spec = klass.contract
22
- bound = Textus::Gate::Binder.public_send(@binder_method, spec, inputs)
23
- store.gate.dispatch(spec:, inputs: bound, role:, session:, surface: @view_key)
24
- end
25
- end
26
- end
27
- end
@@ -1,34 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Textus
4
- module Surface
5
- # Role-scoped identity carrier. Holds the acting identity (role,
6
- # correlation_id, dry_run) bound to a container. All verb methods
7
- # (put, get, accept, ...) are injected by textus.rb's define_method
8
- # loop, which dispatches directly through Gate.
9
- class RoleScope
10
- attr_reader :container, :role, :correlation_id
11
-
12
- def initialize(container:, role:, dry_run: false, correlation_id: nil)
13
- @container = container
14
- @role = role.to_s
15
- @dry_run = dry_run
16
- @correlation_id = correlation_id || SecureRandom.uuid
17
- end
18
-
19
- def dry_run? = !!@dry_run
20
-
21
- def with_role(role)
22
- self.class.new(container: @container, role:, dry_run: @dry_run, correlation_id: @correlation_id)
23
- end
24
-
25
- def with_correlation_id(cid)
26
- self.class.new(container: @container, role: @role, dry_run: @dry_run, correlation_id: cid)
27
- end
28
-
29
- def with_dry_run
30
- self.class.new(container: @container, role: @role, dry_run: true, correlation_id: @correlation_id)
31
- end
32
- end
33
- end
34
- end
@@ -1,16 +0,0 @@
1
- module Textus
2
- module Value
3
- Command = Data.define(:verb, :params, :role) do
4
- def initialize(verb:, params:, role:)
5
- super
6
- params.freeze
7
- freeze
8
- end
9
-
10
- def [](key) = params[key]
11
- def key = params[:key]
12
- def pending_key = params[:pending_key]
13
- def dry_run = params[:dry_run]
14
- end
15
- end
16
- end
@@ -1,89 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "dry-struct"
4
-
5
- module Textus
6
- module Value
7
- class Envelope < Dry::Struct
8
- attribute :protocol, Types::String
9
- attribute :key, Types::String
10
- attribute :lane, Types::String
11
- attribute :owner, Types::String.optional
12
- attribute :path, Types::String
13
- attribute :format, Types::FormatName
14
- attribute :etag, Types::String
15
- attribute :uid, Types::String.optional
16
- attribute :sources, Types::Array.of(Types::String).optional
17
- attribute :schema_ref, Types::String.optional
18
- attribute :meta, Types::Hash.default({}.freeze)
19
- attribute :body, Types::String.optional
20
- attribute :content, Types::Any.optional
21
- attribute :freshness, Types::Any.optional
22
-
23
- # rubocop:disable Metrics/ParameterLists
24
- def self.build(key:, mentry:, path:, meta:, body:, etag:, content: nil, freshness: nil)
25
- # rubocop:enable Metrics/ParameterLists
26
- new(
27
- protocol: Textus::PROTOCOL,
28
- key: key,
29
- lane: mentry.lane,
30
- owner: mentry.owner,
31
- path: path,
32
- format: mentry.format,
33
- uid: extract_uid(meta),
34
- sources: extract_sources(meta),
35
- etag: etag,
36
- schema_ref: mentry.schema,
37
- meta: meta,
38
- body: body,
39
- content: content,
40
- freshness: freshness,
41
- )
42
- end
43
-
44
- def self.extract_uid(meta)
45
- v = meta.is_a?(Hash) ? meta["uid"] : nil
46
- v.is_a?(String) ? v : nil
47
- end
48
-
49
- def self.extract_sources(meta)
50
- v = meta.is_a?(Hash) ? meta["sources"] : nil
51
- v.is_a?(Array) && !v.empty? ? v : nil
52
- end
53
-
54
- def with(**attrs) = self.class.new(to_h.merge(attrs))
55
-
56
- def to_h_for_wire
57
- h = {
58
- "protocol" => protocol,
59
- "key" => key,
60
- "lane" => lane,
61
- "owner" => owner,
62
- "path" => path,
63
- "format" => format,
64
- "_meta" => meta,
65
- "body" => body,
66
- "etag" => etag,
67
- "schema_ref" => schema_ref,
68
- "uid" => uid,
69
- }
70
- h["sources"] = sources if sources
71
- h["content"] = content unless content.nil?
72
- freshness&.to_h_for_wire&.each { |k, v| h[k] = v }
73
- h
74
- end
75
-
76
- def stale?
77
- return false if freshness.nil?
78
-
79
- freshness.stale == true
80
- end
81
-
82
- def fetching?
83
- return false if freshness.nil?
84
-
85
- freshness.fetching == true
86
- end
87
- end
88
- end
89
- end
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Textus
4
- module Value
5
- # Unwraps Dry::Monads results at the Gate seam.
6
- # Every action returns Success(value) or Failure(code:, message:, details:).
7
- # This module converts Failure into an ActionError for surfaces (CLI, MCP)
8
- # that expect exceptions.
9
- module Result
10
- def self.unwrap(result)
11
- case result
12
- when Dry::Monads::Result::Success then result.value!
13
- when Dry::Monads::Result::Failure
14
- failure = result.failure
15
- raise ActionError.new(
16
- failure[:code] || :internal,
17
- failure[:message] || "action failed",
18
- details: failure[:details] || {},
19
- )
20
- else
21
- result
22
- end
23
- end
24
- end
25
- end
26
- end
@@ -1,27 +0,0 @@
1
- module Textus
2
- module Workflow
3
- class Collector
4
- @current = nil
5
-
6
- class << self
7
- attr_reader :current
8
- end
9
-
10
- def self.with(collector)
11
- prev = @current
12
- @current = collector
13
- yield
14
- ensure
15
- @current = prev
16
- end
17
-
18
- def initialize(registry)
19
- @registry = registry
20
- end
21
-
22
- def register(defn)
23
- @registry.register(defn)
24
- end
25
- end
26
- end
27
- end
@@ -1,5 +0,0 @@
1
- module Textus
2
- module Workflow
3
- Context = Data.define(:key, :entry, :config, :lane, :container, :call)
4
- end
5
- end
@@ -1,17 +0,0 @@
1
- module Textus
2
- module Workflow
3
- class Loader
4
- def self.load_all(root)
5
- geometry = Textus::Store::Geometry.new(root)
6
- registry = Registry.new
7
- return registry unless File.directory?(geometry.workflow_dir)
8
-
9
- collector = Collector.new(registry)
10
- Collector.with(collector) do
11
- Dir.glob(File.join(geometry.workflow_dir, "**", "*.rb")).each { |path| load path }
12
- end
13
- registry
14
- end
15
- end
16
- end
17
- end
@@ -1,18 +0,0 @@
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