textus 0.54.2 → 0.55.2

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 (242) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +38 -1
  3. data/README.md +17 -10
  4. data/SPEC.md +39 -11
  5. data/docs/architecture/README.md +23 -11
  6. data/docs/reference/conventions.md +5 -2
  7. data/exe/textus +1 -1
  8. data/lib/textus/boot.rb +64 -93
  9. data/lib/textus/dispatch/binder.rb +48 -0
  10. data/lib/textus/dispatch/contracts.rb +63 -0
  11. data/lib/textus/dispatch/handler_registry.rb +21 -0
  12. data/lib/textus/dispatch/middleware/audit_index.rb +51 -0
  13. data/lib/textus/dispatch/middleware/auth.rb +40 -0
  14. data/lib/textus/dispatch/middleware/base.rb +26 -0
  15. data/lib/textus/dispatch/middleware/binder.rb +20 -0
  16. data/lib/textus/dispatch/middleware/cascade.rb +53 -0
  17. data/lib/textus/dispatch/pipeline.rb +35 -0
  18. data/lib/textus/doctor/check/audit_log.rb +2 -2
  19. data/lib/textus/doctor/check/generator_drift.rb +4 -4
  20. data/lib/textus/doctor/check/orphaned_publish_targets.rb +3 -3
  21. data/lib/textus/doctor/check/protocol_version.rb +2 -2
  22. data/lib/textus/doctor/check/raw_asset_paths.rb +1 -1
  23. data/lib/textus/doctor/check/schema_parse_error.rb +1 -1
  24. data/lib/textus/doctor/check/schema_violations.rb +2 -2
  25. data/lib/textus/doctor/check/schemas.rb +1 -1
  26. data/lib/textus/doctor/check/{notebook_sources.rb → scratchpad_sources.rb} +10 -5
  27. data/lib/textus/doctor/check/sentinels.rb +4 -4
  28. data/lib/textus/doctor/check/templates.rb +1 -1
  29. data/lib/textus/doctor/check/unowned_schema_fields.rb +1 -1
  30. data/lib/textus/doctor/check.rb +9 -10
  31. data/lib/textus/doctor.rb +2 -2
  32. data/lib/textus/errors.rb +8 -0
  33. data/lib/textus/format/base.rb +34 -10
  34. data/lib/textus/format/json.rb +5 -27
  35. data/lib/textus/format/markdown.rb +5 -27
  36. data/lib/textus/format/text.rb +5 -4
  37. data/lib/textus/format/yaml.rb +30 -27
  38. data/lib/textus/format.rb +22 -1
  39. data/lib/textus/handlers/maintenance/boot_store.rb +15 -0
  40. data/lib/textus/handlers/maintenance/doctor_store.rb +15 -0
  41. data/lib/textus/handlers/maintenance/drain_store.rb +21 -0
  42. data/lib/textus/handlers/maintenance/ingest_entry.rb +159 -0
  43. data/lib/textus/handlers/maintenance/jobs_action.rb +21 -0
  44. data/lib/textus/handlers/maintenance/published_entries.rb +17 -0
  45. data/lib/textus/handlers/maintenance/rule_explain.rb +77 -0
  46. data/lib/textus/handlers/maintenance/rule_lint.rb +54 -0
  47. data/lib/textus/handlers/maintenance/rule_list.rb +32 -0
  48. data/lib/textus/handlers/maintenance/schema_envelope.rb +19 -0
  49. data/lib/textus/handlers/read/audit_entries.rb +48 -0
  50. data/lib/textus/handlers/read/blame_entry.rb +71 -0
  51. data/lib/textus/handlers/read/deps_entry.rb +17 -0
  52. data/lib/textus/handlers/read/get_entry.rb +68 -0
  53. data/lib/textus/handlers/read/list_keys.rb +36 -0
  54. data/lib/textus/handlers/read/pulse_entries.rb +66 -0
  55. data/lib/textus/handlers/read/rdeps_entry.rb +21 -0
  56. data/lib/textus/handlers/read/uid_entry.rb +18 -0
  57. data/lib/textus/handlers/read/where_entry.rb +18 -0
  58. data/lib/textus/handlers/write/accept_proposal.rb +39 -0
  59. data/lib/textus/handlers/write/data_mv.rb +55 -0
  60. data/lib/textus/handlers/write/delete_key.rb +17 -0
  61. data/lib/textus/handlers/write/enqueue_job.rb +27 -0
  62. data/lib/textus/handlers/write/key_delete_prefix.rb +32 -0
  63. data/lib/textus/handlers/write/key_mv_prefix.rb +45 -0
  64. data/lib/textus/handlers/write/move_key.rb +80 -0
  65. data/lib/textus/handlers/write/propose_entry.rb +29 -0
  66. data/lib/textus/handlers/write/put_entry.rb +29 -0
  67. data/lib/textus/handlers/write/reject_proposal.rb +29 -0
  68. data/lib/textus/init.rb +5 -7
  69. data/lib/textus/jobs.rb +3 -9
  70. data/lib/textus/manifest/capabilities.rb +4 -4
  71. data/lib/textus/manifest/entry/base.rb +3 -3
  72. data/lib/textus/manifest/entry/publish/subtree_mirror.rb +2 -2
  73. data/lib/textus/manifest/entry/publish/to_paths.rb +2 -2
  74. data/lib/textus/manifest/policy/predicates/author_held.rb +22 -0
  75. data/lib/textus/manifest/policy/predicates/etag_match.rb +18 -0
  76. data/lib/textus/manifest/policy/predicates/fresh_within.rb +13 -0
  77. data/lib/textus/manifest/policy/predicates/lane_deletable_by.rb +31 -0
  78. data/lib/textus/manifest/policy/predicates/lane_writable_by.rb +23 -0
  79. data/lib/textus/manifest/policy/predicates/raw_lane_ingest_only.rb +25 -0
  80. data/lib/textus/manifest/policy/predicates/raw_write_once.rb +24 -0
  81. data/lib/textus/manifest/policy/predicates/schema_valid.rb +41 -0
  82. data/lib/textus/manifest/policy/predicates/target_is_canon.rb +20 -0
  83. data/lib/textus/manifest/policy/predicates.rb +54 -0
  84. data/lib/textus/manifest/policy/retention.rb +1 -1
  85. data/lib/textus/manifest/schema/semantics/cross_field.rb +53 -0
  86. data/lib/textus/manifest/schema/semantics/invariants.rb +125 -0
  87. data/lib/textus/manifest/schema/semantics/migration.rb +83 -0
  88. data/lib/textus/manifest/schema/semantics.rb +11 -216
  89. data/lib/textus/orchestration.rb +55 -0
  90. data/lib/textus/{ports → port}/audit_log.rb +46 -6
  91. data/lib/textus/{ports → port}/build_lock.rb +2 -2
  92. data/lib/textus/{ports → port}/clock.rb +1 -1
  93. data/lib/textus/{ports → port}/publisher.rb +5 -5
  94. data/lib/textus/{core → port}/sentinel.rb +1 -6
  95. data/lib/textus/{ports → port}/sentinel_store.rb +4 -4
  96. data/lib/textus/{ports → port}/storage/file_stat.rb +1 -1
  97. data/lib/textus/port/storage/file_store.rb +49 -0
  98. data/lib/textus/port/storage/interface.rb +17 -0
  99. data/lib/textus/port/store.rb +149 -0
  100. data/lib/textus/{ports → port}/watcher_lock.rb +3 -3
  101. data/lib/textus/produce/engine.rb +1 -11
  102. data/lib/textus/produce/publisher.rb +21 -0
  103. data/lib/textus/schema/registry.rb +42 -0
  104. data/lib/textus/schema/tools.rb +7 -10
  105. data/lib/textus/store/container.rb +173 -0
  106. data/lib/textus/store/cursor.rb +26 -0
  107. data/lib/textus/store/entry/reader.rb +47 -0
  108. data/lib/textus/store/entry/writer.rb +219 -0
  109. data/lib/textus/store/envelope/meta.rb +61 -0
  110. data/lib/textus/store/freshness/drift_detector.rb +93 -0
  111. data/lib/textus/store/freshness/evaluator.rb +20 -0
  112. data/lib/textus/store/freshness/ttl_evaluator.rb +57 -0
  113. data/lib/textus/{core → store}/freshness/verdict.rb +1 -11
  114. data/lib/textus/store/freshness.rb +8 -0
  115. data/lib/textus/store/index/builder.rb +76 -0
  116. data/lib/textus/store/index/lookup.rb +60 -0
  117. data/lib/textus/store/jobs/base.rb +13 -0
  118. data/lib/textus/store/jobs/index.rb +15 -0
  119. data/lib/textus/store/jobs/materialize.rb +15 -0
  120. data/lib/textus/store/jobs/plan.rb +11 -0
  121. data/lib/textus/store/jobs/planner.rb +124 -0
  122. data/lib/textus/store/jobs/queue.rb +162 -0
  123. data/lib/textus/store/jobs/registry.rb +19 -0
  124. data/lib/textus/store/jobs/retention/base.rb +52 -0
  125. data/lib/textus/store/jobs/retention/sweep.rb +55 -0
  126. data/lib/textus/store/jobs/retention.rb +8 -0
  127. data/lib/textus/store/jobs/sweep.rb +21 -0
  128. data/lib/textus/store/jobs/worker.rb +64 -0
  129. data/lib/textus/store/layout.rb +97 -0
  130. data/lib/textus/store.rb +63 -32
  131. data/lib/textus/{surfaces → surface}/cli/group/data.rb +1 -1
  132. data/lib/textus/{surfaces → surface}/cli/group/key.rb +1 -1
  133. data/lib/textus/{surfaces → surface}/cli/group/mcp.rb +1 -1
  134. data/lib/textus/{surfaces → surface}/cli/group/rule.rb +1 -1
  135. data/lib/textus/{surfaces → surface}/cli/group/schema.rb +1 -1
  136. data/lib/textus/{surfaces → surface}/cli/group.rb +2 -2
  137. data/lib/textus/{surfaces → surface}/cli/runner.rb +14 -68
  138. data/lib/textus/surface/cli/sources.rb +41 -0
  139. data/lib/textus/{surfaces → surface}/cli/verb/doctor.rb +5 -6
  140. data/lib/textus/{surfaces → surface}/cli/verb/get.rb +7 -5
  141. data/lib/textus/{surfaces → surface}/cli/verb/init.rb +1 -1
  142. data/lib/textus/{surfaces → surface}/cli/verb/mcp_serve.rb +3 -3
  143. data/lib/textus/surface/cli/verb/put.rb +27 -0
  144. data/lib/textus/{surfaces → surface}/cli/verb/schema_diff.rb +1 -1
  145. data/lib/textus/{surfaces → surface}/cli/verb/schema_init.rb +1 -1
  146. data/lib/textus/{surfaces → surface}/cli/verb/schema_migrate.rb +1 -1
  147. data/lib/textus/{surfaces → surface}/cli/verb/watch.rb +2 -2
  148. data/lib/textus/{surfaces → surface}/cli/verb.rb +3 -8
  149. data/lib/textus/{surfaces → surface}/cli.rb +1 -1
  150. data/lib/textus/surface/mcp/catalog.rb +58 -0
  151. data/lib/textus/{surfaces → surface}/mcp/errors.rb +1 -11
  152. data/lib/textus/surface/mcp/projector.rb +20 -0
  153. data/lib/textus/{surfaces → surface}/mcp/server.rb +24 -35
  154. data/lib/textus/{surfaces → surface}/mcp.rb +2 -2
  155. data/lib/textus/{surfaces → surface}/watcher.rb +8 -8
  156. data/lib/textus/value/call.rb +30 -0
  157. data/lib/textus/value/command.rb +16 -0
  158. data/lib/textus/{core → value}/duration.rb +1 -4
  159. data/lib/textus/value/envelope.rb +90 -0
  160. data/lib/textus/value/etag.rb +39 -0
  161. data/lib/textus/value/payload.rb +7 -0
  162. data/lib/textus/value/result.rb +46 -0
  163. data/lib/textus/value/role.rb +38 -0
  164. data/lib/textus/value/types.rb +13 -0
  165. data/lib/textus/{uid.rb → value/uid.rb} +9 -7
  166. data/lib/textus/verb_registry.rb +417 -0
  167. data/lib/textus/version.rb +1 -1
  168. data/lib/textus/workflow/loader.rb +4 -4
  169. data/lib/textus/workflow/runner.rb +11 -33
  170. data/lib/textus.rb +4 -69
  171. metadata +142 -105
  172. data/lib/textus/action/accept.rb +0 -44
  173. data/lib/textus/action/audit.rb +0 -131
  174. data/lib/textus/action/base.rb +0 -42
  175. data/lib/textus/action/blame.rb +0 -97
  176. data/lib/textus/action/boot.rb +0 -17
  177. data/lib/textus/action/data_mv.rb +0 -69
  178. data/lib/textus/action/deps.rb +0 -34
  179. data/lib/textus/action/doctor.rb +0 -24
  180. data/lib/textus/action/drain.rb +0 -39
  181. data/lib/textus/action/enqueue.rb +0 -53
  182. data/lib/textus/action/get.rb +0 -78
  183. data/lib/textus/action/ingest.rb +0 -203
  184. data/lib/textus/action/jobs.rb +0 -36
  185. data/lib/textus/action/key_delete.rb +0 -36
  186. data/lib/textus/action/key_delete_prefix.rb +0 -44
  187. data/lib/textus/action/key_mv.rb +0 -129
  188. data/lib/textus/action/key_mv_prefix.rb +0 -57
  189. data/lib/textus/action/list.rb +0 -42
  190. data/lib/textus/action/propose.rb +0 -52
  191. data/lib/textus/action/published.rb +0 -24
  192. data/lib/textus/action/pulse.rb +0 -56
  193. data/lib/textus/action/put.rb +0 -52
  194. data/lib/textus/action/rdeps.rb +0 -39
  195. data/lib/textus/action/reject.rb +0 -37
  196. data/lib/textus/action/rule_explain.rb +0 -91
  197. data/lib/textus/action/rule_lint.rb +0 -68
  198. data/lib/textus/action/rule_list.rb +0 -42
  199. data/lib/textus/action/schema_envelope.rb +0 -29
  200. data/lib/textus/action/uid.rb +0 -33
  201. data/lib/textus/action/where.rb +0 -36
  202. data/lib/textus/action/write_verb.rb +0 -44
  203. data/lib/textus/call.rb +0 -28
  204. data/lib/textus/command.rb +0 -41
  205. data/lib/textus/container.rb +0 -26
  206. data/lib/textus/contract/around.rb +0 -29
  207. data/lib/textus/contract/binder.rb +0 -88
  208. data/lib/textus/contract/resources/build_lock.rb +0 -17
  209. data/lib/textus/contract/resources/cursor.rb +0 -26
  210. data/lib/textus/contract/sources.rb +0 -39
  211. data/lib/textus/contract/view.rb +0 -15
  212. data/lib/textus/contract.rb +0 -174
  213. data/lib/textus/core/freshness/evaluator.rb +0 -150
  214. data/lib/textus/core/freshness.rb +0 -11
  215. data/lib/textus/core/retention/sweep.rb +0 -57
  216. data/lib/textus/core/retention.rb +0 -11
  217. data/lib/textus/cursor_store.rb +0 -24
  218. data/lib/textus/envelope/reader.rb +0 -46
  219. data/lib/textus/envelope/writer.rb +0 -209
  220. data/lib/textus/envelope.rb +0 -79
  221. data/lib/textus/etag.rb +0 -36
  222. data/lib/textus/gate/auth.rb +0 -227
  223. data/lib/textus/gate.rb +0 -116
  224. data/lib/textus/jobs/base.rb +0 -23
  225. data/lib/textus/jobs/materialize.rb +0 -20
  226. data/lib/textus/jobs/plan.rb +0 -9
  227. data/lib/textus/jobs/planner.rb +0 -101
  228. data/lib/textus/jobs/retention.rb +0 -48
  229. data/lib/textus/jobs/sweep.rb +0 -27
  230. data/lib/textus/jobs/worker.rb +0 -67
  231. data/lib/textus/layout.rb +0 -91
  232. data/lib/textus/ports/job_store/job.rb +0 -65
  233. data/lib/textus/ports/job_store.rb +0 -123
  234. data/lib/textus/ports/raw_index.rb +0 -61
  235. data/lib/textus/ports/storage/file_store.rb +0 -26
  236. data/lib/textus/role.rb +0 -36
  237. data/lib/textus/schemas.rb +0 -54
  238. data/lib/textus/session.rb +0 -35
  239. data/lib/textus/surfaces/cli/verb/put.rb +0 -30
  240. data/lib/textus/surfaces/mcp/catalog.rb +0 -111
  241. data/lib/textus/surfaces/role_scope.rb +0 -34
  242. data/lib/textus/types.rb +0 -15
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.54.2
4
+ version: 0.55.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patrick
@@ -93,6 +93,20 @@ dependencies:
93
93
  - - ">="
94
94
  - !ruby/object:Gem::Version
95
95
  version: '3.2'
96
+ - !ruby/object:Gem::Dependency
97
+ name: sqlite3
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '2.0'
103
+ type: :runtime
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '2.0'
96
110
  - !ruby/object:Gem::Dependency
97
111
  name: zeitwerk
98
112
  requirement: !ruby/object:Gem::Requirement
@@ -152,63 +166,22 @@ files:
152
166
  - docs/reference/conventions.md
153
167
  - exe/textus
154
168
  - lib/textus.rb
155
- - lib/textus/action/accept.rb
156
- - lib/textus/action/audit.rb
157
- - lib/textus/action/base.rb
158
- - lib/textus/action/blame.rb
159
- - lib/textus/action/boot.rb
160
- - lib/textus/action/data_mv.rb
161
- - lib/textus/action/deps.rb
162
- - lib/textus/action/doctor.rb
163
- - lib/textus/action/drain.rb
164
- - lib/textus/action/enqueue.rb
165
- - lib/textus/action/get.rb
166
- - lib/textus/action/ingest.rb
167
- - lib/textus/action/jobs.rb
168
- - lib/textus/action/key_delete.rb
169
- - lib/textus/action/key_delete_prefix.rb
170
- - lib/textus/action/key_mv.rb
171
- - lib/textus/action/key_mv_prefix.rb
172
- - lib/textus/action/list.rb
173
- - lib/textus/action/propose.rb
174
- - lib/textus/action/published.rb
175
- - lib/textus/action/pulse.rb
176
- - lib/textus/action/put.rb
177
- - lib/textus/action/rdeps.rb
178
- - lib/textus/action/reject.rb
179
- - lib/textus/action/rule_explain.rb
180
- - lib/textus/action/rule_lint.rb
181
- - lib/textus/action/rule_list.rb
182
- - lib/textus/action/schema_envelope.rb
183
- - lib/textus/action/uid.rb
184
- - lib/textus/action/where.rb
185
- - lib/textus/action/write_verb.rb
186
169
  - lib/textus/boot.rb
187
- - lib/textus/call.rb
188
- - lib/textus/command.rb
189
- - lib/textus/container.rb
190
- - lib/textus/contract.rb
191
- - lib/textus/contract/around.rb
192
- - lib/textus/contract/binder.rb
193
- - lib/textus/contract/resources/build_lock.rb
194
- - lib/textus/contract/resources/cursor.rb
195
- - lib/textus/contract/sources.rb
196
- - lib/textus/contract/view.rb
197
- - lib/textus/core/duration.rb
198
- - lib/textus/core/freshness.rb
199
- - lib/textus/core/freshness/evaluator.rb
200
- - lib/textus/core/freshness/verdict.rb
201
- - lib/textus/core/retention.rb
202
- - lib/textus/core/retention/sweep.rb
203
- - lib/textus/core/sentinel.rb
204
- - lib/textus/cursor_store.rb
170
+ - lib/textus/dispatch/binder.rb
171
+ - lib/textus/dispatch/contracts.rb
172
+ - lib/textus/dispatch/handler_registry.rb
173
+ - lib/textus/dispatch/middleware/audit_index.rb
174
+ - lib/textus/dispatch/middleware/auth.rb
175
+ - lib/textus/dispatch/middleware/base.rb
176
+ - lib/textus/dispatch/middleware/binder.rb
177
+ - lib/textus/dispatch/middleware/cascade.rb
178
+ - lib/textus/dispatch/pipeline.rb
205
179
  - lib/textus/doctor.rb
206
180
  - lib/textus/doctor/check.rb
207
181
  - lib/textus/doctor/check/audit_log.rb
208
182
  - lib/textus/doctor/check/generator_drift.rb
209
183
  - lib/textus/doctor/check/illegal_keys.rb
210
184
  - lib/textus/doctor/check/manifest_files.rb
211
- - lib/textus/doctor/check/notebook_sources.rb
212
185
  - lib/textus/doctor/check/orphaned_publish_targets.rb
213
186
  - lib/textus/doctor/check/proposal_targets.rb
214
187
  - lib/textus/doctor/check/protocol_version.rb
@@ -218,38 +191,54 @@ files:
218
191
  - lib/textus/doctor/check/schema_parse_error.rb
219
192
  - lib/textus/doctor/check/schema_violations.rb
220
193
  - lib/textus/doctor/check/schemas.rb
194
+ - lib/textus/doctor/check/scratchpad_sources.rb
221
195
  - lib/textus/doctor/check/sentinels.rb
222
196
  - lib/textus/doctor/check/stale_reviewed_stamp.rb
223
197
  - lib/textus/doctor/check/templates.rb
224
198
  - lib/textus/doctor/check/unowned_schema_fields.rb
225
199
  - lib/textus/doctor/validator.rb
226
- - lib/textus/envelope.rb
227
- - lib/textus/envelope/reader.rb
228
- - lib/textus/envelope/writer.rb
229
200
  - lib/textus/errors.rb
230
- - lib/textus/etag.rb
231
201
  - lib/textus/format.rb
232
202
  - lib/textus/format/base.rb
233
203
  - lib/textus/format/json.rb
234
204
  - lib/textus/format/markdown.rb
235
205
  - lib/textus/format/text.rb
236
206
  - lib/textus/format/yaml.rb
237
- - lib/textus/gate.rb
238
- - lib/textus/gate/auth.rb
207
+ - lib/textus/handlers/maintenance/boot_store.rb
208
+ - lib/textus/handlers/maintenance/doctor_store.rb
209
+ - lib/textus/handlers/maintenance/drain_store.rb
210
+ - lib/textus/handlers/maintenance/ingest_entry.rb
211
+ - lib/textus/handlers/maintenance/jobs_action.rb
212
+ - lib/textus/handlers/maintenance/published_entries.rb
213
+ - lib/textus/handlers/maintenance/rule_explain.rb
214
+ - lib/textus/handlers/maintenance/rule_lint.rb
215
+ - lib/textus/handlers/maintenance/rule_list.rb
216
+ - lib/textus/handlers/maintenance/schema_envelope.rb
217
+ - lib/textus/handlers/read/audit_entries.rb
218
+ - lib/textus/handlers/read/blame_entry.rb
219
+ - lib/textus/handlers/read/deps_entry.rb
220
+ - lib/textus/handlers/read/get_entry.rb
221
+ - lib/textus/handlers/read/list_keys.rb
222
+ - lib/textus/handlers/read/pulse_entries.rb
223
+ - lib/textus/handlers/read/rdeps_entry.rb
224
+ - lib/textus/handlers/read/uid_entry.rb
225
+ - lib/textus/handlers/read/where_entry.rb
226
+ - lib/textus/handlers/write/accept_proposal.rb
227
+ - lib/textus/handlers/write/data_mv.rb
228
+ - lib/textus/handlers/write/delete_key.rb
229
+ - lib/textus/handlers/write/enqueue_job.rb
230
+ - lib/textus/handlers/write/key_delete_prefix.rb
231
+ - lib/textus/handlers/write/key_mv_prefix.rb
232
+ - lib/textus/handlers/write/move_key.rb
233
+ - lib/textus/handlers/write/propose_entry.rb
234
+ - lib/textus/handlers/write/put_entry.rb
235
+ - lib/textus/handlers/write/reject_proposal.rb
239
236
  - lib/textus/init.rb
240
237
  - lib/textus/jobs.rb
241
- - lib/textus/jobs/base.rb
242
- - lib/textus/jobs/materialize.rb
243
- - lib/textus/jobs/plan.rb
244
- - lib/textus/jobs/planner.rb
245
- - lib/textus/jobs/retention.rb
246
- - lib/textus/jobs/sweep.rb
247
- - lib/textus/jobs/worker.rb
248
238
  - lib/textus/key/distance.rb
249
239
  - lib/textus/key/grammar.rb
250
240
  - lib/textus/key/matching.rb
251
241
  - lib/textus/key/path.rb
252
- - lib/textus/layout.rb
253
242
  - lib/textus/manifest.rb
254
243
  - lib/textus/manifest/capabilities.rb
255
244
  - lib/textus/manifest/data.rb
@@ -273,6 +262,16 @@ files:
273
262
  - lib/textus/manifest/entry/validators/publish.rb
274
263
  - lib/textus/manifest/policy.rb
275
264
  - lib/textus/manifest/policy/matcher.rb
265
+ - lib/textus/manifest/policy/predicates.rb
266
+ - lib/textus/manifest/policy/predicates/author_held.rb
267
+ - lib/textus/manifest/policy/predicates/etag_match.rb
268
+ - lib/textus/manifest/policy/predicates/fresh_within.rb
269
+ - lib/textus/manifest/policy/predicates/lane_deletable_by.rb
270
+ - lib/textus/manifest/policy/predicates/lane_writable_by.rb
271
+ - lib/textus/manifest/policy/predicates/raw_lane_ingest_only.rb
272
+ - lib/textus/manifest/policy/predicates/raw_write_once.rb
273
+ - lib/textus/manifest/policy/predicates/schema_valid.rb
274
+ - lib/textus/manifest/policy/predicates/target_is_canon.rb
276
275
  - lib/textus/manifest/policy/publish_target.rb
277
276
  - lib/textus/manifest/policy/react.rb
278
277
  - lib/textus/manifest/policy/retention.rb
@@ -283,53 +282,91 @@ files:
283
282
  - lib/textus/manifest/schema/contract.rb
284
283
  - lib/textus/manifest/schema/keys.rb
285
284
  - lib/textus/manifest/schema/semantics.rb
285
+ - lib/textus/manifest/schema/semantics/cross_field.rb
286
+ - lib/textus/manifest/schema/semantics/invariants.rb
287
+ - lib/textus/manifest/schema/semantics/migration.rb
286
288
  - lib/textus/manifest/schema/validator.rb
287
289
  - lib/textus/manifest/schema/vocabulary.rb
288
- - lib/textus/ports/audit_log.rb
289
- - lib/textus/ports/build_lock.rb
290
- - lib/textus/ports/clock.rb
291
- - lib/textus/ports/job_store.rb
292
- - lib/textus/ports/job_store/job.rb
293
- - lib/textus/ports/publisher.rb
294
- - lib/textus/ports/raw_index.rb
295
- - lib/textus/ports/sentinel_store.rb
296
- - lib/textus/ports/storage/file_stat.rb
297
- - lib/textus/ports/storage/file_store.rb
298
- - lib/textus/ports/watcher_lock.rb
290
+ - lib/textus/orchestration.rb
291
+ - lib/textus/port/audit_log.rb
292
+ - lib/textus/port/build_lock.rb
293
+ - lib/textus/port/clock.rb
294
+ - lib/textus/port/publisher.rb
295
+ - lib/textus/port/sentinel.rb
296
+ - lib/textus/port/sentinel_store.rb
297
+ - lib/textus/port/storage/file_stat.rb
298
+ - lib/textus/port/storage/file_store.rb
299
+ - lib/textus/port/storage/interface.rb
300
+ - lib/textus/port/store.rb
301
+ - lib/textus/port/watcher_lock.rb
299
302
  - lib/textus/produce/engine.rb
303
+ - lib/textus/produce/publisher.rb
300
304
  - lib/textus/produce/render.rb
301
- - lib/textus/role.rb
302
305
  - lib/textus/schema.rb
306
+ - lib/textus/schema/registry.rb
303
307
  - lib/textus/schema/tools.rb
304
- - lib/textus/schemas.rb
305
- - lib/textus/session.rb
306
308
  - lib/textus/store.rb
307
- - lib/textus/surfaces/cli.rb
308
- - lib/textus/surfaces/cli/group.rb
309
- - lib/textus/surfaces/cli/group/data.rb
310
- - lib/textus/surfaces/cli/group/key.rb
311
- - lib/textus/surfaces/cli/group/mcp.rb
312
- - lib/textus/surfaces/cli/group/rule.rb
313
- - lib/textus/surfaces/cli/group/schema.rb
314
- - lib/textus/surfaces/cli/runner.rb
315
- - lib/textus/surfaces/cli/verb.rb
316
- - lib/textus/surfaces/cli/verb/doctor.rb
317
- - lib/textus/surfaces/cli/verb/get.rb
318
- - lib/textus/surfaces/cli/verb/init.rb
319
- - lib/textus/surfaces/cli/verb/mcp_serve.rb
320
- - lib/textus/surfaces/cli/verb/put.rb
321
- - lib/textus/surfaces/cli/verb/schema_diff.rb
322
- - lib/textus/surfaces/cli/verb/schema_init.rb
323
- - lib/textus/surfaces/cli/verb/schema_migrate.rb
324
- - lib/textus/surfaces/cli/verb/watch.rb
325
- - lib/textus/surfaces/mcp.rb
326
- - lib/textus/surfaces/mcp/catalog.rb
327
- - lib/textus/surfaces/mcp/errors.rb
328
- - lib/textus/surfaces/mcp/server.rb
329
- - lib/textus/surfaces/role_scope.rb
330
- - lib/textus/surfaces/watcher.rb
331
- - lib/textus/types.rb
332
- - lib/textus/uid.rb
309
+ - lib/textus/store/container.rb
310
+ - lib/textus/store/cursor.rb
311
+ - lib/textus/store/entry/reader.rb
312
+ - lib/textus/store/entry/writer.rb
313
+ - lib/textus/store/envelope/meta.rb
314
+ - lib/textus/store/freshness.rb
315
+ - lib/textus/store/freshness/drift_detector.rb
316
+ - lib/textus/store/freshness/evaluator.rb
317
+ - lib/textus/store/freshness/ttl_evaluator.rb
318
+ - lib/textus/store/freshness/verdict.rb
319
+ - lib/textus/store/index/builder.rb
320
+ - lib/textus/store/index/lookup.rb
321
+ - lib/textus/store/jobs/base.rb
322
+ - lib/textus/store/jobs/index.rb
323
+ - lib/textus/store/jobs/materialize.rb
324
+ - lib/textus/store/jobs/plan.rb
325
+ - lib/textus/store/jobs/planner.rb
326
+ - lib/textus/store/jobs/queue.rb
327
+ - lib/textus/store/jobs/registry.rb
328
+ - lib/textus/store/jobs/retention.rb
329
+ - lib/textus/store/jobs/retention/base.rb
330
+ - lib/textus/store/jobs/retention/sweep.rb
331
+ - lib/textus/store/jobs/sweep.rb
332
+ - lib/textus/store/jobs/worker.rb
333
+ - lib/textus/store/layout.rb
334
+ - lib/textus/surface/cli.rb
335
+ - lib/textus/surface/cli/group.rb
336
+ - lib/textus/surface/cli/group/data.rb
337
+ - lib/textus/surface/cli/group/key.rb
338
+ - lib/textus/surface/cli/group/mcp.rb
339
+ - lib/textus/surface/cli/group/rule.rb
340
+ - lib/textus/surface/cli/group/schema.rb
341
+ - lib/textus/surface/cli/runner.rb
342
+ - lib/textus/surface/cli/sources.rb
343
+ - lib/textus/surface/cli/verb.rb
344
+ - lib/textus/surface/cli/verb/doctor.rb
345
+ - lib/textus/surface/cli/verb/get.rb
346
+ - lib/textus/surface/cli/verb/init.rb
347
+ - lib/textus/surface/cli/verb/mcp_serve.rb
348
+ - lib/textus/surface/cli/verb/put.rb
349
+ - lib/textus/surface/cli/verb/schema_diff.rb
350
+ - lib/textus/surface/cli/verb/schema_init.rb
351
+ - lib/textus/surface/cli/verb/schema_migrate.rb
352
+ - lib/textus/surface/cli/verb/watch.rb
353
+ - lib/textus/surface/mcp.rb
354
+ - lib/textus/surface/mcp/catalog.rb
355
+ - lib/textus/surface/mcp/errors.rb
356
+ - lib/textus/surface/mcp/projector.rb
357
+ - lib/textus/surface/mcp/server.rb
358
+ - lib/textus/surface/watcher.rb
359
+ - lib/textus/value/call.rb
360
+ - lib/textus/value/command.rb
361
+ - lib/textus/value/duration.rb
362
+ - lib/textus/value/envelope.rb
363
+ - lib/textus/value/etag.rb
364
+ - lib/textus/value/payload.rb
365
+ - lib/textus/value/result.rb
366
+ - lib/textus/value/role.rb
367
+ - lib/textus/value/types.rb
368
+ - lib/textus/value/uid.rb
369
+ - lib/textus/verb_registry.rb
333
370
  - lib/textus/version.rb
334
371
  - lib/textus/workflow.rb
335
372
  - lib/textus/workflow/collector.rb
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Textus
4
- module Action
5
- class Accept < WriteVerb
6
- extend Textus::Contract::DSL
7
-
8
- verb :accept
9
- summary "apply a queued proposal to its target zone; requires the author capability"
10
- surfaces :cli, :mcp
11
- cli "accept"
12
- arg :pending_key, String, required: true, positional: true, description: "the queued proposal's key"
13
-
14
- def initialize(pending_key:)
15
- super()
16
- @pending_key = pending_key
17
- end
18
-
19
- def call(container:, call:)
20
- env = Textus::Action::Get.new(key: @pending_key).call(container: container, call: call)
21
- proposal = env.meta["proposal"] or raise Textus::ProposalError.new("entry has no proposal block: #{@pending_key}")
22
- target = proposal["target_key"] or raise Textus::ProposalError.new("proposal missing target_key")
23
- action = proposal["action"] || "put"
24
-
25
- case action
26
- when "put"
27
- Textus::Action::Put.new(
28
- key: target,
29
- meta: env.meta["_meta"] || {},
30
- body: env.body,
31
- ).call(container: container, call: call)
32
- when "delete"
33
- Textus::Action::KeyDelete.new(key: target).call(container: container, call: call)
34
- else
35
- raise Textus::ProposalError.new("unknown action: #{action}")
36
- end
37
-
38
- Textus::Action::KeyDelete.new(key: @pending_key).call(container: container, call: call)
39
-
40
- { "protocol" => Textus::PROTOCOL, "accepted" => @pending_key, "target_key" => target, "action" => action }
41
- end
42
- end
43
- end
44
- end
@@ -1,131 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "json"
4
- require "time"
5
-
6
- module Textus
7
- module Action
8
- class Audit < Base
9
- extend Textus::Contract::DSL
10
-
11
- verb :audit
12
- summary "Query the audit log with optional filters."
13
- surfaces :cli
14
- cli "audit"
15
- arg :key, String, required: false, description: "filter to rows for this key"
16
- arg :lane, String, required: false, description: "filter to keys in this lane"
17
- arg :role, String, required: false, description: "filter to rows written under this role"
18
- arg :verb, String, required: false, description: "filter to rows for this verb"
19
- arg :since, String, required: false,
20
- coerce: ->(s) { Textus::Action::Audit.parse_since(s, now: Time.now) },
21
- description: "ISO-8601 timestamp or relative offset (e.g. 1h, 30m)"
22
- arg :seq_since, Integer, required: false, description: "return rows with seq > this cursor value"
23
- arg :correlation_id, String, required: false, description: "filter to rows with this correlation_id"
24
- arg :limit, Integer, required: false, description: "maximum number of rows to return"
25
- view(:cli) { |rows, _i| { "verb" => "audit", "rows" => rows } }
26
-
27
- def initialize(**kwargs)
28
- super()
29
- @query = Query.build(**kwargs.slice(:key, :lane, :role, :verb, :since, :seq_since, :correlation_id, :limit))
30
- end
31
-
32
- def args
33
- @query.to_h.compact
34
- end
35
-
36
- def call(container:, **)
37
- @manifest = container.manifest
38
- @root = container.root
39
- @log_path = Textus::Layout.audit_log(container.root)
40
- @audit_log = container.audit_log
41
-
42
- query = @query
43
- check_cursor_expiry!(query.seq_since)
44
-
45
- files = all_log_files
46
- return [] if files.empty?
47
-
48
- rows = []
49
- files.each do |file|
50
- File.foreach(file) do |line|
51
- parsed = parse_row(line.chomp)
52
- next unless parsed
53
- next unless query.matches?(parsed)
54
- next if query.lane && !key_in_lane?(parsed["key"], query.lane)
55
-
56
- rows << parsed
57
- break if limit_reached?(rows, query)
58
- end
59
- break if limit_reached?(rows, query)
60
- end
61
-
62
- rows
63
- end
64
-
65
- def self.parse_since(str, now: Time.now.utc)
66
- return nil if str.nil? || str.empty?
67
- return Time.parse(str) if str =~ /\A\d{4}-\d{2}-\d{2}/
68
-
69
- match = str.match(/\A(\d+)([smhd])\z/) or return nil
70
- mult = { "s" => 1, "m" => 60, "h" => 3600, "d" => 86_400 }[match[2]]
71
- now - (match[1].to_i * mult)
72
- end
73
-
74
- Query = Data.define(:key, :lane, :role, :verb, :since, :seq_since, :correlation_id, :limit) do
75
- # rubocop:disable Metrics/ParameterLists
76
- def self.build(key: nil, lane: nil, role: nil, verb: nil,
77
- since: nil, seq_since: nil, correlation_id: nil, limit: nil)
78
- new(key:, lane:, role:, verb:, since:, seq_since:, correlation_id:, limit:)
79
- end
80
- # rubocop:enable Metrics/ParameterLists
81
-
82
- def matches?(row)
83
- return false if key && row["key"] != key
84
- return false if role && row["role"] != role
85
- return false if verb && row["verb"] != verb
86
- return false if since && (row["ts"].nil? || Time.parse(row["ts"]) < since)
87
- return false if seq_since && (row["seq"].nil? || row["seq"] <= seq_since)
88
- return false if correlation_id && row.dig("extras", "correlation_id") != correlation_id
89
-
90
- true
91
- end
92
- end
93
-
94
- private
95
-
96
- def limit_reached?(rows, query) = query.limit && rows.length >= query.limit
97
-
98
- def check_cursor_expiry!(seq_since)
99
- return unless seq_since
100
-
101
- log = @audit_log || Textus::Ports::AuditLog.new(@root)
102
- min = log.min_available_seq
103
- raise Textus::CursorExpired.new(requested: seq_since, min_available: min) if min && seq_since < min - 1
104
- end
105
-
106
- def all_log_files
107
- rotated = Dir.glob(File.join(Textus::Layout.audit_dir(@root), "audit.log.*"))
108
- .reject { |path| path.end_with?(".meta.json") }
109
- .sort_by { |path| -path.scan(/\d+$/).first.to_i }
110
- active = File.exist?(@log_path) ? [@log_path] : []
111
- rotated + active
112
- end
113
-
114
- def parse_row(line)
115
- return nil if line.empty?
116
- return nil unless line.start_with?("{")
117
-
118
- JSON.parse(line)
119
- rescue JSON::ParserError
120
- nil
121
- end
122
-
123
- def key_in_lane?(key, lane)
124
- mentry = @manifest.resolver.resolve(key).entry
125
- mentry && mentry.lane == lane
126
- rescue Textus::Error
127
- false
128
- end
129
- end
130
- end
131
- end
@@ -1,42 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Textus
4
- module Action
5
- @registry = {}
6
-
7
- def self.registry = @registry
8
-
9
- def self.register(klass)
10
- @registry[klass.name.gsub("::", "/").downcase] = klass
11
- end
12
-
13
- def self.fetch(type)
14
- return @registry[type] if @registry[type]
15
-
16
- match = @registry.values.find { |k| k.const_defined?(:TYPE, false) && type == k::TYPE }
17
- raise Textus::UsageError.new("unknown action type: #{type}") unless match
18
-
19
- @registry[type] = match
20
- end
21
-
22
- class Base
23
- def self.inherited(subclass)
24
- super
25
- Textus::Action.register(subclass) if subclass.name
26
- end
27
-
28
- def call(**)
29
- raise NotImplementedError.new("#{self.class}#call")
30
- end
31
-
32
- def args
33
- params = self.class.instance_method(:initialize).parameters
34
- names = params.select { |t,| %i[key keyreq].include?(t) }.map(&:last)
35
- names.each_with_object({}) do |name, h|
36
- val = instance_variable_get(:"@#{name}")
37
- h[name] = val unless val.nil?
38
- end
39
- end
40
- end
41
- end
42
- end
@@ -1,97 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "open3"
4
-
5
- module Textus
6
- module Action
7
- class Blame < Base
8
- extend Textus::Contract::DSL
9
-
10
- verb :blame
11
- summary "Annotate audit rows for a key with the git commit that introduced each file state."
12
- surfaces :cli
13
- cli "blame"
14
- arg :key, String, required: true, positional: true, description: "entry key to blame"
15
- arg :limit, Integer, required: false, description: "maximum number of audit rows to return"
16
- view(:cli) { |rows, inputs| { "verb" => "blame", "key" => inputs[:key], "rows" => rows } }
17
-
18
- def initialize(key:, limit: nil)
19
- super()
20
- @key = key
21
- @limit = limit
22
- end
23
-
24
- def call(container:, **)
25
- @container = container
26
- @manifest = container.manifest
27
- @root = container.root
28
-
29
- audit_rows = Textus::Action::Audit.new(key: @key, limit: @limit).call(container: container)
30
- path = resolve_path(@key)
31
- return audit_rows.map { |row| row.merge("git" => nil) } unless git_tracked?(path)
32
-
33
- audit_rows.map { |row| row.merge("git" => git_commit_at(path, timestamp: row["ts"])) }
34
- end
35
-
36
- def self.new(*args, **kwargs)
37
- return super(**kwargs) unless args.any?
38
-
39
- positional = instance_method(:initialize).parameters.slice(:keyreq, :key).map(&:last)
40
- mapped = positional.zip(args).to_h
41
- super(**mapped.merge(kwargs))
42
- end
43
-
44
- private
45
-
46
- def resolve_path(key)
47
- res = @manifest.resolver.resolve(key)
48
- mentry = res.entry
49
- path = res.path
50
- path || Textus::Key::Path.resolve(@manifest.data, mentry)
51
- rescue Textus::Error
52
- nil
53
- end
54
-
55
- def git_tracked?(path)
56
- return false if path.nil?
57
- return false unless File.exist?(path)
58
- return false unless git_repo?
59
-
60
- _out, _err, status = Open3.capture3(
61
- "git", "ls-files", "--error-unmatch", path,
62
- chdir: @root
63
- )
64
- status.success?
65
- rescue Errno::ENOENT
66
- false
67
- end
68
-
69
- def git_repo?
70
- dir = @root
71
- loop do
72
- return true if File.directory?(File.join(dir, ".git"))
73
-
74
- parent = File.dirname(dir)
75
- return false if parent == dir
76
-
77
- dir = parent
78
- end
79
- end
80
-
81
- def git_commit_at(path, timestamp:)
82
- args = ["git", "log", "-1"]
83
- args << "--before=#{timestamp}" if timestamp
84
- args += ["--format=%H%x09%an%x09%aI%x09%s", "--", path]
85
- out, _err, status = Open3.capture3(*args, chdir: @root)
86
- return nil unless status.success?
87
-
88
- sha, author, date, subject = out.strip.split("\t", 4)
89
- return nil if sha.nil? || sha.empty?
90
-
91
- { "sha" => sha, "author" => author, "date" => date, "subject" => subject }
92
- rescue Errno::ENOENT
93
- nil
94
- end
95
- end
96
- end
97
- end
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Textus
4
- module Action
5
- class Boot < Base
6
- extend Textus::Contract::DSL
7
-
8
- verb :boot
9
- summary "Return the orientation contract: lanes, agent_quickstart, agent_protocol, and pre-computed artifacts."
10
- surfaces :cli, :mcp
11
-
12
- def call(container:, **)
13
- Textus::Boot.build(container: container)
14
- end
15
- end
16
- end
17
- end