textus 0.55.2 → 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 (338) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +175 -2768
  3. data/README.md +8 -9
  4. data/lib/textus/artifact.rb +8 -0
  5. data/lib/textus/errors.rb +0 -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/{dispatch → protocol}/binder.rb +1 -7
  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/{store → protocol}/freshness/ttl_evaluator.rb +7 -3
  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 → protocol}/layout.rb +16 -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 +22 -21
  129. data/lib/textus/surface/cli/sources.rb +1 -3
  130. data/lib/textus/surface/cli/verb/get.rb +2 -7
  131. data/lib/textus/surface/cli/verb/init.rb +1 -1
  132. data/lib/textus/surface/cli/verb/put.rb +7 -7
  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 +12 -12
  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/server.rb +35 -24
  141. data/lib/textus/surface/watcher.rb +18 -12
  142. data/lib/textus/surface/web.rb +50 -0
  143. data/lib/textus/value/call.rb +1 -1
  144. data/lib/textus/value/etag.rb +7 -7
  145. data/lib/textus/version.rb +1 -1
  146. data/lib/textus/workflow/consumer.rb +39 -0
  147. data/lib/textus/workflow/dsl/lifecycle.rb +32 -0
  148. data/lib/textus/workflow/dsl/pattern_matcher.rb +20 -0
  149. data/lib/textus/workflow/dsl/step_builder.rb +56 -0
  150. data/lib/textus/workflow/dsl.rb +111 -21
  151. data/lib/textus/workflow/event_emitter.rb +21 -0
  152. data/lib/textus/workflow/publisher.rb +59 -0
  153. data/lib/textus/workflow/registry.rb +31 -8
  154. data/lib/textus/workflow/runner.rb +25 -53
  155. data/lib/textus/workflow/scheduler.rb +45 -0
  156. data/lib/textus/workflow/step_executor.rb +78 -0
  157. data/lib/textus/workflow/step_helpers.rb +102 -0
  158. data/lib/textus/workflow.rb +2 -5
  159. data/lib/textus.rb +16 -16
  160. metadata +169 -185
  161. data/SPEC.md +0 -909
  162. data/docs/architecture/README.md +0 -273
  163. data/docs/reference/conventions.md +0 -141
  164. data/lib/textus/boot.rb +0 -181
  165. data/lib/textus/dispatch/contracts.rb +0 -63
  166. data/lib/textus/dispatch/handler_registry.rb +0 -21
  167. data/lib/textus/dispatch/middleware/audit_index.rb +0 -51
  168. data/lib/textus/dispatch/middleware/auth.rb +0 -40
  169. data/lib/textus/dispatch/middleware/base.rb +0 -26
  170. data/lib/textus/dispatch/middleware/binder.rb +0 -20
  171. data/lib/textus/dispatch/middleware/cascade.rb +0 -53
  172. data/lib/textus/dispatch/pipeline.rb +0 -35
  173. data/lib/textus/doctor/check/audit_log.rb +0 -34
  174. data/lib/textus/doctor/check/generator_drift.rb +0 -29
  175. data/lib/textus/doctor/check/illegal_keys.rb +0 -60
  176. data/lib/textus/doctor/check/manifest_files.rb +0 -25
  177. data/lib/textus/doctor/check/orphaned_publish_targets.rb +0 -35
  178. data/lib/textus/doctor/check/proposal_targets.rb +0 -45
  179. data/lib/textus/doctor/check/protocol_version.rb +0 -47
  180. data/lib/textus/doctor/check/publish_tree_index_overlap.rb +0 -48
  181. data/lib/textus/doctor/check/raw_asset_paths.rb +0 -50
  182. data/lib/textus/doctor/check/rule_ambiguity.rb +0 -49
  183. data/lib/textus/doctor/check/schema_parse_error.rb +0 -28
  184. data/lib/textus/doctor/check/schema_violations.rb +0 -28
  185. data/lib/textus/doctor/check/schemas.rb +0 -26
  186. data/lib/textus/doctor/check/scratchpad_sources.rb +0 -55
  187. data/lib/textus/doctor/check/sentinels.rb +0 -60
  188. data/lib/textus/doctor/check/stale_reviewed_stamp.rb +0 -54
  189. data/lib/textus/doctor/check/templates.rb +0 -29
  190. data/lib/textus/doctor/check/unowned_schema_fields.rb +0 -40
  191. data/lib/textus/doctor/check.rb +0 -43
  192. data/lib/textus/doctor/validator.rb +0 -93
  193. data/lib/textus/doctor.rb +0 -54
  194. data/lib/textus/format/base.rb +0 -77
  195. data/lib/textus/format/json.rb +0 -81
  196. data/lib/textus/format/markdown.rb +0 -59
  197. data/lib/textus/format/text.rb +0 -55
  198. data/lib/textus/format/yaml.rb +0 -104
  199. data/lib/textus/format.rb +0 -61
  200. data/lib/textus/handlers/maintenance/boot_store.rb +0 -15
  201. data/lib/textus/handlers/maintenance/doctor_store.rb +0 -15
  202. data/lib/textus/handlers/maintenance/drain_store.rb +0 -21
  203. data/lib/textus/handlers/maintenance/ingest_entry.rb +0 -159
  204. data/lib/textus/handlers/maintenance/jobs_action.rb +0 -21
  205. data/lib/textus/handlers/maintenance/published_entries.rb +0 -17
  206. data/lib/textus/handlers/maintenance/rule_explain.rb +0 -77
  207. data/lib/textus/handlers/maintenance/rule_lint.rb +0 -54
  208. data/lib/textus/handlers/maintenance/rule_list.rb +0 -32
  209. data/lib/textus/handlers/maintenance/schema_envelope.rb +0 -19
  210. data/lib/textus/handlers/read/audit_entries.rb +0 -48
  211. data/lib/textus/handlers/read/blame_entry.rb +0 -71
  212. data/lib/textus/handlers/read/deps_entry.rb +0 -17
  213. data/lib/textus/handlers/read/get_entry.rb +0 -68
  214. data/lib/textus/handlers/read/list_keys.rb +0 -36
  215. data/lib/textus/handlers/read/pulse_entries.rb +0 -66
  216. data/lib/textus/handlers/read/rdeps_entry.rb +0 -21
  217. data/lib/textus/handlers/read/uid_entry.rb +0 -18
  218. data/lib/textus/handlers/read/where_entry.rb +0 -18
  219. data/lib/textus/handlers/write/accept_proposal.rb +0 -39
  220. data/lib/textus/handlers/write/data_mv.rb +0 -55
  221. data/lib/textus/handlers/write/delete_key.rb +0 -17
  222. data/lib/textus/handlers/write/enqueue_job.rb +0 -27
  223. data/lib/textus/handlers/write/key_delete_prefix.rb +0 -32
  224. data/lib/textus/handlers/write/key_mv_prefix.rb +0 -45
  225. data/lib/textus/handlers/write/move_key.rb +0 -80
  226. data/lib/textus/handlers/write/propose_entry.rb +0 -29
  227. data/lib/textus/handlers/write/put_entry.rb +0 -29
  228. data/lib/textus/handlers/write/reject_proposal.rb +0 -29
  229. data/lib/textus/init.rb +0 -157
  230. data/lib/textus/jobs.rb +0 -9
  231. data/lib/textus/key/distance.rb +0 -55
  232. data/lib/textus/key/grammar.rb +0 -33
  233. data/lib/textus/key/matching.rb +0 -24
  234. data/lib/textus/key/path.rb +0 -28
  235. data/lib/textus/manifest/capabilities.rb +0 -29
  236. data/lib/textus/manifest/data.rb +0 -102
  237. data/lib/textus/manifest/entry/base.rb +0 -105
  238. data/lib/textus/manifest/entry/ignore_matcher.rb +0 -46
  239. data/lib/textus/manifest/entry/leaf.rb +0 -17
  240. data/lib/textus/manifest/entry/nested.rb +0 -37
  241. data/lib/textus/manifest/entry/parser.rb +0 -99
  242. data/lib/textus/manifest/entry/produced.rb +0 -36
  243. data/lib/textus/manifest/entry/publish/mode.rb +0 -45
  244. data/lib/textus/manifest/entry/publish/none.rb +0 -14
  245. data/lib/textus/manifest/entry/publish/subtree_mirror.rb +0 -73
  246. data/lib/textus/manifest/entry/publish/template.rb +0 -16
  247. data/lib/textus/manifest/entry/publish/to_paths.rb +0 -76
  248. data/lib/textus/manifest/entry/publish/tree.rb +0 -50
  249. data/lib/textus/manifest/entry/publish.rb +0 -45
  250. data/lib/textus/manifest/entry/validators/format_matrix.rb +0 -21
  251. data/lib/textus/manifest/entry/validators/ignore.rb +0 -28
  252. data/lib/textus/manifest/entry/validators/publish.rb +0 -30
  253. data/lib/textus/manifest/entry/validators.rb +0 -18
  254. data/lib/textus/manifest/entry.rb +0 -8
  255. data/lib/textus/manifest/policy/matcher.rb +0 -51
  256. data/lib/textus/manifest/policy/predicates/author_held.rb +0 -22
  257. data/lib/textus/manifest/policy/predicates/etag_match.rb +0 -18
  258. data/lib/textus/manifest/policy/predicates/fresh_within.rb +0 -13
  259. data/lib/textus/manifest/policy/predicates/lane_deletable_by.rb +0 -31
  260. data/lib/textus/manifest/policy/predicates/lane_writable_by.rb +0 -23
  261. data/lib/textus/manifest/policy/predicates/raw_lane_ingest_only.rb +0 -25
  262. data/lib/textus/manifest/policy/predicates/raw_write_once.rb +0 -24
  263. data/lib/textus/manifest/policy/predicates/schema_valid.rb +0 -41
  264. data/lib/textus/manifest/policy/predicates/target_is_canon.rb +0 -20
  265. data/lib/textus/manifest/policy/predicates.rb +0 -54
  266. data/lib/textus/manifest/policy/publish_target.rb +0 -34
  267. data/lib/textus/manifest/policy/react.rb +0 -30
  268. data/lib/textus/manifest/policy/retention.rb +0 -29
  269. data/lib/textus/manifest/policy/source.rb +0 -30
  270. data/lib/textus/manifest/policy.rb +0 -95
  271. data/lib/textus/manifest/resolver.rb +0 -118
  272. data/lib/textus/manifest/rules.rb +0 -86
  273. data/lib/textus/manifest/schema/contract.rb +0 -61
  274. data/lib/textus/manifest/schema/keys.rb +0 -90
  275. data/lib/textus/manifest/schema/semantics/cross_field.rb +0 -53
  276. data/lib/textus/manifest/schema/semantics/invariants.rb +0 -125
  277. data/lib/textus/manifest/schema/semantics/migration.rb +0 -83
  278. data/lib/textus/manifest/schema/semantics.rb +0 -27
  279. data/lib/textus/manifest/schema/validator.rb +0 -48
  280. data/lib/textus/manifest/schema/vocabulary.rb +0 -25
  281. data/lib/textus/manifest/schema.rb +0 -30
  282. data/lib/textus/manifest.rb +0 -65
  283. data/lib/textus/orchestration.rb +0 -55
  284. data/lib/textus/port/audit_log.rb +0 -268
  285. data/lib/textus/port/build_lock.rb +0 -64
  286. data/lib/textus/port/clock.rb +0 -10
  287. data/lib/textus/port/publisher.rb +0 -60
  288. data/lib/textus/port/sentinel.rb +0 -25
  289. data/lib/textus/port/sentinel_store.rb +0 -83
  290. data/lib/textus/port/storage/file_stat.rb +0 -19
  291. data/lib/textus/port/storage/file_store.rb +0 -49
  292. data/lib/textus/port/storage/interface.rb +0 -17
  293. data/lib/textus/port/store.rb +0 -149
  294. data/lib/textus/port/watcher_lock.rb +0 -48
  295. data/lib/textus/produce/engine.rb +0 -40
  296. data/lib/textus/produce/publisher.rb +0 -21
  297. data/lib/textus/produce/render.rb +0 -20
  298. data/lib/textus/schema/registry.rb +0 -42
  299. data/lib/textus/schema/tools.rb +0 -108
  300. data/lib/textus/schema.rb +0 -100
  301. data/lib/textus/store/container.rb +0 -173
  302. data/lib/textus/store/cursor.rb +0 -26
  303. data/lib/textus/store/entry/reader.rb +0 -47
  304. data/lib/textus/store/entry/writer.rb +0 -219
  305. data/lib/textus/store/envelope/meta.rb +0 -61
  306. data/lib/textus/store/freshness/drift_detector.rb +0 -93
  307. data/lib/textus/store/freshness/evaluator.rb +0 -20
  308. data/lib/textus/store/freshness/verdict.rb +0 -24
  309. data/lib/textus/store/freshness.rb +0 -8
  310. data/lib/textus/store/index/builder.rb +0 -76
  311. data/lib/textus/store/index/lookup.rb +0 -60
  312. data/lib/textus/store/jobs/base.rb +0 -13
  313. data/lib/textus/store/jobs/index.rb +0 -15
  314. data/lib/textus/store/jobs/materialize.rb +0 -15
  315. data/lib/textus/store/jobs/plan.rb +0 -11
  316. data/lib/textus/store/jobs/planner.rb +0 -124
  317. data/lib/textus/store/jobs/queue.rb +0 -162
  318. data/lib/textus/store/jobs/registry.rb +0 -19
  319. data/lib/textus/store/jobs/retention/base.rb +0 -52
  320. data/lib/textus/store/jobs/retention/sweep.rb +0 -55
  321. data/lib/textus/store/jobs/retention.rb +0 -8
  322. data/lib/textus/store/jobs/sweep.rb +0 -21
  323. data/lib/textus/store/jobs/worker.rb +0 -64
  324. data/lib/textus/store.rb +0 -122
  325. data/lib/textus/surface/cli/verb/doctor.rb +0 -20
  326. data/lib/textus/surface/cli/verb/schema_diff.rb +0 -17
  327. data/lib/textus/surface/cli/verb/schema_init.rb +0 -21
  328. data/lib/textus/surface/cli/verb/schema_migrate.rb +0 -21
  329. data/lib/textus/surface/mcp/projector.rb +0 -20
  330. data/lib/textus/surface/mcp.rb +0 -8
  331. data/lib/textus/value/command.rb +0 -16
  332. data/lib/textus/value/envelope.rb +0 -90
  333. data/lib/textus/value/result.rb +0 -46
  334. data/lib/textus/verb_registry.rb +0 -417
  335. data/lib/textus/workflow/collector.rb +0 -27
  336. data/lib/textus/workflow/context.rb +0 -5
  337. data/lib/textus/workflow/loader.rb +0 -17
  338. data/lib/textus/workflow/pattern.rb +0 -18
@@ -1,52 +0,0 @@
1
- require "fileutils"
2
-
3
- module Textus
4
- class Store
5
- module Jobs
6
- module Retention
7
- class Base
8
- def initialize(container:, call:)
9
- @container = container
10
- @call = call
11
- end
12
-
13
- def call(rows)
14
- out = { dropped: [], archived: [], failed: [] }
15
- rows.each do |row|
16
- key = row["key"]
17
- begin
18
- case row["action"]
19
- when "drop"
20
- delete(key)
21
- out[:dropped] << key
22
- when "archive"
23
- archive_leaf(row)
24
- delete(key)
25
- out[:archived] << key
26
- end
27
- rescue Textus::Error => e
28
- out[:failed] << { "key" => key, "error" => e.message }
29
- end
30
- end
31
- out
32
- end
33
-
34
- private
35
-
36
- def archive_leaf(row)
37
- src = row["path"]
38
- dest = @container.layout.archive_path(src)
39
- FileUtils.mkdir_p(File.dirname(dest))
40
- FileUtils.cp(src, dest)
41
- end
42
-
43
- def delete(key)
44
- mentry = @container.manifest.resolver.resolve(key).entry
45
- writer = Textus::Store::Entry::Writer.from(container: @container, call: @call)
46
- writer.delete(key, mentry: mentry)
47
- end
48
- end
49
- end
50
- end
51
- end
52
- end
@@ -1,55 +0,0 @@
1
- require "time"
2
-
3
- module Textus
4
- class Store
5
- module Jobs
6
- module Retention
7
- class Sweep
8
- def self.expired?(ttl_seconds:, mtime:, now:)
9
- return false if ttl_seconds.nil? || mtime.nil?
10
-
11
- (now - mtime).to_i > ttl_seconds
12
- end
13
-
14
- def initialize(manifest:, file_stat:, clock:)
15
- @manifest = manifest
16
- @file_stat = file_stat
17
- @clock = clock
18
- end
19
-
20
- def call(prefix: nil, lane: nil)
21
- @manifest.data.entries
22
- .select { |m| matches?(m, prefix: prefix, lane: lane) }
23
- .flat_map { |m| rows_for(m) }
24
- end
25
-
26
- private
27
-
28
- def matches?(mentry, prefix:, lane:)
29
- return false if lane && mentry.lane != lane
30
- return false if prefix && !Textus::Key::Matching.matches_prefix?(
31
- mentry.key, prefix, nested: mentry.is_a?(Textus::Manifest::Entry::Nested)
32
- )
33
-
34
- true
35
- end
36
-
37
- def rows_for(mentry)
38
- policy = @manifest.rules.for(mentry.key).retention
39
- return [] if policy.nil?
40
-
41
- @manifest.resolver.enumerate(prefix: mentry.key).filter_map do |row|
42
- path = row[:path]
43
- next unless @file_stat.exists?(path)
44
- next unless self.class.expired?(
45
- ttl_seconds: policy.ttl_seconds, mtime: @file_stat.mtime(path), now: @clock.now,
46
- )
47
-
48
- { "key" => row[:key], "path" => path, "action" => policy.action.to_s }
49
- end
50
- end
51
- end
52
- end
53
- end
54
- end
55
- end
@@ -1,8 +0,0 @@
1
- module Textus
2
- class Store
3
- module Jobs
4
- module Retention
5
- end
6
- end
7
- end
8
- 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 = 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
- Retention::Base.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
data/lib/textus/store.rb DELETED
@@ -1,122 +0,0 @@
1
- require "fileutils"
2
-
3
- module Textus
4
- class Store
5
- attr_reader :container, :role, :correlation_id, :cursor, :propose_lane, :contract_etag
6
-
7
- Textus::Store::Container.attribute_names.each do |field|
8
- define_method(field) { @container.public_send(field) }
9
- end
10
-
11
- def self.discover(start_dir = Dir.pwd, root: nil)
12
- explicit = root || ENV.fetch("TEXTUS_ROOT", nil)
13
- return discover_explicit(explicit) if explicit
14
-
15
- ascend_for_store(File.expand_path(start_dir)) ||
16
- raise(IoError.new("no .textus directory found from #{start_dir}"))
17
- end
18
-
19
- private_class_method def self.ascend_for_store(dir)
20
- loop do
21
- candidate = File.join(dir, ".textus")
22
- return new(candidate) if store_dir?(candidate)
23
-
24
- parent = File.dirname(dir)
25
- return nil if parent == dir
26
-
27
- dir = parent
28
- end
29
- end
30
-
31
- private_class_method def self.discover_explicit(root_arg)
32
- abs = File.expand_path(root_arg)
33
- raise IoError.new("no textus store at #{abs}") unless store_dir?(abs)
34
-
35
- new(abs)
36
- end
37
-
38
- private_class_method def self.store_dir?(dir)
39
- File.directory?(dir) && File.exist?(File.join(dir, "manifest.yaml"))
40
- end
41
-
42
- def initialize(root, role: Value::Role::DEFAULT, correlation_id: nil, dry_run: false, container: nil)
43
- @root = File.expand_path(root)
44
- @container = container || build_container(@root)
45
- @role = role.to_s
46
- @correlation_id = correlation_id || SecureRandom.uuid
47
- @dry_run = dry_run
48
- build_session!
49
- end
50
-
51
- def dry_run? = @dry_run
52
-
53
- def with_role(new_role)
54
- _rebuild(role: new_role)
55
- end
56
-
57
- def with_correlation_id(cid)
58
- _rebuild(correlation_id: cid)
59
- end
60
-
61
- def advance_cursor(new_cursor)
62
- dup.tap do |s|
63
- s.instance_variable_set(:@cursor, new_cursor)
64
- end
65
- end
66
-
67
- def check_etag!(observed_etag)
68
- return if observed_etag == @contract_etag
69
-
70
- raise Textus::ContractDrift.new(
71
- "contract changed (manifest/hooks/schemas were #{short_etag(@contract_etag)}, " \
72
- "now #{short_etag(observed_etag)}); re-run boot",
73
- )
74
- end
75
-
76
- private
77
-
78
- def _rebuild(role: @role, correlation_id: @correlation_id, dry_run: @dry_run)
79
- self.class.allocate.tap do |s|
80
- s.instance_variable_set(:@root, @root)
81
- s.instance_variable_set(:@container, @container)
82
- s.instance_variable_set(:@role, role.to_s)
83
- s.instance_variable_set(:@correlation_id, correlation_id || SecureRandom.uuid)
84
- s.instance_variable_set(:@dry_run, dry_run)
85
- s.send(:build_session!)
86
- end
87
- end
88
-
89
- def build_session!
90
- @cursor = @container.audit_log.latest_seq
91
- @propose_lane = @container.manifest.policy.propose_lane_for(@role)
92
- @contract_etag = Value::Etag.for_contract(@root)
93
- end
94
-
95
- def short_etag(etag) = etag.to_s.delete_prefix("sha256:")[0, 8]
96
-
97
- def build_container(root)
98
- manifest = Manifest.load(root)
99
- job_store = Port::Store.new(root: root).setup!
100
- layout = Store::Layout.new(root)
101
- infra = Container::Infrastructure.new(
102
- file_store: Port::Storage::FileStore.new,
103
- schemas: Schema::Registry.new(layout.schemas_dir),
104
- audit_log: Port::AuditLog.new(
105
- layout: layout,
106
- max_size: manifest.data.audit_config[:max_size],
107
- keep: manifest.data.audit_config[:keep],
108
- ),
109
- job_store:,
110
- layout:,
111
- )
112
-
113
- coord_seed = Container::Coordination.new(
114
- manifest:,
115
- workflows: Workflow::Loader.load_all(root),
116
- pipeline: nil,
117
- )
118
-
119
- Container.build(infra, coord_seed)
120
- end
121
- end
122
- end
@@ -1,20 +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
- Textus::VerbRegistry.for(:doctor)
11
- inputs = { checks: checks&.split(",")&.map(&:strip) }
12
- s = store.with_role(resolved_role(store))
13
- res = s.doctor(**inputs)
14
- emit(res, exit_code: res["ok"] ? 0 : 1)
15
- end
16
- end
17
- end
18
- end
19
- end
20
- 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,20 +0,0 @@
1
- module Textus
2
- module Surface
3
- module MCP
4
- class Projector
5
- def initialize(view_key: :default)
6
- @view_key = view_key
7
- end
8
-
9
- def dispatch(verb_name, inputs:, store:)
10
- spec = VerbRegistry.for(verb_name.to_sym)
11
- raise Textus::UsageError.new("unknown verb: #{verb_name}") unless spec
12
-
13
- bound = Textus::Dispatch::Binder.inputs_from_wire(spec, inputs)
14
- result = store.public_send(verb_name.to_sym, **bound)
15
- spec.view(@view_key).call(result, bound)
16
- end
17
- end
18
- end
19
- end
20
- 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,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,90 +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::Any).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
- def self.build(key:, mentry:, path:, meta:, body:, etag:, content: nil, freshness: nil)
24
- new(
25
- protocol: Textus::PROTOCOL,
26
- key: key,
27
- lane: mentry.lane,
28
- owner: mentry.owner,
29
- path: path,
30
- format: mentry.format,
31
- uid: extract_uid(meta),
32
- sources: extract_sources(meta),
33
- etag: etag,
34
- schema_ref: mentry.schema,
35
- meta: meta,
36
- body: body,
37
- content: content,
38
- freshness: freshness,
39
- )
40
- end
41
-
42
- def self.extract_uid(meta)
43
- v = meta.is_a?(Hash) ? meta["uid"] : nil
44
- v.is_a?(String) ? v : nil
45
- end
46
-
47
- def self.extract_sources(meta)
48
- v = meta.is_a?(Hash) ? meta["sources"] : nil
49
- return nil unless v.is_a?(Array) && !v.empty?
50
-
51
- valid = v.select { |s| s.is_a?(String) || (s.is_a?(Hash) && s["key"].is_a?(String)) }
52
- valid.empty? ? nil : valid
53
- end
54
-
55
- def with(**attrs) = self.class.new(to_h.merge(attrs))
56
-
57
- def to_h_for_wire
58
- h = {
59
- "protocol" => protocol,
60
- "key" => key,
61
- "lane" => lane,
62
- "owner" => owner,
63
- "path" => path,
64
- "format" => format,
65
- "_meta" => meta,
66
- "body" => body,
67
- "etag" => etag,
68
- "schema_ref" => schema_ref,
69
- "uid" => uid,
70
- }
71
- h["sources"] = sources if sources
72
- h["content"] = content unless content.nil?
73
- freshness&.to_h_for_wire&.each { |k, v| h[k] = v }
74
- h
75
- end
76
-
77
- def stale?
78
- return false if freshness.nil?
79
-
80
- freshness.stale == true
81
- end
82
-
83
- def fetching?
84
- return false if freshness.nil?
85
-
86
- freshness.fetching == true
87
- end
88
- end
89
- end
90
- end
@@ -1,46 +0,0 @@
1
- module Textus
2
- module Value
3
- # rubocop:disable Lint/ConstantDefinitionInBlock
4
- Result = Data.define(:ok, :value, :error) do
5
- def self.success(value) = new(ok: true, value: value, error: nil)
6
-
7
- def self.failure(code, message, details: {})
8
- new(ok: false, value: nil, error: { code: code, message: message, details: details })
9
- end
10
-
11
- def self.extract(result)
12
- case result
13
- when self
14
- if result.success?
15
- result.value
16
- else
17
- err = result.error
18
- raise Textus::ActionError.new(err[:code] || :error, err[:message] || "action failed", details: err[:details] || {})
19
- end
20
- else
21
- result
22
- end
23
- end
24
-
25
- def success? = ok
26
- def failure? = !ok
27
-
28
- def unwrap
29
- raise Result::UnwrapError.new(error[:code], error[:message], details: error[:details]) unless ok
30
-
31
- value
32
- end
33
-
34
- class UnwrapError < StandardError
35
- attr_reader :code, :details
36
-
37
- def initialize(code, message, details: {})
38
- super(message)
39
- @code = code
40
- @details = details
41
- end
42
- end
43
- end
44
- # rubocop:enable Lint/ConstantDefinitionInBlock
45
- end
46
- end