From da3ac33bb1020815cc9148a7a97826d904dc842d Mon Sep 17 00:00:00 2001 From: Patrick Date: Mon, 6 Jul 2026 00:24:02 +0700 Subject: [PATCH 1/6] feat: web surface, shared dispatch, entry constraint enforcement - Web surface: POST /api/:verb via Sinatra + Puma, GET /api/status - CLI group/verb: 'textus web serve' starts the web server - Shared Dispatch.call: all three surfaces (CLI, MCP, Web) route through it - EntryConstraint: naming/schema/format enforcement at propose, accept, put - MCP Projector removed (pass-through), Catalog calls Dispatch directly - Schema validation in accept handler parses body frontmatter first - 1070 examples, 0 failures, rubocop clean --- .../0004-surface-architecture-session.md | 20 ++++++ .../0138-surface-architecture-web-api.md | 64 +++++++++++++++++++ Gemfile | 4 ++ Gemfile.lock | 36 +++++++++++ lib/textus.rb | 2 + lib/textus/lanes/scratchpad/handlers.rb | 22 +++++++ lib/textus/protocol/entry_constraint.rb | 54 ++++++++++++++++ lib/textus/protocol/store_engine/put.rb | 16 ++--- lib/textus/surface/cli/group/web.rb | 11 ++++ lib/textus/surface/cli/runner.rb | 2 +- lib/textus/surface/cli/verb/web_serve.rb | 22 +++++++ .../surface/{mcp/projector.rb => dispatch.rb} | 14 ++-- lib/textus/surface/mcp/catalog.rb | 3 +- lib/textus/surface/web.rb | 50 +++++++++++++++ spec/conformance/boot/cli_verbs_spec.rb | 2 +- spec/conformance/surface/cli/contract_spec.rb | 6 +- .../surface/adapter_dispatch_parity_spec.rb | 4 +- .../scratchpad/proposal_handlers_spec.rb | 25 ++++++-- 18 files changed, 323 insertions(+), 34 deletions(-) create mode 100644 .textus/data/knowledge/loop/adoption/0004-surface-architecture-session.md create mode 100644 .textus/data/knowledge/loop/evidence/0138-surface-architecture-web-api.md create mode 100644 lib/textus/protocol/entry_constraint.rb create mode 100644 lib/textus/surface/cli/group/web.rb create mode 100644 lib/textus/surface/cli/verb/web_serve.rb rename lib/textus/surface/{mcp/projector.rb => dispatch.rb} (54%) create mode 100644 lib/textus/surface/web.rb diff --git a/.textus/data/knowledge/loop/adoption/0004-surface-architecture-session.md b/.textus/data/knowledge/loop/adoption/0004-surface-architecture-session.md new file mode 100644 index 00000000..16b597a6 --- /dev/null +++ b/.textus/data/knowledge/loop/adoption/0004-surface-architecture-session.md @@ -0,0 +1,20 @@ +--- +node: adoption +confidence: high +signal: session_close +summary: 'Surface architecture session: built web API + shared dispatch, removed pass-through + Projector' +uid: 7864dee380cdd5c8 +--- +node: adoption +confidence: high +signal: session_close +summary: Surface architecture session: built web API + shared dispatch, removed pass-through Projector + +Three observations from the surface architecture session: + +1. **Pass-through detection** — The MCP `Projector` became a delegation-only shell after the shared `Dispatch` module was extracted (15 lines, one delegation method). Architecture reviews should check for delegation-only wrappers: modules whose only public method forwards to another module. The deletion test catches these. + +2. **Verb registry + binder is surface-agnostic** — Shared `Dispatch.call` confirmed VerbRegistry + Binder + Session works identically for CLI, MCP, and Web. All three now route through the same 5-line core. Validates ADR-0063. + +3. **Prototype dependency cost** — Sinatra + Puma + Rackup added ~8 transitive gems (Gemfile only, not gemspec). Acceptable for prototype stage. diff --git a/.textus/data/knowledge/loop/evidence/0138-surface-architecture-web-api.md b/.textus/data/knowledge/loop/evidence/0138-surface-architecture-web-api.md new file mode 100644 index 00000000..5012fc90 --- /dev/null +++ b/.textus/data/knowledge/loop/evidence/0138-surface-architecture-web-api.md @@ -0,0 +1,64 @@ +--- +uid: 0d5255f6ff0daf46 +--- +--- +proposal: + target_key: knowledge.loop.evidence.0138-surface-architecture-web-api + action: put +--- + +# ADR 0138 — Surface Architecture: Web API + Shared Dispatch + +**Date:** 2026-07-05 +**Status:** Proposed +**Refines:** [ADR 0063](./0063-cli-is-a-projection-of-the-contract.md) (CLI/MCP surface contract), [ADR 0058](./0058-one-verb-name-across-surfaces.md) (surface verb consistency) + +> **One sentence:** Adopt a thin shared dispatch module that all three surfaces (CLI, MCP, Web) route through, and a verb-dispatch-only web API at `POST /api/:verb`. + +## Decision + +### 1. Shared Dispatch Module + +Extract a thin core dispatch into `Textus::Surface::Dispatch`: + +```ruby +def self.call(session, verb, raw_args) + spec = VerbRegistry.for(verb) + bound = Binder.inputs_from_wire(spec, raw_args) + session.public_send(verb, **bound) +end +``` + +Each surface calls this and owns its own error translation: + +| Surface | Dispatch | Error Format | +|---------|----------|-------------| +| CLI | Auto-generated Verb classes | Exit codes (Integer) | +| MCP | Projector + Catalog | JSON-RPC error codes | +| Web | POST /api/:verb | HTTP status + JSON `{error, code}` | + +### 2. Web API Surface + +``` +POST /api/:verb — verb dispatch (JSON body → bound args) +GET /api/status — health check: {protocol, lanes[], store_root} +X-Textus-Role header — role selection (default: agent) +``` + +Port 9292, Puma via Sinatra 4.x. + +## Rationale + +- **Consistency:** all three surfaces speak the same verb contract. The web is the same protocol over HTTP, not a separate API. +- **Prototype scope:** dedicated resource endpoints (entries, lanes, audit) duplicate the verb registry. Deferred until a GUI proves which endpoints are worth extracting. +- **Hyrum's Law:** verb-dispatch means every new verb is automatically web-available — no endpoint to add, no contract to maintain separately. + +## Files + +| File | Status | +|------|--------| +| `lib/textus/surface/web.rb` | Built — Sinatra app | +| `lib/textus/surface/cli/group/web.rb` | Built — `textus web` group | +| `lib/textus/surface/cli/verb/web_serve.rb` | Built — `textus web serve` verb | +| `lib/textus/surface/dispatch.rb` | Pending — shared dispatch module | +| `Gemfile` | Modified — sinatra, rackup, puma added | diff --git a/Gemfile b/Gemfile index 32ab4c3a..b137e9b5 100644 --- a/Gemfile +++ b/Gemfile @@ -3,6 +3,10 @@ gemspec gem "mustache", "~> 1.1" +gem "puma", ">= 6.0" +gem "rackup", ">= 2.1" +gem "sinatra", "~> 4.2" + group :development, :test do gem "rubocop", "~> 1.69", require: false gem "rubocop-rspec", "~> 3.3", require: false diff --git a/Gemfile.lock b/Gemfile.lock index db075dcf..2c5c9a2e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -17,6 +17,7 @@ GEM remote: https://rubygems.org/ specs: ast (2.4.3) + base64 (0.3.0) bigdecimal (4.1.2) concurrent-ruby (1.3.6) csv (3.3.5) @@ -71,6 +72,8 @@ GEM mcp (0.20.0) json_schemer (>= 2.4) mustache (1.1.2) + mustermann (3.1.1) + nio4r (2.7.5) parallel (2.1.0) parser (3.3.11.1) ast (~> 2.4.1) @@ -79,7 +82,19 @@ GEM psych (5.3.1) date stringio + puma (8.0.2) + nio4r (~> 2.0) racc (1.8.1) + rack (3.2.6) + rack-protection (4.2.1) + base64 (>= 0.1.0) + logger (>= 1.6.0) + rack (>= 3.0.0, < 4) + rack-session (2.1.2) + base64 (>= 0.1.0) + rack (>= 3.0.0) + rackup (2.3.1) + rack (>= 3) rainbow (3.1.1) rake (13.4.2) rbs (4.0.3) @@ -132,9 +147,17 @@ GEM simplecov-html (0.13.2) simplecov_json_formatter (0.1.4) simpleidn (0.2.3) + sinatra (4.2.1) + logger (>= 1.6.0) + mustermann (~> 3.0) + rack (>= 3.0.0, < 4) + rack-protection (= 4.2.1) + rack-session (>= 2.0.0, < 3) + tilt (~> 2.0) sqlite3 (2.9.5-arm64-darwin) sqlite3 (2.9.5-x86_64-linux-gnu) stringio (3.2.0) + tilt (2.7.0) tsort (0.2.0) unicode-display_width (3.2.0) unicode-emoji (~> 4.1) @@ -148,16 +171,20 @@ PLATFORMS DEPENDENCIES mustache (~> 1.1) + puma (>= 6.0) + rackup (>= 2.1) rake (~> 13.0) rspec (~> 3.13) rubocop (~> 1.69) rubocop-rspec (~> 3.3) ruby-lsp (~> 0.26) simplecov (~> 0.22) + sinatra (~> 4.2) textus! CHECKSUMS ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383 + base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b bigdecimal (4.1.2) sha256=53d217666027eab4280346fba98e7d5b66baaae1b9c3c1c0ffe89d48188a3fbd concurrent-ruby (1.3.6) sha256=6b56837e1e7e5292f9864f34b69c5a2cbc75c0cf5338f1ce9903d10fa762d5ab csv (3.3.5) sha256=6e5134ac3383ef728b7f02725d9872934f523cb40b961479f69cf3afa6c8e73f @@ -181,11 +208,18 @@ CHECKSUMS logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203 mcp (0.20.0) sha256=6b71bfc9a19f6dca34953bded1bd89cba75176a4f8bb293ce82ace065af584f9 mustache (1.1.2) sha256=d420243400354da78ded2d81541b381ad8d94e8e9b95022d0d71d66f8ef36c00 + mustermann (3.1.1) sha256=4c6170c7234d5499c345562ba7c7dfe73e1754286dcc1abb053064d66a127198 + nio4r (2.7.5) sha256=6c90168e48fb5f8e768419c93abb94ba2b892a1d0602cb06eef16d8b7df1dca1 parallel (2.1.0) sha256=b35258865c2e31134c5ecb708beaaf6772adf9d5efae28e93e99260877b09356 parser (3.3.11.1) sha256=d17ace7aabe3e72c3cc94043714be27cc6f852f104d81aa284c2281aecc65d54 prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85 psych (5.3.1) sha256=eb7a57cef10c9d70173ff74e739d843ac3b2c019a003de48447b2963d81b1974 + puma (8.0.2) sha256=c8ed871dfbbe66448ea9ffd46692342d9804d4071522b52b5331b7b6e7b686fb racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f + rack (3.2.6) sha256=5ed78e1f73b2e25679bec7d45ee2d4483cc4146eb1be0264fc4d94cb5ef212c2 + rack-protection (4.2.1) sha256=cf6e2842df8c55f5e4d1a4be015e603e19e9bc3a7178bae58949ccbb58558bac + rack-session (2.1.2) sha256=595434f8c0c3473ae7d7ac56ecda6cc6dfd9d37c0b2b5255330aa1576967ffe8 + rackup (2.3.1) sha256=6c79c26753778e90983761d677a48937ee3192b3ffef6bc963c0950f94688868 rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701 rbs (4.0.3) sha256=5a7bf70e2628549d9a1f44eae447b2cfe55968a9c60cfff52693a4bdcc020e14 @@ -206,10 +240,12 @@ CHECKSUMS simplecov-html (0.13.2) sha256=bd0b8e54e7c2d7685927e8d6286466359b6f16b18cb0df47b508e8d73c777246 simplecov_json_formatter (0.1.4) sha256=529418fbe8de1713ac2b2d612aa3daa56d316975d307244399fa4838c601b428 simpleidn (0.2.3) sha256=08ce96f03fa1605286be22651ba0fc9c0b2d6272c9b27a260bc88be05b0d2c29 + sinatra (4.2.1) sha256=b7aeb9b11d046b552972ade834f1f9be98b185fa8444480688e3627625377080 sqlite3 (2.9.5-arm64-darwin) sha256=d0cf444a70fc9395d513cfbcc1e6719e224aa645314e3824cb0474c721425aa2 sqlite3 (2.9.5-x86_64-linux-gnu) sha256=233dbcb6714148dd23bc5aeb33e8efd6eac974969564ddd5794c23d5f52b231e stringio (3.2.0) sha256=c37cb2e58b4ffbd33fe5cd948c05934af997b36e0b6ca6fdf43afa234cf222e1 textus (0.55.2) + tilt (2.7.0) sha256=0d5b9ba69f6a36490c64b0eee9f6e9aad517e20dcc848800a06eb116f08c6ab3 tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f unicode-display_width (3.2.0) sha256=0cdd96b5681a5949cdbc2c55e7b420facae74c4aaf9a9815eee1087cb1853c42 unicode-emoji (4.2.0) sha256=519e69150f75652e40bf736106cfbc8f0f73aa3fb6a65afe62fefa7f80b0f80f diff --git a/lib/textus.rb b/lib/textus.rb index 097e1e39..d87d72f3 100644 --- a/lib/textus.rb +++ b/lib/textus.rb @@ -14,6 +14,8 @@ module MCP require_relative "textus/surface/mcp/errors" +require "sinatra/base" + loader = Zeitwerk::Loader.for_gem loader.inflector.inflect( "cli" => "CLI", diff --git a/lib/textus/lanes/scratchpad/handlers.rb b/lib/textus/lanes/scratchpad/handlers.rb index cf7b6039..1b27523e 100644 --- a/lib/textus/lanes/scratchpad/handlers.rb +++ b/lib/textus/lanes/scratchpad/handlers.rb @@ -31,6 +31,13 @@ def parent_mentry(ctx) def propose(key:, ctx:, call:, meta: nil, data: nil) body, content = data.is_a?(Hash) ? [nil, data] : [data, nil] + + target_key = meta&.dig("proposal", "target_key") + if target_key + resolution = ctx.manifest.resolver.resolve(target_key) + Textus::Protocol::EntryConstraint.validate_naming!(target_key, resolution.entry) + end + full_key = "#{PROPOSALS_PARENT_KEY}.#{key}" env = ctx.put(key: full_key, meta: meta || {}, body:, content:, call:) env.to_h_for_wire @@ -60,6 +67,21 @@ def accept(pending_key:, ctx:, call:, dry_run: false) case action when "put" target_meta = env.meta.to_h.except("proposal") + + # Parse body frontmatter to extract schema-relevant meta fields + resolution = ctx.manifest.resolver.resolve(target) + if env.body && !env.body.empty? + parsed = Textus::Protocol::Format.for(resolution.entry.format).parse(env.body) + body_meta = parsed["_meta"] || {} + target_meta = body_meta.merge(target_meta) + end + + # Re-validate constraints at promotion time + payload = Textus::Value::Payload.new(meta: target_meta, body: env.body, content: env.content) + Textus::Protocol::EntryConstraint.validate_naming!(target, resolution.entry) + Textus::Protocol::EntryConstraint.validate_schema!(payload, resolution.entry, schemas: ctx.schemas) + Textus::Protocol::EntryConstraint.validate_format!(payload, resolution.entry) + ctx.put(key: target, meta: target_meta, body: env.body, content: env.content, call:) when "delete" ctx.delete(key: target, call:) diff --git a/lib/textus/protocol/entry_constraint.rb b/lib/textus/protocol/entry_constraint.rb new file mode 100644 index 00000000..de755380 --- /dev/null +++ b/lib/textus/protocol/entry_constraint.rb @@ -0,0 +1,54 @@ +module Textus + module Protocol + module EntryConstraint + module_function + + def validate_naming!(key, mentry) + naming = mentry.naming + return unless naming + return unless key.start_with?("#{mentry.key}.") + + last = key.split(".").last + + case naming + when "sequential" + return if last.match?(/\A\d{4}-[a-z][a-z0-9-]*\z/) + + raise UsageError.new( + "naming violation: '#{key}' last segment must match NNNN-slug " \ + "(entry '#{mentry.key}' has naming: sequential)", + ) + when "dated" + return if last.match?(/\A\d{4}-\d{2}-\d{2}-[a-z][a-z0-9-]*\z/) + + raise UsageError.new( + "naming violation: '#{key}' last segment must match YYYY-MM-DD-slug " \ + "(entry '#{mentry.key}' has naming: dated)", + ) + end + end + + def validate_schema!(payload, mentry, schemas:) + schema_name = mentry.schema + return unless schema_name && schemas + + schema = schemas.fetch_or_nil(schema_name) + return unless schema + return if mentry.lane == "scratchpad" + + schema.validate!(payload.meta || {}) + rescue Textus::SchemaViolation + raise if mentry.lane != "scratchpad" + end + + def validate_format!(payload, mentry, format: nil) + fmt_name = format || mentry.format + fmt = Textus::Protocol::Format.for(fmt_name) + fmt.validate_raw_entry!( + { "_meta" => payload.meta, "content" => payload.content }, + mentry.lane, + ) + end + end + end +end diff --git a/lib/textus/protocol/store_engine/put.rb b/lib/textus/protocol/store_engine/put.rb index c667265e..0005c53b 100644 --- a/lib/textus/protocol/store_engine/put.rb +++ b/lib/textus/protocol/store_engine/put.rb @@ -7,7 +7,7 @@ module Put def call(key, mentry:, path:, payload:, deps:, if_etag: nil, fmt: nil) format = fmt || mentry.format - validate_input(path, payload, format, mentry) + validate_input(key, payload, format, mentry, path) validate_schema(payload, mentry, deps) existing_env = read_existing(key, mentry, path, format, deps) @@ -31,7 +31,8 @@ def call(key, mentry:, path:, payload:, deps:, if_etag: nil, fmt: nil) envelope end - def validate_input(path, payload, format, mentry) + def validate_input(key, payload, format, mentry, path) + EntryConstraint.validate_naming!(key, mentry) fmt = Format.for(format) fmt.enforce_name_match!(path, payload.meta) fmt.validate_raw_entry!( @@ -41,16 +42,7 @@ def validate_input(path, payload, format, mentry) end def validate_schema(payload, mentry, deps) - schema_name = mentry.schema - return unless schema_name && deps.schemas - - schema = deps.schemas.fetch_or_nil(schema_name) - return unless schema - return if mentry.lane == "scratchpad" - - schema.validate!(payload.meta || {}) - rescue Textus::SchemaViolation - raise if mentry.lane != "scratchpad" + EntryConstraint.validate_schema!(payload, mentry, schemas: deps.schemas) end def read_existing(key, mentry, path, format, deps) diff --git a/lib/textus/surface/cli/group/web.rb b/lib/textus/surface/cli/group/web.rb new file mode 100644 index 00000000..6b227fc6 --- /dev/null +++ b/lib/textus/surface/cli/group/web.rb @@ -0,0 +1,11 @@ +module Textus + module Surface + class CLI + class Group + class Web < Group + command_name "web" + end + end + end + end +end diff --git a/lib/textus/surface/cli/runner.rb b/lib/textus/surface/cli/runner.rb index f2f47350..34bc037e 100644 --- a/lib/textus/surface/cli/runner.rb +++ b/lib/textus/surface/cli/runner.rb @@ -59,7 +59,7 @@ def dispatch(verb_instance, store, spec) role = verb_instance.resolved_role(store) s = store.with_role(role) - result = s.public_send(spec.verb, **inputs) + result = Textus::Surface::Dispatch.call(s, spec.verb, **inputs) verb_instance.emit(result) rescue Textus::Protocol::MissingArgs => e raise UsageError.new("#{spec.cli_path} requires #{e.missing.first.wire}") diff --git a/lib/textus/surface/cli/verb/web_serve.rb b/lib/textus/surface/cli/verb/web_serve.rb new file mode 100644 index 00000000..27d7d7ba --- /dev/null +++ b/lib/textus/surface/cli/verb/web_serve.rb @@ -0,0 +1,22 @@ +module Textus + module Surface + class CLI + class Verb + class WebServe < Verb + command_name "serve" + parent_group Group::Web + option :as_flag, "--as=ROLE" + option :port, "--port=N" + + def call(store) + role = resolved_role(store, default: Textus::Value::Role::AGENT) + Textus::Surface::Web.set :textus_session, store.with_role(role) + Textus::Surface::Web.set :port, (port || 9292).to_i + Textus::Surface::Web.run! + 0 + end + end + end + end + end +end diff --git a/lib/textus/surface/mcp/projector.rb b/lib/textus/surface/dispatch.rb similarity index 54% rename from lib/textus/surface/mcp/projector.rb rename to lib/textus/surface/dispatch.rb index 3345cca3..cd0fa34d 100644 --- a/lib/textus/surface/mcp/projector.rb +++ b/lib/textus/surface/dispatch.rb @@ -1,18 +1,16 @@ module Textus module Surface - module MCP - class Projector - def initialize(view_key: :default) - @view_key = view_key - end + module Dispatch + module_function - def dispatch(verb_name, inputs:, store:) + def call(session, verb_name, raw_args = nil, **bound) + if raw_args spec = Textus::Protocol::VerbRegistry.for(verb_name.to_sym) raise Textus::UsageError.new("unknown verb: #{verb_name}") unless spec - bound = Textus::Protocol::Binder.inputs_from_wire(spec, inputs) - store.public_send(verb_name.to_sym, **bound) + bound = Textus::Protocol::Binder.inputs_from_wire(spec, raw_args) end + session.public_send(verb_name.to_sym, **bound) end end end diff --git a/lib/textus/surface/mcp/catalog.rb b/lib/textus/surface/mcp/catalog.rb index 859d253c..8a4c40f3 100644 --- a/lib/textus/surface/mcp/catalog.rb +++ b/lib/textus/surface/mcp/catalog.rb @@ -2,7 +2,6 @@ module Textus module Surface module MCP module Catalog - PROJECTOR = Projector.new(view_key: :default).freeze MCP_ADAPTER = Textus::Surface::MCP::Adapters::McpAdapter.new module_function @@ -45,7 +44,7 @@ def call(name, store:, args:) spec = Textus::Protocol::VerbRegistry.for(name.to_sym) raise ToolError.new("unknown tool: #{name}") unless spec&.mcp? - PROJECTOR.dispatch(name, inputs: args, store:) + Textus::Surface::Dispatch.call(store, name.to_sym, args) rescue Textus::Protocol::MissingArgs => e raise ToolError.new("#{name}: missing #{e.missing.map { |a| a.wire.to_s }.join(", ")}") rescue Textus::ContractDrift, Textus::CursorExpired diff --git a/lib/textus/surface/web.rb b/lib/textus/surface/web.rb new file mode 100644 index 00000000..b61655b6 --- /dev/null +++ b/lib/textus/surface/web.rb @@ -0,0 +1,50 @@ +require "sinatra/base" +require "json" + +module Textus + module Surface + class Web < Sinatra::Base + set :textus_session, nil + set :port, 9292 + set :bind, "0.0.0.0" + set :server, "puma" + set :show_exceptions, false + + get "/api/status" do + content_type :json + s = settings.textus_session + boot = s.boot + JSON.generate(protocol: boot["protocol"], lanes: boot["lanes"]&.map { |l| l["name"] }, store_root: boot["store_root"]) + end + + post "/api/:verb" do + content_type :json + session = settings.textus_session.with_role(request.env["HTTP_X_TEXTUS_ROLE"] || "agent") + body = begin + JSON.parse(request.body.read) + rescue StandardError + {} + end + result = Textus::Surface::Dispatch.call(session, params[:verb], body) + JSON.generate(result) + rescue Textus::Error => e + halt status_for(e), JSON.generate(error: e.message, code: e.code.to_s) + end + + not_found do + content_type :json + JSON.generate(error: "not found") + end + + private + + def status_for(err) + case err + when Textus::UnknownKey, Textus::UsageError then 404 + when Textus::EtagMismatch then 409 + else 422 + end + end + end + end +end diff --git a/spec/conformance/boot/cli_verbs_spec.rb b/spec/conformance/boot/cli_verbs_spec.rb index 26e402f7..9cd996ee 100644 --- a/spec/conformance/boot/cli_verbs_spec.rb +++ b/spec/conformance/boot/cli_verbs_spec.rb @@ -5,7 +5,7 @@ # `watch` is the long-running convergence daemon — a process, not an agent-facing # command — so it is deliberately omitted from the boot catalog. BOOT_GUARD_INTENTIONALLY_OMITTED = - %w[deps rdeps graph diff init mcp reject data watch workflow].freeze + %w[deps rdeps graph diff init mcp reject data watch web workflow].freeze RSpec.describe "Boot::CLI_VERBS — the agent-facing command catalog" do # Guard (ADR 0039): a verb's summary is a fact derived from its contract, not diff --git a/spec/conformance/surface/cli/contract_spec.rb b/spec/conformance/surface/cli/contract_spec.rb index d9667afa..00604002 100644 --- a/spec/conformance/surface/cli/contract_spec.rb +++ b/spec/conformance/surface/cli/contract_spec.rb @@ -25,7 +25,7 @@ def run_cli(argv, cwd:) it "every registered verb returns an Integer from a no-op invocation" do with_store do |root| Textus::Surface::CLI.verbs.each_key do |verb| - next if verb == "watch" + next if %w[watch web].include?(verb) code, = run_cli([verb], cwd: root) expect(code).to be_an(Integer), @@ -61,6 +61,7 @@ def run_cli(argv, cwd:) "session" => Textus::Surface::CLI::Group::Session, "drain" => Textus::Surface::CLI::Verb::GenDrain, "watch" => Textus::Surface::CLI::Verb::Watch, + "web" => Textus::Surface::CLI::Group::Web, "workflow" => Textus::Surface::CLI::Group::Workflow, "where" => Textus::Surface::CLI::Verb::GenWhere, } @@ -91,5 +92,8 @@ def run_cli(argv, cwd:) "list" => Textus::Surface::CLI::Verb::GenSchemaList, "show" => Textus::Surface::CLI::Verb::GenSchemaShow, ) + expect(Textus::Surface::CLI::Group::Web.subcommands).to eq( + "serve" => Textus::Surface::CLI::Verb::WebServe, + ) end end diff --git a/spec/integration/surface/adapter_dispatch_parity_spec.rb b/spec/integration/surface/adapter_dispatch_parity_spec.rb index b4b2dacd..76f3b367 100644 --- a/spec/integration/surface/adapter_dispatch_parity_spec.rb +++ b/spec/integration/surface/adapter_dispatch_parity_spec.rb @@ -14,7 +14,7 @@ cwd: tmp) cli = JSON.parse(cli_out.string) - mcp = Textus::Surface::MCP::Projector.new.dispatch(:boot, inputs: {}, store: store) + mcp = Textus::Surface::Dispatch.call(store, :boot, {}) expect(cli["protocol"]).to eq("textus/4") expect(mcp["protocol"]).to eq("textus/4") @@ -29,7 +29,7 @@ cwd: tmp) cli_body = JSON.parse(cli_out.string)["body"] - mcp = Textus::Surface::MCP::Projector.new.dispatch(:get, inputs: { "key" => "knowledge.foo" }, store: store) + mcp = Textus::Surface::Dispatch.call(store, :get, { "key" => "knowledge.foo" }) mcp_body = mcp["body"] expect(cli_body).to eq(mcp_body) diff --git a/spec/unit/lanes/scratchpad/proposal_handlers_spec.rb b/spec/unit/lanes/scratchpad/proposal_handlers_spec.rb index 633ed7e4..17fe16f9 100644 --- a/spec/unit/lanes/scratchpad/proposal_handlers_spec.rb +++ b/spec/unit/lanes/scratchpad/proposal_handlers_spec.rb @@ -1,7 +1,9 @@ RSpec.describe Textus::Lanes::Scratchpad::Handlers do def mock_container(manifest:, store:) c = double("Container") - allow(c).to receive_messages(manifest:, store:, layout: nil, workflow_registry: nil) + schemas = instance_double(Textus::Protocol::Schema::Registry) + allow(schemas).to receive(:fetch_or_nil).and_return(nil) + allow(c).to receive_messages(manifest:, store:, layout: nil, workflow_registry: nil, schemas:) c end @@ -13,8 +15,15 @@ def mock_container(manifest:, store:) container = mock_container(manifest:, store:) call = instance_double(Call, role: "human", correlation_id: "abc") ctx = Textus::Protocol::Handlers::CommandContext.new(store_engine, container, call:) - entry = double("entry", entry: instance_double(Mentry)) - allow(manifest.resolver).to receive(:resolve).with("scratchpad.proposals.decisions.feature-x").and_return(entry) + + target_mentry = instance_double(Mentry, naming: nil, schema: nil, format: "markdown", lane: "knowledge") + target_path = "/tmp/foo.md" + target_resolution = instance_double(Textus::Protocol::Manifest::Resolver::Resolution, entry: target_mentry, path: target_path) + allow(manifest.resolver).to receive(:resolve).with("knowledge.foo").and_return(target_resolution) + + proposals_mentry = instance_double(Mentry) + proposals_entry = double("entry", entry: proposals_mentry) + allow(manifest.resolver).to receive(:resolve).with("scratchpad.proposals.decisions.feature-x").and_return(proposals_entry) env = instance_double(Envelope) allow(env).to receive(:to_h_for_wire).and_return({ "key" => "scratchpad.proposals.decisions.feature-x", "uid" => "abc" }) allow(store_engine).to receive(:put).and_return(env) @@ -49,8 +58,9 @@ def mock_container(manifest:, store:) content: nil) allow(store_engine).to receive(:read).with(key: "scratchpad.proposals.test").and_return(pending_env) - entry = double("entry", entry: instance_double(Mentry)) - allow(manifest.resolver).to receive(:resolve).with("knowledge.foo").and_return(entry) + target_mentry = instance_double(Mentry, naming: nil, schema: nil, format: "markdown", lane: "knowledge") + target_resolution = instance_double(Textus::Protocol::Manifest::Resolver::Resolution, entry: target_mentry, path: "/tmp/foo.md") + allow(manifest.resolver).to receive(:resolve).with("knowledge.foo").and_return(target_resolution) allow(store_engine).to receive(:put) allow(store_engine).to receive(:delete) @@ -77,8 +87,9 @@ def mock_container(manifest:, store:) content: { "data" => 1 }) allow(store_engine).to receive(:read).with(key: "scratchpad.proposals.test").and_return(pending_env) - entry = double("entry", entry: instance_double(Mentry)) - allow(manifest.resolver).to receive(:resolve).with("knowledge.foo").and_return(entry) + target_mentry = instance_double(Mentry, naming: nil, schema: nil, format: "markdown", lane: "knowledge") + target_resolution = instance_double(Textus::Protocol::Manifest::Resolver::Resolution, entry: target_mentry, path: "/tmp/foo.md") + allow(manifest.resolver).to receive(:resolve).with("knowledge.foo").and_return(target_resolution) allow(store_engine).to receive(:put) allow(store_engine).to receive(:delete) From 8af0619bbe0adf9c82747aa74883c119273aa82f Mon Sep 17 00:00:00 2001 From: Patrick Date: Mon, 6 Jul 2026 00:33:23 +0700 Subject: [PATCH 2/6] fix: workflow TTL reader destroys stored value alias ttl every created a getter that called every(:__no_arg__), which set @ttl = nil on every read. The Scheduler's seed_expired read wf.ttl to check staleness, but the read itself destroyed the value, returning nil and preventing TTL-based workflow reseeding. Fix: add early return guard in every() when called with no argument. --- .envrc.example | 9 ++ .textus/data/artifacts/changelog.json | 134 +++++++++++++++++- .textus/data/artifacts/docs/readme.json | 2 +- .textus/data/artifacts/feeds/skills.json | 2 +- .../protocol/0001-what-textus-is.md | 2 +- ...-lanes-and-capability-based-write-gates.md | 4 +- .../protocol/0012-conformance-fixtures.md | 2 +- ...-runtime-artifacts-under-run-and-layout.md | 2 +- .../0070-content-addressed-build-artifacts.md | 4 +- .../0081-docs-become-canon-published-out.md | 4 +- .../loop/execution/runbook/0003-quickstart.md | 2 +- .textus/templates/docs/meta/orientation.erb | 2 +- CHANGELOG.md | 33 +++++ README.md | 2 +- .../0081-docs-become-canon-published-out.md | 4 +- docs/architecture/decisions/README.md | 2 +- lib/textus/infra/port/publisher.rb | 2 +- lib/textus/infra/port/sentinel_store.rb | 2 +- lib/textus/workflow/dsl.rb | 2 + 19 files changed, 196 insertions(+), 20 deletions(-) diff --git a/.envrc.example b/.envrc.example index 910f0cb2..5fd263f3 100644 --- a/.envrc.example +++ b/.envrc.example @@ -3,3 +3,12 @@ export GIT_AUTHOR_NAME="someone" export GIT_AUTHOR_EMAIL="someone@example.com" export OPENCODE_CONFIG="$PWD/opencode.local.json" + +# headroom +export HEADROOM_HOST=0.0.0.0 +export HEADROOM_PORT=8787 +export HEADROOM_BUDGET=100.0 +export HEADROOM_TELEMETRY=off + +# codebase-memory-mcp +export CBM_CACHE_DIR=.cache/codebase-memory/ diff --git a/.textus/data/artifacts/changelog.json b/.textus/data/artifacts/changelog.json index dcb917b1..fa7fe880 100644 --- a/.textus/data/artifacts/changelog.json +++ b/.textus/data/artifacts/changelog.json @@ -1,6 +1,6 @@ { "_meta": { - "generated_at": "2026-07-05T05:39:11Z", + "generated_at": "2026-07-05T17:33:08Z", "uid": "60e228765192c063" }, "entries": [ @@ -8,6 +8,138 @@ "tag": "Unreleased", "date": null, "commits": [ + { + "subject": "feat: web surface, shared dispatch, entry constraint enforcement", + "date": "2026-07-06" + }, + { + "subject": "fix: move CBM_CACHE_DIR constant outside namespace block (rubocop)", + "date": "2026-07-05" + }, + { + "subject": "refactor: simplification phase 2 — pipeline inline, data param, ADR archive, adoption rename, boot improvements", + "date": "2026-07-05" + }, + { + "subject": "chore: fix rubocop hash alignment in bin/smoke", + "date": "2026-07-05" + }, + { + "subject": "feat: add ruby/python/javascript format files with proper extensions", + "date": "2026-07-05" + }, + { + "subject": "feat: per-language script formats (bash/ruby/python/js)", + "date": "2026-07-05" + }, + { + "subject": "chore: gitignore bin/.smoke-sids marker file", + "date": "2026-07-05" + }, + { + "subject": "fix: add --as-format to put for per-write format override", + "date": "2026-07-05" + }, + { + "subject": "fix: script files under sessions get no extension instead of .md", + "date": "2026-07-05" + }, + { + "subject": "fix: smoke output now glance-friendly and session-id capture fixed", + "date": "2026-07-05" + }, + { + "subject": "fix: smoke --clean now sweeps orphan session dirs via marker file", + "date": "2026-07-05" + }, + { + "subject": "chore: fix rubocop offenses for CI compliance", + "date": "2026-07-05" + }, + { + "subject": "feat: smoke tests 4 script formats (bash/ruby/python/js) under sessions..scripts", + "date": "2026-07-05" + }, + { + "subject": "feat: add bin/smoke for agent session lifecycle testing", + "date": "2026-07-05" + }, + { + "subject": "style: disable RSpec/VerifiedDoubles, StubbedMock, MessageSpies, VerifiedDoubleReference in rubocop; deduplicate config", + "date": "2026-07-05" + }, + { + "subject": "style: rubocop autocorrect (non-RSpecVerifiedDoubles), fix rescue modifier/comma/guard-clause in new code", + "date": "2026-07-05" + }, + { + "subject": "feat: strict agent session protocol — visible INPUT/LOOP/OUTPUT in AGENTS.md, boot context in session_open, nodes_checked in session_close, doctor check for skipped constraint, protocol entry 0016", + "date": "2026-07-05" + }, + { + "subject": "fix: Runner::Context needs file_system/schemas for workflow step blocks, fix stale error message (match: → on:)", + "date": "2026-07-05" + }, + { + "subject": "refactor: standardize pipeline shapes — extract Delete/Move steps into separate files, add narrow DeleteDeps/MoveDeps structs", + "date": "2026-07-05" + }, + { + "subject": "refactor: add narrow file queries to QueryContext, update ops handlers; docs: align knowledge store with current architecture — 4 files updated, 3 deleted, data-flow rewritten", + "date": "2026-07-05" + }, + { + "subject": "refactor: workflow redesign — two-seam drain/watch, deleted Engine/Queue/RetryPolicy, inline retry, Publisher uses container.store_engine, StepHelpers extracted from Helpers", + "date": "2026-07-05" + }, + { + "subject": "refactor: collapse 21 single-file verb specs into core_verbs.rb", + "date": "2026-07-05" + }, + { + "subject": "chore: remove dead code (Outcome, Result, verb stubs, session_open/close files, Registry#workflows_for_key, be_success/be_failure matchers)", + "date": "2026-07-05" + }, + { + "subject": "style: suppress rubocop warnings in spec files", + "date": "2026-07-05" + }, + { + "subject": "style: suppress Metrics/ParameterLists in Container", + "date": "2026-07-05" + }, + { + "subject": "style: rubocop auto-correct fixes", + "date": "2026-07-05" + }, + { + "subject": "refactor: split Workflow::DSL::Definition into sub-modules", + "date": "2026-07-05" + }, + { + "subject": "refactor: group 11-step pipeline into 4 phases", + "date": "2026-07-05" + }, + { + "subject": "refactor: split Builder into three testable layers", + "date": "2026-07-05" + }, + { + "subject": "refactor: CQS context split — QueryContext + CommandContext", + "date": "2026-07-05" + }, + { + "subject": "refactor: consolidate authorization into Manifest::Policy", + "date": "2026-07-05" + }, + { + "subject": "refactor: collapse shallow namespace modules", + "date": "2026-07-05" + }, + { + "subject": "chore: fix rubocop violations, key_delete module_function, and refresh docs", + "date": "2026-07-05" + }, { "subject": "refactor: replace hand-written Session delegations with Forwardable", "date": "2026-07-05" diff --git a/.textus/data/artifacts/docs/readme.json b/.textus/data/artifacts/docs/readme.json index 17b69a20..90e2e2b8 100644 --- a/.textus/data/artifacts/docs/readme.json +++ b/.textus/data/artifacts/docs/readme.json @@ -13,7 +13,7 @@ }, { "key": "knowledge.readme.body", - "body": "\n## See it in four commands\n\n```sh\ngem install textus\ntextus init # creates .textus/ with lanes + schemas\n\n# an agent proposes a change — it targets a knowledge entry, but lands in proposals/\ntextus propose notes.oncall --as=agent --stdin <<'JSON'\n{\n \"_meta\": { \"name\": \"oncall\",\n \"proposal\": { \"target_key\": \"knowledge.notes.oncall\", \"action\": \"put\" } },\n \"body\": \"Patrick on call.\\n\"\n}\nJSON\n\n# you accept it — textus promotes to knowledge/ and audits the move\ntextus accept proposals.notes.oncall --as=human\n```\n\nTry the gate the other way (`textus put knowledge.notes.X --as=agent`) and you get `write_forbidden`, with the role that *would* be allowed named in the error. That refusal is the whole point.\n\n## Try it\n\n- **Worked end-to-end store** — the role gate (propose → accept), drain/publish (`CLAUDE.md` / `AGENTS.md` generated from knowledge entries), schemas, ERB templates, and workflows: [`.textus/`](.textus/)\n- **Wire textus into Claude Code via MCP** — 4 steps, ~5 minutes: [`docs/how-to/agents-mcp.md`](docs/how-to/agents-mcp.md)\n\n## Protocol, not just a gem\n\nThis Ruby gem is the reference implementation of **`textus/4`** — a wire format and storage convention any language can speak. The protocol owns the envelope shape, the role/lane gate, the audit log format, and the key grammar. The gem version (semver, see badge) and the protocol version (`textus/4`) move independently; envelopes carry the `protocol` field so consumers can pin to the contract, not the implementation.\n\n- Specification: the wire protocol spec (`textus get knowledge.specs.*`)\n- Architecture: [`docs/architecture/README.md`](docs/architecture/README.md)\n- Per-release notes: [`CHANGELOG.md`](CHANGELOG.md)\n\nA second implementation in another language would share the same `.textus/` directory and the same audit log. That's deliberate.\n\n## Install\n\n```sh\ngem install textus\n```\n\nOr from this repo:\n\n```sh\nbundle install\nbundle exec exe/textus --help\n```\n\n## What `textus init` gives you\n\nYou get `.textus/` with all five lane directories under `data/`, baseline schemas, a starter manifest, and a gitignored `.state/` for disposable runtime state (the audit log, per-role cursors, produce locks). Roles declare capabilities; each lane declares a `kind:`, and write authority is derived from the role's capabilities crossed with the lane's kind:\n\n```yaml\nroles:\n - { name: human, can: [author, propose] }\n - { name: agent, can: [propose, keep] }\n - { name: automation, can: [converge] }\n\nlanes:\n - { name: knowledge, kind: canon } # author — canonical truth\n - { name: scratchpad, kind: workspace } # keep — agent's own durable lane\n - { name: proposals, kind: queue } # propose — proposals awaiting accept\n - { name: artifacts, kind: machine } # converge — computed outputs + external inputs\n```\n\n```\n.textus/\n manifest.yaml # role capabilities + lane kinds + key-to-path mapping\n schemas/ # YAML field shapes per entry family\n templates/ # ERB templates for produced entries\n workflows/ # Ruby workflow files (Textus.workflow DSL) for data acquisition\n .gitignore # generated — ignores .state/ and any tracked:false entries\n data/ # one dir per lane; kinds + capabilities are in the manifest above\n knowledge/ # e.g. identity (knowledge.identity.*), voice, decisions, notes\n scratchpad/\n proposals/\n artifacts/ # machine lane: computed outputs + external inputs\n .state/ # disposable runtime state — gitignored, safe to delete (ADR 0038)\n audit/audit.log # append-only NDJSON event ledger, every write (rotates at ~10 MB)\n cursors/ # per-role pulse cursor — where `pulse --since` resumes\n locks/ # per-key produce locks + the produce mutex\n sentinels/ # publish bookkeeping (target sha) — regenerated on drain (ADR 0070)\n indexes/raw.yaml # raw lane content-hash/URL index — regenerable cache\n```\n\nManifest `path:` fields are relative to `.textus/data/`. So `knowledge.notes.org.jane` lives at `.textus/data/knowledge/notes/org/jane.md`.\n\nRead and write:\n\n```sh\ntextus get knowledge.notes.org.jane\ntextus list --lane=knowledge\nprintf '%s' '{\"_meta\":{\"name\":\"bob\",\"org\":\"acme\"},\"body\":\"hi\\n\"}' \\\n | textus put knowledge.notes.bob --as=human --stdin\ntextus drain --as=automation # re-pull stale inputs + recompute derived outputs\ntextus rule list # show every rule block\ntextus audit --limit=20 # query the audit log\n```\n\n(All verbs return JSON envelopes; `--output=json` is the default and the only format.)\n\nFor a worked store — knowledge entries, a staged proposal, schemas, ERB templates, and a `drain` that publishes `CLAUDE.md` / `AGENTS.md` — see [`.textus/`](.textus/).\n\n## What's shipped\n\n- **Per-entry formats & publish.** `format: markdown|json|yaml|text` per entry; a typed `publish:` block (`to:` for file fan-out, `tree:` for a whole-subtree mirror) byte-copies derived files to their consumer paths. (the wire protocol spec §5.2–5.3)\n- **Stable identity.** Auto-minted `uid:` survives writes and `textus key mv`; reorganising never breaks references.\n- **Capability × lane-kind gate.** Writes carry `--as=`; a role may write a lane iff it holds the capability the lane's `kind:` requires (`canon`→`author`, `workspace`→`keep`, `machine`→`converge`, `queue`→`propose`). The wrong role gets `write_forbidden` naming the capability needed and the roles that hold it. (the wire protocol spec §5)\n- **Agent loop.** `textus boot` orients a fresh session; `textus pulse --since=N` is the per-turn heartbeat (changed entries, pending proposals, index etag for catalog drift detection). ([docs/how-to/agents-mcp.md](docs/how-to/agents-mcp.md))\n- **MCP surface.** The official `mcp` Ruby SDK drives the stdio JSON-RPC server; protocol version auto-negotiated up to `2025-11-25`. Wire textus into Claude Code, Cursor, or any MCP host in one config block.\n- **`textus doctor`.** Health checks across schemas, workflow registrations, keys, sentinels, and the audit log.\n- **`raw` lane and `ingest` verb.** Write-once intake lane for external URL bookmarks, files, and binary assets. Three source kinds (`url`/`file`/`asset`); daily key derivation; scratchpad stub per ingest. See \"Intake and ingest\" section below.\n\n## CLI and lanes\n\nEvery command operates on one store, located in this order: `--root ` flag → **`TEXTUS_ROOT`** env → walk up from the working directory for a `.textus/` (the wire protocol spec §3.1). Write verbs require `--as=`, resolved as: `--as` flag → **`TEXTUS_ROLE`** env → `.textus/role` file → default `human` (the wire protocol spec §5.1). Default roles: `human`, `agent`, `automation` (rename or add your own in the manifest's `roles:` block). All verbs accept `--output=json` and return the envelope defined in the wire protocol spec §8.\n\n- Full verb table — read, write, health, scaffolding — is in the wire protocol spec §9.\n- Lane semantics and the capability × lane-kind mapping live in the wire protocol spec §5, with the reference in [`docs/reference/lanes.md`](docs/reference/lanes.md).\n\n`textus boot` prints the same information for the current store: lanes, entry families with schemas, registered workflows, write flows, and the verb catalog. Run it inside a store and you get the live picture; reach for the SPEC when you want the contract.\n\n## Produce and publish\n\nProduced entries (`kind: produced`) declare how they're acquired in one `source:` block; `drain` materialises them. Two built-in modes, plus workflows for custom data acquisition:\n\n- **`source: { from: external, command: \"...\", sources: [...] }`** — *externally managed*: an out-of-band command or workflow writes the file; textus tracks staleness via declared `sources`.\n- **`source: { from: external, command: \"true\", sources: [] }` + a workflow** — *workflow-driven*: a `Textus.workflow` block (in `.textus/workflows/`) acquires and shapes the data on `drain`.\n\nPublishing is one typed `publish:` block (ADR 0052/0094). Each target is either `{ to: path, template?: name }` for a single file (optionally rendered through an ERB template) or `{ tree: \"dir\" }` to mirror a whole stored subtree. Sentinels for every published file live under `.textus/.state/sentinels/` (git-ignored, regenerated on drain). See SPEC §5.2, §5.3, §5.12.\n\nTemplates live in `.textus/templates/` as ERB files (`.erb`). The template receives the entry's `content` hash as local variables via `ERB#result_with_hash`. If `inject_boot: true`, a `boot` variable is also available with the live orientation context.\n\n## Workflows\n\ntextus extends through **workflows** — a `Textus.workflow` block placed in `.textus/workflows/**/*.rb`. Each workflow matches a produced entry by key glob, then runs one or more named steps to acquire its data:\n\n```ruby\n# .textus/workflows/docs/my_report.rb\nTextus.workflow \"my_report\" do\n match \"artifacts.my-report\"\n\n step :build do |_, ctx|\n # read from knowledge, fetch external data, compute anything\n rows = ctx.container.manifest.resolver\n .enumerate(prefix: \"knowledge.notes\")\n .map { |r| { \"key\" => r[:key], \"title\" => r[:entry].schema } }\n { \"content\" => { \"entries\" => rows } }\n end\nend\n```\n\n`drain` discovers all workflow files, matches them against produced entries, and runs the steps. The result is written back to the entry's data path; `publish:` then copies it to its consumer paths.\n\n## Intake and ingest\n\nThe `raw` lane is the inbound counterpart to `artifacts`: where `drain` materialises\n**outbound** computed outputs, `ingest` receives **inbound** external source material.\n\n**The ingest principle:** prefer a reference over a copy. Store body or asset only when the\ncontent itself is the value — human-authored notes, brainstorm outputs, context you want to\nannotate. For everything else, the URL is enough. If the source is private or\naccess-restricted, set `access: private` in `source:` so downstream workflows can handle it\nappropriately.\n\n**Three source kinds:**\n\n| Kind | Stores | Use when |\n|------|--------|----------|\n| `url` | URL reference only (`body: null`) | Bookmarking a page, skill, or doc for later annotation |\n| `file` | File body text | Valuable human-authored content (brainstorm notes, meeting summaries) |\n| `asset` | Binary at `assets/raw/` | Screenshots, PDFs — only when the asset itself is the artefact |\n\n**Write-once** — the same slug on the same day cannot be overwritten. Delete and re-ingest to replace.\n\n```sh\n# bookmark a skill reference — URL only, body stays null\ntextus ingest url agentskills-io-brainstorming \\\n --url=https://agentskills.io/skills/brainstorming \\\n --label=\"brainstorming skill\" \\\n --as=agent\n\n# see what landed in the raw lane\ntextus list --lane=raw\n\n# a scratchpad stub was created alongside — annotate it\ntextus get scratchpad.notes.raw\n```\n\nStale produced entries are re-materialised by `drain`, not by reads — `get` is a pure read (ADR 0089).\n\n```sh\ntextus drain --as=automation # re-materialise every stale produced entry\ntextus drain artifacts.feeds.skills --as=automation # scope to one prefix\ntextus get artifacts.feeds.skills # a pure read; carries a freshness verdict\n```\n\nSchemas (`.textus/schemas/.yaml`) declare field shapes, per-field `maintained_by:` ownership, and an `evolution:` block (`added_in`, `deprecated_at`, `migrate_from`). Full contract in SPEC §5.8.\n\nSee [`docs/how-to/agents-mcp.md`](docs/how-to/agents-mcp.md) for the agent boot → pulse loop.\n\n## Examples\n\n[`.textus/`](.textus/) — textus as a project's own context store. Human-authored `knowledge/` (project facts, runbooks, ADRs), a staged proposal showing the agent-propose / human-accept loop, schemas validating each family, ERB templates and workflows, and a `drain` that publishes the orientation artifact to `CLAUDE.md` and `AGENTS.md`. Includes a copy-paste adoption recipe for your own repo.\n\n## Tests\n\n```sh\nbundle exec rspec\n```\n\nIncludes conformance fixtures A–I from SPEC §12.\n\n## Code quality\n\n```sh\nbundle exec rubocop # lint\nbundle exec rubocop -A # lint + autocorrect\n```\n\nLefthook hooks (`brew bundle install` then `lefthook install`) run rubocop on `pre-commit` and `rspec + rubocop` on `pre-push`. Bypass with `LEFTHOOK=0 git commit ...` when needed. CI runs `rspec` (Ruby 3.3 / 3.4) and `rubocop` via GitHub Actions.\n\n## License\n\n[MIT](LICENSE)\n" + "body": "\n## See it in four commands\n\n```sh\ngem install textus\ntextus init # creates .textus/ with lanes + schemas\n\n# an agent proposes a change — it targets a knowledge entry, but lands in proposals/\ntextus propose notes.oncall --as=agent --stdin <<'JSON'\n{\n \"_meta\": { \"name\": \"oncall\",\n \"proposal\": { \"target_key\": \"knowledge.notes.oncall\", \"action\": \"put\" } },\n \"body\": \"Patrick on call.\\n\"\n}\nJSON\n\n# you accept it — textus promotes to knowledge/ and audits the move\ntextus accept proposals.notes.oncall --as=human\n```\n\nTry the gate the other way (`textus put knowledge.notes.X --as=agent`) and you get `write_forbidden`, with the role that *would* be allowed named in the error. That refusal is the whole point.\n\n## Try it\n\n- **Worked end-to-end store** — the role gate (propose → accept), drain/publish (`CLAUDE.md` / `AGENTS.md` generated from knowledge entries), schemas, ERB templates, and workflows: [`.textus/`](.textus/)\n- **Wire textus into Claude Code via MCP** — 4 steps, ~5 minutes: [`docs/how-to/agents-mcp.md`](docs/how-to/agents-mcp.md)\n\n## Protocol, not just a gem\n\nThis Ruby gem is the reference implementation of **`textus/4`** — a wire format and storage convention any language can speak. The protocol owns the envelope shape, the role/lane gate, the audit log format, and the key grammar. The gem version (semver, see badge) and the protocol version (`textus/4`) move independently; envelopes carry the `protocol` field so consumers can pin to the contract, not the implementation.\n\n- Specification: the wire protocol spec (`textus get knowledge.specs.*`)\n- Architecture: [`docs/architecture/README.md`](docs/architecture/README.md)\n- Per-release notes: [`CHANGELOG.md`](CHANGELOG.md)\n\nA second implementation in another language would share the same `.textus/` directory and the same audit log. That's deliberate.\n\n## Install\n\n```sh\ngem install textus\n```\n\nOr from this repo:\n\n```sh\nbundle install\nbundle exec exe/textus --help\n```\n\n## What `textus init` gives you\n\nYou get `.textus/` with all five lane directories under `data/`, baseline schemas, a starter manifest, and a gitignored `.state/` for disposable runtime state (the audit log, per-role cursors, produce locks). Roles declare capabilities; each lane declares a `kind:`, and write authority is derived from the role's capabilities crossed with the lane's kind:\n\n```yaml\nroles:\n - { name: human, can: [author, propose] }\n - { name: agent, can: [propose, keep] }\n - { name: automation, can: [converge] }\n\nlanes:\n - { name: knowledge, kind: canon } # author — canonical truth\n - { name: scratchpad, kind: workspace } # keep — agent's own durable lane\n - { name: proposals, kind: queue } # propose — proposals awaiting accept\n - { name: artifacts, kind: machine } # converge — computed outputs + external inputs\n```\n\n```\n.textus/\n manifest.yaml # role capabilities + lane kinds + key-to-path mapping\n schemas/ # YAML field shapes per entry family\n templates/ # ERB templates for produced entries\n workflows/ # Ruby workflow files (Textus.workflow DSL) for data acquisition\n .gitignore # generated — ignores .state/ and any tracked:false entries\n data/ # one dir per lane; kinds + capabilities are in the manifest above\n knowledge/ # e.g. identity (knowledge.identity.*), voice, decisions, notes\n scratchpad/\n proposals/\n artifacts/ # machine lane: computed outputs + external inputs\n .state/ # disposable runtime state — gitignored, safe to delete (ADR 0038)\n audit/audit.log # append-only NDJSON event ledger, every write (rotates at ~10 MB)\n cursors/ # per-role pulse cursor — where `pulse --since` resumes\n locks/ # per-key produce locks + the produce mutex\n sentinels/ # publish bookkeeping (target sha) — regenerated on drain (ADR 0070)\n indexes/raw.yaml # raw lane content-hash/URL index — regenerable cache\n```\n\nManifest `path:` fields are relative to `.textus/data/`. So `knowledge.notes.org.jane` lives at `.textus/data/knowledge/notes/org/jane.md`.\n\nRead and write:\n\n```sh\ntextus get knowledge.notes.org.jane\ntextus list --lane=knowledge\nprintf '%s' '{\"_meta\":{\"name\":\"bob\",\"org\":\"acme\"},\"body\":\"hi\\n\"}' \\\n | textus put knowledge.notes.bob --as=human --stdin\ntextus drain --as=automation # re-pull stale inputs + recompute derived outputs\ntextus rule list # show every rule block\ntextus audit --limit=20 # query the audit log\n```\n\n(All verbs return JSON envelopes; `--output=json` is the default and the only format.)\n\nFor a worked store — knowledge entries, a staged proposal, schemas, ERB templates, and a `drain` that publishes `CLAUDE.md` / `AGENTS.md` — see [`.textus/`](.textus/).\n\n## What's shipped\n\n- **Per-entry formats & publish.** `format: markdown|json|yaml|text` per entry; a typed `publish:` block (`to:` for file fan-out, `tree:` for a whole-subtree mirror) byte-copies derived files to their consumer paths. (the wire protocol spec §5.2–5.3)\n- **Stable identity.** Auto-minted `uid:` survives writes and `textus key mv`; reorganising never breaks references.\n- **Capability × lane-kind gate.** Writes carry `--as=`; a role may write a lane iff it holds the capability the lane's `kind:` requires (`canon`→`author`, `workspace`→`keep`, `machine`→`converge`, `queue`→`propose`). The wrong role gets `write_forbidden` naming the capability needed and the roles that hold it. (the wire protocol spec §5)\n- **Agent loop.** `textus boot` orients a fresh session; `textus pulse --since=N` is the per-turn heartbeat (changed entries, pending proposals, index etag for catalog drift detection). ([docs/how-to/agents-mcp.md](docs/how-to/agents-mcp.md))\n- **MCP surface.** The official `mcp` Ruby SDK drives the stdio JSON-RPC server; protocol version auto-negotiated up to `2025-11-25`. Wire textus into Claude Code, Cursor, or any MCP host in one config block.\n- **`textus doctor`.** Health checks across schemas, workflow registrations, keys, sentinels, and the audit log.\n- **`raw` lane and `ingest` verb.** Write-once intake lane for external URL bookmarks, files, and binary assets. Three source kinds (`url`/`file`/`asset`); daily key derivation; scratchpad stub per ingest. See \"Intake and ingest\" section below.\n\n## CLI and lanes\n\nEvery command operates on one store, located in this order: `--root ` flag → **`TEXTUS_ROOT`** env → walk up from the working directory for a `.textus/` (the wire protocol spec §3.1). Write verbs require `--as=`, resolved as: `--as` flag → **`TEXTUS_ROLE`** env → `.textus/role` file → default `human` (the wire protocol spec §5.1). Default roles: `human`, `agent`, `automation` (rename or add your own in the manifest's `roles:` block). All verbs accept `--output=json` and return the envelope defined in the wire protocol spec §8.\n\n- Full verb table — read, write, health, scaffolding — is in the wire protocol spec §9.\n- Lane semantics and the capability × lane-kind mapping live in the wire protocol spec §5, with the reference in [`docs/reference/lanes.md`](docs/reference/lanes.md).\n\n`textus boot` prints the same information for the current store: lanes, entry families with schemas, registered workflows, write flows, and the verb catalog. Run it inside a store and you get the live picture; reach for the SPEC when you want the contract.\n\n## Produce and publish\n\nProduced entries (`kind: produced`) declare how they're acquired in one `source:` block; `drain` materialises them. Two built-in modes, plus workflows for custom data acquisition:\n\n- **`source: { from: external, command: \"...\", sources: [...] }`** — *externally managed*: an out-of-band command or workflow writes the file; textus tracks staleness via declared `sources`.\n- **`source: { from: external, command: \"true\", sources: [] }` + a workflow** — *workflow-driven*: a `Textus.workflow` block (in `.textus/workflows/`) acquires and shapes the data on `drain`.\n\nPublishing is one typed `publish:` block (ADR 0052/0094). Each target is either `{ to: path, template?: name }` for a single file (optionally rendered through an ERB template) or `{ tree: \"dir\" }` to mirror a whole stored subtree. Sentinels for every published file live under `.textus/track/sentinels/` (git-ignored, regenerated on drain). See SPEC §5.2, §5.3, §5.12.\n\nTemplates live in `.textus/templates/` as ERB files (`.erb`). The template receives the entry's `content` hash as local variables via `ERB#result_with_hash`. If `inject_boot: true`, a `boot` variable is also available with the live orientation context.\n\n## Workflows\n\ntextus extends through **workflows** — a `Textus.workflow` block placed in `.textus/workflows/**/*.rb`. Each workflow matches a produced entry by key glob, then runs one or more named steps to acquire its data:\n\n```ruby\n# .textus/workflows/docs/my_report.rb\nTextus.workflow \"my_report\" do\n match \"artifacts.my-report\"\n\n step :build do |_, ctx|\n # read from knowledge, fetch external data, compute anything\n rows = ctx.container.manifest.resolver\n .enumerate(prefix: \"knowledge.notes\")\n .map { |r| { \"key\" => r[:key], \"title\" => r[:entry].schema } }\n { \"content\" => { \"entries\" => rows } }\n end\nend\n```\n\n`drain` discovers all workflow files, matches them against produced entries, and runs the steps. The result is written back to the entry's data path; `publish:` then copies it to its consumer paths.\n\n## Intake and ingest\n\nThe `raw` lane is the inbound counterpart to `artifacts`: where `drain` materialises\n**outbound** computed outputs, `ingest` receives **inbound** external source material.\n\n**The ingest principle:** prefer a reference over a copy. Store body or asset only when the\ncontent itself is the value — human-authored notes, brainstorm outputs, context you want to\nannotate. For everything else, the URL is enough. If the source is private or\naccess-restricted, set `access: private` in `source:` so downstream workflows can handle it\nappropriately.\n\n**Three source kinds:**\n\n| Kind | Stores | Use when |\n|------|--------|----------|\n| `url` | URL reference only (`body: null`) | Bookmarking a page, skill, or doc for later annotation |\n| `file` | File body text | Valuable human-authored content (brainstorm notes, meeting summaries) |\n| `asset` | Binary at `assets/raw/` | Screenshots, PDFs — only when the asset itself is the artefact |\n\n**Write-once** — the same slug on the same day cannot be overwritten. Delete and re-ingest to replace.\n\n```sh\n# bookmark a skill reference — URL only, body stays null\ntextus ingest url agentskills-io-brainstorming \\\n --url=https://agentskills.io/skills/brainstorming \\\n --label=\"brainstorming skill\" \\\n --as=agent\n\n# see what landed in the raw lane\ntextus list --lane=raw\n\n# a scratchpad stub was created alongside — annotate it\ntextus get scratchpad.notes.raw\n```\n\nStale produced entries are re-materialised by `drain`, not by reads — `get` is a pure read (ADR 0089).\n\n```sh\ntextus drain --as=automation # re-materialise every stale produced entry\ntextus drain artifacts.feeds.skills --as=automation # scope to one prefix\ntextus get artifacts.feeds.skills # a pure read; carries a freshness verdict\n```\n\nSchemas (`.textus/schemas/.yaml`) declare field shapes, per-field `maintained_by:` ownership, and an `evolution:` block (`added_in`, `deprecated_at`, `migrate_from`). Full contract in SPEC §5.8.\n\nSee [`docs/how-to/agents-mcp.md`](docs/how-to/agents-mcp.md) for the agent boot → pulse loop.\n\n## Examples\n\n[`.textus/`](.textus/) — textus as a project's own context store. Human-authored `knowledge/` (project facts, runbooks, ADRs), a staged proposal showing the agent-propose / human-accept loop, schemas validating each family, ERB templates and workflows, and a `drain` that publishes the orientation artifact to `CLAUDE.md` and `AGENTS.md`. Includes a copy-paste adoption recipe for your own repo.\n\n## Tests\n\n```sh\nbundle exec rspec\n```\n\nIncludes conformance fixtures A–I from SPEC §12.\n\n## Code quality\n\n```sh\nbundle exec rubocop # lint\nbundle exec rubocop -A # lint + autocorrect\n```\n\nLefthook hooks (`brew bundle install` then `lefthook install`) run rubocop on `pre-commit` and `rspec + rubocop` on `pre-push`. Bypass with `LEFTHOOK=0 git commit ...` when needed. CI runs `rspec` (Ruby 3.3 / 3.4) and `rubocop` via GitHub Actions.\n\n## License\n\n[MIT](LICENSE)\n" } ] } diff --git a/.textus/data/artifacts/feeds/skills.json b/.textus/data/artifacts/feeds/skills.json index f2c71ddb..4d0a5a41 100644 --- a/.textus/data/artifacts/feeds/skills.json +++ b/.textus/data/artifacts/feeds/skills.json @@ -1,6 +1,6 @@ { "_meta": { - "generated_at": "2026-07-05T08:07:25Z", + "generated_at": "2026-07-05T17:33:08Z", "uid": "36318f45b6fec691" }, "skills": [ diff --git a/.textus/data/knowledge/loop/constraint/protocol/0001-what-textus-is.md b/.textus/data/knowledge/loop/constraint/protocol/0001-what-textus-is.md index 26fb69f6..d47154e6 100644 --- a/.textus/data/knowledge/loop/constraint/protocol/0001-what-textus-is.md +++ b/.textus/data/knowledge/loop/constraint/protocol/0001-what-textus-is.md @@ -26,5 +26,5 @@ textus is organized as five composable layers. Each layer has a single responsib | L1 | **Store** | Plain-file backend: `.textus/data//...` with YAML frontmatter + Markdown body, addressed by dotted keys, schema-validated, etag-versioned. | | L2 | **Sources** | Produced entries in the `artifacts` machine lane declare a `source:` block (`from: external` + a `Textus.workflow` block) that acquires their data on `drain`. textus *describes* sources; the workflow DSL acquires data and returns it to the store. | | L3 | **Source** | An entry's `source:` *acquires* **data** — a pure in-process projection from store entries (select/pluck/sort/transform), an external fetch via a handler, or an out-of-band command. Acquire-only: rendering is not a source concern. No shell execution. | -| L4 | **Publish** | Emits a produced entry's data to repo-relative paths, declared via a **list** of `publish:` targets. A target with no `template:` copies the data verbatim (json/yaml re-serialized without `_meta`; other formats byte-copied); a target with a `template:` renders the data through it. A `{ tree: }` target mirrors a subtree (ADR 0047). Published artifacts are clean content — textus's `_meta` provenance stays in the store. A sentinel under `.textus/.state/sentinels/.textus-managed.json` (git-ignored runtime state) records the source, sha256, and `mode: "copy"`. | +| L4 | **Publish** | Emits a produced entry's data to repo-relative paths, declared via a **list** of `publish:` targets. A target with no `template:` copies the data verbatim (json/yaml re-serialized without `_meta`; other formats byte-copied); a target with a `template:` renders the data through it. A `{ tree: }` target mirrors a subtree (ADR 0047). Published artifacts are clean content — textus's `_meta` provenance stays in the store. A sentinel under `.textus/track/sentinels/.textus-managed.json` (git-ignored runtime state) records the source, sha256, and `mode: "copy"`. | | L5 | **Consumers** | Anything that reads the published files or calls the CLI — editors, LLM tools, MCP servers, CI jobs, dashboards. textus is agnostic about who consumes; the envelope is the contract. | diff --git a/.textus/data/knowledge/loop/constraint/protocol/0005-lanes-and-capability-based-write-gates.md b/.textus/data/knowledge/loop/constraint/protocol/0005-lanes-and-capability-based-write-gates.md index 8cf5f0b2..33d34b1b 100644 --- a/.textus/data/knowledge/loop/constraint/protocol/0005-lanes-and-capability-based-write-gates.md +++ b/.textus/data/knowledge/loop/constraint/protocol/0005-lanes-and-capability-based-write-gates.md @@ -182,9 +182,9 @@ A **to-target** carries `to:` (required) and optionally `template:` / `inject_bo The ERB template receives the entry's `content` hash as local variables via `ERB#result_with_hash`. Templates live under `.textus/templates/` as `.erb` files. If `inject_boot: true` is set on the publish target, a `boot` variable is also available with the live orientation context. -A sentinel is written for each published file at `/.state/sentinels/.textus-managed.json` (git-ignored runtime state — ADR 0070), recording `source`, `target`, the target's sha256, and `mode: "copy"`. Sentinels live under the store's runtime tree rather than beside the consumer file so target directories stay clean, and are regenerated by the next drain (via content-identical adoption) rather than committed. The sentinel exists so out-of-band edits can be detected on the next publish — textus refuses to clobber a destination that is not either missing, marked as managed, or **byte-identical to the source being published**. An identical destination is *adopted*: its sentinel is written and management proceeds (the copy is a content no-op), so an artifact tree already on disk onboards without a manual delete. An unmanaged destination whose content **differs**, or any unmanaged symlink, is still refused (ADR 0050). Legacy sibling sentinels (`.textus-managed.json`) are still recognised as managed and are migrated to the new location on the next publish. +A sentinel is written for each published file at `/track/sentinels/.textus-managed.json` (git-ignored runtime state — ADR 0070), recording `source`, `target`, the target's sha256, and `mode: "copy"`. Sentinels live under the store's runtime tree rather than beside the consumer file so target directories stay clean, and are regenerated by the next drain (via content-identical adoption) rather than committed. The sentinel exists so out-of-band edits can be detected on the next publish — textus refuses to clobber a destination that is not either missing, marked as managed, or **byte-identical to the source being published**. An identical destination is *adopted*: its sentinel is written and management proceeds (the copy is a content no-op), so an artifact tree already on disk onboards without a manual delete. An unmanaged destination whose content **differs**, or any unmanaged symlink, is still refused (ADR 0050). Legacy sibling sentinels (`.textus-managed.json`) are still recognised as managed and are migrated to the new location on the next publish. -**Subtree mirror.** A nested entry MAY include a `{ tree: "dir" }` target (see §4). On every drain/serve pass, textus walks the entry's full stored subtree (`data//**`), applies the entry's `ignore:` filter, and byte-copies each file to the target directory, preserving relative layout — one sentinel per file under `/.state/sentinels/`. The mirror is path-driven: no keys are enumerated, no template variables are interpreted, and mirrored files are opaque payload (never addressable). On rebuild, the entire target directory is pruned of textus-managed files the current source no longer produces; unmanaged files are never touched. The convergence envelope grows a `published_leaves` array — one row per mirrored file, with `key`, `source`, and `target` — alongside the existing `produced` array, plus a `pruned` array listing any orphaned managed files removed on this pass. Targets that would resolve outside the repo root are refused. When a `{ tree: }` target overlaps another entry's `{ to: }` target (e.g. a derived `SKILL.md` written into the mirrored dir), the mirroring entry must `ignore:` that filename or prune will delete it — `doctor` flags this as `publish.tree_index_overlap` (ADR 0047). +**Subtree mirror.** A nested entry MAY include a `{ tree: "dir" }` target (see §4). On every drain/serve pass, textus walks the entry's full stored subtree (`data//**`), applies the entry's `ignore:` filter, and byte-copies each file to the target directory, preserving relative layout — one sentinel per file under `/track/sentinels/`. The mirror is path-driven: no keys are enumerated, no template variables are interpreted, and mirrored files are opaque payload (never addressable). On rebuild, the entire target directory is pruned of textus-managed files the current source no longer produces; unmanaged files are never touched. The convergence envelope grows a `published_leaves` array — one row per mirrored file, with `key`, `source`, and `target` — alongside the existing `produced` array, plus a `pruned` array listing any orphaned managed files removed on this pass. Targets that would resolve outside the repo root are refused. When a `{ tree: }` target overlaps another entry's `{ to: }` target (e.g. a derived `SKILL.md` written into the mirrored dir), the mirroring entry must `ignore:` that filename or prune will delete it — `doctor` flags this as `publish.tree_index_overlap` (ADR 0047). **Publish presence is a uniform rule across all kinds.** Absent → the entry is terminal data (consumed internally via another entry's `select`, or read via `get`). Present → emit to the listed targets, every kind through one publish path. A `from: command` entry with publish targets emits the bytes the command already wrote into the store; without targets it is a staleness-only signal. diff --git a/.textus/data/knowledge/loop/constraint/protocol/0012-conformance-fixtures.md b/.textus/data/knowledge/loop/constraint/protocol/0012-conformance-fixtures.md index ef292871..23cb0dec 100644 --- a/.textus/data/knowledge/loop/constraint/protocol/0012-conformance-fixtures.md +++ b/.textus/data/knowledge/loop/constraint/protocol/0012-conformance-fixtures.md @@ -21,7 +21,7 @@ Given a manifest entry `artifacts.feeds.skills` with `kind: produced` and `sourc Given a produced entry with a to-target `{ to:, template: }`, `textus drain` renders the entry's stored data through the named ERB template (under `.textus/templates/`) and emits a file whose contents match the expected rendered output byte-for-byte (after trailing-newline normalization). Two to-targets with different templates produce different bytes from the one entry. **Fixture G — Copy publish:** -Given a manifest entry with a templateless to-target `publish: [{ to: }]`, a successful `textus drain` for that entry leaves a plain file at `` whose contents are the entry's content re-serialized without `_meta` (byte-identical to a clean consumer config), accompanied by a sentinel at `.textus/.state/sentinels/.textus-managed.json` recording `source`, `target`, `sha256`, and `mode: "copy"`. Re-running `drain` is idempotent. +Given a manifest entry with a templateless to-target `publish: [{ to: }]`, a successful `textus drain` for that entry leaves a plain file at `` whose contents are the entry's content re-serialized without `_meta` (byte-identical to a clean consumer config), accompanied by a sentinel at `.textus/track/sentinels/.textus-managed.json` recording `source`, `target`, `sha256`, and `mode: "copy"`. Re-running `drain` is idempotent. **Fixture H — Audit log format:** Every successful write verb (`put`, `key_delete`, `key_mv`, `accept`, `schema migrate`) appends exactly one line per affected key to the audit log, in the canonical format defined in §audit (timestamp, actor role, verb, key, etag-before, etag-after). Convergence (`drain`/`serve`) writes through these same verbs (`put` for a produced entry, `key_delete` for a swept one), so it appends per the underlying write, not under a distinct `drain` verb. No write produces zero or multiple lines per key. diff --git a/.textus/data/knowledge/loop/evidence/0038-runtime-artifacts-under-run-and-layout.md b/.textus/data/knowledge/loop/evidence/0038-runtime-artifacts-under-run-and-layout.md index 819c8be0..3e520915 100644 --- a/.textus/data/knowledge/loop/evidence/0038-runtime-artifacts-under-run-and-layout.md +++ b/.textus/data/knowledge/loop/evidence/0038-runtime-artifacts-under-run-and-layout.md @@ -1,7 +1,7 @@ # ADR 0038 — Runtime artifacts live under `.run/`; one `Layout` owns the map **Date:** 2026-05-31 -**Status:** Accepted · the `sentinels/` → `:config` classification (Decision §1) is **superseded by [ADR 0070](./0070-content-addressed-build-artifacts.md)** — sentinels are machine-generated runtime state and move under `.run/sentinels/`; the `.run/` layout and `Layout`-as-map decisions stand +**Status:** Accepted · the `sentinels/` → `:config` classification (Decision §1) is **superseded by [ADR 0070](./0070-content-addressed-build-artifacts.md)** — sentinels are machine-generated runtime state and move under `track/sentinels/`; the `.run/` layout and `Layout`-as-map decisions stand **Refines:** [ADR 0036](./0036-transports-as-pure-framings.md) (per-role cursor cache under `.state/`), [ADR 0025](./0025-boot-doctor-as-verbs-and-etag-via-port.md) (doctor as a verb that inspects the store on disk) ## Context diff --git a/.textus/data/knowledge/loop/evidence/0070-content-addressed-build-artifacts.md b/.textus/data/knowledge/loop/evidence/0070-content-addressed-build-artifacts.md index 51a2910a..312e1350 100644 --- a/.textus/data/knowledge/loop/evidence/0070-content-addressed-build-artifacts.md +++ b/.textus/data/knowledge/loop/evidence/0070-content-addressed-build-artifacts.md @@ -2,7 +2,7 @@ **Date:** 2026-06-03 **Status:** Accepted -**Supersedes:** [ADR 0038](./0038-runtime-artifacts-under-run-and-layout.md)'s classification of `sentinels/` as `:config` ("tracked deliberately") — sentinels move to the runtime side (`.run/sentinels/`, git-ignored). The rest of ADR 0038 (the `.run/` layout, `Layout` as the path map) stands. +**Supersedes:** [ADR 0038](./0038-runtime-artifacts-under-run-and-layout.md)'s classification of `sentinels/` as `:config` ("tracked deliberately") — sentinels move to the runtime side (`track/sentinels/`, git-ignored). The rest of ADR 0038 (the `.run/` layout, `Layout` as the path map) stands. **Touches:** [ADR 0050](./0050-native-authoring-and-content-identical-adoption.md) (content-identical adoption — determinism is what makes adoption reliable across a clone, which in turn makes git-ignoring sentinels safe), [ADR 0024](./0024-domain-purity-ports.md) (`FreshWithin` is a domain predicate). Surfaced by the #161 integration review (F1 — the "highest leverage" item). > **One sentence:** textus stamped a fresh `generated_at` into every built artifact and then carried a whole `IdempotentWrite` module to *un*-stamp it on rebuild — so this ADR removes the timestamp from the tracked output entirely, making artifacts content-addressed (a rebuild on unchanged sources is a byte-for-byte no-op) and deleting the guard that existed only to reverse textus's own side effect. @@ -33,7 +33,7 @@ A clarifying distinction the review surfaced: the `generated.at` that `Domain::S - **Rebuilds are no-ops; publish `cp` is a content no-op; the `sentinel.drift` warning class collapses.** A deterministic artifact reproduces byte-for-byte, so `build` writes nothing when sources are unchanged, the published copy's bytes don't move, and a `git` revert restores bytes whose sha still matches the sentinel. "Is this diff real?" is answered by the diff itself. - **`IdempotentWrite` and its tests are gone** — less surface, one fewer parse-and-rewrite path, no format-specific timestamp dig. - **One-time churn on upgrade.** The first `build` after this change rewrites each artifact once to drop the timestamp line (and refreshes its sentinel sha); steady state is silent thereafter. The repo's own dogfooded artifacts are regenerated in the same commit. -- **Determinism is the precondition that makes ADR 0050 adoption reliable — so sentinels move to the runtime side.** A freshly-cloned, textus-managed file now equals a fresh build byte-for-byte, so content-identical adoption always succeeds for an unmodified managed file. That makes it *safe* to treat sentinels as regenerable runtime state: they relocate from `/sentinels/` (tracked) to `/.run/sentinels/` (git-ignored, via `Layout.sentinels`), the two formerly-tracked sentinels are `git rm --cache`d, and a fresh clone's first `build` regenerates them by adoption. This **supersedes ADR 0038's `:config` classification** of `sentinels/`: a sentinel is machine-generated (the target's sha), not authored source — it was bucketed with `manifest.yaml`/`schemas/` by location, not by nature, and tracking it was partly *forced* by the pre-determinism timestamp skew (a clone's `AGENTS.md` differed from a fresh build, so adoption failed and the guard refused — tracking papered over it). The tracked-sentinel churn class disappears with the timestamp it mirrored. +- **Determinism is the precondition that makes ADR 0050 adoption reliable — so sentinels move to the runtime side.** A freshly-cloned, textus-managed file now equals a fresh build byte-for-byte, so content-identical adoption always succeeds for an unmodified managed file. That makes it *safe* to treat sentinels as regenerable runtime state: they relocate from `/sentinels/` (tracked) to `/track/sentinels/` (git-ignored, via `Layout.sentinels`), the two formerly-tracked sentinels are `git rm --cache`d, and a fresh clone's first `build` regenerates them by adoption. This **supersedes ADR 0038's `:config` classification** of `sentinels/`: a sentinel is machine-generated (the target's sha), not authored source — it was bucketed with `manifest.yaml`/`schemas/` by location, not by nature, and tracking it was partly *forced* by the pre-determinism timestamp skew (a clone's `AGENTS.md` differed from a fresh build, so adoption failed and the guard refused — tracking papered over it). The tracked-sentinel churn class disappears with the timestamp it mirrored. - **No protocol bump.** This narrows what the builder writes and relocates a runtime file; neither is the wire contract. `textus/3` is unchanged. ## Alternatives considered diff --git a/.textus/data/knowledge/loop/evidence/0081-docs-become-canon-published-out.md b/.textus/data/knowledge/loop/evidence/0081-docs-become-canon-published-out.md index 86f587b6..82aa42e5 100644 --- a/.textus/data/knowledge/loop/evidence/0081-docs-become-canon-published-out.md +++ b/.textus/data/knowledge/loop/evidence/0081-docs-become-canon-published-out.md @@ -2,7 +2,7 @@ **Date:** 2026-06-04 **Status:** Accepted -**Touches:** [ADR 0050](./0050-native-authoring-and-content-identical-adoption.md) (content-identical adoption — the `guard_clobber` branch this migration rides on), [ADR 0046](./0046-publish-leaf-subtrees.md) and [ADR 0047](./0047-publish-tree-keyless-subtree-mirror.md) (the leaf/tree publish modes), [ADR 0052](./0052-typed-publish-block.md) (the typed `publish:` block those modes now fold into), [ADR 0070](./0070-content-addressed-build-artifacts.md) (moved sentinels out of tree to `.run/sentinels/` — why published docs are not annotated), [ADR 0041](./0041-dogfood-textus-in-its-own-repo.md) (textus dogfoods itself — the `knowledge` zone this extends), [ADR 0044](./0044-system-actors-resolved-by-capability.md) (`build` runs as the build-capable actor automatically). +**Touches:** [ADR 0050](./0050-native-authoring-and-content-identical-adoption.md) (content-identical adoption — the `guard_clobber` branch this migration rides on), [ADR 0046](./0046-publish-leaf-subtrees.md) and [ADR 0047](./0047-publish-tree-keyless-subtree-mirror.md) (the leaf/tree publish modes), [ADR 0052](./0052-typed-publish-block.md) (the typed `publish:` block those modes now fold into), [ADR 0070](./0070-content-addressed-build-artifacts.md) (moved sentinels out of tree to `track/sentinels/` — why published docs are not annotated), [ADR 0041](./0041-dogfood-textus-in-its-own-repo.md) (textus dogfoods itself — the `knowledge` zone this extends), [ADR 0044](./0044-system-actors-resolved-by-capability.md) (`build` runs as the build-capable actor automatically). > **One sentence:** textus already dogfoods itself (ADR 0041) with a `knowledge` zone (kind `canon`) that holds only `project.md` + the runbooks, while every other committed doc under `docs/` is an un-owned flat file an agent can silently drift; this ADR makes **every committed `docs/` file canon** — authored under `.textus/zones/knowledge/`, published *back out* to its original `docs/` path by `textus build` — turning `docs/` into a committed, sentinel-managed mirror (the same status `CLAUDE.md`/`AGENTS.md` already hold), using ADR 0050's content-identical adoption so the first `build` is a **zero `git diff`** with links, blame, and layout preserved. @@ -26,7 +26,7 @@ We already publish prose-heavy, multi-file artifacts in their **native shape** ( 5. **Entry keys use hyphens, not underscores.** The key segment grammar is `[a-z0-9][a-z0-9-]*` (`lib/textus/key/grammar.rb:4`) — lowercase, digits, hyphens; underscores are rejected. So directory keys are e.g. `knowledge.how-to`, `knowledge.reference`, `knowledge.architecture.decisions`. -6. **Sentinels stay out of tree (ADR 0070).** Publish sentinels live under `.textus/.run/sentinels/` (gitignored), so the published `docs/` files are **not annotated** with any textus marker — they read as plain, clean docs to anyone who opens them on GitHub. +6. **Sentinels stay out of tree (ADR 0070).** Publish sentinels live under `.textus/track/sentinels/` (gitignored), so the published `docs/` files are **not annotated** with any textus marker — they read as plain, clean docs to anyone who opens them on GitHub. 7. **`build` runs as the build-capable actor automatically (ADR 0044).** No caller-role gymnastics: the existing build-actor resolution covers the docs publish like any other. diff --git a/.textus/data/knowledge/loop/execution/runbook/0003-quickstart.md b/.textus/data/knowledge/loop/execution/runbook/0003-quickstart.md index cc9b0781..9c1c92ab 100644 --- a/.textus/data/knowledge/loop/execution/runbook/0003-quickstart.md +++ b/.textus/data/knowledge/loop/execution/runbook/0003-quickstart.md @@ -135,7 +135,7 @@ Produced entries (`kind: produced`) declare how they're acquired in one `source: - **`source: { from: external, command: "...", sources: [...] }`** — *externally managed*: an out-of-band command or workflow writes the file; textus tracks staleness via declared `sources`. - **`source: { from: external, command: "true", sources: [] }` + a workflow** — *workflow-driven*: a `Textus.workflow` block (in `.textus/workflows/`) acquires and shapes the data on `drain`. -Publishing is one typed `publish:` block (ADR 0052/0094). Each target is either `{ to: path, template?: name }` for a single file (optionally rendered through an ERB template) or `{ tree: "dir" }` to mirror a whole stored subtree. Sentinels for every published file live under `.textus/.state/sentinels/` (git-ignored, regenerated on drain). See SPEC §5.2, §5.3, §5.12. +Publishing is one typed `publish:` block (ADR 0052/0094). Each target is either `{ to: path, template?: name }` for a single file (optionally rendered through an ERB template) or `{ tree: "dir" }` to mirror a whole stored subtree. Sentinels for every published file live under `.textus/track/sentinels/` (git-ignored, regenerated on drain). See SPEC §5.2, §5.3, §5.12. Templates live in `.textus/templates/` as ERB files (`.erb`). The template receives the entry's `content` hash as local variables via `ERB#result_with_hash`. If `inject_boot: true`, a `boot` variable is also available with the live orientation context. diff --git a/.textus/templates/docs/meta/orientation.erb b/.textus/templates/docs/meta/orientation.erb index 1c93426f..3829b521 100644 --- a/.textus/templates/docs/meta/orientation.erb +++ b/.textus/templates/docs/meta/orientation.erb @@ -64,7 +64,7 @@ If you skip a node, record why in session notes. ## Docs -Read the map with `textus get artifacts.docs.index`; decision log: `textus get artifacts.decisions.log`. +Read the map with `textus get artifacts.docs.readme`; decision log: `textus get artifacts.decisions.log`. # RTK (Rust Token Killer) - Token-Optimized Commands diff --git a/CHANGELOG.md b/CHANGELOG.md index 04109656..7fbeb696 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,39 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +- feat: web surface, shared dispatch, entry constraint enforcement +- fix: move CBM_CACHE_DIR constant outside namespace block (rubocop) +- refactor: simplification phase 2 — pipeline inline, data param, ADR archive, adoption rename, boot improvements +- chore: fix rubocop hash alignment in bin/smoke +- feat: add ruby/python/javascript format files with proper extensions +- feat: per-language script formats (bash/ruby/python/js) +- chore: gitignore bin/.smoke-sids marker file +- fix: add --as-format to put for per-write format override +- fix: script files under sessions get no extension instead of .md +- fix: smoke output now glance-friendly and session-id capture fixed +- fix: smoke --clean now sweeps orphan session dirs via marker file +- chore: fix rubocop offenses for CI compliance +- feat: smoke tests 4 script formats (bash/ruby/python/js) under sessions..scripts +- feat: add bin/smoke for agent session lifecycle testing +- style: disable RSpec/VerifiedDoubles, StubbedMock, MessageSpies, VerifiedDoubleReference in rubocop; deduplicate config +- style: rubocop autocorrect (non-RSpecVerifiedDoubles), fix rescue modifier/comma/guard-clause in new code +- feat: strict agent session protocol — visible INPUT/LOOP/OUTPUT in AGENTS.md, boot context in session_open, nodes_checked in session_close, doctor check for skipped constraint, protocol entry 0016 +- fix: Runner::Context needs file_system/schemas for workflow step blocks, fix stale error message (match: → on:) +- refactor: standardize pipeline shapes — extract Delete/Move steps into separate files, add narrow DeleteDeps/MoveDeps structs +- refactor: add narrow file queries to QueryContext, update ops handlers; docs: align knowledge store with current architecture — 4 files updated, 3 deleted, data-flow rewritten +- refactor: workflow redesign — two-seam drain/watch, deleted Engine/Queue/RetryPolicy, inline retry, Publisher uses container.store_engine, StepHelpers extracted from Helpers +- refactor: collapse 21 single-file verb specs into core_verbs.rb +- chore: remove dead code (Outcome, Result, verb stubs, session_open/close files, Registry#workflows_for_key, be_success/be_failure matchers) +- style: suppress rubocop warnings in spec files +- style: suppress Metrics/ParameterLists in Container +- style: rubocop auto-correct fixes +- refactor: split Workflow::DSL::Definition into sub-modules +- refactor: group 11-step pipeline into 4 phases +- refactor: split Builder into three testable layers +- refactor: CQS context split — QueryContext + CommandContext +- refactor: consolidate authorization into Manifest::Policy +- refactor: collapse shallow namespace modules +- chore: fix rubocop violations, key_delete module_function, and refresh docs - refactor: replace hand-written Session delegations with Forwardable - refactor: extract merge_issues helper in Definition#check - refactor: split VerbRegistry into per-verb files under verb_registry/verbs/ diff --git a/README.md b/README.md index a704271a..c63dbba9 100644 --- a/README.md +++ b/README.md @@ -225,7 +225,7 @@ Produced entries (`kind: produced`) declare how they're acquired in one `source: - **`source: { from: external, command: "...", sources: [...] }`** — *externally managed*: an out-of-band command or workflow writes the file; textus tracks staleness via declared `sources`. - **`source: { from: external, command: "true", sources: [] }` + a workflow** — *workflow-driven*: a `Textus.workflow` block (in `.textus/workflows/`) acquires and shapes the data on `drain`. -Publishing is one typed `publish:` block (ADR 0052/0094). Each target is either `{ to: path, template?: name }` for a single file (optionally rendered through an ERB template) or `{ tree: "dir" }` to mirror a whole stored subtree. Sentinels for every published file live under `.textus/.state/sentinels/` (git-ignored, regenerated on drain). See SPEC §5.2, §5.3, §5.12. +Publishing is one typed `publish:` block (ADR 0052/0094). Each target is either `{ to: path, template?: name }` for a single file (optionally rendered through an ERB template) or `{ tree: "dir" }` to mirror a whole stored subtree. Sentinels for every published file live under `.textus/track/sentinels/` (git-ignored, regenerated on drain). See SPEC §5.2, §5.3, §5.12. Templates live in `.textus/templates/` as ERB files (`.erb`). The template receives the entry's `content` hash as local variables via `ERB#result_with_hash`. If `inject_boot: true`, a `boot` variable is also available with the live orientation context. diff --git a/docs/architecture/archive/0081-docs-become-canon-published-out.md b/docs/architecture/archive/0081-docs-become-canon-published-out.md index 86f587b6..82aa42e5 100644 --- a/docs/architecture/archive/0081-docs-become-canon-published-out.md +++ b/docs/architecture/archive/0081-docs-become-canon-published-out.md @@ -2,7 +2,7 @@ **Date:** 2026-06-04 **Status:** Accepted -**Touches:** [ADR 0050](./0050-native-authoring-and-content-identical-adoption.md) (content-identical adoption — the `guard_clobber` branch this migration rides on), [ADR 0046](./0046-publish-leaf-subtrees.md) and [ADR 0047](./0047-publish-tree-keyless-subtree-mirror.md) (the leaf/tree publish modes), [ADR 0052](./0052-typed-publish-block.md) (the typed `publish:` block those modes now fold into), [ADR 0070](./0070-content-addressed-build-artifacts.md) (moved sentinels out of tree to `.run/sentinels/` — why published docs are not annotated), [ADR 0041](./0041-dogfood-textus-in-its-own-repo.md) (textus dogfoods itself — the `knowledge` zone this extends), [ADR 0044](./0044-system-actors-resolved-by-capability.md) (`build` runs as the build-capable actor automatically). +**Touches:** [ADR 0050](./0050-native-authoring-and-content-identical-adoption.md) (content-identical adoption — the `guard_clobber` branch this migration rides on), [ADR 0046](./0046-publish-leaf-subtrees.md) and [ADR 0047](./0047-publish-tree-keyless-subtree-mirror.md) (the leaf/tree publish modes), [ADR 0052](./0052-typed-publish-block.md) (the typed `publish:` block those modes now fold into), [ADR 0070](./0070-content-addressed-build-artifacts.md) (moved sentinels out of tree to `track/sentinels/` — why published docs are not annotated), [ADR 0041](./0041-dogfood-textus-in-its-own-repo.md) (textus dogfoods itself — the `knowledge` zone this extends), [ADR 0044](./0044-system-actors-resolved-by-capability.md) (`build` runs as the build-capable actor automatically). > **One sentence:** textus already dogfoods itself (ADR 0041) with a `knowledge` zone (kind `canon`) that holds only `project.md` + the runbooks, while every other committed doc under `docs/` is an un-owned flat file an agent can silently drift; this ADR makes **every committed `docs/` file canon** — authored under `.textus/zones/knowledge/`, published *back out* to its original `docs/` path by `textus build` — turning `docs/` into a committed, sentinel-managed mirror (the same status `CLAUDE.md`/`AGENTS.md` already hold), using ADR 0050's content-identical adoption so the first `build` is a **zero `git diff`** with links, blame, and layout preserved. @@ -26,7 +26,7 @@ We already publish prose-heavy, multi-file artifacts in their **native shape** ( 5. **Entry keys use hyphens, not underscores.** The key segment grammar is `[a-z0-9][a-z0-9-]*` (`lib/textus/key/grammar.rb:4`) — lowercase, digits, hyphens; underscores are rejected. So directory keys are e.g. `knowledge.how-to`, `knowledge.reference`, `knowledge.architecture.decisions`. -6. **Sentinels stay out of tree (ADR 0070).** Publish sentinels live under `.textus/.run/sentinels/` (gitignored), so the published `docs/` files are **not annotated** with any textus marker — they read as plain, clean docs to anyone who opens them on GitHub. +6. **Sentinels stay out of tree (ADR 0070).** Publish sentinels live under `.textus/track/sentinels/` (gitignored), so the published `docs/` files are **not annotated** with any textus marker — they read as plain, clean docs to anyone who opens them on GitHub. 7. **`build` runs as the build-capable actor automatically (ADR 0044).** No caller-role gymnastics: the existing build-actor resolution covers the docs publish like any other. diff --git a/docs/architecture/decisions/README.md b/docs/architecture/decisions/README.md index ab279c7d..1ba7b071 100644 --- a/docs/architecture/decisions/README.md +++ b/docs/architecture/decisions/README.md @@ -94,7 +94,7 @@ the original reasoning intact. The history is the point. | [0067](../archive/0067-per-surface-views.md) | Per-surface `view`s replace `response`/`cli_response` and the `Proc#arity` sniff: one `views` map, every view called uniformly as `(result, inputs)`; `Contract::View.render` is the single shaping entry point | | [0068](../archive/0068-declarative-facets-dissolve-escape-hatches.md) | Declarative `source:`/`coerce:`/`cli_stdin`/`around:`/`cli_default:` dissolve the acquisition, coercion, stateful-wrapper, surface-default, and multi-dispatch escape hatches — `HAND_AUTHORED_VERBS` drops 18 → 7 (the behavioral floor); `key delete-prefix`/`key mv-prefix` split out (breaking) | | [0069](../archive/0069-single-path-lifecycle.md) | Single-path lifecycle: views self-shape on every surface (drop the CLI runner `to_h_for_wire` pre-wire), one normalizer home (`Binder.inputs_from_wire`), validation is unconditional (`validate:` dropped; `required:` is an honest invariant; `put`/`propose` `meta` → `required: false`), and `HAND_AUTHORED_VERBS` splits into `BEHAVIORAL_HATCHES` + `NON_PROJECTED_CLI` (guarded) — finishes 0066/0067/0068 (breaking) | -| [0070](../archive/0070-content-addressed-build-artifacts.md) | Built artifacts are content-addressed — the builder stamps no `generated_at`; `IdempotentWrite` deleted (byte-equality suffices), publish `cp` becomes a content no-op; sentinels move to `.run/sentinels/` (git-ignored), superseding ADR 0038's `:config` classification — kills the `sentinel.drift`/churn class outright | +| [0070](../archive/0070-content-addressed-build-artifacts.md) | Built artifacts are content-addressed — the builder stamps no `generated_at`; `IdempotentWrite` deleted (byte-equality suffices), publish `cp` becomes a content no-op; sentinels move to `track/sentinels/` (git-ignored), superseding ADR 0038's `:config` classification — kills the `sentinel.drift`/churn class outright | | [0071](../archive/0071-dry-run-is-opt-in.md) | `dry_run` is an opt-in preview, not a default — the four bulk verbs (`zone_mv`/`key_mv_prefix`/`key_delete_prefix`/`migrate`) apply by default on every surface again; reverses ADR 0060 §2, restores ADR 0036 symmetry (breaking) | | [0072](../archive/0072-accept-reject-gate-by-capability.md) | `accept`/`reject` gate by capability, not by transport — surface them to MCP; the closed-floor `author_held` guard is the single gate (default-`agent` connections can't promote; ADR 0040 pins the connection role at launch); corrects the omit-list that conflated authority (`accept`/`reject`) with steering (`build`); closes the propose→accept loop over one transport (#161 F7) | | [0073](../archive/0073-surfaces-declare-external-projections.md) | `surfaces` declares external projections (`:cli`, `:mcp`); Ruby is the implicit always-present base — drop the `:ruby` token (100% present, no `ruby?` predicate, inert since 0069 made validation unconditional); empty `surfaces` becomes the honest home for a Ruby-only internal verb; `Read::Capabilities` derives the `"ruby"` base instead of reading the token so the #161 F4 integrator payload stays byte-identical; vocabulary subtraction (#161) | diff --git a/lib/textus/infra/port/publisher.rb b/lib/textus/infra/port/publisher.rb index 46b4f69f..e1261918 100644 --- a/lib/textus/infra/port/publisher.rb +++ b/lib/textus/infra/port/publisher.rb @@ -6,7 +6,7 @@ module Port # artifact; no parsing or stripping. # # Sentinel I/O is delegated to Textus::Infra::Port::SentinelStore. Sentinels live - # under `/.run/sentinels/` (runtime, git-ignored — ADR 0070) and + # under `/track/sentinels/` (runtime, git-ignored — ADR 0070) and # mirror the target's repo-relative layout so consumer directories aren't # polluted with `.textus-managed.json` siblings. # diff --git a/lib/textus/infra/port/sentinel_store.rb b/lib/textus/infra/port/sentinel_store.rb index 8ac244af..5d420e46 100644 --- a/lib/textus/infra/port/sentinel_store.rb +++ b/lib/textus/infra/port/sentinel_store.rb @@ -2,7 +2,7 @@ module Textus module Infra module Port # Persistence adapter for sentinel files. Owns the on-disk JSON shape, the - # path layout (/.run/sentinels/.textus-managed.json + # path layout (/track/sentinels/.textus-managed.json # — runtime, git-ignored, ADR 0070), and all File/FileUtils I/O. # Core::Sentinel is a pure value object that depends on this port for # reads and writes. diff --git a/lib/textus/workflow/dsl.rb b/lib/textus/workflow/dsl.rb index afb73f08..0d5334bd 100644 --- a/lib/textus/workflow/dsl.rb +++ b/lib/textus/workflow/dsl.rb @@ -107,6 +107,8 @@ def priority(n = nil) # ── Lifecycle ── def every(cadence = :__no_arg__) + return @ttl if cadence == :__no_arg__ + @ttl = ttl_value(cadence) end From 2a82b16a291c4259fbdc5cbf5838963f913bbc5c Mon Sep 17 00:00:00 2001 From: Patrick Date: Mon, 6 Jul 2026 00:36:35 +0700 Subject: [PATCH 3/6] fix: changelog shows only feat/fix commits grouped by type - Filter commits by conventional commit type (only feat:, fix:) - Group into 'Features' and 'Bug Fixes' sections per Keep a Changelog - Update ERB template to render grouped sections - Internal commits (chore, refactor, style, docs, test) excluded --- .textus/data/artifacts/changelog.json | 4690 +++------------------ .textus/data/artifacts/feeds/skills.json | 2 +- .textus/templates/docs/meta/changelog.erb | 11 + .textus/workflows/config/changelog.rb | 24 +- CHANGELOG.md | 1214 +----- 5 files changed, 807 insertions(+), 5134 deletions(-) diff --git a/.textus/data/artifacts/changelog.json b/.textus/data/artifacts/changelog.json index fa7fe880..79d60038 100644 --- a/.textus/data/artifacts/changelog.json +++ b/.textus/data/artifacts/changelog.json @@ -1,4150 +1,682 @@ { "_meta": { - "generated_at": "2026-07-05T17:33:08Z", + "generated_at": "2026-07-05T17:36:20Z", "uid": "60e228765192c063" }, "entries": [ { "tag": "Unreleased", "date": null, - "commits": [ - { - "subject": "feat: web surface, shared dispatch, entry constraint enforcement", - "date": "2026-07-06" - }, - { - "subject": "fix: move CBM_CACHE_DIR constant outside namespace block (rubocop)", - "date": "2026-07-05" - }, - { - "subject": "refactor: simplification phase 2 — pipeline inline, data param, ADR archive, adoption rename, boot improvements", - "date": "2026-07-05" - }, - { - "subject": "chore: fix rubocop hash alignment in bin/smoke", - "date": "2026-07-05" - }, - { - "subject": "feat: add ruby/python/javascript format files with proper extensions", - "date": "2026-07-05" - }, - { - "subject": "feat: per-language script formats (bash/ruby/python/js)", - "date": "2026-07-05" - }, - { - "subject": "chore: gitignore bin/.smoke-sids marker file", - "date": "2026-07-05" - }, - { - "subject": "fix: add --as-format to put for per-write format override", - "date": "2026-07-05" - }, - { - "subject": "fix: script files under sessions get no extension instead of .md", - "date": "2026-07-05" - }, - { - "subject": "fix: smoke output now glance-friendly and session-id capture fixed", - "date": "2026-07-05" - }, - { - "subject": "fix: smoke --clean now sweeps orphan session dirs via marker file", - "date": "2026-07-05" - }, - { - "subject": "chore: fix rubocop offenses for CI compliance", - "date": "2026-07-05" - }, - { - "subject": "feat: smoke tests 4 script formats (bash/ruby/python/js) under sessions..scripts", - "date": "2026-07-05" - }, - { - "subject": "feat: add bin/smoke for agent session lifecycle testing", - "date": "2026-07-05" - }, - { - "subject": "style: disable RSpec/VerifiedDoubles, StubbedMock, MessageSpies, VerifiedDoubleReference in rubocop; deduplicate config", - "date": "2026-07-05" - }, - { - "subject": "style: rubocop autocorrect (non-RSpecVerifiedDoubles), fix rescue modifier/comma/guard-clause in new code", - "date": "2026-07-05" - }, - { - "subject": "feat: strict agent session protocol — visible INPUT/LOOP/OUTPUT in AGENTS.md, boot context in session_open, nodes_checked in session_close, doctor check for skipped constraint, protocol entry 0016", - "date": "2026-07-05" - }, - { - "subject": "fix: Runner::Context needs file_system/schemas for workflow step blocks, fix stale error message (match: → on:)", - "date": "2026-07-05" - }, - { - "subject": "refactor: standardize pipeline shapes — extract Delete/Move steps into separate files, add narrow DeleteDeps/MoveDeps structs", - "date": "2026-07-05" - }, - { - "subject": "refactor: add narrow file queries to QueryContext, update ops handlers; docs: align knowledge store with current architecture — 4 files updated, 3 deleted, data-flow rewritten", - "date": "2026-07-05" - }, - { - "subject": "refactor: workflow redesign — two-seam drain/watch, deleted Engine/Queue/RetryPolicy, inline retry, Publisher uses container.store_engine, StepHelpers extracted from Helpers", - "date": "2026-07-05" - }, - { - "subject": "refactor: collapse 21 single-file verb specs into core_verbs.rb", - "date": "2026-07-05" - }, - { - "subject": "chore: remove dead code (Outcome, Result, verb stubs, session_open/close files, Registry#workflows_for_key, be_success/be_failure matchers)", - "date": "2026-07-05" - }, - { - "subject": "style: suppress rubocop warnings in spec files", - "date": "2026-07-05" - }, - { - "subject": "style: suppress Metrics/ParameterLists in Container", - "date": "2026-07-05" - }, - { - "subject": "style: rubocop auto-correct fixes", - "date": "2026-07-05" - }, - { - "subject": "refactor: split Workflow::DSL::Definition into sub-modules", - "date": "2026-07-05" - }, - { - "subject": "refactor: group 11-step pipeline into 4 phases", - "date": "2026-07-05" - }, - { - "subject": "refactor: split Builder into three testable layers", - "date": "2026-07-05" - }, - { - "subject": "refactor: CQS context split — QueryContext + CommandContext", - "date": "2026-07-05" - }, - { - "subject": "refactor: consolidate authorization into Manifest::Policy", - "date": "2026-07-05" - }, - { - "subject": "refactor: collapse shallow namespace modules", - "date": "2026-07-05" - }, - { - "subject": "chore: fix rubocop violations, key_delete module_function, and refresh docs", - "date": "2026-07-05" - }, - { - "subject": "refactor: replace hand-written Session delegations with Forwardable", - "date": "2026-07-05" - }, - { - "subject": "refactor: extract merge_issues helper in Definition#check", - "date": "2026-07-05" - }, - { - "subject": "refactor: split VerbRegistry into per-verb files under verb_registry/verbs/", - "date": "2026-07-05" - }, - { - "subject": "refactor: move MovePipeline emit_event dedup guards into shared Pipeline.emit_event", - "date": "2026-07-05" - }, - { - "subject": "refactor: extract shared CheckEtag/ResolvePath into Pipeline module", - "date": "2026-07-05" - }, - { - "subject": "chore: remove vestigial Materialize workflow", - "date": "2026-07-05" - }, - { - "subject": "refactor(workflow): eliminate Materialize re-dispatch and deepen Runner", - "date": "2026-07-05" - }, - { - "subject": "refactor(workflow): extract shared Consumer and decouple event emission from pipeline", - "date": "2026-07-05" - }, - { - "subject": "feat: wire TTL scheduler + refactor Workflow module ownership", - "date": "2026-07-05" - }, - { - "subject": "style: fix rubocop safe autocorrects and update Lint/UnusedMethodArgument excludes for refactored handlers", - "date": "2026-07-05" - }, - { - "subject": "fix: deps/rdeps/graph category -> read (sed artifact from ac02cb77c)", - "date": "2026-07-05" - }, - { - "subject": "refactor: pull serialize_for_put up to Format::Base", - "date": "2026-07-05" - }, - { - "subject": "docs: update ADR-0133 to Accepted", - "date": "2026-07-05" - }, - { - "subject": "docs: update ADR-0133 to Accepted, add session handoff", - "date": "2026-07-05" - }, - { - "subject": "refactor: extract shared ContextWith module for pipeline context objects", - "date": "2026-07-05" - }, - { - "subject": "refactor: extract handler response hash builder into Concern", - "date": "2026-07-05" - }, - { - "subject": "refactor: bundle StoreEngine dependencies into WriteDeps", - "date": "2026-07-05" - }, - { - "subject": "refactor: simplify store_engine pipelines — remove dead code, extract shared helpers", - "date": "2026-07-05" - }, - { - "subject": "refactor: extract domain logic from handlers and MCP server into testable service objects", - "date": "2026-07-05" - }, - { - "subject": "refactor: eliminate double-resolve, push move orchestration into pipeline, remove dead code", - "date": "2026-07-05" - }, - { - "subject": "refactor(store-engine): Standardize StoreEngine method signatures to all-keyword args", - "date": "2026-07-05" - }, - { - "subject": "refactor: consolidate config workflows, fix renderer trigger, add pipeline", - "date": "2026-07-05" - }, - { - "subject": "refactor: remove redundant lane/kind/nested/owner from manifest schema", - "date": "2026-07-05" - }, - { - "subject": "refactor: strip path/publish/source from manifest, remove dead code", - "date": "2026-07-05" - }, - { - "subject": "refactor: move system verb routing from Gate override hash to VerbSpec#system?", - "date": "2026-07-05" - }, - { - "subject": "refactor: StoreEngine + Handlers::Read/Write/System split", - "date": "2026-07-05" - }, - { - "subject": "fix: skip tracked:false entries in doctor manifest check, fix schema ref", - "date": "2026-07-05" - }, - { - "subject": "fix: correct doctor schema parsing, commit generated artifacts", - "date": "2026-07-05" - }, - { - "subject": "refactor: consolidate lane/role docs workflows, fix nested schema loading", - "date": "2026-07-05" - }, - { - "subject": "refactor: split HandlerContext into ReadOps/WriteOps/System capability interfaces", - "date": "2026-07-05" - }, - { - "subject": "test: add unit tests for Diff module (body, meta, schema, summary)", - "date": "2026-07-05" - }, - { - "subject": "refactor: remove dead publish code, inline Mode+None into publish.rb, inline VAR_RE into tree.rb", - "date": "2026-07-05" - }, - { - "subject": "refactor: fold StepScope and Context into Runner as inner classes", - "date": "2026-07-05" - }, - { - "subject": "refactor: inline Workflow::Loader into Registry", - "date": "2026-07-05" - }, - { - "subject": "refactor: inline Workflow::Pattern into DSL::Definition", - "date": "2026-07-05" - }, - { - "subject": "feat: workflow DSL redesign — on/save/notify, priority routing, doctor consolidation", - "date": "2026-07-05" - }, - { - "subject": "feat: track ingested_at, ingest_count, duplicated_at in raw lane ingest flow", - "date": "2026-07-05" - }, - { - "subject": "chore: regenerate boot artifact and orientation docs after dogfood flow changes", - "date": "2026-07-04" - }, - { - "subject": "chore: remove stale boot cache", - "date": "2026-07-04" - }, - { - "subject": "fix: update orientation template and boot.rb for procedures and feedback criteria", - "date": "2026-07-04" - }, - { - "subject": "refactor: move propose/accept/reject handlers from proposal lane to scratchpad lane", - "date": "2026-07-04" - }, - { - "subject": "chore: remove proposals lane from V4::LANES", - "date": "2026-07-04" - }, - { - "subject": "chore: regenerate artifacts after dogfood flow changes", - "date": "2026-07-04" - }, - { - "subject": "fix: materialize workflow — use container.workflow_registry", - "date": "2026-07-04" - }, - { - "subject": "feat: add shape validation for execution sub-families", - "date": "2026-07-04" - }, - { - "subject": "feat: add procedure entries — doctor, session, deploy", - "date": "2026-07-04" - }, - { - "subject": "refactor: move execution entries to runbook/ subdirectory", - "date": "2026-07-04" - }, - { - "subject": "feat: add execution schemas — runbook, checklist, procedure", - "date": "2026-07-04" - }, - { - "subject": "feat: add feedback criteria entries — protocol-gap, repetitive-task, intent-drift", - "date": "2026-07-04" - }, - { - "subject": "refactor: feedback node becomes criteria — move solid-audit to judgment.engineering", - "date": "2026-07-04" - }, - { - "subject": "refactor: update orientation template — remove propose recipe", - "date": "2026-07-04" - }, - { - "subject": "refactor: clean up scratchpad — replace notes/scripts with proposals", - "date": "2026-07-04" - }, - { - "subject": "refactor: remove proposals lane (folded into scratchpad)", - "date": "2026-07-04" - }, - { - "subject": "feat: surface session_open/session_close in agent boot catalog", - "date": "2026-07-04" - }, - { - "subject": "docs: dogfood flow implementation plan", - "date": "2026-07-04" - }, - { - "subject": "docs: dogfood flow design — session lifecycle, feedback criteria, execution zone", - "date": "2026-07-04" - }, - { - "subject": "fix: dogfood audit — orientation keys, doctor cascade, session lifecycle", - "date": "2026-07-04" - }, - { - "subject": "refactor: remove legacy store files, aliases, and dual code paths", - "date": "2026-07-04" - }, - { - "subject": "refactor: reorganize infra/store with Base, split concerns, standardize API", - "date": "2026-07-04" - }, - { - "subject": "docs: lock store naming conventions into knowledge loop", - "date": "2026-07-04" - }, - { - "subject": "refactor: consolidate drain/watch around shared Async::Queue", - "date": "2026-07-04" - }, - { - "subject": "refactor: consolidate dual paths and fix silent error swallowing", - "date": "2026-07-04" - }, - { - "subject": "feat: add code-pattern entries to knowledge loop (0011-0014)", - "date": "2026-07-04" - }, - { - "subject": "refactor: remove dead parameters from handlers, Data.define, and pipeline", - "date": "2026-07-04" - }, - { - "subject": "fix: replace role-based event suppression with self-loop detection via Engine::IN_FLIGHT", - "date": "2026-07-04" - }, - { - "subject": "fix: prevent infinite event loop — skip workflow event emission for automation-role writes", - "date": "2026-07-04" - }, - { - "subject": "fix: update etag spec for layout-object API, remove naming-check debug output", - "date": "2026-07-04" - }, - { - "subject": "refactor: merge overlapping doctor checks (sentinels+orphaned, schema+unowned, manifest+templates+schemas)", - "date": "2026-07-04" - }, - { - "subject": "refactor: extract build_lanes/build_roles helpers, simplify 5 workflows", - "date": "2026-07-04" - }, - { - "subject": "refactor: collapse 8 static-template workflows into 1 generic render workflow", - "date": "2026-07-04" - }, - { - "subject": "fix: optimize naming-check to only scan naming-constrained entries directly", - "date": "2026-07-04" - }, - { - "subject": "fix: use resolver.enumerate instead of Dir.children in naming-check (respects ignore patterns)", - "date": "2026-07-04" - }, - { - "subject": "fix: reconnect event pipeline, move EventEmitter, add workflow_spec verb, remove published/schema verbs", - "date": "2026-07-04" - }, - { - "subject": "fix: reorder knowledge loop keys and restore execution schema", - "date": "2026-07-04" - }, - { - "subject": "feat: add boot_refresh produce workflow, fix guard clause", - "date": "2026-07-04" - }, - { - "subject": "fix: renumber duplicate ADRs 0117/0120/0121/0125 → 0131-0134, fill stub reference docs", - "date": "2026-07-04" - }, - { - "subject": "chore: remove stale hand-authored doc not produced by any workflow", - "date": "2026-07-04" - }, - { - "subject": "fix: remove dead doctor CLI declaration, fix pattern-crossrefs and Workflow::Context#read", - "date": "2026-07-04" - }, - { - "subject": "fix: update .textus dogfood workflows for store API and clean stale artifacts", - "date": "2026-07-04" - }, - { - "subject": "style: fix 133 rubocop offenses (trailing whitespace, alignment, hash alignment, etc.)", - "date": "2026-07-04" - }, - { - "subject": "refactor: migrate flat-file stores to Infra::Store with SQLite-backed sub-stores", - "date": "2026-07-04" - }, - { - "subject": "refactor: remove dead code (Doctor, Retention, TraceBuffer, SequelAdapter, 3 error classes, spec redirects)", - "date": "2026-07-04" - }, - { - "subject": "refactor: fix drain container crash, remove thread-local registry, delete ScopedContext", - "date": "2026-07-04" - }, - { - "subject": "refactor: collapse Container structs, unify Publisher, remove EventStore Interface", - "date": "2026-07-04" - }, - { - "subject": "refactor: unify result shape, remove views, deepen VerbSpec", - "date": "2026-07-04" - }, - { - "subject": "refactor: replace error hashes with exceptions in handlers and gate", - "date": "2026-07-04" - }, - { - "subject": "fix: drop stale contract check, add new verbs to CLI catalog and snapshot", - "date": "2026-07-04" - }, - { - "subject": "refactor: move Container/Builder/WritePipeline/Retention from infra/store/ to protocol/", - "date": "2026-07-04" - }, - { - "subject": "cleanup: remove dead VERB_TO_CONTRACT, Binder.command, Value::Result wrap, move human helpers to lanes/, delete agent/ and human/ dirs", - "date": "2026-07-04" - }, - { - "subject": "refactor: remove Value::Result wrapping from Gate dispatch", - "date": "2026-07-04" - }, - { - "subject": "cleanup: remove dead Binder.command method and Pending data class", - "date": "2026-07-04" - }, - { - "subject": "cleanup: remove dead VERB_TO_CONTRACT lookup table", - "date": "2026-07-04" - }, - { - "subject": "fix(scratchpad): use body for session storage, fix call forwarding", - "date": "2026-07-03" - }, - { - "subject": "feat(lanes): add Knowledge/Scratchpad handlers, remove old Human/Agent stubs", - "date": "2026-07-03" - }, - { - "subject": "refactor: move lane namespaces under Textus::Lanes:: (Ingest, Proposal, Artifact)", - "date": "2026-07-03" - }, - { - "subject": "refactor: move Gate::Builtin to Protocol::Handlers, add HandlerContext, update handlers to use ctx", - "date": "2026-07-03" - }, - { - "subject": "feat(protocol): add HandlerContext — restricted container view for handlers", - "date": "2026-07-03" - }, - { - "subject": "fix: add spec_helper constant stubs for Manifest/Envelope/Call/Entry/Mentry/Container", - "date": "2026-07-03" - }, - { - "subject": "cleanup: remove unused value types (Command, Trace)", - "date": "2026-07-03" - }, - { - "subject": "feat(protocol): add Session class with method_missing, replace Store in CLI/MCP surfaces", - "date": "2026-07-03" - }, - { - "subject": "feat(protocol): add Session class, replace Store facade in CLI and MCP surfaces", - "date": "2026-07-03" - }, - { - "subject": "refactor: move store classes into protocol/ (event_store, entry_store, cursor, freshness, trace_buffer)", - "date": "2026-07-03" - }, - { - "subject": "cleanup: move binder+contracts from dispatch/ to protocol/, remove empty dispatch/ dir", - "date": "2026-07-03" - }, - { - "subject": "cleanup: remove use_cases/ and dispatch/ middleware/pipeline after migration", - "date": "2026-07-03" - }, - { - "subject": "refactor(gate): route un-laned verbs to Gate::Builtin, remove pipeline path", - "date": "2026-07-03" - }, - { - "subject": "feat(protocol): add Gate::Builtin with 20 internal handler methods", - "date": "2026-07-03" - }, - { - "subject": "feat(proposal): add Proposal::Handlers with propose/accept/reject/diff", - "date": "2026-07-03" - }, - { - "subject": "refactor(human): move proposal handlers to Proposal lane (was Human lane)", - "date": "2026-07-03" - }, - { - "subject": "cleanup: remove migrated proposal use cases (propose/accept/reject/diff)", - "date": "2026-07-03" - }, - { - "subject": "feat(protocol): wire propose/accept/reject/diff verbs to Human lane", - "date": "2026-07-03" - }, - { - "subject": "feat(human): add Handlers.diff for proposal diff preview", - "date": "2026-07-03" - }, - { - "subject": "feat(human): add Handlers.reject for proposal rejection", - "date": "2026-07-03" - }, - { - "subject": "feat(human): add Handlers.accept for proposal acceptance with dry-run", - "date": "2026-07-03" - }, - { - "subject": "feat(human): add Handlers.propose for proposal creation", - "date": "2026-07-03" - }, - { - "subject": "cleanup: remove migrated UseCases::Ops::IngestEntry", - "date": "2026-07-03" - }, - { - "subject": "feat(protocol): wire ingest verb to Ingest lane via VerbSpec lane: field", - "date": "2026-07-03" - }, - { - "subject": "feat(ingest): rebuild Handlers with real ingest orchestration", - "date": "2026-07-03" - }, - { - "subject": "feat(ingest): rebuild Resolver with real supersede logic", - "date": "2026-07-03" - }, - { - "subject": "feat(ingest): add EntryTypes sub-modules for link/asset/text content builders", - "date": "2026-07-03" - }, - { - "subject": "feat(ingest): add IndexRebuilder for event store index rebuild", - "date": "2026-07-03" - }, - { - "subject": "feat(ingest): add Dedup for content hash and URL duplicate detection", - "date": "2026-07-03" - }, - { - "subject": "feat(ingest): add KeyBuilder for key derivation and content hashing", - "date": "2026-07-03" - }, - { - "subject": "feat(protocol): Gate dispatches directly to lane handlers when VerbSpec has lane", - "date": "2026-07-03" - }, - { - "subject": "feat(protocol): add Lane.handler_for to resolve lane name to handler module", - "date": "2026-07-03" - }, - { - "subject": "feat(protocol): add lane: field to VerbSpec", - "date": "2026-07-03" - }, - { - "subject": "feat(infra): add Store::Index::Lookup and Store::Index::Builder classes", - "date": "2026-07-03" - }, - { - "subject": "Implement Protocol::Manifest and related schema components", - "date": "2026-07-03" - }, - { - "subject": "Add core infrastructure for Textus event processing and storage", - "date": "2026-07-03" - }, - { - "subject": "Refactor Textus to use Protocol namespace", - "date": "2026-07-03" - }, - { - "subject": "refactor: remove dead code, fix Gate authorization, drop audit_events table", - "date": "2026-07-03" - }, - { - "subject": "chore: remove accidentally committed backup files", - "date": "2026-07-03" - }, - { - "subject": "phase(cleanup): remove orphaned SqliteAdapter — Phase 5", - "date": "2026-07-03" - }, - { - "subject": "phase(surface): update Watcher to use Protocol::Async and Infra::Locks — Phase 4", - "date": "2026-07-03" - }, - { - "subject": "phase(lanes): add scoped Workflow::ScopedContext — Phase 3 complete", - "date": "2026-07-03" - }, - { - "subject": "feat(human): create Textus::Human with LOOP Runner, Freeform, and handlers", - "date": "2026-07-03" - }, - { - "subject": "feat(agent): create Textus::Agent with Session, Workspace, Runner, Evidence, and handlers", - "date": "2026-07-03" - }, - { - "subject": "feat(artifact): create Textus::Artifact with cache, lifecycle, and handlers", - "date": "2026-07-03" - }, - { - "subject": "feat(ingest): create Textus::Ingest with resolver, entry types, and handlers", - "date": "2026-07-03" - }, - { - "subject": "phase(protocol): add Gate, Async, wire into Builder — Phase 2 complete", - "date": "2026-07-03" - }, - { - "subject": "feat(protocol): add Protocol::Manifest alias for Textus::Manifest", - "date": "2026-07-03" - }, - { - "subject": "phase(protocol): move Index, Links, Audit, Envelope to Protocol:: namespace with forwarding facades", - "date": "2026-07-03" - }, - { - "subject": "phase(protocol): move Layout, Key, Schema to Protocol:: namespace with forwarding facades", - "date": "2026-07-03" - }, - { - "subject": "feat(protocol): move Format strategies to Protocol::Format with forwarding facades", - "date": "2026-07-03" - }, - { - "subject": "chore: ignore headroom backup file", - "date": "2026-07-03" - }, - { - "subject": "feat(infra): wire Infra::FileStore, Infra::Database, Infra::Clock in Store::Builder", - "date": "2026-07-03" - }, - { - "subject": "feat(infra): extract Infra::Locks with BuildLock and WatcherLock", - "date": "2026-07-03" - }, - { - "subject": "feat(infra): extract Infra::Clock with now method", - "date": "2026-07-03" - }, - { - "subject": "feat(infra): extract Infra::Database with table setup, FTS5, and transaction support", - "date": "2026-07-03" - }, - { - "subject": "feat(infra): extract Infra::FileStore with 17 methods matching current FileSystem interface", - "date": "2026-07-03" - }, - { - "subject": "refactor: hard-cut legacy workflow step syntax", - "date": "2026-07-03" - }, - { - "subject": "fix: add lsp defaults to opencode workflow config", - "date": "2026-07-03" - }, - { - "subject": "fix: restore workflow artifact generation and docs publish", - "date": "2026-07-03" - }, - { - "subject": "fix: harden publish/parser and resolve lint regressions", - "date": "2026-07-03" - }, - { - "subject": "docs: mark ADR-0125 container/double-emission notes superseded", - "date": "2026-07-03" - }, - { - "subject": "refactor: group port by concern (storage vs concurrency)", - "date": "2026-07-03" - }, - { - "subject": "refactor: rename store/jobs and fold materialize into workflow/instances", - "date": "2026-07-03" - }, - { - "subject": "fix: protect .textus/data canon from direct agent writes", - "date": "2026-07-03" - }, - { - "subject": "fix: remove stale jobs CLI help text", - "date": "2026-07-03" - }, - { - "subject": "refactor: extract MCP contract-drift annotation from Server#dispatch", - "date": "2026-07-03" - }, - { - "subject": "refactor: fold entry indexing into the synchronous write path", - "date": "2026-07-03" - }, - { - "subject": "fix: wire Consumer and EventEmitter callers to EventStore", - "date": "2026-07-03" - }, - { - "subject": "refactor: Produce::EventEmitter depends on EventStore", - "date": "2026-07-03" - }, - { - "subject": "fix: Events::Consumer retry path delegates to EventStore and RetryPolicy", - "date": "2026-07-03" - }, - { - "subject": "refactor: trim Store::EventStore to only own the events table", - "date": "2026-07-03" - }, - { - "subject": "fix: add Store::EntryIndex and correct entries FTS indexing/search", - "date": "2026-07-03" - }, - { - "subject": "refactor: extract Events::RetryPolicy as its own decision object", - "date": "2026-07-03" - }, - { - "subject": "fix: rename db -> database in consumer.rb; move ADR 0127 to .textus via textus put; add ADR 0129 DependencyAdapter gate", - "date": "2026-07-03" - }, - { - "subject": "style: fix rubocop enable directives in write_pipeline and event_store", - "date": "2026-07-03" - }, - { - "subject": "fix: missing keyword database in converge_now drain call", - "date": "2026-07-03" - }, - { - "subject": "docs: add ADR 0127 — interface consolidation decisions", - "date": "2026-07-03" - }, - { - "subject": "chore: remove legacy code — Entry::Reader/Writer refs, dead LinkEdgeStore spec", - "date": "2026-07-03" - }, - { - "subject": "fix: drain_store call recursion, boot protocol key, backward compat methods", - "date": "2026-07-03" - }, - { - "subject": "fix: use respond_to? in HandlerResolver, fix BootStore Infrastructure ref", - "date": "2026-07-03" - }, - { - "subject": "style: fix remaining rubocop offenses", - "date": "2026-07-03" - }, - { - "subject": "fix: Boot.CLI_VERBS uses Catalog.build", - "date": "2026-07-03" - }, - { - "subject": "style: fix all rubocop offenses", - "date": "2026-07-03" - }, - { - "subject": "refactor: route ports through FileSystem, split Boot", - "date": "2026-07-03" - }, - { - "subject": "feat: add Events middleware, convert all use cases to classes", - "date": "2026-07-03" - }, - { - "subject": "fix: add trace_buffer/reader/writer accessors to Container, update specs", - "date": "2026-07-03" - }, - { - "subject": "refactor: create Store::LinkGraph, rewire Builder to Container", - "date": "2026-07-03" - }, - { - "subject": "refactor: create Store::EntryStore, retire Entry::Reader + Entry::Writer", - "date": "2026-07-03" - }, - { - "subject": "feat: add Store::Container with grouped components", - "date": "2026-07-03" - }, - { - "subject": "refactor: split Port::Store into Port::Database + Port::EventStore", - "date": "2026-07-03" - }, - { - "subject": "refactor: create Port::FileSystem + Store::FileSystem, retire Storage/", - "date": "2026-07-03" - }, - { - "subject": "refactor: split WriteStep into write_pipeline/ directory", - "date": "2026-07-03" - }, - { - "subject": "refactor: extract VerbSpec into verb_registry/verb_spec.rb", - "date": "2026-07-03" - }, - { - "subject": "refactor: extract ArgSpec into verb_registry/arg_spec.rb", - "date": "2026-07-03" - }, - { - "subject": "fix: skip publish in Runner when data is nil, remove publish from system workflows that manage their own output; fix pulse_entries, LinkEdgeStore, CLI verbs catalog, specs for architecture changes; all 1014 tests pass", - "date": "2026-07-02" - }, - { - "subject": "feat: implement new event system with Sequel-backed events table, Workflow DSL handles, Registry, EventEmitter, Consumer, Materialize/Index workflows, rewire Builder/Infrastructure/Watcher/Drain/LinkEdgeStore", - "date": "2026-07-02" - }, - { - "subject": "refactor: remove Event::Bus, Event module, CascadeSubscriber, Store::Jobs namespace", - "date": "2026-07-02" - }, - { - "subject": "refactor: remove write-time schema validation from WriteStep and Writer", - "date": "2026-07-02" - }, - { - "subject": "refactor: remove rule system from manifest, data.rb, contracts, verb registry, CLI, auth, publisher, ttl evaluator, retention sweep", - "date": "2026-07-02" - }, - { - "subject": "refactor: remove enqueue verb", - "date": "2026-07-02" - }, - { - "subject": "refactor: remove doctor verb and module", - "date": "2026-07-02" - }, - { - "subject": "feat: add sequel gem and SequelAdapter dependency adapter", - "date": "2026-07-02" - }, - { - "subject": "refactor: move knowledge sections under knowledge.loop.* — clean separation of loop vs reference data", - "date": "2026-07-02" - }, - { - "subject": "refactor: streamline conformance fixture — drop project-specific entries, align with repo shape", - "date": "2026-07-02" - }, - { - "subject": "refactor(init): use consolidated entries for project-agnostic scaffold", - "date": "2026-07-02" - }, - { - "subject": "refactor: align init and fixture entry names with .textus/manifest.yaml", - "date": "2026-07-02" - }, - { - "subject": "refactor: add naming/schema governance to repo manifest entries", - "date": "2026-07-02" - }, - { - "subject": "refactor: complete manifest entry simplification — align init, fixtures, helpers to repo shape", - "date": "2026-07-02" - }, - { - "subject": "refactor(test-support): match repo entry shape; remove migrate_legacy_manifest", - "date": "2026-07-02" - }, - { - "subject": "refactor: simplify repo manifest entries; fix resolve_format for explicit path override", - "date": "2026-07-02" - }, - { - "subject": "refactor(init): simplify DEFAULT_MANIFEST and AGENT_ENTRIES — match repo entry shape", - "date": "2026-07-02" - }, - { - "subject": "refactor: delete IgnoreMatcher; remove ignore system from Resolver, SubtreeMirror, Tree, schema, validators", - "date": "2026-07-02" - }, - { - "subject": "refactor(nested): remove ignore/ignored? — no longer read from manifest", - "date": "2026-07-02" - }, - { - "subject": "refactor(base): remove ignore/ignored? stubs; make schema/naming optional", - "date": "2026-07-02" - }, - { - "subject": "refactor(parser): stop reading schema/naming/ignore/publish/source from manifest; infer kind/format from disk", - "date": "2026-07-02" - }, - { - "subject": "docs: design doc for manifest entry simplification", - "date": "2026-07-02" - }, - { - "subject": "feat: protocol textus/4 — manifest data: shape, roles/lanes hardcoded", - "date": "2026-07-02" - }, - { - "subject": "refactor(manifest): drop owner from envelope and where output", - "date": "2026-07-02" - }, - { - "subject": "refactor(manifest): Entry::Parser infers kind from lane+disk shape, adds naming:, drops owner:/format:", - "date": "2026-07-02" - }, - { - "subject": "refactor(manifest): Data.parse reads data: grouped-by-lane, rejects stale roles:/lanes:/owner:", - "date": "2026-07-02" - }, - { - "subject": "refactor(manifest): repoint write-gating predicates to Protocol::V4::LANES writers", - "date": "2026-07-02" - }, - { - "subject": "refactor(manifest): Domain::Lane and Policy read writers from Protocol::V4::LANES directly", - "date": "2026-07-02" - }, - { - "subject": "feat(manifest): add Textus::Protocol::V4 fixed lane/naming table", - "date": "2026-07-02" - }, - { - "subject": "test: relax build-lock drain soft-miss assertion", - "date": "2026-07-02" - }, - { - "subject": "chore: satisfy pre-push rubocop naming rules", - "date": "2026-07-02" - }, - { - "subject": "chore: clean up lint warnings in workflow and jobs code", - "date": "2026-07-02" - }, - { - "subject": "refactor(doctor): batch-port remaining class checks to workflows", - "date": "2026-07-02" - }, - { - "subject": "refactor(doctor): port PublishTreeIndexOverlap check to a workflow", - "date": "2026-07-02" - }, - { - "subject": "refactor(doctor): port OrphanedPublishTargets check to a workflow", - "date": "2026-07-02" - }, - { - "subject": "refactor(doctor): port SchemaViolations check to a workflow", - "date": "2026-07-02" - }, - { - "subject": "refactor(doctor): port UnownedSchemaFields check to a workflow", - "date": "2026-07-02" - }, - { - "subject": "refactor(doctor): port AuditLog check to a workflow", - "date": "2026-07-02" - }, - { - "subject": "refactor(doctor): port Sentinels check to a workflow", - "date": "2026-07-02" - }, - { - "subject": "refactor(doctor): port IllegalKeys check to a workflow", - "date": "2026-07-02" - }, - { - "subject": "refactor(doctor): port SchemaParseError check to a workflow", - "date": "2026-07-02" - }, - { - "subject": "refactor(doctor): port ProtocolVersion check to a workflow", - "date": "2026-07-02" - }, - { - "subject": "refactor(loop): renumber scratchpad.notes to NNNN-topic-slug.md, scaffold sessions/scripts", - "date": "2026-07-02" - }, - { - "subject": "feat(workflow): add shape() DSL primitive, check_naming helper, loop-shape workflow", - "date": "2026-07-02" - }, - { - "subject": "feat(loop): add loop summary to boot orientation output", - "date": "2026-07-02" - }, - { - "subject": "feat(loop): add Loop node table to explanation concepts template", - "date": "2026-07-02" - }, - { - "subject": "refactor(loop): realign task 6/7 identity and readme extraction", - "date": "2026-07-02" - }, - { - "subject": "refactor(loop): fold knowledge.readme fragments into intent", - "date": "2026-07-02" - }, - { - "subject": "refactor(loop): fold knowledge.project into intent section", - "date": "2026-07-02" - }, - { - "subject": "refactor(loop): retire knowledge.architecture into loop sections", - "date": "2026-07-02" - }, - { - "subject": "feat(loop): add knowledge.feedback zone", - "date": "2026-07-02" - }, - { - "subject": "refactor(loop): split knowledge.patterns into judgment.engineering/.agent-behavior", - "date": "2026-07-02" - }, - { - "subject": "refactor(loop): split knowledge.rules/specs into constraint.repo/.protocol", - "date": "2026-07-02" - }, - { - "subject": "refactor(verbs): standardize verb-registry surfaces, naming, jobs split, error typing", - "date": "2026-07-02" - }, - { - "subject": "refactor(architecture): deepen v2 — flatten jobs ceremony, extract publisher/validator/stepscope, delete facade", - "date": "2026-07-02" - }, - { - "subject": "feat(architecture): integrate container's writer and reader into use cases and workflow runner", - "date": "2026-07-02" - }, - { - "subject": "docs(architecture): sync docs to phase 3 refactor — Infrastructure container, workflow DSL surface, no Registry/Collector/Publisher", - "date": "2026-07-02" - }, - { - "subject": "refactor(architecture): deepen phase 3 — Writer port, single Infrastructure, manifest→workflow boundary", - "date": "2026-07-02" - }, - { - "subject": "Revert \"feat(federation): add federation sync workflow — auto-mirrors remote store entries on drain\"", - "date": "2026-07-01" - }, - { - "subject": "feat(federation): add federation sync workflow — auto-mirrors remote store entries on drain", - "date": "2026-07-01" - }, - { - "subject": "feat(workflow): migrate 4 more Doctor checks to validation workflows", - "date": "2026-07-01" - }, - { - "subject": "feat(workflow): migrate Doctor::RuleAmbiguity to self-contained validation workflow", - "date": "2026-07-01" - }, - { - "subject": "feat(workflow): add validate step type, multi-key matching, and validation workflow POC", - "date": "2026-07-01" - }, - { - "subject": "chore: revert solid-process POC (gem not mature enough)", - "date": "2026-07-01" - }, - { - "subject": "refactor(specs): remove 6 redundant files, simplify 5 overlapping specs, drop 6 dead refs", - "date": "2026-07-01" - }, - { - "subject": "feat(pipeline): enforce cross-reference chain — decisions→architecture, patterns→decisions, runbooks→patterns", - "date": "2026-07-01" - }, - { - "subject": "chore: regenerate changelog", - "date": "2026-07-01" - }, - { - "subject": "chore: track knowledge.architecture entries and manifest", - "date": "2026-07-01" - }, - { - "subject": "feat(patterns): add unified-dispatch, middleware-chain, handler-needs, store-builder + agent training", - "date": "2026-07-01" - }, - { - "subject": "docs: document 6 intentional Reader/Writer bypass sites (P3)", - "date": "2026-07-01" - }, - { - "subject": "refactor: drop Ctx alias, rename ctx.rb → infrastructure.rb", - "date": "2026-07-01" - }, - { - "subject": "refactor: clean up P0-P2 anti-patterns — HANDLES_ALL, boot.rb layering, InfrastructureProxy rename, Store::Builder", - "date": "2026-07-01" - }, - { - "subject": "feat(knowledge): enforce pipeline structure with conformance spec + agent protocol", - "date": "2026-07-01" - }, - { - "subject": "feat(observability): Trace middleware — auto-instrumented dispatch with ring buffer (Phase 5)", - "date": "2026-07-01" - }, - { - "subject": "feat(conformance): verb completeness, downward layer import, port contract guards (Phase 4)", - "date": "2026-07-01" - }, - { - "subject": "feat(ports): formal port interfaces with conformance verification (Phase 3)", - "date": "2026-07-01" - }, - { - "subject": "feat(domain): extract Domain::Key, Domain::Lane, Domain::Envelope — pure domain layer (Phase 2)", - "date": "2026-07-01" - }, - { - "subject": "feat: land ADR-0120, ADR-0121, ADR-0125 — unified dispatch, bounded use cases, interface hygiene", - "date": "2026-07-01" - }, - { - "subject": "refactor: remove unused handler modules and simplify dispatch logic", - "date": "2026-07-01" - }, - { - "subject": "docs: refresh generated changelog and skills feed", - "date": "2026-07-01" - }, - { - "subject": "refactor: simplify entry read handlers and pulse fallback", - "date": "2026-07-01" - }, - { - "subject": "fix: align trigger catalog and etag conformance guards", - "date": "2026-07-01" - }, - { - "subject": "docs: add dependency adoption gate guidance", - "date": "2026-07-01" - }, - { - "subject": "docs: add architecture patterns for triggers and adapters", - "date": "2026-07-01" - }, - { - "subject": "refactor: centralize trigger vocabulary in TriggerCatalog", - "date": "2026-07-01" - }, - { - "subject": "refactor: add dependency adapter modules for runtime deps", - "date": "2026-07-01" - }, - { - "subject": "refactor: model jobs and produce results with outcome values", - "date": "2026-07-01" - }, - { - "subject": "refactor: extract drain jobs pulse per-verb handlers", - "date": "2026-07-01" - }, - { - "subject": "refactor: extract key_mv key_delete data_mv handlers", - "date": "2026-07-01" - }, - { - "subject": "refactor: extract get put list per-verb handlers", - "date": "2026-07-01" - }, - { - "subject": "refactor: add concern handlers for dispatch domains", - "date": "2026-07-01" - }, - { - "subject": "refactor: narrow handler invocation to keyword interface", - "date": "2026-07-01" - }, - { - "subject": "refactor: route CLI and MCP via VerbDispatch", - "date": "2026-07-01" - }, - { - "subject": "refactor: introduce Infrastructure aliases and dispatch seam baseline", - "date": "2026-07-01" - }, - { - "subject": "feat: implement ADRs 0121-0125, including bounded use-case objects, workflow parallel steps, and session resilience; update changelog and context documentation", - "date": "2026-07-01" - }, - { - "subject": "feat: add ADR-0125 for Bounded Use-Case Objects and related documentation; update manifest and CLI argument formatting", - "date": "2026-07-01" - }, - { - "subject": "refactor: consolidate handlers into UseCases and remove Orchestration layer", - "date": "2026-07-01" - }, - { - "subject": "feat: Implement ADRs 0121-0124 — link graph, proposal diff, session resilience, parallel workflows", - "date": "2026-07-01" - }, - { - "subject": "chore: fix 3 rubocop offenses and regenerate docs", - "date": "2026-07-01" - }, - { - "subject": "fix: rule_trace verb category should be :read, not :maintenance", - "date": "2026-07-01" - }, - { - "subject": "feat: add rule_trace verb — trace rule resolution candidates, winners, and effective RuleSet", - "date": "2026-07-01" - }, - { - "subject": "refactor: decompose Writer#move into WriteStep::DEFAULT_MOVE chain", - "date": "2026-07-01" - }, - { - "subject": "refactor: decompose Writer#delete into WriteStep::DEFAULT_DELETE chain", - "date": "2026-07-01" - }, - { - "subject": "refactor: two-phase Manifest.build — derived_entry? now correct", - "date": "2026-07-01" - }, - { - "subject": "refactor(store)!: delete deprecated Container, Assembler, Cascade middleware", - "date": "2026-07-01" - }, - { - "subject": "refactor(store): build Ctx+HandlerResolver pipeline internally", - "date": "2026-07-01" - }, - { - "subject": "refactor: convert all write and maintenance handlers to pure modules (HANDLES/NEEDS/self.call)", - "date": "2026-07-01" - }, - { - "subject": "refactor: convert all read handlers to pure modules (HANDLES/NEEDS/self.call)", - "date": "2026-07-01" - }, - { - "subject": "feat: add HandlerResolver — discovers pure handler modules by convention", - "date": "2026-07-01" - }, - { - "subject": "feat: add CascadeSubscriber — event-driven replacement for Cascade middleware", - "date": "2026-07-01" - }, - { - "subject": "feat: add Store::Ctx, typed Event structs, and session-scoped Event::Bus", - "date": "2026-07-01" - }, - { - "subject": "feat: Introduce Boot.wire and Typed Events", - "date": "2026-06-30" - }, - { - "subject": "refactor: drop positional-arg support from entry/ops/rule — keyword args only", - "date": "2026-06-30" - }, - { - "subject": "feat: replace generated verb methods with entry/ops/rule noun-domain API on Store", - "date": "2026-06-30" - }, - { - "subject": "refactor: replace Container#build_pipeline with declarative HANDLER_MANIFEST in Dispatch::Assembler", - "date": "2026-06-30" - }, - { - "subject": "refactor: decompose Entry::Writer#put into WriteStep::DEFAULT_PUT chain", - "date": "2026-06-30" - }, - { - "subject": "chore: save architecture redesign plan to scratchpad", - "date": "2026-06-30" - }, - { - "subject": "refactor: remove manual uid from all workflows — textus manages uid natively via inject_all", - "date": "2026-06-30" - }, - { - "subject": "fix(ci): stable uid in architecture-index — use JSON.generate instead of Hash#inspect (Ruby 3.3 vs 3.4 diverge)", - "date": "2026-06-30" - }, - { - "subject": "fix(ci): fetch-depth: 0 on docs job — git log needs full history for changelog workflow", - "date": "2026-06-30" - }, - { - "subject": "refactor: clean up textus.rb — inline MCP namespace stub, remove dead ignores, drop duplicate Textus.workflow", - "date": "2026-06-30" - }, - { - "subject": "fix: gemspec — drop partial docs/ from shipped files, ship code + README + CHANGELOG only", - "date": "2026-06-30" - }, - { - "subject": "fix: gemspec — remove stale SPEC.md reference, update protocol version to textus/4, point docs_uri to README", - "date": "2026-06-30" - }, - { - "subject": "feat: Claude Code hooks — block direct writes to textus-managed artifacts", - "date": "2026-06-30" - }, - { - "subject": "chore: add textus drain to pre-push hook + docs-protocol-guard on pre-commit", - "date": "2026-06-30" - }, - { - "subject": "refactor(Move1): rename Produce::ContextHelpers → Textus::ContainerHelpers — neutral top-level module used by both publish and workflow contexts", - "date": "2026-06-30" - }, - { - "subject": "refactor(M3): rename Render::Context#binding → #to_erb_binding — avoids shadowing Ruby built-in", - "date": "2026-06-30" - }, - { - "subject": "refactor(M2): remove silent StandardError rescue in read_family — reader.read returns nil for absent files", - "date": "2026-06-30" - }, - { - "subject": "refactor(M1): drop edge_store param from Publisher — ToPaths reads container.link_edge_store directly", - "date": "2026-06-30" - }, - { - "subject": "fix(critical): Container#link_edge_store shared — Engine + Workflow::Runner both record edges, RdepsEntry queries same instance (C1+C2)", - "date": "2026-06-30" - }, - { - "subject": "chore: drop redundant require_relative — Zeitwerk autoloads lib/textus/**", - "date": "2026-06-30" - }, - { - "subject": "refactor: extract ContextHelpers — manifest/repo_root shared across PublishContext + Workflow::Context (Audit #4)", - "date": "2026-06-30" - }, - { - "subject": "refactor: Container#read_family — hide manifest.resolver.enumerate from workflows (Audit #5)", - "date": "2026-06-30" - }, - { - "subject": "feat: wire LinkEdgeStore recording through Engine → Render → UriRewriter (ADR-0121 complete)", - "date": "2026-06-30" - }, - { - "subject": "refactor: move link rewriting into Render — ToPaths drops UriRewriter dependency (Audit #2)", - "date": "2026-06-30" - }, - { - "subject": "refactor: extract Render::Context — testable binding factory (Audit #1)", - "date": "2026-06-30" - }, - { - "subject": "feat: ADR-0121 Phase 1 complete — textus-native link resolution (filesystem mode)", - "date": "2026-06-30" - }, - { - "subject": "refactor: migrate template cross-links to textus_link helper", - "date": "2026-06-30" - }, - { - "subject": "feat: LinkEdgeStore + extend rdeps to include link dependency edges", - "date": "2026-06-30" - }, - { - "subject": "feat: add UriRewriter — post-process textus: URIs in rendered output", - "date": "2026-06-30" - }, - { - "subject": "feat: inject textus_link method into ERB template context via binding", - "date": "2026-06-30" - }, - { - "subject": "feat: add Textus::Links::Resolver — resolves textus:KEY to relative paths", - "date": "2026-06-30" - }, - { - "subject": "feat: add ADR-0121 — textus-native link resolution via textus: URI scheme", - "date": "2026-06-30" - }, - { - "subject": "feat: remove SPEC.md — spec lives in knowledge.specs/* via textus protocol", - "date": "2026-06-30" - }, - { - "subject": "fix: changelog filters merge commits (--no-merges), delete unmanaged CHANGELOG/COC files", - "date": "2026-06-30" - }, - { - "subject": "fix: remove orphaned docs/design/architecture.md sentinel", - "date": "2026-06-30" - }, - { - "subject": "refactor: rules key ordering, contributing/security/changelog/coc as artifacts", - "date": "2026-06-30" - }, - { - "subject": "fix: decisions log workflow — use container.reader.read, fix relative links in template", - "date": "2026-06-30" - }, - { - "subject": "refactor: move knowledge.spec → artifacts.spec, remove orphaned artifacts, integrate decision log", - "date": "2026-06-30" - }, - { - "subject": "chore: remove stale knowledge sources replaced by artifact equivalents", - "date": "2026-06-30" - }, - { - "subject": "chore: remove knowledge/cookbook source directory", - "date": "2026-06-30" - }, - { - "subject": "fix: clean orphaned sentinels/files, update decisions README link, restore ignore", - "date": "2026-06-30" - }, - { - "subject": "refactor: rename adr-log to decision log, remove docs/README.md index artifact", - "date": "2026-06-30" - }, - { - "subject": "refactor: prune legacy knowledge, remove cookbook, reorganize workflows+templates", - "date": "2026-06-30" - }, - { - "subject": "feat: convert all how-to, reference, cookbook, explanation docs to generated artifacts", - "date": "2026-06-30" - }, - { - "subject": "feat: wire artifacts.how-to.agents-mcp — pilot how-to artifact pattern", - "date": "2026-06-30" - }, - { - "subject": "feat: wire artifacts.architecture.index — generates lane/role tables + static layer layout", - "date": "2026-06-30" - }, - { - "subject": "feat: delete design/invariants monolith — replaced by atomic canon + assembler artifact", - "date": "2026-06-30" - }, - { - "subject": "feat: wire artifacts.design.invariants — assembles goals + rules atoms via workflow", - "date": "2026-06-30" - }, - { - "subject": "feat: decompose design/invariants into atomic knowledge.goals.* and knowledge.rules.* entries", - "date": "2026-06-30" - }, - { - "subject": "feat: add knowledge.goals and knowledge.rules nested families to manifest", - "date": "2026-06-30" - }, - { - "subject": "feat: add ADR-0120 — atomic canon + composed artifacts principle", - "date": "2026-06-30" - } - ] + "groups": { + "Features": [ + { + "subject": "add Knowledge/Scratchpad handlers, remove old Human/Agent stubs", + "date": "2026-07-03" + }, + { + "subject": "add HandlerContext — restricted container view for handlers", + "date": "2026-07-03" + }, + { + "subject": "add Session class with method_missing, replace Store in CLI/MCP surfaces", + "date": "2026-07-03" + }, + { + "subject": "add Session class, replace Store facade in CLI and MCP surfaces", + "date": "2026-07-03" + }, + { + "subject": "add Gate::Builtin with 20 internal handler methods", + "date": "2026-07-03" + }, + { + "subject": "add Proposal::Handlers with propose/accept/reject/diff", + "date": "2026-07-03" + }, + { + "subject": "wire propose/accept/reject/diff verbs to Human lane", + "date": "2026-07-03" + }, + { + "subject": "add Handlers.diff for proposal diff preview", + "date": "2026-07-03" + }, + { + "subject": "add Handlers.reject for proposal rejection", + "date": "2026-07-03" + }, + { + "subject": "add Handlers.accept for proposal acceptance with dry-run", + "date": "2026-07-03" + }, + { + "subject": "add Handlers.propose for proposal creation", + "date": "2026-07-03" + }, + { + "subject": "wire ingest verb to Ingest lane via VerbSpec lane: field", + "date": "2026-07-03" + }, + { + "subject": "rebuild Handlers with real ingest orchestration", + "date": "2026-07-03" + }, + { + "subject": "rebuild Resolver with real supersede logic", + "date": "2026-07-03" + }, + { + "subject": "add EntryTypes sub-modules for link/asset/text content builders", + "date": "2026-07-03" + }, + { + "subject": "add IndexRebuilder for event store index rebuild", + "date": "2026-07-03" + }, + { + "subject": "add Dedup for content hash and URL duplicate detection", + "date": "2026-07-03" + }, + { + "subject": "add KeyBuilder for key derivation and content hashing", + "date": "2026-07-03" + }, + { + "subject": "Gate dispatches directly to lane handlers when VerbSpec has lane", + "date": "2026-07-03" + }, + { + "subject": "add Lane.handler_for to resolve lane name to handler module", + "date": "2026-07-03" + }, + { + "subject": "add lane: field to VerbSpec", + "date": "2026-07-03" + }, + { + "subject": "add Store::Index::Lookup and Store::Index::Builder classes", + "date": "2026-07-03" + }, + { + "subject": "create Textus::Human with LOOP Runner, Freeform, and handlers", + "date": "2026-07-03" + }, + { + "subject": "create Textus::Agent with Session, Workspace, Runner, Evidence, and handlers", + "date": "2026-07-03" + }, + { + "subject": "create Textus::Artifact with cache, lifecycle, and handlers", + "date": "2026-07-03" + }, + { + "subject": "create Textus::Ingest with resolver, entry types, and handlers", + "date": "2026-07-03" + }, + { + "subject": "add Protocol::Manifest alias for Textus::Manifest", + "date": "2026-07-03" + }, + { + "subject": "move Format strategies to Protocol::Format with forwarding facades", + "date": "2026-07-03" + }, + { + "subject": "wire Infra::FileStore, Infra::Database, Infra::Clock in Store::Builder", + "date": "2026-07-03" + }, + { + "subject": "extract Infra::Locks with BuildLock and WatcherLock", + "date": "2026-07-03" + }, + { + "subject": "extract Infra::Clock with now method", + "date": "2026-07-03" + }, + { + "subject": "extract Infra::Database with table setup, FTS5, and transaction support", + "date": "2026-07-03" + }, + { + "subject": "extract Infra::FileStore with 17 methods matching current FileSystem interface", + "date": "2026-07-03" + }, + { + "subject": "add Textus::Protocol::V4 fixed lane/naming table", + "date": "2026-07-02" + }, + { + "subject": "add shape() DSL primitive, check_naming helper, loop-shape workflow", + "date": "2026-07-02" + }, + { + "subject": "add loop summary to boot orientation output", + "date": "2026-07-02" + }, + { + "subject": "add Loop node table to explanation concepts template", + "date": "2026-07-02" + }, + { + "subject": "add knowledge.feedback zone", + "date": "2026-07-02" + }, + { + "subject": "integrate container's writer and reader into use cases and workflow runner", + "date": "2026-07-02" + }, + { + "subject": "add federation sync workflow — auto-mirrors remote store entries on drain", + "date": "2026-07-01" + }, + { + "subject": "migrate 4 more Doctor checks to validation workflows", + "date": "2026-07-01" + }, + { + "subject": "migrate Doctor::RuleAmbiguity to self-contained validation workflow", + "date": "2026-07-01" + }, + { + "subject": "add validate step type, multi-key matching, and validation workflow POC", + "date": "2026-07-01" + }, + { + "subject": "enforce cross-reference chain — decisions→architecture, patterns→decisions, runbooks→patterns", + "date": "2026-07-01" + }, + { + "subject": "add unified-dispatch, middleware-chain, handler-needs, store-builder + agent training", + "date": "2026-07-01" + }, + { + "subject": "enforce pipeline structure with conformance spec + agent protocol", + "date": "2026-07-01" + }, + { + "subject": "Trace middleware — auto-instrumented dispatch with ring buffer (Phase 5)", + "date": "2026-07-01" + }, + { + "subject": "verb completeness, downward layer import, port contract guards (Phase 4)", + "date": "2026-07-01" + }, + { + "subject": "formal port interfaces with conformance verification (Phase 3)", + "date": "2026-07-01" + }, + { + "subject": "extract Domain::Key, Domain::Lane, Domain::Envelope — pure domain layer (Phase 2)", + "date": "2026-07-01" + } + ], + "Bug Fixes": [ + { + "subject": "use body for session storage, fix call forwarding", + "date": "2026-07-03" + }, + { + "subject": "stable uid in architecture-index — use JSON.generate instead of Hash#inspect (Ruby 3.3 vs 3.4 diverge)", + "date": "2026-06-30" + }, + { + "subject": "fetch-depth: 0 on docs job — git log needs full history for changelog workflow", + "date": "2026-06-30" + }, + { + "subject": "Container#link_edge_store shared — Engine + Workflow::Runner both record edges, RdepsEntry queries same instance (C1+C2)", + "date": "2026-06-30" + } + ] + } }, { "tag": "v0.55.2", "date": "2026-06-30", - "commits": [ - { - "subject": "chore: bump version to 0.55.2", - "date": "2026-06-30" - }, - { - "subject": "docs: fix 15 stale content issues found in audit", - "date": "2026-06-30" - }, - { - "subject": "chore: sync how-to store source files and refresh boot artifact", - "date": "2026-06-30" - }, - { - "subject": "docs: rewrite how-to guides — add MCP protocol lifecycle, remove stale content", - "date": "2026-06-30" - }, - { - "subject": "fix: harden MCP server dispatch and resource handling", - "date": "2026-06-30" - }, - { - "subject": "fix: scratchpad sources doctor check handles object-form sources", - "date": "2026-06-30" - }, - { - "subject": "feat: expand sources with suspended flag at get time", - "date": "2026-06-30" - }, - { - "subject": "feat: normalize sources to {key,etag} objects at write time", - "date": "2026-06-30" - }, - { - "subject": "refactor: harden API and interface boundaries across Store, MCP, and registry", - "date": "2026-06-30" - }, - { - "subject": "refactor: update all notebook→scratchpad references in docs, specs, lib, and store data", - "date": "2026-06-30" - }, - { - "subject": "refactor: rename notebook lane to scratchpad across manifest, docs, lib, and specs", - "date": "2026-06-30" - }, - { - "subject": "feat: extend list with q: FTS and schema: filter via SQLite entries index", - "date": "2026-06-30" - }, - { - "subject": "feat: shadow writes to SQLite audit_events, pulse reads from index with flat-log fallback", - "date": "2026-06-30" - }, - { - "subject": "feat: consolidate boot into artifacts.boot, drain computes stable content, boot injects live fields", - "date": "2026-06-30" - }, - { - "subject": "chore: remove generated reference docs, superpowers scratch dirs, fix broken links", - "date": "2026-06-30" - }, - { - "subject": "split Freshness::Evaluator into TtlEvaluator + DriftDetector", - "date": "2026-06-29" - }, - { - "subject": "flatten Container wiring — remove hypothetical HandlerFactoryRegistry+Adapter seam", - "date": "2026-06-29" - }, - { - "subject": "cascade delegates reactive enqueue to Planner — removes manifest-walking from middleware", - "date": "2026-06-29" - }, - { - "subject": "extract Produce::Publisher — single seam for publish-after-materialise", - "date": "2026-06-29" - }, - { - "subject": "fix: correct indentation in write_entry method for better readability", - "date": "2026-06-29" - }, - { - "subject": "refactor: group handlers into read/write/maintenance subdirs (S-1)", - "date": "2026-06-29" - }, - { - "subject": "refactor: rename Store::Envelope::Reader/Writer → Store::Entry::Reader/Writer (I/O for entries, not envelopes)", - "date": "2026-06-29" - }, - { - "subject": "refactor: move Handlers::Orchestration → Textus::Orchestration (not a handler, shared service)", - "date": "2026-06-29" - }, - { - "subject": "refactor: rename Store::Geometry → Store::Layout (filesystem layout, not spatial geometry)", - "date": "2026-06-29" - }, - { - "subject": "refactor: inline Dispatch.dispatch into Store, move unwrap to Value::Result.extract", - "date": "2026-06-29" - }, - { - "subject": "refactor: private_constant VERB_TO_CONTRACT/CONTRACT_TO_VERB, expose via contract_class_for", - "date": "2026-06-29" - }, - { - "subject": "refactor: rename IngestEntry :zone → :lane to complete ADR-0114 vocabulary migration", - "date": "2026-06-29" - }, - { - "subject": "refactor: rename Schema::Store → Schema::Registry (schema registry not a sub-store)", - "date": "2026-06-29" - }, - { - "subject": "refactor: rename Container.build_full → Container.build (no contrast remains)", - "date": "2026-06-29" - }, - { - "subject": "refactor: add Container#wire! to encapsulate post-construction boot mutation", - "date": "2026-06-29" - }, - { - "subject": "refactor: extract Pipeline I/O to direct Writer/Reader calls, reduce Pipeline to single dispatch method", - "date": "2026-06-29" - }, - { - "subject": "refactor: remove Store#as and Store#session, migrate all callers to with_role", - "date": "2026-06-29" - }, - { - "subject": "refactor: delete dead Dispatch::Middleware::Audit (unwired, inline Writer audit is the seam)", - "date": "2026-06-29" - }, - { - "subject": "refactor: delete dead handler registration path (build_pipeline + register_pipeline_registry_handlers)", - "date": "2026-06-29" - }, - { - "subject": "feat: add architecture and design invariants documentation; update job queue handling", - "date": "2026-06-29" - }, - { - "subject": "style(rubocop): align ADR-0121 refactor formatting", - "date": "2026-06-26" - }, - { - "subject": "refactor: api and interface hygiene consolidation (ADR-0121)", - "date": "2026-06-23" - }, - { - "subject": "fix(docs): regenerate adr-log.json to include new ADR 0117", - "date": "2026-06-23" - }, - { - "subject": "fix(docs): deterministic ADR log uid across platforms; rubocop balance", - "date": "2026-06-23" - }, - { - "subject": "workflow(adr_log): produce deterministic uid for adr-log to avoid CI drift", - "date": "2026-06-23" - }, - { - "subject": "Regenerate docs artifacts: ADR log updated by textus drain --as=automation", - "date": "2026-06-23" - }, - { - "subject": "ci(rubocop): ignore var/ temp clones from drain runs", - "date": "2026-06-23" - }, - { - "subject": "chore(docs): add generated reference/specs files", - "date": "2026-06-23" - }, - { - "subject": "ci(docs): run textus drain as automation in CI to stabilise generated metadata", - "date": "2026-06-23" - }, - { - "subject": "chore(docs): regenerate docs via textus drain", - "date": "2026-06-23" - }, - { - "subject": "docs: annotate HandlerFactoryRegistry to note it replaces Builder seam", - "date": "2026-06-23" - }, - { - "subject": "docs(adr): record adoption of HandlerFactoryRegistry + Adapter for pipeline composition (ADR 0117)", - "date": "2026-06-23" - }, - { - "subject": "refactor(container): switch pipeline composition to HandlerFactoryRegistry + Adapter (Design B)", - "date": "2026-06-23" - }, - { - "subject": "feat(container): make pipeline composition pluggable; support HandlerFactoryRegistry+Adapter via TEXTUS_PIPELINE_ADAPTER=1", - "date": "2026-06-23" - }, - { - "subject": "refactor(container): extract pipeline registry registrations to helper and simplify build_pipeline", - "date": "2026-06-23" - }, - { - "subject": "refactor(container): extract handler registration and writer factory helpers from build_full", - "date": "2026-06-23" - }, - { - "subject": "chore(ci): suppress method-size rubocop for build_full to satisfy CI", - "date": "2026-06-23" - }, - { - "subject": "chore: add Design B adapter + registry; fix style", - "date": "2026-06-23" - }, - { - "subject": "feat(design-b): add Pipeline Adapter and HandlerFactoryRegistry for alternative composition seam", - "date": "2026-06-23" - }, - { - "subject": "chore(deepening): incremental pipeline+reader/writer injection, format registry, file_store helpers", - "date": "2026-06-23" - }, - { - "subject": "docs: add engineering skills process contract", - "date": "2026-06-23" - }, - { - "subject": "refactor: remove dispatch session_default plumbing", - "date": "2026-06-23" - }, - { - "subject": "refactor: remove unused dispatch contract from_wire helpers", - "date": "2026-06-23" - }, - { - "subject": "refactor: deepen dispatch, orchestration, and geometry seams", - "date": "2026-06-23" - }, - { - "subject": "fix: update detect_skills to include descendant directories for SKILL.md", - "date": "2026-06-23" - }, - { - "subject": "style: suppress remaining lint/metrics for predicate interface and build_pipeline", - "date": "2026-06-23" - }, - { - "subject": "style: fix rubocop issues — line length, block alignment, suppress structural cops", - "date": "2026-06-23" - }, - { - "subject": "fix: update workflow Bus.dispatch → Dispatch.dispatch", - "date": "2026-06-23" - }, - { - "subject": "docs: update conventions.md, regenerate ref docs", - "date": "2026-06-23" - }, - { - "subject": "refactor: unify dispatch, rename Bus→Dispatch, re-home modules", - "date": "2026-06-23" - }, - { - "subject": "refactor(bus)!: Complete Gate-to-Bus migration with shared predicate classes", - "date": "2026-06-22" - }, - { - "subject": "refactor: replace action/contract layer with Bus pipeline architecture", - "date": "2026-06-22" - }, - { - "subject": "Architecture deepening phase 3 — write-path standardisation, container fix, format dedup", - "date": "2026-06-22" - }, - { - "subject": "textus 0.55.1 — CI fix: converge_now purges done jobs", - "date": "2026-06-22" - }, - { - "subject": "fix: purge done jobs before reseeding in converge_now to fix publish_tree prune test", - "date": "2026-06-22" - } - ] - }, - { - "tag": "v0.55.0", - "date": "2026-06-22", - "commits": [ - { - "subject": "textus 0.55.0 — Architecture Deepening Phase 2 (#234)", - "date": "2026-06-22" - }, - { - "subject": "Architecture-deepening: enforce 10 conventions structurally, kill dual paths (#233)", - "date": "2026-06-22" - }, - { - "subject": "feat: action composition + contract/impl split + container wiring (#232)", - "date": "2026-06-21" - }, - { - "subject": "feat: knowledge structure audit + sources as first-class envelope field (ADR-0118) (#231)", - "date": "2026-06-20" - }, - { - "subject": "refactor: split knowledge.readme into composed fragments + template prototype (#230)", - "date": "2026-06-20" - }, - { - "subject": "Architecture deepening — module restructuring, seam sealing, code smell cleanup (#229)", - "date": "2026-06-20" - }, - { - "subject": "feat: centralized SQLite index and job queue (#228)", - "date": "2026-06-19" - }, - { - "subject": "refactor: separate contract declaration from dispatch machinery (#227)", - "date": "2026-06-19" - } - ] - }, - { - "tag": "v0.54.2", - "date": "2026-06-19", - "commits": [ - { - "subject": "feat: ingest dedup with supersede chain, .run → .state rename (#226)", - "date": "2026-06-19" - }, - { - "subject": "feat: restructure artifacts lane into config/docs/system concerns (#225)", - "date": "2026-06-18" - }, - { - "subject": "feat: workflow rename, feeds pipeline, raw/ingest docs, SPEC v4, docs audit (#224)", - "date": "2026-06-18" - } - ] - }, - { - "tag": "v0.54.1", - "date": "2026-06-17", - "commits": [ - { - "subject": "feat: MCP SDK + dry ecosystem + ERB templates (v0.54.1) (#223)", - "date": "2026-06-17" - }, - { - "subject": "fix: use Time.now.utc in ingest spec to match action's key derivation", - "date": "2026-06-17" - }, - { - "subject": "fix: give artifacts.mcp explicit path to avoid collision with artifacts.mcp-config", - "date": "2026-06-17" - }, - { - "subject": "fix: sort MCP tool catalog alphabetically for cross-platform determinism", - "date": "2026-06-16" - }, - { - "subject": "chore: move templates into templates/artifacts/ to mirror workflow structure", - "date": "2026-06-16" - }, - { - "subject": "feat: promote events.md and mcp.md to produced artifacts", - "date": "2026-06-16" - }, - { - "subject": "feat: update index_key handling in boot process based on artifacts presence", - "date": "2026-06-16" - }, - { - "subject": "feat: add StaleReviewedStamp doctor check for knowledge doc currency", - "date": "2026-06-16" - }, - { - "subject": "chore: move SPEC.md into knowledge zone (knowledge.spec)", - "date": "2026-06-16" - }, - { - "subject": "chore: adopt contributor-conventions.md into knowledge zone", - "date": "2026-06-16" - }, - { - "subject": "fix: correct ADR filenames in CHANGELOG links (0114/0115/0116)", - "date": "2026-06-16" - }, - { - "subject": "chore: expand 0.54.0 CHANGELOG — covers all 3 PRs and standalone commits", - "date": "2026-06-16" - }, - { - "subject": "chore: release 0.54.0", - "date": "2026-06-16" - }, - { - "subject": "fix: move string-described specs to conformance per SpecLayout rule", - "date": "2026-06-16" - }, - { - "subject": "fix: advertise resources capability in MCP initialize handshake", - "date": "2026-06-16" - }, - { - "subject": "refactor: move ContractDrift from MCP surface to core Textus::ContractDrift", - "date": "2026-06-16" - }, - { - "subject": "refactor: apply Sandi Metz rules to MCP Server — extract methods and Routing module", - "date": "2026-06-16" - }, - { - "subject": "chore: remove remaining --lean references from plugin manifest spec", - "date": "2026-06-16" - }, - { - "subject": "fix: claude_plugin uses plain textus (external use); mcp_config keeps bundle exec (dev)", - "date": "2026-06-16" - }, - { - "subject": "chore: refresh artifact files from drain", - "date": "2026-06-16" - }, - { - "subject": "fix: adr_log workflow needs include_keyless: true for tree-publish knowledge.decisions", - "date": "2026-06-16" - }, - { - "subject": "style: rubocop auto-fixes — alignment, redundant initialize", - "date": "2026-06-16" - }, - { - "subject": "refactor: slim pulse — remove stale/doctor/next_due_at, add index_etag, delete Scanner", - "date": "2026-06-16" - }, - { - "subject": "feat: add MCP resources/list and resources/read — exposes machine-lane artifacts", - "date": "2026-06-16" - }, - { - "subject": "refactor: boot reads from artifacts, removes --lean flag and inline computation", - "date": "2026-06-16" - }, - { - "subject": "feat: add artifacts.index produced workflow — pre-computed full-store catalog", - "date": "2026-06-16" - }, - { - "subject": "refactor: standardize all workflows — :build step name, content wrapper, fix commands", - "date": "2026-06-16" - }, - { - "subject": "chore: organise workflows into artifacts/ and config/ subdirectories", - "date": "2026-06-16" - }, - { - "subject": "chore: migrate artifact data files from artifacts/derived/ to artifacts/", - "date": "2026-06-16" - }, - { - "subject": "refactor: flatten artifacts.derived.* to artifacts.* in manifest + workflow matches", - "date": "2026-06-16" - }, - { - "subject": "fix: rewrite adr_log + orientation workflows — use resolver.enumerate, fix List bug", - "date": "2026-06-16" - }, - { - "subject": "style: fix extra blank lines left by BURN deletion (rubocop)", - "date": "2026-06-16" - }, - { - "subject": "remove: delete dead derived_write? no-op guard from WriteVerb#cascade_to_rdeps", - "date": "2026-06-16" - }, - { - "subject": "remove: delete stale publish_each runtime guard from Entry::Publish (validator catches it at load)", - "date": "2026-06-16" - }, - { - "subject": "remove: drop duplicate inline check_action! from put/key_delete/reject (Gate covers these)", - "date": "2026-06-16" - }, - { - "subject": "remove: drop cross-lane notebook side-effect from Action::Ingest", - "date": "2026-06-16" - }, - { - "subject": "remove: delete dead BURN = :sync constant from all actions", - "date": "2026-06-16" - }, - { - "subject": "fix: move doctor check specs to spec/integration/doctor/check/ per layout rule", - "date": "2026-06-16" - }, - { - "subject": "feat: implement raw ingest pipeline — Action::Ingest, doctor checks, asset sentinel, ADR 0116", - "date": "2026-06-16" - }, - { - "subject": "add gate predicates: raw_lane_ingest_only + raw_write_once", - "date": "2026-06-16" - }, - { - "subject": "add Command::Ingest struct and gate verb routing stub", - "date": "2026-06-16" - }, - { - "subject": "expand zone-kind bijection: add raw→ingest (ADR 0114)", - "date": "2026-06-16" - }, - { - "subject": "remove Jobs::Refresh — workflow covers periodic re-derivation (ADR 0115)", - "date": "2026-06-16" - }, - { - "subject": "fix: resolve all rubocop offenses (autocorrect + nest workflow errors spec)", - "date": "2026-06-16" - }, - { - "subject": "docs: rewrite architecture README for workflow redesign — remove Step system, add Workflow layer", - "date": "2026-06-16" - }, - { - "subject": "refactor: remove data/ prefix and explicit path: from spec manifest fixtures", - "date": "2026-06-16" - }, - { - "subject": "refactor: rename docs-readme→docs-index, architecture-readme→architecture-index; drop last explicit path: fields", - "date": "2026-06-16" - }, - { - "subject": "refactor: strip data/ prefix from manifest paths — implicit via normalize_relative_path", - "date": "2026-06-16" - }, - { - "subject": "feat: make path: optional in manifest entries — derive from key + format + kind", - "date": "2026-06-16" - }, - { - "subject": "remove: source: field narrowed to external-only keys; fetch/derive schema fields removed", - "date": "2026-06-16" - }, - { - "subject": "remove: strip fetch/derive methods from Produced; freshness/pulse use retention rules; boot drops intake/derived keys; deps/rdeps simplified", - "date": "2026-06-16" - }, - { - "subject": "breaking: narrow Manifest::Policy::Source to from: external only — fetch/derive removed", - "date": "2026-06-16" - }, - { - "subject": "remove: delete dead step/events/projection code (step system fully removed)", - "date": "2026-06-16" - }, - { - "subject": "fix: triage remaining failures — boot/mcp/spec-layout cleanup post workflow redesign", - "date": "2026-06-16" - }, - { - "subject": "fix: update spec fixtures and container/store specs for workflows Container field", - "date": "2026-06-16" - }, - { - "subject": "remove: delete stale specs for removed features (observers, events, steps, intake_registration)", - "date": "2026-06-16" - }, - { - "subject": "fix: keyword args on Produce::Engine; delete Textus::Projection; init scaffolds workflows/ not steps/", - "date": "2026-06-16" - }, - { - "subject": "remove: strip final Step:: references from entry/base, role_scope, mcp/server", - "date": "2026-06-16" - }, - { - "subject": "remove: drop hook_errors from pulse output (step event bus removed)", - "date": "2026-06-16" - }, - { - "subject": "refactor: replace boot hooks output with workflows list; remove Step::Catalog refs", - "date": "2026-06-16" - }, - { - "subject": "remove: strip step system from doctor — delete IntakeRegistration check and run_registered_checks", - "date": "2026-06-16" - }, - { - "subject": "migrate: .textus/ steps -> workflows; strip source/handler/rules from manifest", - "date": "2026-06-16" - }, - { - "subject": "remove: delete dead hooks check and audit_subscriber (step system removed)", - "date": "2026-06-16" - }, - { - "subject": "remove: delete no-op Events validator (step system removed)", - "date": "2026-06-16" - }, - { - "subject": "feat: wire Workflow::Runner into Store; remove step/ and container.steps.publish", - "date": "2026-06-16" - }, - { - "subject": "feat: add Workflow system (errors, Context, Pattern, DSL, Registry, Loader, Runner)", - "date": "2026-06-16" - }, - { - "subject": "refactor: partially implement Task 5 (rename pipeline/ to produce/)", - "date": "2026-06-16" - }, - { - "subject": "remove: delete handler_permit (doctor-only field with no runtime enforcement)", - "date": "2026-06-16" - }, - { - "subject": "fix: remove accidentally committed produce/acquire/ copies with wrong constants", - "date": "2026-06-16" - }, - { - "subject": "refactor: rename pipeline/ to produce/ and update callsites", - "date": "2026-06-16" - }, - { - "subject": "refactor: rename background/ to jobs/ (Background::* → Jobs::*)", - "date": "2026-06-16" - }, - { - "subject": "refactor: rename background/ to jobs/ (Background::* -> Jobs::*)", - "date": "2026-06-16" - }, - { - "subject": "refactor: rename Ports::Queue to Ports::JobStore", - "date": "2026-06-16" - }, - { - "subject": "refactor: flatten envelope/io/ to envelope/ and rename Ports::Queue to Ports::JobStore", - "date": "2026-06-16" - }, - { - "subject": "refactor: flatten envelope/io/ to envelope/", - "date": "2026-06-16" - }, - { - "subject": "refactor: rename entry/ format strategies to format/", - "date": "2026-06-16" - } - ] + "groups": { + "Features": [ + { + "subject": "make pipeline composition pluggable; support HandlerFactoryRegistry+Adapter via TEXTUS_PIPELINE_ADAPTER=1", + "date": "2026-06-23" + }, + { + "subject": "add Pipeline Adapter and HandlerFactoryRegistry for alternative composition seam", + "date": "2026-06-23" + } + ], + "Bug Fixes": [ + { + "subject": "regenerate adr-log.json to include new ADR 0117", + "date": "2026-06-23" + }, + { + "subject": "deterministic ADR log uid across platforms; rubocop balance", + "date": "2026-06-23" + } + ] + } }, { "tag": "v0.53.0", "date": "2026-06-15", - "commits": [ - { - "subject": "fix: rubocop line length in merged audit_log_spec.rb", - "date": "2026-06-15" - }, - { - "subject": "chore: regenerate generated files via textus drain", - "date": "2026-06-15" - }, - { - "subject": "chore: bump to 0.53.0", - "date": "2026-06-15" - }, - { - "subject": "chore(dev): apply rubocop formatting", - "date": "2026-06-15" - }, - { - "subject": "chore(dev): add affaan-m/ecc as skill source", - "date": "2026-06-15" - }, - { - "subject": "refactor: replace hand-rolled mustache with mustache gem", - "date": "2026-06-15" - }, - { - "subject": "refactor: generate args method dynamically in Action::Base", - "date": "2026-06-15" - }, - { - "subject": "chore: add bin/dev for local skill sync from GitHub", - "date": "2026-06-15" - }, - { - "subject": "refactor: extract ErrorInfo parameter object for Error#initialize", - "date": "2026-06-15" - }, - { - "subject": "refactor: extract verify_integrity per-line checking into sub-methods", - "date": "2026-06-15" - }, - { - "subject": "refactor: break Init.run into focused class methods", - "date": "2026-06-15" - }, - { - "subject": "refactor: pull auth/writer/reader helpers into WriteVerb", - "date": "2026-06-15" - }, - { - "subject": "refactor: deduplicate Gate::Auth#check! and #check_action!", - "date": "2026-06-15" - }, - { - "subject": "refactor: extract Writer#put into composed methods", - "date": "2026-06-15" - }, - { - "subject": "fix(cli): restore role_filter in RoleScope verb dispatch", - "date": "2026-06-15" - }, - { - "subject": "refactor: rename Background::Planner::Planner → Planner::Plan (resolve double-name)", - "date": "2026-06-14" - }, - { - "subject": "refactor: remove redundant container: param from Gate#dispatch (always @container)", - "date": "2026-06-14" - }, - { - "subject": "chore: delete empty Textus::Dispatch module (dead since Dispatcher was removed)", - "date": "2026-06-14" - }, - { - "subject": "refactor: remove dead else branch and role_filter from RoleScope verb dispatch", - "date": "2026-06-14" - }, - { - "subject": "refactor: remove double auth in Accept; drop dead UnknownKey rescue from check_action!", - "date": "2026-06-14" - }, - { - "subject": "refactor: inline dispatch_bound into define_method; delete build_command+build_action from RoleScope", - "date": "2026-06-14" - }, - { - "subject": "fix: Background::Job registry O(1) hash lookup, delete stale domain/jobs spec, add pending_key guard test", - "date": "2026-06-14" - }, - { - "subject": "spec: remove obsolete target_is_canon tests (removed from accept FLOOR)", - "date": "2026-06-14" - }, - { - "subject": "docs: run textus drain — regenerate verbs ref, ADR log, hooks desc", - "date": "2026-06-14" - }, - { - "subject": "refactor: move Core::Jobs::Job to Ports::Queue::Job; delete orphaned Core::Jobs::Registry", - "date": "2026-06-14" - }, - { - "subject": "refactor: collapse Planner::Seeder+Scheduler into Planner.seed; unify manual.kick+schedule.tick into convergence trigger", - "date": "2026-06-14" - }, - { - "subject": "refactor: move job handlers to Background::Job:: with own registry; remove Action::Background::", - "date": "2026-06-14" - }, - { - "subject": "docs: ADR 0115 — document role trust model as asserted identities with ambient authority", - "date": "2026-06-14" - }, - { - "subject": "fix: detect seq gaps and regressions in AuditLog#verify_integrity", - "date": "2026-06-14" - }, - { - "subject": "fix: thread caller role through Doctor::Check dispatch instead of hardcoding human", - "date": "2026-06-14" - }, - { - "subject": "fix: add 1MB message size ceiling to MCP server handle_line", - "date": "2026-06-14" - }, - { - "subject": "fix: route dispatch_bound through Gate for verb-based commands", - "date": "2026-06-14" - }, - { - "subject": "fix: add pending_key to Command::Propose; enforce propose+accept auth at Gate via FLOOR", - "date": "2026-06-14" - }, - { - "subject": "fix: remove AUTOMATION bypass from Gate::Auth#check! — capability check handles it", - "date": "2026-06-14" - }, - { - "subject": "refactor: replace command_to_action regex with VERB_COMMAND.invert constant", - "date": "2026-06-14" - }, - { - "subject": "fix: propose_spec path depth (../../../../ → ../../../) — CI at 0 failures", - "date": "2026-06-14" - }, - { - "subject": "fix: propose_spec path depth and etag_spec glob — 3 CI failures down to 0", - "date": "2026-06-14" - }, - { - "subject": "docs: refresh architecture README for Surface → Command → Gate → Action; update how-to/contributor refs", - "date": "2026-06-14" - }, - { - "subject": "fix: restore empty-holders edge case in Gate::Auth; fix accept GuardFailed for UnknownKey", - "date": "2026-06-14" - }, - { - "subject": "refactor: clean Action::Background — remove dead BURN/Contract::DSL, simplify Action.fetch", - "date": "2026-06-14" - }, - { - "subject": "fix: remove Gate-level audit recording — actions handle their own; fix Audit role filter mapping", - "date": "2026-06-14" - }, - { - "subject": "refactor: install! and verbs.rb use Gate::ROUTES; replace store.as hook scope", - "date": "2026-06-14" - }, - { - "subject": "refactor: rename Dispatch::Planner/Pipeline/Runtime — eliminate Dispatch:: namespace", - "date": "2026-06-14" - }, - { - "subject": "fix: align Command structs with action kwargs, fix MCP dispatch", - "date": "2026-06-14" - }, - { - "subject": "chore: skip audit for read commands in Gate, final cleanup", - "date": "2026-06-14" - }, - { - "subject": "refactor: delete old dispatch layer (Dispatcher, Gate, Auth, Ledger, Executor, Event)", - "date": "2026-06-14" - }, - { - "subject": "feat: add Surfaces::Watcher — consolidates watch + schedule triggers", - "date": "2026-06-14" - }, - { - "subject": "feat: wire MCP surface to Gate", - "date": "2026-06-14" - }, - { - "subject": "feat: wire CLI surface to Gate — delete RoleScope", - "date": "2026-06-14" - }, - { - "subject": "feat: wire Gate into Container and Store", - "date": "2026-06-14" - }, - { - "subject": "refactor: rename Dispatch::Actions → Action namespace", - "date": "2026-06-14" - }, - { - "subject": "feat: add Textus::Gate with ROUTES map and Gate::Auth", - "date": "2026-06-14" - }, - { - "subject": "feat: add Gate::Auth — command-based auth wrapper", - "date": "2026-06-14" - }, - { - "subject": "feat: add Command namespace and Textus::Events constants", - "date": "2026-06-14" - }, - { - "subject": "test: delete specs for dispatch layer being replaced", - "date": "2026-06-14" - }, - { - "subject": "test: move string-described specs to conformance/ per spec_layout convention", - "date": "2026-06-14" - }, - { - "subject": "test(config): volatile tagging for dispatch/ and surfaces/ unit specs; move manifest fixture to conformance", - "date": "2026-06-14" - }, - { - "subject": "refactor(spec): update terminology and structure for lanes and sources", - "date": "2026-06-14" - }, - { - "subject": "test(manifest): live fixture contract for project .textus manifest", - "date": "2026-06-14" - }, - { - "subject": "test(core): integration spec for Retention::Sweep GC reporting", - "date": "2026-06-14" - }, - { - "subject": "test(core): integration spec for Freshness::Evaluator currency verdicts", - "date": "2026-06-14" - }, - { - "subject": "test(core): unit spec for Core::Sentinel orphan/drift predicates", - "date": "2026-06-14" - }, - { - "subject": "test(core): unit spec for Freshness::Verdict wire serialization", - "date": "2026-06-14" - }, - { - "subject": "test(support): add core domain doubles shared context", - "date": "2026-06-14" - }, - { - "subject": "docs: update README zone→lane vocabulary and hooks→steps extension model", - "date": "2026-06-14" - }, - { - "subject": "docs: update README source vocabulary (from: handler/project/command → fetch/derive/external)", - "date": "2026-06-14" - }, - { - "subject": "fix: restore event lifecycle, drain seeding, and produce_failed after consolidation", - "date": "2026-06-14" - }, - { - "subject": "chore: remove freshness.rb and pipeline/events.rb (dissolved in refactor)", - "date": "2026-06-14" - }, - { - "subject": "refactor: remove ValidateAll verb from Dispatcher::VERBS; role_authority spec calls Doctor::Validator directly", - "date": "2026-06-14" - }, - { - "subject": "docs: add stable/volatile spec convention; delete completed superpowers docs", - "date": "2026-06-14" - }, - { - "subject": "test: add specs for new actions and planner; update fixture callers", - "date": "2026-06-14" - }, - { - "subject": "test: delete stale unit specs for dissolved layers", - "date": "2026-06-14" - }, - { - "subject": "feat: replace ACTIONS_BY_TRIGGER with rules-driven planner", - "date": "2026-06-14" - }, - { - "subject": "feat: route async job execution through Dispatch::Gate; delete Handlers registry", - "date": "2026-06-14" - }, - { - "subject": "refactor: migrate intake steps.publish calls to Gate events", - "date": "2026-06-14" - }, - { - "subject": "refactor: delete Domain::Action stub — no callers", - "date": "2026-06-14" - }, - { - "subject": "refactor: delete Domain::Action stub — no callers", - "date": "2026-06-14" - }, - { - "subject": "refactor: delete capabilities verb — Dispatcher::VERBS is the static contract", - "date": "2026-06-14" - }, - { - "subject": "refactor: move produce/ to dispatch/pipeline/", - "date": "2026-06-14" - }, - { - "subject": "refactor: move maintenance/ to dispatch/runtime/", - "date": "2026-06-14" - }, - { - "subject": "refactor: move jobs/ to dispatch/planner/", - "date": "2026-06-14" - }, - { - "subject": "test(dispatch): migrate verb specs to actions layout", - "date": "2026-06-14" - }, - { - "subject": "refactor(dispatch): migrate runtime verb paths to actions", - "date": "2026-06-14" - }, - { - "subject": "refactor!: complete foundation refactor and dispatch gate rollout", - "date": "2026-06-13" - }, - { - "subject": "feat(core)!: cut over zones to data and adopt watch runtime", - "date": "2026-06-13" - }, - { - "subject": "refactor(step): remove hook-era surfaces and restore full-suite green", - "date": "2026-06-13" - }, - { - "subject": "refactor(step): wire Step::Registry into Container + Store, drop events/rpc fields", - "date": "2026-06-10" - }, - { - "subject": "feat(step): port the five built-in fetch handlers to Step::Fetch", - "date": "2026-06-10" - }, - { - "subject": "feat(step): add convention-discovery Step::Loader", - "date": "2026-06-10" - }, - { - "subject": "feat(step): add Step::Registry unifying observe bus + invocable table", - "date": "2026-06-10" - }, - { - "subject": "feat(step): add Discovery for path -> (kind, name)", - "date": "2026-06-10" - }, - { - "subject": "feat(step): add Step base classes for the five step kinds", - "date": "2026-06-10" - }, - { - "subject": "Configure opencode.json and enhance agent orientation", - "date": "2026-06-10" - }, - { - "subject": "feat: integrate opencode configuration and orientation", - "date": "2026-06-10" - }, - { - "subject": "docs: publish ADR 0113 + regenerate adr-log (drain)", - "date": "2026-06-09" - }, - { - "subject": "feat: proposal block speaks _meta, not frontmatter (ADR 0113)", - "date": "2026-06-09" - }, - { - "subject": "chore: release textus 0.52.0 (ADR 0112 — produced authority reference)", - "date": "2026-06-09" - }, - { - "subject": "docs: ADR 0112 + dedupe authority tables into generated authority.md", - "date": "2026-06-09" - }, - { - "subject": "feat: project the authority model as docs/reference/authority.md (ADR 0112)", - "date": "2026-06-09" - }, - { - "subject": "feat: add authority handler projecting the lane/role/zone model (ADR 0112)", - "date": "2026-06-09" - }, - { - "subject": "refactor: finish converge rename in SPEC.md + example manifest (ADR 0111)", - "date": "2026-06-08" - }, - { - "subject": "refactor: rename reconcile capability to converge; sweep residual debt (ADR 0111)", - "date": "2026-06-08" - }, - { - "subject": "test(queue): make #198 conformance green for added drain/jobs/serve surface", - "date": "2026-06-08" - }, - { - "subject": "docs(queue): republish root files (README/CLAUDE/AGENTS/CONTRIBUTING/SECURITY) for drain rename", - "date": "2026-06-08" - }, - { - "subject": "docs(queue): complete reconcile→drain rename in canon docs + templates", - "date": "2026-06-08" - }, - { - "subject": "docs(queue): regenerate verbs.md for drain/jobs (docs-freshness gate)", - "date": "2026-06-08" - }, - { - "subject": "feat(queue): add enqueue verb — general runner bounded by the allow-list", - "date": "2026-06-08" - }, - { - "subject": "docs(queue): ADR 0110 — job queue model, drain/serve rename, async-only", - "date": "2026-06-08" - }, - { - "subject": "feat(queue)!: hard-rename reconcile verb to drain/serve; regenerate docs", - "date": "2026-06-08" - }, - { - "subject": "feat(queue): scheduler seeds TTL re-pull/sweep into serve tick", - "date": "2026-06-08" - }, - { - "subject": "fix(queue): re-materialize dependents on delete/rename (ADR 0087 gap)", - "date": "2026-06-08" - }, - { - "subject": "feat(queue): drop source.on_write knob (materialize is async-only)", - "date": "2026-06-08" - }, - { - "subject": "refactor(queue): retire in-process AsyncRunner (queue is the only deferral)", - "date": "2026-06-08" - }, - { - "subject": "feat(queue): write subscriber enqueues materialize (async-only)", - "date": "2026-06-08" - }, - { - "subject": "test(queue): satisfy spec-layout guard (constant-described; worker_config is integration)", - "date": "2026-06-08" - }, - { - "subject": "test(queue): drain/reconcile convergence parity (effect, not result shape)", - "date": "2026-06-08" - }, - { - "subject": "feat(queue): add serve daemon (tick = reclaim + serial drain)", - "date": "2026-06-08" - }, - { - "subject": "feat(queue): add jobs verb (list/retry/purge)", - "date": "2026-06-08" - }, - { - "subject": "feat(queue): add drain verb (seed + serial drain + health)", - "date": "2026-06-08" - }, - { - "subject": "feat(queue): add Jobs::Seeder (mirrors Reconcile produce_scope)", - "date": "2026-06-08" - }, - { - "subject": "feat(queue): register materialize/re-pull/sweep convergence handlers", - "date": "2026-06-08" - }, - { - "subject": "refactor(reconcile): extract Retention::Apply for reuse by sweep handler", - "date": "2026-06-08" - }, - { - "subject": "feat(queue): add manifest worker: config (pool/poll/lease_ttl/max_attempts)", - "date": "2026-06-08" - }, - { - "subject": "feat(queue): add Worker.drain_pool (N-thread, exactly-once)", - "date": "2026-06-08" - }, - { - "subject": "feat(queue): add Maintenance::Worker single-pass drain", - "date": "2026-06-08" - }, - { - "subject": "feat(queue): add Domain::Jobs::Registry closed allow-list", - "date": "2026-06-08" - }, - { - "subject": "feat(queue): add lease reclaim + cover concurrent single-claim", - "date": "2026-06-08" - }, - { - "subject": "feat(queue): add lease (atomic claim), ack, fail with dead-letter", - "date": "2026-06-08" - }, - { - "subject": "feat(queue): add Ports::Queue enqueue with dedup + ready_ids", - "date": "2026-06-08" - }, - { - "subject": "feat(queue): add Domain::Jobs::Job with stable dedup id", - "date": "2026-06-08" - }, - { - "subject": "feat(queue): add Layout.queue paths under .run/queue", - "date": "2026-06-08" - } - ] - }, - { - "tag": "v0.51.0", - "date": "2026-06-08", - "commits": [ - { - "subject": "textus 0.51.0 — the reconcile era + SPEC sync (#196)", - "date": "2026-06-08" - }, - { - "subject": "ADR 0109 — board-exact schema split + single port shape (supersede 0107/0108) (#195)", - "date": "2026-06-08" - }, - { - "subject": "ADR 0108 — name + document the two port shapes (don't convert) (#194)", - "date": "2026-06-08" - }, - { - "subject": "ADR 0107 — split manifest/schema.rb: data vs validation walk (#193)", - "date": "2026-06-08" - }, - { - "subject": "ADR 0106 — make the hexagonal layering executable (#192)", - "date": "2026-06-08" - }, - { - "subject": "ADR 0105 — verb routing ⟺ contract bijection (#191)", - "date": "2026-06-08" - }, - { - "subject": "docs: ADR 0104 — finish the root-doc canon move (CONTRIBUTING + SECURITY) (#190)", - "date": "2026-06-08" - }, - { - "subject": "ADR 0100/0101 — produce/ topology + interface fossils (#189)", - "date": "2026-06-08" - }, - { - "subject": "ADR 0099 — one Freshness evaluator (collapse the staleness family) (#188)", - "date": "2026-06-07" - }, - { - "subject": "ADR 0098 — docs SSoT/DRY/SOLID cleanup (produce vs guard) (#187)", - "date": "2026-06-07" - }, - { - "subject": "ADR 0097 — reference docs become produced (verbs, schema, ADR log) (#186)", - "date": "2026-06-07" - }, - { - "subject": "test(conformance): consolidate the conformance spec tier (67→19 loose, zero coverage loss) (#185)", - "date": "2026-06-07" - }, - { - "subject": "ADR 0095: collapse derived/intake into one produced kind (#184)", - "date": "2026-06-07" - }, - { - "subject": "ADR 0094: source produces data, publish renders (#183)", - "date": "2026-06-07" - }, - { - "subject": "ADR 0093 — source + retention over one reconcile engine (incl. ADR 0092) (#182)", - "date": "2026-06-07" - }, - { - "subject": "ADR 0092: spec-suite churn-resistance — fixture vocabulary, retired-token guard, conformance split (#181)", - "date": "2026-06-06" - }, - { - "subject": "ADR 0091 — one `machine` zone-kind; entry-kind is the single discriminator (#180)", - "date": "2026-06-06" - }, - { - "subject": "Automation is one capability + one `upkeep` policy (ADR 0090) (#179)", - "date": "2026-06-06" - }, - { - "subject": "Reconcile-era design cleanup (WS3/WS2/WS4/WS5 — ADR 0088, 0089) (#178)", - "date": "2026-06-05" - }, - { - "subject": "fix: scrub deleted fetch/fetch_all/build verbs from agent-facing strings (WS1) (#177)", - "date": "2026-06-05" - }, - { - "subject": "Fold build into reconcile (ADR 0087) (#176)", - "date": "2026-06-05" - } - ] - }, - { - "tag": "v0.50.0", - "date": "2026-06-05", - "commits": [ - { - "subject": "textus 0.50.0 — observability verbs + boot lifecycle + textus-managed agent config (#175)", - "date": "2026-06-05" - } - ] - }, - { - "tag": "v0.49.0", - "date": "2026-06-04", - "commits": [ - { - "subject": "textus 0.49.0 — normalize the key-verb family + remove migrate (ADR 0082) (#174)", - "date": "2026-06-04" - }, - { - "subject": "textus 0.48.0 — docs become canon (ADR 0081) (#173)", - "date": "2026-06-04" - }, - { - "subject": "test(suite): split specs into unit/integration/conformance + SOLID foundation (ADR 0080) (#172)", - "date": "2026-06-04" - }, - { - "subject": "feat: unify staleness + retention into one lifecycle policy (ADR 0079) (#171)", - "date": "2026-06-04" - } - ] + "groups": { + "Features": [ + { + "subject": "feat(core)!: cut over zones to data and adopt watch runtime", + "date": "2026-06-13" + }, + { + "subject": "port the five built-in fetch handlers to Step::Fetch", + "date": "2026-06-10" + }, + { + "subject": "add convention-discovery Step::Loader", + "date": "2026-06-10" + }, + { + "subject": "add Step::Registry unifying observe bus + invocable table", + "date": "2026-06-10" + }, + { + "subject": "add Discovery for path -> (kind, name)", + "date": "2026-06-10" + }, + { + "subject": "add Step base classes for the five step kinds", + "date": "2026-06-10" + }, + { + "subject": "add enqueue verb — general runner bounded by the allow-list", + "date": "2026-06-08" + }, + { + "subject": "feat(queue)!: hard-rename reconcile verb to drain/serve; regenerate docs", + "date": "2026-06-08" + }, + { + "subject": "scheduler seeds TTL re-pull/sweep into serve tick", + "date": "2026-06-08" + }, + { + "subject": "drop source.on_write knob (materialize is async-only)", + "date": "2026-06-08" + }, + { + "subject": "write subscriber enqueues materialize (async-only)", + "date": "2026-06-08" + }, + { + "subject": "add serve daemon (tick = reclaim + serial drain)", + "date": "2026-06-08" + }, + { + "subject": "add jobs verb (list/retry/purge)", + "date": "2026-06-08" + }, + { + "subject": "add drain verb (seed + serial drain + health)", + "date": "2026-06-08" + }, + { + "subject": "add Jobs::Seeder (mirrors Reconcile produce_scope)", + "date": "2026-06-08" + }, + { + "subject": "register materialize/re-pull/sweep convergence handlers", + "date": "2026-06-08" + }, + { + "subject": "add manifest worker: config (pool/poll/lease_ttl/max_attempts)", + "date": "2026-06-08" + }, + { + "subject": "add Worker.drain_pool (N-thread, exactly-once)", + "date": "2026-06-08" + }, + { + "subject": "add Maintenance::Worker single-pass drain", + "date": "2026-06-08" + }, + { + "subject": "add Domain::Jobs::Registry closed allow-list", + "date": "2026-06-08" + }, + { + "subject": "add lease reclaim + cover concurrent single-claim", + "date": "2026-06-08" + }, + { + "subject": "add lease (atomic claim), ack, fail with dead-letter", + "date": "2026-06-08" + }, + { + "subject": "add Ports::Queue enqueue with dedup + ready_ids", + "date": "2026-06-08" + }, + { + "subject": "add Domain::Jobs::Job with stable dedup id", + "date": "2026-06-08" + }, + { + "subject": "add Layout.queue paths under .run/queue", + "date": "2026-06-08" + } + ], + "Bug Fixes": [ + { + "subject": "restore role_filter in RoleScope verb dispatch", + "date": "2026-06-15" + }, + { + "subject": "re-materialize dependents on delete/rename (ADR 0087 gap)", + "date": "2026-06-08" + } + ] + } }, { "tag": "v0.47.1", "date": "2026-06-04", - "commits": [ - { - "subject": "Release textus 0.47.1 — External entries are a non-build path (#170)", - "date": "2026-06-04" - }, - { - "subject": "fix(build): make External a non-build path; parse compute.command (#169)", - "date": "2026-06-04" - } - ] - }, - { - "tag": "v0.47.0", - "date": "2026-06-04", - "commits": [ - { - "subject": "Release textus 0.47.0 — close the agent loop over MCP (#168)", - "date": "2026-06-04" - }, - { - "subject": "test(spec-layout): guard the spec/ ↔ lib/textus/ mirror (#167)", - "date": "2026-06-04" - }, - { - "subject": "init --with-agent profile + surface build to MCP (ADRs 0076/0077) (#166)", - "date": "2026-06-03" - }, - { - "subject": "Contract-etag drift guard + session_opened connect event (ADR 0074, 0075) (#165)", - "date": "2026-06-03" - }, - { - "subject": "test: consolidate two duplicate spec examples (zero coverage loss) (#164)", - "date": "2026-06-03" - } - ] - }, - { - "tag": "v0.46.0", - "date": "2026-06-03", - "commits": [ - { - "subject": "ADR 0073: surfaces declares external projections; Ruby is the implicit base (#163)", - "date": "2026-06-03" - }, - { - "subject": "Integration feedback from patrick-nexus: F1–F7 (#161) (#162)", - "date": "2026-06-03" - }, - { - "subject": "docs: README — correct the .textus/ tree (audit log lives under .run/) (#160)", - "date": "2026-06-03" - }, - { - "subject": "docs: README — surface store discovery + TEXTUS_ROOT (#159)", - "date": "2026-06-03" - }, - { - "subject": "docs: README readability — hook tables + newcomer-friendlier opening (#158)", - "date": "2026-06-03" - }, - { - "subject": "docs: freshness audit — verb drift, event-catalog dedup, output phrasing (#157)", - "date": "2026-06-03" - } - ] - }, - { - "tag": "v0.45.1", - "date": "2026-06-03", - "commits": [ - { - "subject": "0.45.1 — streamline: kill the last dual-paths (ADR 0069) (#156)", - "date": "2026-06-03" - }, - { - "subject": "0.45.0 — the contract owns the request lifecycle (ADRs 0066–0068) (#155)", - "date": "2026-06-03" - }, - { - "subject": "textus 0.44.1 — finish the CLI contract projection (ADR 0065) (#154)", - "date": "2026-06-03" - }, - { - "subject": "0.44.0 — one verb name across surfaces; CLI is a contract projection (ADRs 0058–0063) (#153)", - "date": "2026-06-03" - } - ] - }, - { - "tag": "v0.43.2", - "date": "2026-06-02", - "commits": [ - { - "subject": "textus 0.43.2 — agent-legible MCP contracts (ADR 0057) (#152)", - "date": "2026-06-02" - } - ] + "groups": { + "Bug Fixes": [ + { + "subject": "make External a non-build path; parse compute.command (#169)", + "date": "2026-06-04" + } + ] + } }, { "tag": "v0.43.1", "date": "2026-06-02", - "commits": [ - { - "subject": "textus 0.43.1 — boot agent surface derives from the MCP catalog (ADR 0056) (#151)", - "date": "2026-06-02" - }, - { - "subject": "feat(boot): derive read_verbs from the MCP catalog; recipes reference verbs (ADR 0056) + draft entry-level desc (ADR 0054) (#150)", - "date": "2026-06-02" - }, - { - "subject": "docs: fix load-invalid owner archetypes + refresh reviewed stamps (#149)", - "date": "2026-06-02" - } - ] + "groups": { + "Features": [ + { + "subject": "derive read_verbs from the MCP catalog; recipes reference verbs (ADR 0056) + draft entry-level desc (ADR 0054) (#150)", + "date": "2026-06-02" + } + ] + } }, { "tag": "v0.43.0", "date": "2026-06-02", - "commits": [ - { - "subject": "feat(publish)!: typed publish: block + remove index_filename (ADR 0052, 0053) (#148)", - "date": "2026-06-02" - } - ] + "groups": { + "Features": [ + { + "subject": "feat(publish)!: typed publish: block + remove index_filename (ADR 0052, 0053) (#148)", + "date": "2026-06-02" + } + ] + } }, { "tag": "v0.42.0", "date": "2026-06-02", - "commits": [ - { - "subject": "feat(publish)!: remove publish_each — collapse to two modes (ADR 0051) (#147)", - "date": "2026-06-02" - } - ] + "groups": { + "Features": [ + { + "subject": "feat(publish)!: remove publish_each — collapse to two modes (ADR 0051) (#147)", + "date": "2026-06-02" + } + ] + } }, { "tag": "v0.41.0", "date": "2026-06-02", - "commits": [ - { - "subject": "textus 0.41.0 — publish_tree subtree mirror + content-identical publish adoption (#146)", - "date": "2026-06-02" - }, - { - "subject": "feat(publish): adopt a byte-identical target instead of refusing (ADR 0050) (#144)", - "date": "2026-06-02" - }, - { - "subject": "refactor(publish): resolve publish mode into a sum type; one shared subtree mirror (ADR 0049) (#143)", - "date": "2026-06-02" - }, - { - "subject": "ADR 0048 — fetch subsystem: one home per concern (#142)", - "date": "2026-06-02" - }, - { - "subject": "fix(gemspec): package the docs that moved — gem shipped no architecture/conventions doc (#141)", - "date": "2026-06-02" - }, - { - "subject": "publish_tree: a key-less subtree mirror for a derived-index leaf (ADR 0047) (#140)", - "date": "2026-06-02" - }, - { - "subject": "test: modernize spec suite onto current lane vocabulary (#139)", - "date": "2026-06-02" - } - ] - }, - { - "tag": "v0.40.0", - "date": "2026-06-02", - "commits": [ - { - "subject": "textus 0.40.0 — publish_each owns multi-file leaf subtrees (ADR 0046) (#138)", - "date": "2026-06-02" - }, - { - "subject": "publish_each copies a leaf's whole subtree when index_filename is set (ADR 0046) (#136)", - "date": "2026-06-02" - }, - { - "subject": "Owner-subject validation at load (#135 items 1+2) (#137)", - "date": "2026-06-02" - }, - { - "subject": "Close the role-name set to {human, agent, automation} (ADR 0045) (#134)", - "date": "2026-06-01" - }, - { - "subject": "Resolve system actors by capability (ADR 0044) + close role-name set (ADR 0045, proposed) (#133)", - "date": "2026-06-01" - }, - { - "subject": "refactor(hooks): Catalog as single source of truth for event tables (#131)", - "date": "2026-06-01" - } - ] + "groups": { + "Features": [ + { + "subject": "adopt a byte-identical target instead of refusing (ADR 0050) (#144)", + "date": "2026-06-02" + } + ], + "Bug Fixes": [ + { + "subject": "package the docs that moved — gem shipped no architecture/conventions doc (#141)", + "date": "2026-06-02" + } + ] + } }, { "tag": "v0.39.1", "date": "2026-06-01", - "commits": [ - { - "subject": "textus 0.39.1 — feed ergonomics: feeds.machine env snapshot + intake cookbook (ADR 0043) (#130)", - "date": "2026-06-01" - }, - { - "subject": "feat(init): scaffold feeds.machines.* (nested) with a local env snapshot (ADR 0043) (#128)", - "date": "2026-06-01" - }, - { - "subject": "docs(how-to): fix zone-rename drift in agents-mcp + :publish event name (#129)", - "date": "2026-06-01" - }, - { - "subject": "cookbook: multi-machine environment-scan recipe (nested feeds.machines.*) (#127)", - "date": "2026-06-01" - }, - { - "subject": "examples: use feeds.machine env snapshot, matching `textus init` (#126)", - "date": "2026-06-01" - }, - { - "subject": "ADR 0043 — feed ergonomics without breaking core purity (#125)", - "date": "2026-06-01" - }, - { - "subject": "chore: remove legacy ARCHITECTURE.md redirect stub (#124)", - "date": "2026-06-01" - }, - { - "subject": "docs(readme): redesign flow diagram — group writers, colour-code roles (#123)", - "date": "2026-06-01" - } - ] - }, - { - "tag": "v0.39.0", - "date": "2026-06-01", - "commits": [ - { - "subject": "textus 0.39.0 — native ignore patterns for entry enumeration (ADR 0042) (#122)", - "date": "2026-06-01" - }, - { - "subject": "docs(readme): add trust×durability quadrant; sharpen the flow diagram (#121)", - "date": "2026-06-01" - }, - { - "subject": "feat: native ignore patterns for entry enumeration + doctor (ADR 0042) (#120)", - "date": "2026-06-01" - }, - { - "subject": "feat: dogfood textus in its own repo — self-dev store + MCP wiring (ADR 0041) (#118)", - "date": "2026-06-01" - }, - { - "subject": "docs: fold docs/ into Diátaxis directories (#117)", - "date": "2026-05-31" - } - ] + "groups": { + "Features": [ + { + "subject": "scaffold feeds.machines.* (nested) with a local env snapshot (ADR 0043) (#128)", + "date": "2026-06-01" + } + ] + } }, { "tag": "v0.38.0", "date": "2026-05-31", - "commits": [ - { - "subject": "textus 0.38.0 — MCP serve acts as `agent` by default (ADR 0040) (#116)", - "date": "2026-05-31" - }, - { - "subject": "textus 0.37.0 — MCP catalog derive-or-guard (ADR 0039) (#115)", - "date": "2026-05-31" - }, - { - "subject": "feat: runtime artifacts under .run/, Textus::Layout owns the on-disk map (ADR 0038) (#113)", - "date": "2026-05-31" - }, - { - "subject": "ADR 0037: boot/pulse derive-or-guard — anti-drift contract specs (#114)", - "date": "2026-05-31" - }, - { - "subject": "docs(examples): consolidate to a single reference example (project) (#112)", - "date": "2026-05-31" - }, - { - "subject": "textus 0.36.0 — transports as pure framings: one verb vocabulary + lifted session (ADR 0036) (#111)", - "date": "2026-05-31" - }, - { - "subject": "docs: fix confirmed drift against v0.35.1 (capability canon, §9 verbs, write/ listing, 0.35 notes) (#110)", - "date": "2026-05-31" - }, - { - "subject": "fix(gemspec): align metadata with current project (textus/3 + coordination-space description) (#109)", - "date": "2026-05-31" - }, - { - "subject": "textus 0.35.2 — Evaluation field rename + Container doc fix (internal) (#108)", - "date": "2026-05-31" - } - ] - }, - { - "tag": "v0.35.1", - "date": "2026-05-31", - "commits": [ - { - "subject": "textus 0.35.1 — RSpec foundation consolidation (test-only) (#107)", - "date": "2026-05-31" - }, - { - "subject": "textus 0.35.0 — proposal target-canon constraint + author_held (ADR 0035) (#106)", - "date": "2026-05-31" - }, - { - "subject": "textus 0.34.0 — unify the Lane vocabulary + finish boot's kind-derived zone naming (ADR 0034) (#105)", - "date": "2026-05-31" - }, - { - "subject": "test(spec): build shared RSpec foundation and migrate the suite onto it (#104)", - "date": "2026-05-31" - }, - { - "subject": "textus 0.33.0 — complete primitive set (workspace + keep) + vocabulary (ADR 0031→0033) (#103)", - "date": "2026-05-31" - }, - { - "subject": "textus 0.32.1 — unified-Guard spec cleanup (#102)", - "date": "2026-05-30" - }, - { - "subject": "textus 0.32.0 — unified Guard engine (ADR 0031) + drop read_policy (ADR 0032) (#101)", - "date": "2026-05-30" - }, - { - "subject": "textus 0.31.0 — capability-based roles (ADR 0030) (#100)", - "date": "2026-05-30" - }, - { - "subject": "docs: refresh content to current v0.30 behavior (#99)", - "date": "2026-05-30" - }, - { - "subject": "Add brand identity: \"Lanes\" logo, asset set, and DESIGN.md (#98)", - "date": "2026-05-30" - }, - { - "subject": "docs: restructure for clarity + maintainability (#97)", - "date": "2026-05-30" - } - ] - }, - { - "tag": "v0.30.0", - "date": "2026-05-29", - "commits": [ - { - "subject": "textus 0.30.0 — mandatory zone kind (strict) + retention (ADR 0028 moves 1 & 4) (#96)", - "date": "2026-05-29" - }, - { - "subject": "textus 0.29.2 — converge hook registries; de-leak MCP transport (ADR 0027) (#95)", - "date": "2026-05-29" - }, - { - "subject": "textus 0.29.1 — use-case construction seams (ADR 0026) (#94)", - "date": "2026-05-29" - } - ] - }, - { - "tag": "v0.29.0", - "date": "2026-05-29", - "commits": [ - { - "subject": "textus 0.29.0 — Honest Domain (domain purity via ports) (#93)", - "date": "2026-05-29" - }, - { - "subject": "textus 0.28.0 — consistency sweep & legacy cleanup (#92)", - "date": "2026-05-29" - }, - { - "subject": "textus 0.27.0 — architecture redesign (Container + Call + Dispatcher) (#91)", - "date": "2026-05-29" - }, - { - "subject": "docs: cleanup — version drift, SPEC↔ARCH alignment, OSS scaffolding (#90)", - "date": "2026-05-29" - } - ] - }, - { - "tag": "v0.26.0", - "date": "2026-05-28", - "commits": [ - { - "subject": "textus 0.26.0 — architecture consolidation (#89)", - "date": "2026-05-28" - }, - { - "subject": "textus 0.25.1 — application layering (Ports + EnvelopeIO split + Manifest carving) (#88)", - "date": "2026-05-28" - }, - { - "subject": "textus 0.25.0 — pulse hardening (#87)", - "date": "2026-05-28" - }, - { - "subject": "textus 0.24.0 — context-structure ergonomics (#86)", - "date": "2026-05-28" - }, - { - "subject": "textus 0.23.0 — agent gate (MCP) + docs truth-up (#85)", - "date": "2026-05-28" - } - ] + "groups": { + "Bug Fixes": [ + { + "subject": "align metadata with current project (textus/3 + coordination-space description) (#109)", + "date": "2026-05-31" + } + ] + } }, { "tag": "v0.22.0", "date": "2026-05-28", - "commits": [ - { - "subject": "textus 0.22.0 — entry polymorphism pass (#84)", - "date": "2026-05-28" - }, - { - "subject": "textus 0.21.1 — intake entries as builder outputs (#83)", - "date": "2026-05-28" - }, - { - "subject": "feat!: 0.21.0 — agent memory integration (boot + pulse) (#81)", - "date": "2026-05-27" - } - ] - }, - { - "tag": "v0.20.2", - "date": "2026-05-27", - "commits": [ - { - "subject": "0.20.2 — finish role-kinds migration (#79)", - "date": "2026-05-27" - }, - { - "subject": "0.20.1 — user-defined role kinds (#72) (#78)", - "date": "2026-05-27" - }, - { - "subject": "chore: prune dead code and tighten manifest seams (#77)", - "date": "2026-05-27" - } - ] - }, - { - "tag": "v0.20.0", - "date": "2026-05-27", - "commits": [ - { - "subject": "chore(release): textus 0.20.0 — architecture redesign (#76)", - "date": "2026-05-27" - }, - { - "subject": "refactor(0.20.0): merge Build into Publish (#75)", - "date": "2026-05-27" - }, - { - "subject": "refactor(0.20.0): discriminated Manifest::Entry kinds (#74)", - "date": "2026-05-27" - }, - { - "subject": "refactor(0.20.0): narrowed hook payload (HookContext) (#73)", - "date": "2026-05-27" - }, - { - "subject": "refactor(0.20.0): unified Hooks::Bus (#71)", - "date": "2026-05-27" - }, - { - "subject": "refactor(0.20.0): extract Manifest::Resolver (#70)", - "date": "2026-05-27" - }, - { - "subject": "refactor(0.20.0): kill top-level utility modules (#69)", - "date": "2026-05-27" - }, - { - "subject": "chore(release): textus 0.19.1 — drop textus/2 migration hint (#68)", - "date": "2026-05-27" - }, - { - "subject": "feat: explicit dependencies (v0.19.0) (#67)", - "date": "2026-05-27" - }, - { - "subject": "fix: hook dispatcher safety (v0.18.1) (#66)", - "date": "2026-05-27" - } - ] - }, - { - "tag": "v0.18.0", - "date": "2026-05-27", - "commits": [ - { - "subject": "feat: extract storage ports, Store as composition root (v0.18.0) (#65)", - "date": "2026-05-27" - }, - { - "subject": "feat: flatten Operations, centralize authz, explicit hook registration (v0.17.0) (#64)", - "date": "2026-05-27" - }, - { - "subject": "feat: type cleanup & glue (v0.16.0) (#63)", - "date": "2026-05-26" - } - ] - }, - { - "tag": "v0.15.0", - "date": "2026-05-26", - "commits": [ - { - "subject": "feat: operation boundaries reshape (v0.15.0) (#62)", - "date": "2026-05-26" - } - ] - }, - { - "tag": "v0.14.4", - "date": "2026-05-26", - "commits": [ - { - "subject": "fix: lock hygiene + build/freshness decoupling (#58, #59) (#60)", - "date": "2026-05-26" - } - ] - }, - { - "tag": "v0.14.3", - "date": "2026-05-26", - "commits": [ - { - "subject": "feat: top-level build lock (v0.14.3, closes #56) (#57)", - "date": "2026-05-26" - } - ] - }, - { - "tag": "v0.14.2", - "date": "2026-05-26", - "commits": [ - { - "subject": "feat: per-rule fetch_timeout_seconds override (#54) (#55)", - "date": "2026-05-26" - } - ] - }, - { - "tag": "v0.14.1", - "date": "2026-05-26", - "commits": [ - { - "subject": "release: 0.14.1 (#53)", - "date": "2026-05-26" - }, - { - "subject": "build: skip rewrite when only generated_at would change (#52)", - "date": "2026-05-26" - }, - { - "subject": "Extract Manifest.check_version! to dedupe parse/load version guard (#51)", - "date": "2026-05-26" - } - ] - }, - { - "tag": "v0.14.0", - "date": "2026-05-26", - "commits": [ - { - "subject": "docs(readme): refresh for v0.14.0 — typed envelopes, Build/Publish split, spec count (#50)", - "date": "2026-05-26" - }, - { - "subject": "v0.14.0 — Phase 4: Build/Publish split + Envelope Data.define (#49)", - "date": "2026-05-26" - }, - { - "subject": "v0.13.1 — Phase 3: Manifest::Entry split (Parser + Validators) (#48)", - "date": "2026-05-26" - }, - { - "subject": "v0.13.0 — Phase 2: Format-strategy extraction (#47)", - "date": "2026-05-26" - }, - { - "subject": "v0.12.6 — Examples reorg (project + claude-plugin) (#46)", - "date": "2026-05-26" - }, - { - "subject": "v0.12.5 — Docs refresh for textus/3 + Operations facade (#45)", - "date": "2026-05-26" - }, - { - "subject": "v0.12.4 — Store facade final removal (Phase 1) (#44)", - "date": "2026-05-26" - }, - { - "subject": "v0.12.3 — Agent protocol block in textus intro (#43)", - "date": "2026-05-26" - }, - { - "subject": "v0.12.2 — Operations rename + Store facade removal (breaking) (#42)", - "date": "2026-05-26" - } - ] + "groups": { + "Features": [ + { + "subject": "feat!: 0.21.0 — agent memory integration (boot + pulse) (#81)", + "date": "2026-05-27" + } + ] + } }, { "tag": "v0.12.1", "date": "2026-05-26", - "commits": [ - { - "subject": "v0.12.1 — fix textus/2 hint at manifest parser (#41)", - "date": "2026-05-26" - }, - { - "subject": "v0.12.0 — Legacy Sweep: delete textus/2 compat shims (#40)", - "date": "2026-05-25" - }, - { - "subject": "0.11.0 — textus/3 vocabulary redesign (BREAKING) (#39)", - "date": "2026-05-25" - }, - { - "subject": "fix(doctor): IllegalKeys honors index_filename — skip non-matching siblings (#38)", - "date": "2026-05-25" - } - ] + "groups": { + "Bug Fixes": [ + { + "subject": "IllegalKeys honors index_filename — skip non-matching siblings (#38)", + "date": "2026-05-25" + } + ] + } }, { "tag": "v0.10.5", "date": "2026-05-25", - "commits": [ - { - "subject": "0.10.5 — tech-debt cleanup + index_filename + docs polish (#37)", - "date": "2026-05-25" - }, - { - "subject": "feat(manifest): index_filename — surface a fixed basename as the per-directory row (#36)", - "date": "2026-05-25" - }, - { - "subject": "ci: accept unbracketed CHANGELOG headings when extracting release notes (#35)", - "date": "2026-05-25" - } - ] - }, - { - "tag": "v0.10.4", - "date": "2026-05-25", - "commits": [ - { - "subject": "0.10.4 — GitHub folder intake recipe + skill-bundle deferral ADR (#34)", - "date": "2026-05-25" - } - ] - }, - { - "tag": "v0.10.3", - "date": "2026-05-23", - "commits": [ - { - "subject": "0.10.3 — documentation refresh and legacy-code removal (#33)", - "date": "2026-05-23" - } - ] - }, - { - "tag": "v0.10.2", - "date": "2026-05-23", - "commits": [ - { - "subject": "0.10.2 — doctor and store cleanup (#32)", - "date": "2026-05-23" - } - ] - }, - { - "tag": "v0.10.1", - "date": "2026-05-22", - "commits": [ - { - "subject": "0.10.1 — documentation refresh and spec hygiene (#31)", - "date": "2026-05-22" - } - ] - }, - { - "tag": "v0.10.0", - "date": "2026-05-22", - "commits": [ - { - "subject": "0.10.0 — shim removal, signal-based zone detection, Builder extraction (#30)", - "date": "2026-05-22" - } - ] - }, - { - "tag": "v0.9.2", - "date": "2026-05-22", - "commits": [ - { - "subject": "0.9.2 — policies, audit verbs, zone rename (#29)", - "date": "2026-05-22" - }, - { - "subject": "0.9.1 — write-path layering + request Context (#28)", - "date": "2026-05-22" - }, - { - "subject": "0.9.0 — intake, event standardization, read-time freshness, layered architecture (#27)", - "date": "2026-05-22" - }, - { - "subject": "0.8.3 — :mv, :reject, :loaded events (#26)", - "date": "2026-05-22" - }, - { - "subject": "0.8.2 — hook DSL sugar + :publish event (#24)", - "date": "2026-05-22" - } - ] - }, - { - "tag": "v0.8.1", - "date": "2026-05-21", - "commits": [ - { - "subject": "0.8.1 — terminology cleanup: extension → hook (#23)", - "date": "2026-05-21" - } - ] - }, - { - "tag": "v0.8.0", - "date": "2026-05-21", - "commits": [ - { - "subject": "docs: 0.8 refresh — README, SPEC.md, examples, doctor cleanup, spec layout (#22)", - "date": "2026-05-21" - }, - { - "subject": "0.8.0 — folder restructure, Zeitwerk autoload, Doctor::Check split (#21)", - "date": "2026-05-21" - }, - { - "subject": "0.7.0 — Reader/Writer split, EventBus, Builder pipeline (#20)", - "date": "2026-05-21" - }, - { - "subject": "0.6.1 — deprecation cleanup + drop migrate v2 (#19)", - "date": "2026-05-21" - }, - { - "subject": "feat: 0.6 hook unification — collapse 4 DSL verbs into Textus.hook(event, name) (#18)", - "date": "2026-05-21" - } - ] + "groups": { + "Features": [ + { + "subject": "index_filename — surface a fixed basename as the per-directory row (#36)", + "date": "2026-05-25" + } + ] + } }, { "tag": "v0.5.0", "date": "2026-05-21", - "commits": [ - { - "subject": "chore(release): 0.5.0 — wire textus/2, CLI groups, Store split, NDJSON audit log (#17)", - "date": "2026-05-21" - }, - { - "subject": "refactor(store): split Store into facade + Mover/Staleness/Validator/Events (#16)", - "date": "2026-05-21" - }, - { - "subject": "feat(protocol): bump to textus/2; unify _meta block across formats (#14)", - "date": "2026-05-21" - }, - { - "subject": "feat(audit): switch audit log to true NDJSON; legacy TSV reads still parse (#13)", - "date": "2026-05-21" - }, - { - "subject": "refactor: v0.5 wrap-up batch A — legacy cruft + ManifestEntry split + CLI subcommand groups (#12)", - "date": "2026-05-21" - }, - { - "subject": "refactor(cli): hash dispatch + alphabetize requires + unify PROTOCOL refs (#11)", - "date": "2026-05-21" - }, - { - "subject": "refactor(cli): extract per-verb command objects (CLI 434 → 96 LOC) (#9)", - "date": "2026-05-21" - }, - { - "subject": "feat(cli): fold validate-all into doctor --check=schema_violations (#8)", - "date": "2026-05-21" - }, - { - "subject": "feat(cli): default --format=json (#7)", - "date": "2026-05-21" - }, - { - "subject": "docs(architecture): rewrite layering section as module clusters (#6)", - "date": "2026-05-20" - } - ] + "groups": { + "Features": [ + { + "subject": "bump to textus/2; unify _meta block across formats (#14)", + "date": "2026-05-21" + }, + { + "subject": "switch audit log to true NDJSON; legacy TSV reads still parse (#13)", + "date": "2026-05-21" + }, + { + "subject": "fold validate-all into doctor --check=schema_violations (#8)", + "date": "2026-05-21" + }, + { + "subject": "default --format=json (#7)", + "date": "2026-05-21" + } + ] + } }, { "tag": "v0.4.0", "date": "2026-05-20", - "commits": [ - { - "subject": "v0.4.0: extension API redesign — action primitive + doctor_check (#4) (#5)", - "date": "2026-05-20" - }, - { - "subject": "chore(release): refresh Gemfile.lock for 0.3.0", - "date": "2026-05-20" - }, - { - "subject": "chore(release): 0.3.0 — configurable store root", - "date": "2026-05-20" - }, - { - "subject": "docs(spec): document store root resolution precedence (§3.1)", - "date": "2026-05-20" - }, - { - "subject": "feat(cli): accept --root= flag before subcommand", - "date": "2026-05-20" - }, - { - "subject": "feat(store): accept explicit root via kwarg + TEXTUS_ROOT env", - "date": "2026-05-20" - }, - { - "subject": "test(store): pin TEXTUS_ROOT/--root precedence (failing)", - "date": "2026-05-20" - }, - { - "subject": "docs: badges + CONTRIBUTING + SECURITY", - "date": "2026-05-20" - }, - { - "subject": "docs(readme): refresh for 0.2 — agent integration as the headline", - "date": "2026-05-20" - } - ] + "groups": { + "Features": [ + { + "subject": "accept --root= flag before subcommand", + "date": "2026-05-20" + }, + { + "subject": "accept explicit root via kwarg + TEXTUS_ROOT env", + "date": "2026-05-20" + } + ] + } }, { "tag": "v0.2.0", "date": "2026-05-20", - "commits": [ - { - "subject": "release: prep 0.2.0 (workflow + changelog + gemspec cleanup)", - "date": "2026-05-20" - }, - { - "subject": "ci: complete CHECKSUMS in Gemfile.lock", - "date": "2026-05-20" - }, - { - "subject": "feat(intro): textus intro verb + inject_intro builder flag", - "date": "2026-05-20" - }, - { - "subject": "examples(claude-plugin): pending zone walkthrough (AI propose → human accept)", - "date": "2026-05-19" - }, - { - "subject": "examples(claude-plugin): align with current library surface", - "date": "2026-05-19" - }, - { - "subject": "feat(init): declare all five zones and pre-create their directories", - "date": "2026-05-19" - }, - { - "subject": "fix(store): pass suggestions on UnknownKey from get/put/mv raise sites", - "date": "2026-05-19" - }, - { - "subject": "feat(doctor): health-check verb + actionable error hints", - "date": "2026-05-19" - }, - { - "subject": "feat(publish): publish_each for nested entries; example mirrors agents/skills/commands automatically", - "date": "2026-05-19" - }, - { - "subject": "feat(uid): stable Textus UID + textus mv preserves identity across moves", - "date": "2026-05-19" - }, - { - "subject": "refactor(publisher): move sentinels under .textus/sentinels/", - "date": "2026-05-19" - }, - { - "subject": "examples(claude-plugin): real Claude plugin layout managed by textus", - "date": "2026-05-19" - }, - { - "subject": "fix(projection): drop duplicate generated_at on Hash reducer results", - "date": "2026-05-19" - }, - { - "subject": "docs(spec): per-entry formats, key grammar, envelope additions", - "date": "2026-05-19" - }, - { - "subject": "examples(claude-plugin): json/yaml marketplace + deep nested working.network", - "date": "2026-05-19" - }, - { - "subject": "feat(extensions): normalize fetcher results into {frontmatter, body, content}", - "date": "2026-05-19" - }, - { - "subject": "refactor(publisher): rename Symlink → Publisher; copy-only publish", - "date": "2026-05-19" - }, - { - "subject": "feat(builder): per-format build pipelines + _meta injection for structured outputs", - "date": "2026-05-19" - }, - { - "subject": "feat(cli): textus migrate-keys helper for key-grammar migration", - "date": "2026-05-19" - }, - { - "subject": "feat(manifest): per-entry format field + strict key grammar enforcement", - "date": "2026-05-19" - }, - { - "subject": "feat(entry): add per-format storage strategies (markdown, json, yaml, text)", - "date": "2026-05-19" - }, - { - "subject": "examples(claude-plugin): make bin/notify-build a real script", - "date": "2026-05-19" - }, - { - "subject": "examples: showcase full 0.2 surface end-to-end", - "date": "2026-05-19" - }, - { - "subject": "docs(readme): bump version reference to 0.2.0; disambiguate git hooks from Textus.hook", - "date": "2026-05-19" - }, - { - "subject": "docs: rewrite §5.4/5.9/5.10/5.11 for extension surface; bump 0.2.0", - "date": "2026-05-19" - }, - { - "subject": "chore: migrate example plugin extensions to new DSL", - "date": "2026-05-19" - }, - { - "subject": "feat(init): scaffold .textus/extensions/ with README", - "date": "2026-05-19" - }, - { - "subject": "feat(cli): add refresh + extensions list; rename --parse to --fetcher; remove hooks list", - "date": "2026-05-19" - }, - { - "subject": "feat(manifest): validate event names against ExtensionRegistry::EVENTS", - "date": "2026-05-19" - }, - { - "subject": "feat(audit): include pending_key/target_key in event_error extras", - "date": "2026-05-19" - }, - { - "subject": "feat(audit): optional JSON extras column for event/error records", - "date": "2026-05-19" - }, - { - "subject": "feat(events): fire :build after derived materialize and :accept after proposal", - "date": "2026-05-19" - }, - { - "subject": "test(events): assert :refresh does not fire on unchanged bytes", - "date": "2026-05-19" - }, - { - "subject": "feat(events): fire :refresh with change=:created/:updated", - "date": "2026-05-19" - }, - { - "subject": "chore(store): grep-friendly TODO marker for hook-error audit gap", - "date": "2026-05-19" - }, - { - "subject": "feat(events): fire :put and :delete after successful writes", - "date": "2026-05-19" - }, - { - "subject": "feat(refresh): wrap fetcher exceptions with fetcher-name context", - "date": "2026-05-19" - }, - { - "subject": "feat(refresh): in-process fetcher driver with 2s timeout", - "date": "2026-05-19" - }, - { - "subject": "refactor(store_view): WRITE_METHODS loop, accept coverage, drift guard", - "date": "2026-05-19" - }, - { - "subject": "feat(store_view): read-only proxy for extension code", - "date": "2026-05-19" - }, - { - "subject": "refactor(projection): extract REDUCER_TIMEOUT_SECONDS; default config to {}", - "date": "2026-05-19" - }, - { - "subject": "feat(projection): rename transform→reducer; route via store registry", - "date": "2026-05-19" - }, - { - "subject": "refactor(manifest): fetcher_config always defaults to {}; fetcher.nil? is the discriminator", - "date": "2026-05-19" - }, - { - "subject": "feat(manifest): source.fetcher/config/ttl with legacy-key rejection", - "date": "2026-05-19" - }, - { - "subject": "chore(cli): mark --parse= bridge as temporary; timeout returns in task 14", - "date": "2026-05-19" - }, - { - "subject": "feat(fetchers): port built-in parsers to BuiltinFetchers in new registry", - "date": "2026-05-19" - }, - { - "subject": "feat(store): wrap extension load failures with filename context", - "date": "2026-05-19" - }, - { - "subject": "fix(store): deterministic extension load order via Dir.glob.sort", - "date": "2026-05-19" - }, - { - "subject": "feat(store): per-store ExtensionRegistry loaded from .textus/extensions/", - "date": "2026-05-19" - }, - { - "subject": "refactor(dsl): private THREAD_REGISTRY_KEY; restructure DSL spec", - "date": "2026-05-19" - }, - { - "subject": "feat(dsl): add Textus.fetcher/reducer/hook with thread-scoped registry", - "date": "2026-05-19" - }, - { - "subject": "fix(registry): hooks() read should not auto-register the queried event", - "date": "2026-05-19" - }, - { - "subject": "feat(registry): add ExtensionRegistry with fetcher/reducer/hook slots", - "date": "2026-05-19" - }, - { - "subject": "scrub: replace 'envato' with 'acme' in fixtures and docs; sync README ruby versions", - "date": "2026-05-19" - }, - { - "subject": "ci: drop ruby 3.1/3.2, target 3.3 and 3.4", - "date": "2026-05-19" - }, - { - "subject": "release prep: CHANGELOG, gemspec metadata URIs, CI workflow, Brewfile for lefthook", - "date": "2026-05-19" - }, - { - "subject": "quality: add rubocop config and lefthook git hooks; autocorrect existing code", - "date": "2026-05-19" - }, - { - "subject": "gemspec: exclude internal plan docs from packaged gem", - "date": "2026-05-19" - }, - { - "subject": "spec: align §6+ examples and keys with canon/working zone names", - "date": "2026-05-19" - }, - { - "subject": "readme: rewrite for 0.1.0 release with current zone model and v1.1 features", - "date": "2026-05-19" - }, - { - "subject": "entry: force UTF-8 on parsed content, reject invalid encoding", - "date": "2026-05-19" - }, - { - "subject": "example: wire lowercase parser into intake.upstream.notes", - "date": "2026-05-19" - }, - { - "subject": "cli: fix --zone=Z + --format=json flag combo on list/stale", - "date": "2026-05-19" - }, - { - "subject": "example: README tour demonstrating parser, calculator, hooks, schema ownership", - "date": "2026-05-19" - }, - { - "subject": "example: project schema with maintained_by per field", - "date": "2026-05-19" - }, - { - "subject": "example: rank-by-recency calculator wired into derived.claude.root", - "date": "2026-05-19" - }, - { - "subject": "example: register lowercase parser to demo .textus/parsers/*.rb auto-load", - "date": "2026-05-19" - }, - { - "subject": "example: add intake zone with releases.rss source and on_stale hook", - "date": "2026-05-19" - }, - { - "subject": "spec: §5.10 hooks declaration + §9 hooks verb", - "date": "2026-05-19" - }, - { - "subject": "hooks: list verb exposes declared hooks; runners execute, textus declares", - "date": "2026-05-19" - }, - { - "subject": "manifest: parse hooks: block on entries", - "date": "2026-05-19" - }, - { - "subject": "spec: §5.9 document calculators extension point", - "date": "2026-05-19" - }, - { - "subject": "projection: apply transform: NAME between pluck and sort", - "date": "2026-05-19" - }, - { - "subject": "calculators: registry + auto-load with 2s timeout (mirrors parsers)", - "date": "2026-05-19" - }, - { - "subject": "spec: document role_authority override (human always wins)", - "date": "2026-05-19" - }, - { - "subject": "validate-all: cross-check field last-writer against schema.maintained_by", - "date": "2026-05-19" - }, - { - "subject": "audit-log: expose last_writer_for(key)", - "date": "2026-05-19" - }, - { - "subject": "spec: document schema fields.maintained_by and evolution block", - "date": "2026-05-19" - }, - { - "subject": "schema-migrate: auto-apply evolution.migrate_from when --rename omitted", - "date": "2026-05-19" - }, - { - "subject": "schema: parse maintained_by per field + evolution block", - "date": "2026-05-19" - }, - { - "subject": "examples: ship refresh-intake.sh proving the runner-textus boundary", - "date": "2026-05-19" - }, - { - "subject": "examples: 50-line MCP server wrapping textus get/list/put", - "date": "2026-05-19" - }, - { - "subject": "examples: claude-plugin reference flow with templates, Rakefile, lefthook", - "date": "2026-05-19" - }, - { - "subject": "schema-tools: init from entry, diff against entries, migrate field renames", - "date": "2026-05-19" - }, - { - "subject": "init: textus init --profile= scaffolds .textus/ from bundled profiles", - "date": "2026-05-19" - }, - { - "subject": "proposal: accept verb copies pending patch into target, deletes pending", - "date": "2026-05-19" - }, - { - "subject": "put: --parse=NAME runs registered parser on stdin, stamps last_refreshed_at", - "date": "2026-05-19" - }, - { - "subject": "stale: detect intake entries past their source.ttl", - "date": "2026-05-19" - }, - { - "subject": "parsers: auto-load .textus/parsers/*.rb with 2s timeout bound", - "date": "2026-05-19" - }, - { - "subject": "deps/rdeps/published: walk projection.select + generator.sources", - "date": "2026-05-19" - }, - { - "subject": "builder: textus build materializes derived entries + publishes symlinks", - "date": "2026-05-19" - }, - { - "subject": "symlink: publish_to with copy-mode fallback and sentinel", - "date": "2026-05-19" - }, - { - "subject": "parsers: json, csv, markdown-links, ical-events, rss (stdlib-only)", - "date": "2026-05-19" - }, - { - "subject": "projection: select/pluck/sort_by/limit engine, capped at 1000", - "date": "2026-05-19" - }, - { - "subject": "mustache: vendor minimal stdlib-only engine with depth bound", - "date": "2026-05-19" - }, - { - "subject": "list/stale: accept --zone=Z filter", - "date": "2026-05-19" - }, - { - "subject": "cli: wire --as flag, delete verb, validate-all verb", - "date": "2026-05-19" - }, - { - "subject": "store: validate-all walks every entry and reports violations", - "date": "2026-05-19" - }, - { - "subject": "store: role-gated put, delete verb, audit log on every write", - "date": "2026-05-19" - }, - { - "subject": "audit-log: append-only tsv with flock on every write", - "date": "2026-05-19" - }, - { - "subject": "role: resolve from flag > env > .textus/role > default human", - "date": "2026-05-19" - }, - { - "subject": "manifest: parse zones block with writable_by; synthesize legacy zones if absent", - "date": "2026-05-19" - }, - { - "subject": "errors: add InvalidRole/InvalidProjection/TemplateError/PublishError/ProposalError", - "date": "2026-05-19" - }, - { - "subject": "layout: move user content under .textus/zones/; manifest paths stay short", - "date": "2026-05-19" - }, - { - "subject": "spec: rewrite CLI surface and conformance fixtures for v1.0", - "date": "2026-05-19" - }, - { - "subject": "spec: add compute, publish, intake, pending, audit, security-bounds sections", - "date": "2026-05-19" - }, - { - "subject": "spec: rewrite §1-5 for v1.0 general-purpose framing and role-based zones", - "date": "2026-05-19" - }, - { - "subject": "Initial commit: textus/1 reference Ruby implementation", - "date": "2026-05-18" - } - ] + "groups": { + "Features": [ + { + "subject": "textus intro verb + inject_intro builder flag", + "date": "2026-05-20" + }, + { + "subject": "declare all five zones and pre-create their directories", + "date": "2026-05-19" + }, + { + "subject": "health-check verb + actionable error hints", + "date": "2026-05-19" + }, + { + "subject": "publish_each for nested entries; example mirrors agents/skills/commands automatically", + "date": "2026-05-19" + }, + { + "subject": "stable Textus UID + textus mv preserves identity across moves", + "date": "2026-05-19" + }, + { + "subject": "normalize fetcher results into {frontmatter, body, content}", + "date": "2026-05-19" + }, + { + "subject": "per-format build pipelines + _meta injection for structured outputs", + "date": "2026-05-19" + }, + { + "subject": "textus migrate-keys helper for key-grammar migration", + "date": "2026-05-19" + }, + { + "subject": "per-entry format field + strict key grammar enforcement", + "date": "2026-05-19" + }, + { + "subject": "add per-format storage strategies (markdown, json, yaml, text)", + "date": "2026-05-19" + }, + { + "subject": "scaffold .textus/extensions/ with README", + "date": "2026-05-19" + }, + { + "subject": "add refresh + extensions list; rename --parse to --fetcher; remove hooks list", + "date": "2026-05-19" + }, + { + "subject": "validate event names against ExtensionRegistry::EVENTS", + "date": "2026-05-19" + }, + { + "subject": "include pending_key/target_key in event_error extras", + "date": "2026-05-19" + }, + { + "subject": "optional JSON extras column for event/error records", + "date": "2026-05-19" + }, + { + "subject": "fire :build after derived materialize and :accept after proposal", + "date": "2026-05-19" + }, + { + "subject": "fire :refresh with change=:created/:updated", + "date": "2026-05-19" + }, + { + "subject": "fire :put and :delete after successful writes", + "date": "2026-05-19" + }, + { + "subject": "wrap fetcher exceptions with fetcher-name context", + "date": "2026-05-19" + }, + { + "subject": "in-process fetcher driver with 2s timeout", + "date": "2026-05-19" + }, + { + "subject": "read-only proxy for extension code", + "date": "2026-05-19" + }, + { + "subject": "rename transform→reducer; route via store registry", + "date": "2026-05-19" + }, + { + "subject": "source.fetcher/config/ttl with legacy-key rejection", + "date": "2026-05-19" + }, + { + "subject": "port built-in parsers to BuiltinFetchers in new registry", + "date": "2026-05-19" + }, + { + "subject": "wrap extension load failures with filename context", + "date": "2026-05-19" + }, + { + "subject": "per-store ExtensionRegistry loaded from .textus/extensions/", + "date": "2026-05-19" + }, + { + "subject": "add Textus.fetcher/reducer/hook with thread-scoped registry", + "date": "2026-05-19" + }, + { + "subject": "add ExtensionRegistry with fetcher/reducer/hook slots", + "date": "2026-05-19" + } + ], + "Bug Fixes": [ + { + "subject": "pass suggestions on UnknownKey from get/put/mv raise sites", + "date": "2026-05-19" + }, + { + "subject": "drop duplicate generated_at on Hash reducer results", + "date": "2026-05-19" + }, + { + "subject": "deterministic extension load order via Dir.glob.sort", + "date": "2026-05-19" + }, + { + "subject": "hooks() read should not auto-register the queried event", + "date": "2026-05-19" + } + ] + } } ] } diff --git a/.textus/data/artifacts/feeds/skills.json b/.textus/data/artifacts/feeds/skills.json index 4d0a5a41..82569074 100644 --- a/.textus/data/artifacts/feeds/skills.json +++ b/.textus/data/artifacts/feeds/skills.json @@ -1,6 +1,6 @@ { "_meta": { - "generated_at": "2026-07-05T17:33:08Z", + "generated_at": "2026-07-05T17:36:20Z", "uid": "36318f45b6fec691" }, "skills": [ diff --git a/.textus/templates/docs/meta/changelog.erb b/.textus/templates/docs/meta/changelog.erb index e7c3f7d1..88e81c60 100644 --- a/.textus/templates/docs/meta/changelog.erb +++ b/.textus/templates/docs/meta/changelog.erb @@ -9,8 +9,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 <% entries.each do |entry| -%> ## <%= entry["tag"] %><%= entry["date"] && !entry["date"].empty? ? " — #{entry["date"]}" : "" %> +<% if entry["groups"] -%> +<% entry["groups"].each do |group_name, commits| -%> +### <%= group_name %> + +<% commits.each do |commit| -%> +- <%= commit["subject"] %> +<% end -%> + +<% end -%> +<% elsif entry["commits"] -%> <% entry["commits"].each do |commit| -%> - <%= commit["subject"] %> <% end -%> <% end -%> +<% end -%> diff --git a/.textus/workflows/config/changelog.rb b/.textus/workflows/config/changelog.rb index 29480fc5..0a8c687f 100644 --- a/.textus/workflows/config/changelog.rb +++ b/.textus/workflows/config/changelog.rb @@ -3,23 +3,27 @@ on "artifacts.changelog" every "30m" + TYPE_GROUPS = { + "feat" => "Features", + "fix" => "Bug Fixes", + }.freeze + step :build do |_, _| - # skip: merge commits, and housekeeping drain commits that would otherwise - # create a self-referential loop (drain produces a commit → commit appears - # in changelog → drain produces a new file → repeat). skip_pattern = /\A(chore: textus drain|Merge )/ raw = `git log --no-merges --pretty=format:"%D|||%s|||%ad" --date=short 2>/dev/null`.strip lines = raw.split("\n").map { |l| l.split("|||") } + type_order = TYPE_GROUPS.values entries = [] current_tag = "Unreleased" current_date = nil - current_commits = [] + current_by_type = {} flush = lambda do - entries << { "tag" => current_tag, "date" => current_date, "commits" => current_commits.dup } unless current_commits.empty? - current_commits = [] + groups = type_order.filter_map { |name| [name, current_by_type[name]] if current_by_type[name]&.any? }.to_h + entries << { "tag" => current_tag, "date" => current_date, "groups" => groups } unless groups.empty? + current_by_type = {} end lines.each do |refs, subject, date| @@ -31,7 +35,13 @@ end next unless subject && !subject.to_s.strip.match?(skip_pattern) - current_commits << { "subject" => subject.to_s.strip, "date" => date.to_s.strip } + subj = subject.to_s.strip + type = subj.match(/\A([a-z]+)[(!]/)&.captures&.first + group = TYPE_GROUPS[type] + next unless group + + clean = subj.sub(/\A[a-z]+(\([^)]*\))?:[:\s]*/, "") + (current_by_type[group] ||= []) << { "subject" => clean, "date" => date.to_s.strip } end flush.call diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fbeb696..b45e9519 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,1110 +8,230 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased -- feat: web surface, shared dispatch, entry constraint enforcement -- fix: move CBM_CACHE_DIR constant outside namespace block (rubocop) -- refactor: simplification phase 2 — pipeline inline, data param, ADR archive, adoption rename, boot improvements -- chore: fix rubocop hash alignment in bin/smoke -- feat: add ruby/python/javascript format files with proper extensions -- feat: per-language script formats (bash/ruby/python/js) -- chore: gitignore bin/.smoke-sids marker file -- fix: add --as-format to put for per-write format override -- fix: script files under sessions get no extension instead of .md -- fix: smoke output now glance-friendly and session-id capture fixed -- fix: smoke --clean now sweeps orphan session dirs via marker file -- chore: fix rubocop offenses for CI compliance -- feat: smoke tests 4 script formats (bash/ruby/python/js) under sessions..scripts -- feat: add bin/smoke for agent session lifecycle testing -- style: disable RSpec/VerifiedDoubles, StubbedMock, MessageSpies, VerifiedDoubleReference in rubocop; deduplicate config -- style: rubocop autocorrect (non-RSpecVerifiedDoubles), fix rescue modifier/comma/guard-clause in new code -- feat: strict agent session protocol — visible INPUT/LOOP/OUTPUT in AGENTS.md, boot context in session_open, nodes_checked in session_close, doctor check for skipped constraint, protocol entry 0016 -- fix: Runner::Context needs file_system/schemas for workflow step blocks, fix stale error message (match: → on:) -- refactor: standardize pipeline shapes — extract Delete/Move steps into separate files, add narrow DeleteDeps/MoveDeps structs -- refactor: add narrow file queries to QueryContext, update ops handlers; docs: align knowledge store with current architecture — 4 files updated, 3 deleted, data-flow rewritten -- refactor: workflow redesign — two-seam drain/watch, deleted Engine/Queue/RetryPolicy, inline retry, Publisher uses container.store_engine, StepHelpers extracted from Helpers -- refactor: collapse 21 single-file verb specs into core_verbs.rb -- chore: remove dead code (Outcome, Result, verb stubs, session_open/close files, Registry#workflows_for_key, be_success/be_failure matchers) -- style: suppress rubocop warnings in spec files -- style: suppress Metrics/ParameterLists in Container -- style: rubocop auto-correct fixes -- refactor: split Workflow::DSL::Definition into sub-modules -- refactor: group 11-step pipeline into 4 phases -- refactor: split Builder into three testable layers -- refactor: CQS context split — QueryContext + CommandContext -- refactor: consolidate authorization into Manifest::Policy -- refactor: collapse shallow namespace modules -- chore: fix rubocop violations, key_delete module_function, and refresh docs -- refactor: replace hand-written Session delegations with Forwardable -- refactor: extract merge_issues helper in Definition#check -- refactor: split VerbRegistry into per-verb files under verb_registry/verbs/ -- refactor: move MovePipeline emit_event dedup guards into shared Pipeline.emit_event -- refactor: extract shared CheckEtag/ResolvePath into Pipeline module -- chore: remove vestigial Materialize workflow -- refactor(workflow): eliminate Materialize re-dispatch and deepen Runner -- refactor(workflow): extract shared Consumer and decouple event emission from pipeline -- feat: wire TTL scheduler + refactor Workflow module ownership -- style: fix rubocop safe autocorrects and update Lint/UnusedMethodArgument excludes for refactored handlers -- fix: deps/rdeps/graph category -> read (sed artifact from ac02cb77c) -- refactor: pull serialize_for_put up to Format::Base -- docs: update ADR-0133 to Accepted -- docs: update ADR-0133 to Accepted, add session handoff -- refactor: extract shared ContextWith module for pipeline context objects -- refactor: extract handler response hash builder into Concern -- refactor: bundle StoreEngine dependencies into WriteDeps -- refactor: simplify store_engine pipelines — remove dead code, extract shared helpers -- refactor: extract domain logic from handlers and MCP server into testable service objects -- refactor: eliminate double-resolve, push move orchestration into pipeline, remove dead code -- refactor(store-engine): Standardize StoreEngine method signatures to all-keyword args -- refactor: consolidate config workflows, fix renderer trigger, add pipeline -- refactor: remove redundant lane/kind/nested/owner from manifest schema -- refactor: strip path/publish/source from manifest, remove dead code -- refactor: move system verb routing from Gate override hash to VerbSpec#system? -- refactor: StoreEngine + Handlers::Read/Write/System split -- fix: skip tracked:false entries in doctor manifest check, fix schema ref -- fix: correct doctor schema parsing, commit generated artifacts -- refactor: consolidate lane/role docs workflows, fix nested schema loading -- refactor: split HandlerContext into ReadOps/WriteOps/System capability interfaces -- test: add unit tests for Diff module (body, meta, schema, summary) -- refactor: remove dead publish code, inline Mode+None into publish.rb, inline VAR_RE into tree.rb -- refactor: fold StepScope and Context into Runner as inner classes -- refactor: inline Workflow::Loader into Registry -- refactor: inline Workflow::Pattern into DSL::Definition -- feat: workflow DSL redesign — on/save/notify, priority routing, doctor consolidation -- feat: track ingested_at, ingest_count, duplicated_at in raw lane ingest flow -- chore: regenerate boot artifact and orientation docs after dogfood flow changes -- chore: remove stale boot cache -- fix: update orientation template and boot.rb for procedures and feedback criteria -- refactor: move propose/accept/reject handlers from proposal lane to scratchpad lane -- chore: remove proposals lane from V4::LANES -- chore: regenerate artifacts after dogfood flow changes -- fix: materialize workflow — use container.workflow_registry -- feat: add shape validation for execution sub-families -- feat: add procedure entries — doctor, session, deploy -- refactor: move execution entries to runbook/ subdirectory -- feat: add execution schemas — runbook, checklist, procedure -- feat: add feedback criteria entries — protocol-gap, repetitive-task, intent-drift -- refactor: feedback node becomes criteria — move solid-audit to judgment.engineering -- refactor: update orientation template — remove propose recipe -- refactor: clean up scratchpad — replace notes/scripts with proposals -- refactor: remove proposals lane (folded into scratchpad) -- feat: surface session_open/session_close in agent boot catalog -- docs: dogfood flow implementation plan -- docs: dogfood flow design — session lifecycle, feedback criteria, execution zone -- fix: dogfood audit — orientation keys, doctor cascade, session lifecycle -- refactor: remove legacy store files, aliases, and dual code paths -- refactor: reorganize infra/store with Base, split concerns, standardize API -- docs: lock store naming conventions into knowledge loop -- refactor: consolidate drain/watch around shared Async::Queue -- refactor: consolidate dual paths and fix silent error swallowing -- feat: add code-pattern entries to knowledge loop (0011-0014) -- refactor: remove dead parameters from handlers, Data.define, and pipeline -- fix: replace role-based event suppression with self-loop detection via Engine::IN_FLIGHT -- fix: prevent infinite event loop — skip workflow event emission for automation-role writes -- fix: update etag spec for layout-object API, remove naming-check debug output -- refactor: merge overlapping doctor checks (sentinels+orphaned, schema+unowned, manifest+templates+schemas) -- refactor: extract build_lanes/build_roles helpers, simplify 5 workflows -- refactor: collapse 8 static-template workflows into 1 generic render workflow -- fix: optimize naming-check to only scan naming-constrained entries directly -- fix: use resolver.enumerate instead of Dir.children in naming-check (respects ignore patterns) -- fix: reconnect event pipeline, move EventEmitter, add workflow_spec verb, remove published/schema verbs -- fix: reorder knowledge loop keys and restore execution schema -- feat: add boot_refresh produce workflow, fix guard clause -- fix: renumber duplicate ADRs 0117/0120/0121/0125 → 0131-0134, fill stub reference docs -- chore: remove stale hand-authored doc not produced by any workflow -- fix: remove dead doctor CLI declaration, fix pattern-crossrefs and Workflow::Context#read -- fix: update .textus dogfood workflows for store API and clean stale artifacts -- style: fix 133 rubocop offenses (trailing whitespace, alignment, hash alignment, etc.) -- refactor: migrate flat-file stores to Infra::Store with SQLite-backed sub-stores -- refactor: remove dead code (Doctor, Retention, TraceBuffer, SequelAdapter, 3 error classes, spec redirects) -- refactor: fix drain container crash, remove thread-local registry, delete ScopedContext -- refactor: collapse Container structs, unify Publisher, remove EventStore Interface -- refactor: unify result shape, remove views, deepen VerbSpec -- refactor: replace error hashes with exceptions in handlers and gate -- fix: drop stale contract check, add new verbs to CLI catalog and snapshot -- refactor: move Container/Builder/WritePipeline/Retention from infra/store/ to protocol/ -- cleanup: remove dead VERB_TO_CONTRACT, Binder.command, Value::Result wrap, move human helpers to lanes/, delete agent/ and human/ dirs -- refactor: remove Value::Result wrapping from Gate dispatch -- cleanup: remove dead Binder.command method and Pending data class -- cleanup: remove dead VERB_TO_CONTRACT lookup table -- fix(scratchpad): use body for session storage, fix call forwarding -- feat(lanes): add Knowledge/Scratchpad handlers, remove old Human/Agent stubs -- refactor: move lane namespaces under Textus::Lanes:: (Ingest, Proposal, Artifact) -- refactor: move Gate::Builtin to Protocol::Handlers, add HandlerContext, update handlers to use ctx -- feat(protocol): add HandlerContext — restricted container view for handlers -- fix: add spec_helper constant stubs for Manifest/Envelope/Call/Entry/Mentry/Container -- cleanup: remove unused value types (Command, Trace) -- feat(protocol): add Session class with method_missing, replace Store in CLI/MCP surfaces -- feat(protocol): add Session class, replace Store facade in CLI and MCP surfaces -- refactor: move store classes into protocol/ (event_store, entry_store, cursor, freshness, trace_buffer) -- cleanup: move binder+contracts from dispatch/ to protocol/, remove empty dispatch/ dir -- cleanup: remove use_cases/ and dispatch/ middleware/pipeline after migration -- refactor(gate): route un-laned verbs to Gate::Builtin, remove pipeline path -- feat(protocol): add Gate::Builtin with 20 internal handler methods -- feat(proposal): add Proposal::Handlers with propose/accept/reject/diff -- refactor(human): move proposal handlers to Proposal lane (was Human lane) -- cleanup: remove migrated proposal use cases (propose/accept/reject/diff) -- feat(protocol): wire propose/accept/reject/diff verbs to Human lane -- feat(human): add Handlers.diff for proposal diff preview -- feat(human): add Handlers.reject for proposal rejection -- feat(human): add Handlers.accept for proposal acceptance with dry-run -- feat(human): add Handlers.propose for proposal creation -- cleanup: remove migrated UseCases::Ops::IngestEntry -- feat(protocol): wire ingest verb to Ingest lane via VerbSpec lane: field -- feat(ingest): rebuild Handlers with real ingest orchestration -- feat(ingest): rebuild Resolver with real supersede logic -- feat(ingest): add EntryTypes sub-modules for link/asset/text content builders -- feat(ingest): add IndexRebuilder for event store index rebuild -- feat(ingest): add Dedup for content hash and URL duplicate detection -- feat(ingest): add KeyBuilder for key derivation and content hashing -- feat(protocol): Gate dispatches directly to lane handlers when VerbSpec has lane -- feat(protocol): add Lane.handler_for to resolve lane name to handler module -- feat(protocol): add lane: field to VerbSpec -- feat(infra): add Store::Index::Lookup and Store::Index::Builder classes -- Implement Protocol::Manifest and related schema components -- Add core infrastructure for Textus event processing and storage -- Refactor Textus to use Protocol namespace -- refactor: remove dead code, fix Gate authorization, drop audit_events table -- chore: remove accidentally committed backup files -- phase(cleanup): remove orphaned SqliteAdapter — Phase 5 -- phase(surface): update Watcher to use Protocol::Async and Infra::Locks — Phase 4 -- phase(lanes): add scoped Workflow::ScopedContext — Phase 3 complete -- feat(human): create Textus::Human with LOOP Runner, Freeform, and handlers -- feat(agent): create Textus::Agent with Session, Workspace, Runner, Evidence, and handlers -- feat(artifact): create Textus::Artifact with cache, lifecycle, and handlers -- feat(ingest): create Textus::Ingest with resolver, entry types, and handlers -- phase(protocol): add Gate, Async, wire into Builder — Phase 2 complete -- feat(protocol): add Protocol::Manifest alias for Textus::Manifest -- phase(protocol): move Index, Links, Audit, Envelope to Protocol:: namespace with forwarding facades -- phase(protocol): move Layout, Key, Schema to Protocol:: namespace with forwarding facades -- feat(protocol): move Format strategies to Protocol::Format with forwarding facades -- chore: ignore headroom backup file -- feat(infra): wire Infra::FileStore, Infra::Database, Infra::Clock in Store::Builder -- feat(infra): extract Infra::Locks with BuildLock and WatcherLock -- feat(infra): extract Infra::Clock with now method -- feat(infra): extract Infra::Database with table setup, FTS5, and transaction support -- feat(infra): extract Infra::FileStore with 17 methods matching current FileSystem interface -- refactor: hard-cut legacy workflow step syntax -- fix: add lsp defaults to opencode workflow config -- fix: restore workflow artifact generation and docs publish -- fix: harden publish/parser and resolve lint regressions -- docs: mark ADR-0125 container/double-emission notes superseded -- refactor: group port by concern (storage vs concurrency) -- refactor: rename store/jobs and fold materialize into workflow/instances -- fix: protect .textus/data canon from direct agent writes -- fix: remove stale jobs CLI help text -- refactor: extract MCP contract-drift annotation from Server#dispatch -- refactor: fold entry indexing into the synchronous write path -- fix: wire Consumer and EventEmitter callers to EventStore -- refactor: Produce::EventEmitter depends on EventStore -- fix: Events::Consumer retry path delegates to EventStore and RetryPolicy -- refactor: trim Store::EventStore to only own the events table -- fix: add Store::EntryIndex and correct entries FTS indexing/search -- refactor: extract Events::RetryPolicy as its own decision object -- fix: rename db -> database in consumer.rb; move ADR 0127 to .textus via textus put; add ADR 0129 DependencyAdapter gate -- style: fix rubocop enable directives in write_pipeline and event_store -- fix: missing keyword database in converge_now drain call -- docs: add ADR 0127 — interface consolidation decisions -- chore: remove legacy code — Entry::Reader/Writer refs, dead LinkEdgeStore spec -- fix: drain_store call recursion, boot protocol key, backward compat methods -- fix: use respond_to? in HandlerResolver, fix BootStore Infrastructure ref -- style: fix remaining rubocop offenses -- fix: Boot.CLI_VERBS uses Catalog.build -- style: fix all rubocop offenses -- refactor: route ports through FileSystem, split Boot -- feat: add Events middleware, convert all use cases to classes -- fix: add trace_buffer/reader/writer accessors to Container, update specs -- refactor: create Store::LinkGraph, rewire Builder to Container -- refactor: create Store::EntryStore, retire Entry::Reader + Entry::Writer -- feat: add Store::Container with grouped components -- refactor: split Port::Store into Port::Database + Port::EventStore -- refactor: create Port::FileSystem + Store::FileSystem, retire Storage/ -- refactor: split WriteStep into write_pipeline/ directory -- refactor: extract VerbSpec into verb_registry/verb_spec.rb -- refactor: extract ArgSpec into verb_registry/arg_spec.rb -- fix: skip publish in Runner when data is nil, remove publish from system workflows that manage their own output; fix pulse_entries, LinkEdgeStore, CLI verbs catalog, specs for architecture changes; all 1014 tests pass -- feat: implement new event system with Sequel-backed events table, Workflow DSL handles, Registry, EventEmitter, Consumer, Materialize/Index workflows, rewire Builder/Infrastructure/Watcher/Drain/LinkEdgeStore -- refactor: remove Event::Bus, Event module, CascadeSubscriber, Store::Jobs namespace -- refactor: remove write-time schema validation from WriteStep and Writer -- refactor: remove rule system from manifest, data.rb, contracts, verb registry, CLI, auth, publisher, ttl evaluator, retention sweep -- refactor: remove enqueue verb -- refactor: remove doctor verb and module -- feat: add sequel gem and SequelAdapter dependency adapter -- refactor: move knowledge sections under knowledge.loop.* — clean separation of loop vs reference data -- refactor: streamline conformance fixture — drop project-specific entries, align with repo shape -- refactor(init): use consolidated entries for project-agnostic scaffold -- refactor: align init and fixture entry names with .textus/manifest.yaml -- refactor: add naming/schema governance to repo manifest entries -- refactor: complete manifest entry simplification — align init, fixtures, helpers to repo shape -- refactor(test-support): match repo entry shape; remove migrate_legacy_manifest -- refactor: simplify repo manifest entries; fix resolve_format for explicit path override -- refactor(init): simplify DEFAULT_MANIFEST and AGENT_ENTRIES — match repo entry shape -- refactor: delete IgnoreMatcher; remove ignore system from Resolver, SubtreeMirror, Tree, schema, validators -- refactor(nested): remove ignore/ignored? — no longer read from manifest -- refactor(base): remove ignore/ignored? stubs; make schema/naming optional -- refactor(parser): stop reading schema/naming/ignore/publish/source from manifest; infer kind/format from disk -- docs: design doc for manifest entry simplification -- feat: protocol textus/4 — manifest data: shape, roles/lanes hardcoded -- refactor(manifest): drop owner from envelope and where output -- refactor(manifest): Entry::Parser infers kind from lane+disk shape, adds naming:, drops owner:/format: -- refactor(manifest): Data.parse reads data: grouped-by-lane, rejects stale roles:/lanes:/owner: -- refactor(manifest): repoint write-gating predicates to Protocol::V4::LANES writers -- refactor(manifest): Domain::Lane and Policy read writers from Protocol::V4::LANES directly -- feat(manifest): add Textus::Protocol::V4 fixed lane/naming table -- test: relax build-lock drain soft-miss assertion -- chore: satisfy pre-push rubocop naming rules -- chore: clean up lint warnings in workflow and jobs code -- refactor(doctor): batch-port remaining class checks to workflows -- refactor(doctor): port PublishTreeIndexOverlap check to a workflow -- refactor(doctor): port OrphanedPublishTargets check to a workflow -- refactor(doctor): port SchemaViolations check to a workflow -- refactor(doctor): port UnownedSchemaFields check to a workflow -- refactor(doctor): port AuditLog check to a workflow -- refactor(doctor): port Sentinels check to a workflow -- refactor(doctor): port IllegalKeys check to a workflow -- refactor(doctor): port SchemaParseError check to a workflow -- refactor(doctor): port ProtocolVersion check to a workflow -- refactor(loop): renumber scratchpad.notes to NNNN-topic-slug.md, scaffold sessions/scripts -- feat(workflow): add shape() DSL primitive, check_naming helper, loop-shape workflow -- feat(loop): add loop summary to boot orientation output -- feat(loop): add Loop node table to explanation concepts template -- refactor(loop): realign task 6/7 identity and readme extraction -- refactor(loop): fold knowledge.readme fragments into intent -- refactor(loop): fold knowledge.project into intent section -- refactor(loop): retire knowledge.architecture into loop sections -- feat(loop): add knowledge.feedback zone -- refactor(loop): split knowledge.patterns into judgment.engineering/.agent-behavior -- refactor(loop): split knowledge.rules/specs into constraint.repo/.protocol -- refactor(verbs): standardize verb-registry surfaces, naming, jobs split, error typing -- refactor(architecture): deepen v2 — flatten jobs ceremony, extract publisher/validator/stepscope, delete facade -- feat(architecture): integrate container's writer and reader into use cases and workflow runner -- docs(architecture): sync docs to phase 3 refactor — Infrastructure container, workflow DSL surface, no Registry/Collector/Publisher -- refactor(architecture): deepen phase 3 — Writer port, single Infrastructure, manifest→workflow boundary -- Revert "feat(federation): add federation sync workflow — auto-mirrors remote store entries on drain" -- feat(federation): add federation sync workflow — auto-mirrors remote store entries on drain -- feat(workflow): migrate 4 more Doctor checks to validation workflows -- feat(workflow): migrate Doctor::RuleAmbiguity to self-contained validation workflow -- feat(workflow): add validate step type, multi-key matching, and validation workflow POC -- chore: revert solid-process POC (gem not mature enough) -- refactor(specs): remove 6 redundant files, simplify 5 overlapping specs, drop 6 dead refs -- feat(pipeline): enforce cross-reference chain — decisions→architecture, patterns→decisions, runbooks→patterns -- chore: regenerate changelog -- chore: track knowledge.architecture entries and manifest -- feat(patterns): add unified-dispatch, middleware-chain, handler-needs, store-builder + agent training -- docs: document 6 intentional Reader/Writer bypass sites (P3) -- refactor: drop Ctx alias, rename ctx.rb → infrastructure.rb -- refactor: clean up P0-P2 anti-patterns — HANDLES_ALL, boot.rb layering, InfrastructureProxy rename, Store::Builder -- feat(knowledge): enforce pipeline structure with conformance spec + agent protocol -- feat(observability): Trace middleware — auto-instrumented dispatch with ring buffer (Phase 5) -- feat(conformance): verb completeness, downward layer import, port contract guards (Phase 4) -- feat(ports): formal port interfaces with conformance verification (Phase 3) -- feat(domain): extract Domain::Key, Domain::Lane, Domain::Envelope — pure domain layer (Phase 2) -- feat: land ADR-0120, ADR-0121, ADR-0125 — unified dispatch, bounded use cases, interface hygiene -- refactor: remove unused handler modules and simplify dispatch logic -- docs: refresh generated changelog and skills feed -- refactor: simplify entry read handlers and pulse fallback -- fix: align trigger catalog and etag conformance guards -- docs: add dependency adoption gate guidance -- docs: add architecture patterns for triggers and adapters -- refactor: centralize trigger vocabulary in TriggerCatalog -- refactor: add dependency adapter modules for runtime deps -- refactor: model jobs and produce results with outcome values -- refactor: extract drain jobs pulse per-verb handlers -- refactor: extract key_mv key_delete data_mv handlers -- refactor: extract get put list per-verb handlers -- refactor: add concern handlers for dispatch domains -- refactor: narrow handler invocation to keyword interface -- refactor: route CLI and MCP via VerbDispatch -- refactor: introduce Infrastructure aliases and dispatch seam baseline -- feat: implement ADRs 0121-0125, including bounded use-case objects, workflow parallel steps, and session resilience; update changelog and context documentation -- feat: add ADR-0125 for Bounded Use-Case Objects and related documentation; update manifest and CLI argument formatting -- refactor: consolidate handlers into UseCases and remove Orchestration layer -- feat: Implement ADRs 0121-0124 — link graph, proposal diff, session resilience, parallel workflows -- chore: fix 3 rubocop offenses and regenerate docs -- fix: rule_trace verb category should be :read, not :maintenance -- feat: add rule_trace verb — trace rule resolution candidates, winners, and effective RuleSet -- refactor: decompose Writer#move into WriteStep::DEFAULT_MOVE chain -- refactor: decompose Writer#delete into WriteStep::DEFAULT_DELETE chain -- refactor: two-phase Manifest.build — derived_entry? now correct -- refactor(store)!: delete deprecated Container, Assembler, Cascade middleware -- refactor(store): build Ctx+HandlerResolver pipeline internally -- refactor: convert all write and maintenance handlers to pure modules (HANDLES/NEEDS/self.call) -- refactor: convert all read handlers to pure modules (HANDLES/NEEDS/self.call) -- feat: add HandlerResolver — discovers pure handler modules by convention -- feat: add CascadeSubscriber — event-driven replacement for Cascade middleware -- feat: add Store::Ctx, typed Event structs, and session-scoped Event::Bus -- feat: Introduce Boot.wire and Typed Events -- refactor: drop positional-arg support from entry/ops/rule — keyword args only -- feat: replace generated verb methods with entry/ops/rule noun-domain API on Store -- refactor: replace Container#build_pipeline with declarative HANDLER_MANIFEST in Dispatch::Assembler -- refactor: decompose Entry::Writer#put into WriteStep::DEFAULT_PUT chain -- chore: save architecture redesign plan to scratchpad -- refactor: remove manual uid from all workflows — textus manages uid natively via inject_all -- fix(ci): stable uid in architecture-index — use JSON.generate instead of Hash#inspect (Ruby 3.3 vs 3.4 diverge) -- fix(ci): fetch-depth: 0 on docs job — git log needs full history for changelog workflow -- refactor: clean up textus.rb — inline MCP namespace stub, remove dead ignores, drop duplicate Textus.workflow -- fix: gemspec — drop partial docs/ from shipped files, ship code + README + CHANGELOG only -- fix: gemspec — remove stale SPEC.md reference, update protocol version to textus/4, point docs_uri to README -- feat: Claude Code hooks — block direct writes to textus-managed artifacts -- chore: add textus drain to pre-push hook + docs-protocol-guard on pre-commit -- refactor(Move1): rename Produce::ContextHelpers → Textus::ContainerHelpers — neutral top-level module used by both publish and workflow contexts -- refactor(M3): rename Render::Context#binding → #to_erb_binding — avoids shadowing Ruby built-in -- refactor(M2): remove silent StandardError rescue in read_family — reader.read returns nil for absent files -- refactor(M1): drop edge_store param from Publisher — ToPaths reads container.link_edge_store directly -- fix(critical): Container#link_edge_store shared — Engine + Workflow::Runner both record edges, RdepsEntry queries same instance (C1+C2) -- chore: drop redundant require_relative — Zeitwerk autoloads lib/textus/** -- refactor: extract ContextHelpers — manifest/repo_root shared across PublishContext + Workflow::Context (Audit #4) -- refactor: Container#read_family — hide manifest.resolver.enumerate from workflows (Audit #5) -- feat: wire LinkEdgeStore recording through Engine → Render → UriRewriter (ADR-0121 complete) -- refactor: move link rewriting into Render — ToPaths drops UriRewriter dependency (Audit #2) -- refactor: extract Render::Context — testable binding factory (Audit #1) -- feat: ADR-0121 Phase 1 complete — textus-native link resolution (filesystem mode) -- refactor: migrate template cross-links to textus_link helper -- feat: LinkEdgeStore + extend rdeps to include link dependency edges -- feat: add UriRewriter — post-process textus: URIs in rendered output -- feat: inject textus_link method into ERB template context via binding -- feat: add Textus::Links::Resolver — resolves textus:KEY to relative paths -- feat: add ADR-0121 — textus-native link resolution via textus: URI scheme -- feat: remove SPEC.md — spec lives in knowledge.specs/* via textus protocol -- fix: changelog filters merge commits (--no-merges), delete unmanaged CHANGELOG/COC files -- fix: remove orphaned docs/design/architecture.md sentinel -- refactor: rules key ordering, contributing/security/changelog/coc as artifacts -- fix: decisions log workflow — use container.reader.read, fix relative links in template -- refactor: move knowledge.spec → artifacts.spec, remove orphaned artifacts, integrate decision log -- chore: remove stale knowledge sources replaced by artifact equivalents -- chore: remove knowledge/cookbook source directory -- fix: clean orphaned sentinels/files, update decisions README link, restore ignore -- refactor: rename adr-log to decision log, remove docs/README.md index artifact -- refactor: prune legacy knowledge, remove cookbook, reorganize workflows+templates -- feat: convert all how-to, reference, cookbook, explanation docs to generated artifacts -- feat: wire artifacts.how-to.agents-mcp — pilot how-to artifact pattern -- feat: wire artifacts.architecture.index — generates lane/role tables + static layer layout -- feat: delete design/invariants monolith — replaced by atomic canon + assembler artifact -- feat: wire artifacts.design.invariants — assembles goals + rules atoms via workflow -- feat: decompose design/invariants into atomic knowledge.goals.* and knowledge.rules.* entries -- feat: add knowledge.goals and knowledge.rules nested families to manifest -- feat: add ADR-0120 — atomic canon + composed artifacts principle +### Features + +- add Knowledge/Scratchpad handlers, remove old Human/Agent stubs +- add HandlerContext — restricted container view for handlers +- add Session class with method_missing, replace Store in CLI/MCP surfaces +- add Session class, replace Store facade in CLI and MCP surfaces +- add Gate::Builtin with 20 internal handler methods +- add Proposal::Handlers with propose/accept/reject/diff +- wire propose/accept/reject/diff verbs to Human lane +- add Handlers.diff for proposal diff preview +- add Handlers.reject for proposal rejection +- add Handlers.accept for proposal acceptance with dry-run +- add Handlers.propose for proposal creation +- wire ingest verb to Ingest lane via VerbSpec lane: field +- rebuild Handlers with real ingest orchestration +- rebuild Resolver with real supersede logic +- add EntryTypes sub-modules for link/asset/text content builders +- add IndexRebuilder for event store index rebuild +- add Dedup for content hash and URL duplicate detection +- add KeyBuilder for key derivation and content hashing +- Gate dispatches directly to lane handlers when VerbSpec has lane +- add Lane.handler_for to resolve lane name to handler module +- add lane: field to VerbSpec +- add Store::Index::Lookup and Store::Index::Builder classes +- create Textus::Human with LOOP Runner, Freeform, and handlers +- create Textus::Agent with Session, Workspace, Runner, Evidence, and handlers +- create Textus::Artifact with cache, lifecycle, and handlers +- create Textus::Ingest with resolver, entry types, and handlers +- add Protocol::Manifest alias for Textus::Manifest +- move Format strategies to Protocol::Format with forwarding facades +- wire Infra::FileStore, Infra::Database, Infra::Clock in Store::Builder +- extract Infra::Locks with BuildLock and WatcherLock +- extract Infra::Clock with now method +- extract Infra::Database with table setup, FTS5, and transaction support +- extract Infra::FileStore with 17 methods matching current FileSystem interface +- add Textus::Protocol::V4 fixed lane/naming table +- add shape() DSL primitive, check_naming helper, loop-shape workflow +- add loop summary to boot orientation output +- add Loop node table to explanation concepts template +- add knowledge.feedback zone +- integrate container's writer and reader into use cases and workflow runner +- add federation sync workflow — auto-mirrors remote store entries on drain +- migrate 4 more Doctor checks to validation workflows +- migrate Doctor::RuleAmbiguity to self-contained validation workflow +- add validate step type, multi-key matching, and validation workflow POC +- enforce cross-reference chain — decisions→architecture, patterns→decisions, runbooks→patterns +- add unified-dispatch, middleware-chain, handler-needs, store-builder + agent training +- enforce pipeline structure with conformance spec + agent protocol +- Trace middleware — auto-instrumented dispatch with ring buffer (Phase 5) +- verb completeness, downward layer import, port contract guards (Phase 4) +- formal port interfaces with conformance verification (Phase 3) +- extract Domain::Key, Domain::Lane, Domain::Envelope — pure domain layer (Phase 2) + +### Bug Fixes + +- use body for session storage, fix call forwarding +- stable uid in architecture-index — use JSON.generate instead of Hash#inspect (Ruby 3.3 vs 3.4 diverge) +- fetch-depth: 0 on docs job — git log needs full history for changelog workflow +- Container#link_edge_store shared — Engine + Workflow::Runner both record edges, RdepsEntry queries same instance (C1+C2) ## v0.55.2 — 2026-06-30 -- chore: bump version to 0.55.2 -- docs: fix 15 stale content issues found in audit -- chore: sync how-to store source files and refresh boot artifact -- docs: rewrite how-to guides — add MCP protocol lifecycle, remove stale content -- fix: harden MCP server dispatch and resource handling -- fix: scratchpad sources doctor check handles object-form sources -- feat: expand sources with suspended flag at get time -- feat: normalize sources to {key,etag} objects at write time -- refactor: harden API and interface boundaries across Store, MCP, and registry -- refactor: update all notebook→scratchpad references in docs, specs, lib, and store data -- refactor: rename notebook lane to scratchpad across manifest, docs, lib, and specs -- feat: extend list with q: FTS and schema: filter via SQLite entries index -- feat: shadow writes to SQLite audit_events, pulse reads from index with flat-log fallback -- feat: consolidate boot into artifacts.boot, drain computes stable content, boot injects live fields -- chore: remove generated reference docs, superpowers scratch dirs, fix broken links -- split Freshness::Evaluator into TtlEvaluator + DriftDetector -- flatten Container wiring — remove hypothetical HandlerFactoryRegistry+Adapter seam -- cascade delegates reactive enqueue to Planner — removes manifest-walking from middleware -- extract Produce::Publisher — single seam for publish-after-materialise -- fix: correct indentation in write_entry method for better readability -- refactor: group handlers into read/write/maintenance subdirs (S-1) -- refactor: rename Store::Envelope::Reader/Writer → Store::Entry::Reader/Writer (I/O for entries, not envelopes) -- refactor: move Handlers::Orchestration → Textus::Orchestration (not a handler, shared service) -- refactor: rename Store::Geometry → Store::Layout (filesystem layout, not spatial geometry) -- refactor: inline Dispatch.dispatch into Store, move unwrap to Value::Result.extract -- refactor: private_constant VERB_TO_CONTRACT/CONTRACT_TO_VERB, expose via contract_class_for -- refactor: rename IngestEntry :zone → :lane to complete ADR-0114 vocabulary migration -- refactor: rename Schema::Store → Schema::Registry (schema registry not a sub-store) -- refactor: rename Container.build_full → Container.build (no contrast remains) -- refactor: add Container#wire! to encapsulate post-construction boot mutation -- refactor: extract Pipeline I/O to direct Writer/Reader calls, reduce Pipeline to single dispatch method -- refactor: remove Store#as and Store#session, migrate all callers to with_role -- refactor: delete dead Dispatch::Middleware::Audit (unwired, inline Writer audit is the seam) -- refactor: delete dead handler registration path (build_pipeline + register_pipeline_registry_handlers) -- feat: add architecture and design invariants documentation; update job queue handling -- style(rubocop): align ADR-0121 refactor formatting -- refactor: api and interface hygiene consolidation (ADR-0121) -- fix(docs): regenerate adr-log.json to include new ADR 0117 -- fix(docs): deterministic ADR log uid across platforms; rubocop balance -- workflow(adr_log): produce deterministic uid for adr-log to avoid CI drift -- Regenerate docs artifacts: ADR log updated by textus drain --as=automation -- ci(rubocop): ignore var/ temp clones from drain runs -- chore(docs): add generated reference/specs files -- ci(docs): run textus drain as automation in CI to stabilise generated metadata -- chore(docs): regenerate docs via textus drain -- docs: annotate HandlerFactoryRegistry to note it replaces Builder seam -- docs(adr): record adoption of HandlerFactoryRegistry + Adapter for pipeline composition (ADR 0117) -- refactor(container): switch pipeline composition to HandlerFactoryRegistry + Adapter (Design B) -- feat(container): make pipeline composition pluggable; support HandlerFactoryRegistry+Adapter via TEXTUS_PIPELINE_ADAPTER=1 -- refactor(container): extract pipeline registry registrations to helper and simplify build_pipeline -- refactor(container): extract handler registration and writer factory helpers from build_full -- chore(ci): suppress method-size rubocop for build_full to satisfy CI -- chore: add Design B adapter + registry; fix style -- feat(design-b): add Pipeline Adapter and HandlerFactoryRegistry for alternative composition seam -- chore(deepening): incremental pipeline+reader/writer injection, format registry, file_store helpers -- docs: add engineering skills process contract -- refactor: remove dispatch session_default plumbing -- refactor: remove unused dispatch contract from_wire helpers -- refactor: deepen dispatch, orchestration, and geometry seams -- fix: update detect_skills to include descendant directories for SKILL.md -- style: suppress remaining lint/metrics for predicate interface and build_pipeline -- style: fix rubocop issues — line length, block alignment, suppress structural cops -- fix: update workflow Bus.dispatch → Dispatch.dispatch -- docs: update conventions.md, regenerate ref docs -- refactor: unify dispatch, rename Bus→Dispatch, re-home modules -- refactor(bus)!: Complete Gate-to-Bus migration with shared predicate classes -- refactor: replace action/contract layer with Bus pipeline architecture -- Architecture deepening phase 3 — write-path standardisation, container fix, format dedup -- textus 0.55.1 — CI fix: converge_now purges done jobs -- fix: purge done jobs before reseeding in converge_now to fix publish_tree prune test +### Features -## v0.55.0 — 2026-06-22 +- make pipeline composition pluggable; support HandlerFactoryRegistry+Adapter via TEXTUS_PIPELINE_ADAPTER=1 +- add Pipeline Adapter and HandlerFactoryRegistry for alternative composition seam -- textus 0.55.0 — Architecture Deepening Phase 2 (#234) -- Architecture-deepening: enforce 10 conventions structurally, kill dual paths (#233) -- feat: action composition + contract/impl split + container wiring (#232) -- feat: knowledge structure audit + sources as first-class envelope field (ADR-0118) (#231) -- refactor: split knowledge.readme into composed fragments + template prototype (#230) -- Architecture deepening — module restructuring, seam sealing, code smell cleanup (#229) -- feat: centralized SQLite index and job queue (#228) -- refactor: separate contract declaration from dispatch machinery (#227) +### Bug Fixes -## v0.54.2 — 2026-06-19 - -- feat: ingest dedup with supersede chain, .run → .state rename (#226) -- feat: restructure artifacts lane into config/docs/system concerns (#225) -- feat: workflow rename, feeds pipeline, raw/ingest docs, SPEC v4, docs audit (#224) - -## v0.54.1 — 2026-06-17 - -- feat: MCP SDK + dry ecosystem + ERB templates (v0.54.1) (#223) -- fix: use Time.now.utc in ingest spec to match action's key derivation -- fix: give artifacts.mcp explicit path to avoid collision with artifacts.mcp-config -- fix: sort MCP tool catalog alphabetically for cross-platform determinism -- chore: move templates into templates/artifacts/ to mirror workflow structure -- feat: promote events.md and mcp.md to produced artifacts -- feat: update index_key handling in boot process based on artifacts presence -- feat: add StaleReviewedStamp doctor check for knowledge doc currency -- chore: move SPEC.md into knowledge zone (knowledge.spec) -- chore: adopt contributor-conventions.md into knowledge zone -- fix: correct ADR filenames in CHANGELOG links (0114/0115/0116) -- chore: expand 0.54.0 CHANGELOG — covers all 3 PRs and standalone commits -- chore: release 0.54.0 -- fix: move string-described specs to conformance per SpecLayout rule -- fix: advertise resources capability in MCP initialize handshake -- refactor: move ContractDrift from MCP surface to core Textus::ContractDrift -- refactor: apply Sandi Metz rules to MCP Server — extract methods and Routing module -- chore: remove remaining --lean references from plugin manifest spec -- fix: claude_plugin uses plain textus (external use); mcp_config keeps bundle exec (dev) -- chore: refresh artifact files from drain -- fix: adr_log workflow needs include_keyless: true for tree-publish knowledge.decisions -- style: rubocop auto-fixes — alignment, redundant initialize -- refactor: slim pulse — remove stale/doctor/next_due_at, add index_etag, delete Scanner -- feat: add MCP resources/list and resources/read — exposes machine-lane artifacts -- refactor: boot reads from artifacts, removes --lean flag and inline computation -- feat: add artifacts.index produced workflow — pre-computed full-store catalog -- refactor: standardize all workflows — :build step name, content wrapper, fix commands -- chore: organise workflows into artifacts/ and config/ subdirectories -- chore: migrate artifact data files from artifacts/derived/ to artifacts/ -- refactor: flatten artifacts.derived.* to artifacts.* in manifest + workflow matches -- fix: rewrite adr_log + orientation workflows — use resolver.enumerate, fix List bug -- style: fix extra blank lines left by BURN deletion (rubocop) -- remove: delete dead derived_write? no-op guard from WriteVerb#cascade_to_rdeps -- remove: delete stale publish_each runtime guard from Entry::Publish (validator catches it at load) -- remove: drop duplicate inline check_action! from put/key_delete/reject (Gate covers these) -- remove: drop cross-lane notebook side-effect from Action::Ingest -- remove: delete dead BURN = :sync constant from all actions -- fix: move doctor check specs to spec/integration/doctor/check/ per layout rule -- feat: implement raw ingest pipeline — Action::Ingest, doctor checks, asset sentinel, ADR 0116 -- add gate predicates: raw_lane_ingest_only + raw_write_once -- add Command::Ingest struct and gate verb routing stub -- expand zone-kind bijection: add raw→ingest (ADR 0114) -- remove Jobs::Refresh — workflow covers periodic re-derivation (ADR 0115) -- fix: resolve all rubocop offenses (autocorrect + nest workflow errors spec) -- docs: rewrite architecture README for workflow redesign — remove Step system, add Workflow layer -- refactor: remove data/ prefix and explicit path: from spec manifest fixtures -- refactor: rename docs-readme→docs-index, architecture-readme→architecture-index; drop last explicit path: fields -- refactor: strip data/ prefix from manifest paths — implicit via normalize_relative_path -- feat: make path: optional in manifest entries — derive from key + format + kind -- remove: source: field narrowed to external-only keys; fetch/derive schema fields removed -- remove: strip fetch/derive methods from Produced; freshness/pulse use retention rules; boot drops intake/derived keys; deps/rdeps simplified -- breaking: narrow Manifest::Policy::Source to from: external only — fetch/derive removed -- remove: delete dead step/events/projection code (step system fully removed) -- fix: triage remaining failures — boot/mcp/spec-layout cleanup post workflow redesign -- fix: update spec fixtures and container/store specs for workflows Container field -- remove: delete stale specs for removed features (observers, events, steps, intake_registration) -- fix: keyword args on Produce::Engine; delete Textus::Projection; init scaffolds workflows/ not steps/ -- remove: strip final Step:: references from entry/base, role_scope, mcp/server -- remove: drop hook_errors from pulse output (step event bus removed) -- refactor: replace boot hooks output with workflows list; remove Step::Catalog refs -- remove: strip step system from doctor — delete IntakeRegistration check and run_registered_checks -- migrate: .textus/ steps -> workflows; strip source/handler/rules from manifest -- remove: delete dead hooks check and audit_subscriber (step system removed) -- remove: delete no-op Events validator (step system removed) -- feat: wire Workflow::Runner into Store; remove step/ and container.steps.publish -- feat: add Workflow system (errors, Context, Pattern, DSL, Registry, Loader, Runner) -- refactor: partially implement Task 5 (rename pipeline/ to produce/) -- remove: delete handler_permit (doctor-only field with no runtime enforcement) -- fix: remove accidentally committed produce/acquire/ copies with wrong constants -- refactor: rename pipeline/ to produce/ and update callsites -- refactor: rename background/ to jobs/ (Background::* → Jobs::*) -- refactor: rename background/ to jobs/ (Background::* -> Jobs::*) -- refactor: rename Ports::Queue to Ports::JobStore -- refactor: flatten envelope/io/ to envelope/ and rename Ports::Queue to Ports::JobStore -- refactor: flatten envelope/io/ to envelope/ -- refactor: rename entry/ format strategies to format/ +- regenerate adr-log.json to include new ADR 0117 +- deterministic ADR log uid across platforms; rubocop balance ## v0.53.0 — 2026-06-15 -- fix: rubocop line length in merged audit_log_spec.rb -- chore: regenerate generated files via textus drain -- chore: bump to 0.53.0 -- chore(dev): apply rubocop formatting -- chore(dev): add affaan-m/ecc as skill source -- refactor: replace hand-rolled mustache with mustache gem -- refactor: generate args method dynamically in Action::Base -- chore: add bin/dev for local skill sync from GitHub -- refactor: extract ErrorInfo parameter object for Error#initialize -- refactor: extract verify_integrity per-line checking into sub-methods -- refactor: break Init.run into focused class methods -- refactor: pull auth/writer/reader helpers into WriteVerb -- refactor: deduplicate Gate::Auth#check! and #check_action! -- refactor: extract Writer#put into composed methods -- fix(cli): restore role_filter in RoleScope verb dispatch -- refactor: rename Background::Planner::Planner → Planner::Plan (resolve double-name) -- refactor: remove redundant container: param from Gate#dispatch (always @container) -- chore: delete empty Textus::Dispatch module (dead since Dispatcher was removed) -- refactor: remove dead else branch and role_filter from RoleScope verb dispatch -- refactor: remove double auth in Accept; drop dead UnknownKey rescue from check_action! -- refactor: inline dispatch_bound into define_method; delete build_command+build_action from RoleScope -- fix: Background::Job registry O(1) hash lookup, delete stale domain/jobs spec, add pending_key guard test -- spec: remove obsolete target_is_canon tests (removed from accept FLOOR) -- docs: run textus drain — regenerate verbs ref, ADR log, hooks desc -- refactor: move Core::Jobs::Job to Ports::Queue::Job; delete orphaned Core::Jobs::Registry -- refactor: collapse Planner::Seeder+Scheduler into Planner.seed; unify manual.kick+schedule.tick into convergence trigger -- refactor: move job handlers to Background::Job:: with own registry; remove Action::Background:: -- docs: ADR 0115 — document role trust model as asserted identities with ambient authority -- fix: detect seq gaps and regressions in AuditLog#verify_integrity -- fix: thread caller role through Doctor::Check dispatch instead of hardcoding human -- fix: add 1MB message size ceiling to MCP server handle_line -- fix: route dispatch_bound through Gate for verb-based commands -- fix: add pending_key to Command::Propose; enforce propose+accept auth at Gate via FLOOR -- fix: remove AUTOMATION bypass from Gate::Auth#check! — capability check handles it -- refactor: replace command_to_action regex with VERB_COMMAND.invert constant -- fix: propose_spec path depth (../../../../ → ../../../) — CI at 0 failures -- fix: propose_spec path depth and etag_spec glob — 3 CI failures down to 0 -- docs: refresh architecture README for Surface → Command → Gate → Action; update how-to/contributor refs -- fix: restore empty-holders edge case in Gate::Auth; fix accept GuardFailed for UnknownKey -- refactor: clean Action::Background — remove dead BURN/Contract::DSL, simplify Action.fetch -- fix: remove Gate-level audit recording — actions handle their own; fix Audit role filter mapping -- refactor: install! and verbs.rb use Gate::ROUTES; replace store.as hook scope -- refactor: rename Dispatch::Planner/Pipeline/Runtime — eliminate Dispatch:: namespace -- fix: align Command structs with action kwargs, fix MCP dispatch -- chore: skip audit for read commands in Gate, final cleanup -- refactor: delete old dispatch layer (Dispatcher, Gate, Auth, Ledger, Executor, Event) -- feat: add Surfaces::Watcher — consolidates watch + schedule triggers -- feat: wire MCP surface to Gate -- feat: wire CLI surface to Gate — delete RoleScope -- feat: wire Gate into Container and Store -- refactor: rename Dispatch::Actions → Action namespace -- feat: add Textus::Gate with ROUTES map and Gate::Auth -- feat: add Gate::Auth — command-based auth wrapper -- feat: add Command namespace and Textus::Events constants -- test: delete specs for dispatch layer being replaced -- test: move string-described specs to conformance/ per spec_layout convention -- test(config): volatile tagging for dispatch/ and surfaces/ unit specs; move manifest fixture to conformance -- refactor(spec): update terminology and structure for lanes and sources -- test(manifest): live fixture contract for project .textus manifest -- test(core): integration spec for Retention::Sweep GC reporting -- test(core): integration spec for Freshness::Evaluator currency verdicts -- test(core): unit spec for Core::Sentinel orphan/drift predicates -- test(core): unit spec for Freshness::Verdict wire serialization -- test(support): add core domain doubles shared context -- docs: update README zone→lane vocabulary and hooks→steps extension model -- docs: update README source vocabulary (from: handler/project/command → fetch/derive/external) -- fix: restore event lifecycle, drain seeding, and produce_failed after consolidation -- chore: remove freshness.rb and pipeline/events.rb (dissolved in refactor) -- refactor: remove ValidateAll verb from Dispatcher::VERBS; role_authority spec calls Doctor::Validator directly -- docs: add stable/volatile spec convention; delete completed superpowers docs -- test: add specs for new actions and planner; update fixture callers -- test: delete stale unit specs for dissolved layers -- feat: replace ACTIONS_BY_TRIGGER with rules-driven planner -- feat: route async job execution through Dispatch::Gate; delete Handlers registry -- refactor: migrate intake steps.publish calls to Gate events -- refactor: delete Domain::Action stub — no callers -- refactor: delete Domain::Action stub — no callers -- refactor: delete capabilities verb — Dispatcher::VERBS is the static contract -- refactor: move produce/ to dispatch/pipeline/ -- refactor: move maintenance/ to dispatch/runtime/ -- refactor: move jobs/ to dispatch/planner/ -- test(dispatch): migrate verb specs to actions layout -- refactor(dispatch): migrate runtime verb paths to actions -- refactor!: complete foundation refactor and dispatch gate rollout +### Features + - feat(core)!: cut over zones to data and adopt watch runtime -- refactor(step): remove hook-era surfaces and restore full-suite green -- refactor(step): wire Step::Registry into Container + Store, drop events/rpc fields -- feat(step): port the five built-in fetch handlers to Step::Fetch -- feat(step): add convention-discovery Step::Loader -- feat(step): add Step::Registry unifying observe bus + invocable table -- feat(step): add Discovery for path -> (kind, name) -- feat(step): add Step base classes for the five step kinds -- Configure opencode.json and enhance agent orientation -- feat: integrate opencode configuration and orientation -- docs: publish ADR 0113 + regenerate adr-log (drain) -- feat: proposal block speaks _meta, not frontmatter (ADR 0113) -- chore: release textus 0.52.0 (ADR 0112 — produced authority reference) -- docs: ADR 0112 + dedupe authority tables into generated authority.md -- feat: project the authority model as docs/reference/authority.md (ADR 0112) -- feat: add authority handler projecting the lane/role/zone model (ADR 0112) -- refactor: finish converge rename in SPEC.md + example manifest (ADR 0111) -- refactor: rename reconcile capability to converge; sweep residual debt (ADR 0111) -- test(queue): make #198 conformance green for added drain/jobs/serve surface -- docs(queue): republish root files (README/CLAUDE/AGENTS/CONTRIBUTING/SECURITY) for drain rename -- docs(queue): complete reconcile→drain rename in canon docs + templates -- docs(queue): regenerate verbs.md for drain/jobs (docs-freshness gate) -- feat(queue): add enqueue verb — general runner bounded by the allow-list -- docs(queue): ADR 0110 — job queue model, drain/serve rename, async-only +- port the five built-in fetch handlers to Step::Fetch +- add convention-discovery Step::Loader +- add Step::Registry unifying observe bus + invocable table +- add Discovery for path -> (kind, name) +- add Step base classes for the five step kinds +- add enqueue verb — general runner bounded by the allow-list - feat(queue)!: hard-rename reconcile verb to drain/serve; regenerate docs -- feat(queue): scheduler seeds TTL re-pull/sweep into serve tick -- fix(queue): re-materialize dependents on delete/rename (ADR 0087 gap) -- feat(queue): drop source.on_write knob (materialize is async-only) -- refactor(queue): retire in-process AsyncRunner (queue is the only deferral) -- feat(queue): write subscriber enqueues materialize (async-only) -- test(queue): satisfy spec-layout guard (constant-described; worker_config is integration) -- test(queue): drain/reconcile convergence parity (effect, not result shape) -- feat(queue): add serve daemon (tick = reclaim + serial drain) -- feat(queue): add jobs verb (list/retry/purge) -- feat(queue): add drain verb (seed + serial drain + health) -- feat(queue): add Jobs::Seeder (mirrors Reconcile produce_scope) -- feat(queue): register materialize/re-pull/sweep convergence handlers -- refactor(reconcile): extract Retention::Apply for reuse by sweep handler -- feat(queue): add manifest worker: config (pool/poll/lease_ttl/max_attempts) -- feat(queue): add Worker.drain_pool (N-thread, exactly-once) -- feat(queue): add Maintenance::Worker single-pass drain -- feat(queue): add Domain::Jobs::Registry closed allow-list -- feat(queue): add lease reclaim + cover concurrent single-claim -- feat(queue): add lease (atomic claim), ack, fail with dead-letter -- feat(queue): add Ports::Queue enqueue with dedup + ready_ids -- feat(queue): add Domain::Jobs::Job with stable dedup id -- feat(queue): add Layout.queue paths under .run/queue - -## v0.51.0 — 2026-06-08 - -- textus 0.51.0 — the reconcile era + SPEC sync (#196) -- ADR 0109 — board-exact schema split + single port shape (supersede 0107/0108) (#195) -- ADR 0108 — name + document the two port shapes (don't convert) (#194) -- ADR 0107 — split manifest/schema.rb: data vs validation walk (#193) -- ADR 0106 — make the hexagonal layering executable (#192) -- ADR 0105 — verb routing ⟺ contract bijection (#191) -- docs: ADR 0104 — finish the root-doc canon move (CONTRIBUTING + SECURITY) (#190) -- ADR 0100/0101 — produce/ topology + interface fossils (#189) -- ADR 0099 — one Freshness evaluator (collapse the staleness family) (#188) -- ADR 0098 — docs SSoT/DRY/SOLID cleanup (produce vs guard) (#187) -- ADR 0097 — reference docs become produced (verbs, schema, ADR log) (#186) -- test(conformance): consolidate the conformance spec tier (67→19 loose, zero coverage loss) (#185) -- ADR 0095: collapse derived/intake into one produced kind (#184) -- ADR 0094: source produces data, publish renders (#183) -- ADR 0093 — source + retention over one reconcile engine (incl. ADR 0092) (#182) -- ADR 0092: spec-suite churn-resistance — fixture vocabulary, retired-token guard, conformance split (#181) -- ADR 0091 — one `machine` zone-kind; entry-kind is the single discriminator (#180) -- Automation is one capability + one `upkeep` policy (ADR 0090) (#179) -- Reconcile-era design cleanup (WS3/WS2/WS4/WS5 — ADR 0088, 0089) (#178) -- fix: scrub deleted fetch/fetch_all/build verbs from agent-facing strings (WS1) (#177) -- Fold build into reconcile (ADR 0087) (#176) - -## v0.50.0 — 2026-06-05 - -- textus 0.50.0 — observability verbs + boot lifecycle + textus-managed agent config (#175) - -## v0.49.0 — 2026-06-04 - -- textus 0.49.0 — normalize the key-verb family + remove migrate (ADR 0082) (#174) -- textus 0.48.0 — docs become canon (ADR 0081) (#173) -- test(suite): split specs into unit/integration/conformance + SOLID foundation (ADR 0080) (#172) -- feat: unify staleness + retention into one lifecycle policy (ADR 0079) (#171) +- scheduler seeds TTL re-pull/sweep into serve tick +- drop source.on_write knob (materialize is async-only) +- write subscriber enqueues materialize (async-only) +- add serve daemon (tick = reclaim + serial drain) +- add jobs verb (list/retry/purge) +- add drain verb (seed + serial drain + health) +- add Jobs::Seeder (mirrors Reconcile produce_scope) +- register materialize/re-pull/sweep convergence handlers +- add manifest worker: config (pool/poll/lease_ttl/max_attempts) +- add Worker.drain_pool (N-thread, exactly-once) +- add Maintenance::Worker single-pass drain +- add Domain::Jobs::Registry closed allow-list +- add lease reclaim + cover concurrent single-claim +- add lease (atomic claim), ack, fail with dead-letter +- add Ports::Queue enqueue with dedup + ready_ids +- add Domain::Jobs::Job with stable dedup id +- add Layout.queue paths under .run/queue + +### Bug Fixes + +- restore role_filter in RoleScope verb dispatch +- re-materialize dependents on delete/rename (ADR 0087 gap) ## v0.47.1 — 2026-06-04 -- Release textus 0.47.1 — External entries are a non-build path (#170) -- fix(build): make External a non-build path; parse compute.command (#169) - -## v0.47.0 — 2026-06-04 - -- Release textus 0.47.0 — close the agent loop over MCP (#168) -- test(spec-layout): guard the spec/ ↔ lib/textus/ mirror (#167) -- init --with-agent profile + surface build to MCP (ADRs 0076/0077) (#166) -- Contract-etag drift guard + session_opened connect event (ADR 0074, 0075) (#165) -- test: consolidate two duplicate spec examples (zero coverage loss) (#164) - -## v0.46.0 — 2026-06-03 - -- ADR 0073: surfaces declares external projections; Ruby is the implicit base (#163) -- Integration feedback from patrick-nexus: F1–F7 (#161) (#162) -- docs: README — correct the .textus/ tree (audit log lives under .run/) (#160) -- docs: README — surface store discovery + TEXTUS_ROOT (#159) -- docs: README readability — hook tables + newcomer-friendlier opening (#158) -- docs: freshness audit — verb drift, event-catalog dedup, output phrasing (#157) - -## v0.45.1 — 2026-06-03 - -- 0.45.1 — streamline: kill the last dual-paths (ADR 0069) (#156) -- 0.45.0 — the contract owns the request lifecycle (ADRs 0066–0068) (#155) -- textus 0.44.1 — finish the CLI contract projection (ADR 0065) (#154) -- 0.44.0 — one verb name across surfaces; CLI is a contract projection (ADRs 0058–0063) (#153) - -## v0.43.2 — 2026-06-02 +### Bug Fixes -- textus 0.43.2 — agent-legible MCP contracts (ADR 0057) (#152) +- make External a non-build path; parse compute.command (#169) ## v0.43.1 — 2026-06-02 -- textus 0.43.1 — boot agent surface derives from the MCP catalog (ADR 0056) (#151) -- feat(boot): derive read_verbs from the MCP catalog; recipes reference verbs (ADR 0056) + draft entry-level desc (ADR 0054) (#150) -- docs: fix load-invalid owner archetypes + refresh reviewed stamps (#149) +### Features + +- derive read_verbs from the MCP catalog; recipes reference verbs (ADR 0056) + draft entry-level desc (ADR 0054) (#150) ## v0.43.0 — 2026-06-02 +### Features + - feat(publish)!: typed publish: block + remove index_filename (ADR 0052, 0053) (#148) ## v0.42.0 — 2026-06-02 +### Features + - feat(publish)!: remove publish_each — collapse to two modes (ADR 0051) (#147) ## v0.41.0 — 2026-06-02 -- textus 0.41.0 — publish_tree subtree mirror + content-identical publish adoption (#146) -- feat(publish): adopt a byte-identical target instead of refusing (ADR 0050) (#144) -- refactor(publish): resolve publish mode into a sum type; one shared subtree mirror (ADR 0049) (#143) -- ADR 0048 — fetch subsystem: one home per concern (#142) -- fix(gemspec): package the docs that moved — gem shipped no architecture/conventions doc (#141) -- publish_tree: a key-less subtree mirror for a derived-index leaf (ADR 0047) (#140) -- test: modernize spec suite onto current lane vocabulary (#139) +### Features -## v0.40.0 — 2026-06-02 +- adopt a byte-identical target instead of refusing (ADR 0050) (#144) -- textus 0.40.0 — publish_each owns multi-file leaf subtrees (ADR 0046) (#138) -- publish_each copies a leaf's whole subtree when index_filename is set (ADR 0046) (#136) -- Owner-subject validation at load (#135 items 1+2) (#137) -- Close the role-name set to {human, agent, automation} (ADR 0045) (#134) -- Resolve system actors by capability (ADR 0044) + close role-name set (ADR 0045, proposed) (#133) -- refactor(hooks): Catalog as single source of truth for event tables (#131) +### Bug Fixes -## v0.39.1 — 2026-06-01 +- package the docs that moved — gem shipped no architecture/conventions doc (#141) -- textus 0.39.1 — feed ergonomics: feeds.machine env snapshot + intake cookbook (ADR 0043) (#130) -- feat(init): scaffold feeds.machines.* (nested) with a local env snapshot (ADR 0043) (#128) -- docs(how-to): fix zone-rename drift in agents-mcp + :publish event name (#129) -- cookbook: multi-machine environment-scan recipe (nested feeds.machines.*) (#127) -- examples: use feeds.machine env snapshot, matching `textus init` (#126) -- ADR 0043 — feed ergonomics without breaking core purity (#125) -- chore: remove legacy ARCHITECTURE.md redirect stub (#124) -- docs(readme): redesign flow diagram — group writers, colour-code roles (#123) +## v0.39.1 — 2026-06-01 -## v0.39.0 — 2026-06-01 +### Features -- textus 0.39.0 — native ignore patterns for entry enumeration (ADR 0042) (#122) -- docs(readme): add trust×durability quadrant; sharpen the flow diagram (#121) -- feat: native ignore patterns for entry enumeration + doctor (ADR 0042) (#120) -- feat: dogfood textus in its own repo — self-dev store + MCP wiring (ADR 0041) (#118) -- docs: fold docs/ into Diátaxis directories (#117) +- scaffold feeds.machines.* (nested) with a local env snapshot (ADR 0043) (#128) ## v0.38.0 — 2026-05-31 -- textus 0.38.0 — MCP serve acts as `agent` by default (ADR 0040) (#116) -- textus 0.37.0 — MCP catalog derive-or-guard (ADR 0039) (#115) -- feat: runtime artifacts under .run/, Textus::Layout owns the on-disk map (ADR 0038) (#113) -- ADR 0037: boot/pulse derive-or-guard — anti-drift contract specs (#114) -- docs(examples): consolidate to a single reference example (project) (#112) -- textus 0.36.0 — transports as pure framings: one verb vocabulary + lifted session (ADR 0036) (#111) -- docs: fix confirmed drift against v0.35.1 (capability canon, §9 verbs, write/ listing, 0.35 notes) (#110) -- fix(gemspec): align metadata with current project (textus/3 + coordination-space description) (#109) -- textus 0.35.2 — Evaluation field rename + Container doc fix (internal) (#108) - -## v0.35.1 — 2026-05-31 - -- textus 0.35.1 — RSpec foundation consolidation (test-only) (#107) -- textus 0.35.0 — proposal target-canon constraint + author_held (ADR 0035) (#106) -- textus 0.34.0 — unify the Lane vocabulary + finish boot's kind-derived zone naming (ADR 0034) (#105) -- test(spec): build shared RSpec foundation and migrate the suite onto it (#104) -- textus 0.33.0 — complete primitive set (workspace + keep) + vocabulary (ADR 0031→0033) (#103) -- textus 0.32.1 — unified-Guard spec cleanup (#102) -- textus 0.32.0 — unified Guard engine (ADR 0031) + drop read_policy (ADR 0032) (#101) -- textus 0.31.0 — capability-based roles (ADR 0030) (#100) -- docs: refresh content to current v0.30 behavior (#99) -- Add brand identity: "Lanes" logo, asset set, and DESIGN.md (#98) -- docs: restructure for clarity + maintainability (#97) - -## v0.30.0 — 2026-05-29 +### Bug Fixes -- textus 0.30.0 — mandatory zone kind (strict) + retention (ADR 0028 moves 1 & 4) (#96) -- textus 0.29.2 — converge hook registries; de-leak MCP transport (ADR 0027) (#95) -- textus 0.29.1 — use-case construction seams (ADR 0026) (#94) - -## v0.29.0 — 2026-05-29 - -- textus 0.29.0 — Honest Domain (domain purity via ports) (#93) -- textus 0.28.0 — consistency sweep & legacy cleanup (#92) -- textus 0.27.0 — architecture redesign (Container + Call + Dispatcher) (#91) -- docs: cleanup — version drift, SPEC↔ARCH alignment, OSS scaffolding (#90) - -## v0.26.0 — 2026-05-28 - -- textus 0.26.0 — architecture consolidation (#89) -- textus 0.25.1 — application layering (Ports + EnvelopeIO split + Manifest carving) (#88) -- textus 0.25.0 — pulse hardening (#87) -- textus 0.24.0 — context-structure ergonomics (#86) -- textus 0.23.0 — agent gate (MCP) + docs truth-up (#85) +- align metadata with current project (textus/3 + coordination-space description) (#109) ## v0.22.0 — 2026-05-28 -- textus 0.22.0 — entry polymorphism pass (#84) -- textus 0.21.1 — intake entries as builder outputs (#83) -- feat!: 0.21.0 — agent memory integration (boot + pulse) (#81) - -## v0.20.2 — 2026-05-27 - -- 0.20.2 — finish role-kinds migration (#79) -- 0.20.1 — user-defined role kinds (#72) (#78) -- chore: prune dead code and tighten manifest seams (#77) - -## v0.20.0 — 2026-05-27 +### Features -- chore(release): textus 0.20.0 — architecture redesign (#76) -- refactor(0.20.0): merge Build into Publish (#75) -- refactor(0.20.0): discriminated Manifest::Entry kinds (#74) -- refactor(0.20.0): narrowed hook payload (HookContext) (#73) -- refactor(0.20.0): unified Hooks::Bus (#71) -- refactor(0.20.0): extract Manifest::Resolver (#70) -- refactor(0.20.0): kill top-level utility modules (#69) -- chore(release): textus 0.19.1 — drop textus/2 migration hint (#68) -- feat: explicit dependencies (v0.19.0) (#67) -- fix: hook dispatcher safety (v0.18.1) (#66) - -## v0.18.0 — 2026-05-27 - -- feat: extract storage ports, Store as composition root (v0.18.0) (#65) -- feat: flatten Operations, centralize authz, explicit hook registration (v0.17.0) (#64) -- feat: type cleanup & glue (v0.16.0) (#63) - -## v0.15.0 — 2026-05-26 - -- feat: operation boundaries reshape (v0.15.0) (#62) - -## v0.14.4 — 2026-05-26 - -- fix: lock hygiene + build/freshness decoupling (#58, #59) (#60) - -## v0.14.3 — 2026-05-26 - -- feat: top-level build lock (v0.14.3, closes #56) (#57) - -## v0.14.2 — 2026-05-26 - -- feat: per-rule fetch_timeout_seconds override (#54) (#55) - -## v0.14.1 — 2026-05-26 - -- release: 0.14.1 (#53) -- build: skip rewrite when only generated_at would change (#52) -- Extract Manifest.check_version! to dedupe parse/load version guard (#51) - -## v0.14.0 — 2026-05-26 - -- docs(readme): refresh for v0.14.0 — typed envelopes, Build/Publish split, spec count (#50) -- v0.14.0 — Phase 4: Build/Publish split + Envelope Data.define (#49) -- v0.13.1 — Phase 3: Manifest::Entry split (Parser + Validators) (#48) -- v0.13.0 — Phase 2: Format-strategy extraction (#47) -- v0.12.6 — Examples reorg (project + claude-plugin) (#46) -- v0.12.5 — Docs refresh for textus/3 + Operations facade (#45) -- v0.12.4 — Store facade final removal (Phase 1) (#44) -- v0.12.3 — Agent protocol block in textus intro (#43) -- v0.12.2 — Operations rename + Store facade removal (breaking) (#42) +- feat!: 0.21.0 — agent memory integration (boot + pulse) (#81) ## v0.12.1 — 2026-05-26 -- v0.12.1 — fix textus/2 hint at manifest parser (#41) -- v0.12.0 — Legacy Sweep: delete textus/2 compat shims (#40) -- 0.11.0 — textus/3 vocabulary redesign (BREAKING) (#39) -- fix(doctor): IllegalKeys honors index_filename — skip non-matching siblings (#38) - -## v0.10.5 — 2026-05-25 +### Bug Fixes -- 0.10.5 — tech-debt cleanup + index_filename + docs polish (#37) -- feat(manifest): index_filename — surface a fixed basename as the per-directory row (#36) -- ci: accept unbracketed CHANGELOG headings when extracting release notes (#35) +- IllegalKeys honors index_filename — skip non-matching siblings (#38) -## v0.10.4 — 2026-05-25 - -- 0.10.4 — GitHub folder intake recipe + skill-bundle deferral ADR (#34) - -## v0.10.3 — 2026-05-23 - -- 0.10.3 — documentation refresh and legacy-code removal (#33) - -## v0.10.2 — 2026-05-23 - -- 0.10.2 — doctor and store cleanup (#32) - -## v0.10.1 — 2026-05-22 - -- 0.10.1 — documentation refresh and spec hygiene (#31) - -## v0.10.0 — 2026-05-22 - -- 0.10.0 — shim removal, signal-based zone detection, Builder extraction (#30) - -## v0.9.2 — 2026-05-22 - -- 0.9.2 — policies, audit verbs, zone rename (#29) -- 0.9.1 — write-path layering + request Context (#28) -- 0.9.0 — intake, event standardization, read-time freshness, layered architecture (#27) -- 0.8.3 — :mv, :reject, :loaded events (#26) -- 0.8.2 — hook DSL sugar + :publish event (#24) - -## v0.8.1 — 2026-05-21 - -- 0.8.1 — terminology cleanup: extension → hook (#23) +## v0.10.5 — 2026-05-25 -## v0.8.0 — 2026-05-21 +### Features -- docs: 0.8 refresh — README, SPEC.md, examples, doctor cleanup, spec layout (#22) -- 0.8.0 — folder restructure, Zeitwerk autoload, Doctor::Check split (#21) -- 0.7.0 — Reader/Writer split, EventBus, Builder pipeline (#20) -- 0.6.1 — deprecation cleanup + drop migrate v2 (#19) -- feat: 0.6 hook unification — collapse 4 DSL verbs into Textus.hook(event, name) (#18) +- index_filename — surface a fixed basename as the per-directory row (#36) ## v0.5.0 — 2026-05-21 -- chore(release): 0.5.0 — wire textus/2, CLI groups, Store split, NDJSON audit log (#17) -- refactor(store): split Store into facade + Mover/Staleness/Validator/Events (#16) -- feat(protocol): bump to textus/2; unify _meta block across formats (#14) -- feat(audit): switch audit log to true NDJSON; legacy TSV reads still parse (#13) -- refactor: v0.5 wrap-up batch A — legacy cruft + ManifestEntry split + CLI subcommand groups (#12) -- refactor(cli): hash dispatch + alphabetize requires + unify PROTOCOL refs (#11) -- refactor(cli): extract per-verb command objects (CLI 434 → 96 LOC) (#9) -- feat(cli): fold validate-all into doctor --check=schema_violations (#8) -- feat(cli): default --format=json (#7) -- docs(architecture): rewrite layering section as module clusters (#6) +### Features + +- bump to textus/2; unify _meta block across formats (#14) +- switch audit log to true NDJSON; legacy TSV reads still parse (#13) +- fold validate-all into doctor --check=schema_violations (#8) +- default --format=json (#7) ## v0.4.0 — 2026-05-20 -- v0.4.0: extension API redesign — action primitive + doctor_check (#4) (#5) -- chore(release): refresh Gemfile.lock for 0.3.0 -- chore(release): 0.3.0 — configurable store root -- docs(spec): document store root resolution precedence (§3.1) -- feat(cli): accept --root= flag before subcommand -- feat(store): accept explicit root via kwarg + TEXTUS_ROOT env -- test(store): pin TEXTUS_ROOT/--root precedence (failing) -- docs: badges + CONTRIBUTING + SECURITY -- docs(readme): refresh for 0.2 — agent integration as the headline +### Features + +- accept --root= flag before subcommand +- accept explicit root via kwarg + TEXTUS_ROOT env ## v0.2.0 — 2026-05-20 -- release: prep 0.2.0 (workflow + changelog + gemspec cleanup) -- ci: complete CHECKSUMS in Gemfile.lock -- feat(intro): textus intro verb + inject_intro builder flag -- examples(claude-plugin): pending zone walkthrough (AI propose → human accept) -- examples(claude-plugin): align with current library surface -- feat(init): declare all five zones and pre-create their directories -- fix(store): pass suggestions on UnknownKey from get/put/mv raise sites -- feat(doctor): health-check verb + actionable error hints -- feat(publish): publish_each for nested entries; example mirrors agents/skills/commands automatically -- feat(uid): stable Textus UID + textus mv preserves identity across moves -- refactor(publisher): move sentinels under .textus/sentinels/ -- examples(claude-plugin): real Claude plugin layout managed by textus -- fix(projection): drop duplicate generated_at on Hash reducer results -- docs(spec): per-entry formats, key grammar, envelope additions -- examples(claude-plugin): json/yaml marketplace + deep nested working.network -- feat(extensions): normalize fetcher results into {frontmatter, body, content} -- refactor(publisher): rename Symlink → Publisher; copy-only publish -- feat(builder): per-format build pipelines + _meta injection for structured outputs -- feat(cli): textus migrate-keys helper for key-grammar migration -- feat(manifest): per-entry format field + strict key grammar enforcement -- feat(entry): add per-format storage strategies (markdown, json, yaml, text) -- examples(claude-plugin): make bin/notify-build a real script -- examples: showcase full 0.2 surface end-to-end -- docs(readme): bump version reference to 0.2.0; disambiguate git hooks from Textus.hook -- docs: rewrite §5.4/5.9/5.10/5.11 for extension surface; bump 0.2.0 -- chore: migrate example plugin extensions to new DSL -- feat(init): scaffold .textus/extensions/ with README -- feat(cli): add refresh + extensions list; rename --parse to --fetcher; remove hooks list -- feat(manifest): validate event names against ExtensionRegistry::EVENTS -- feat(audit): include pending_key/target_key in event_error extras -- feat(audit): optional JSON extras column for event/error records -- feat(events): fire :build after derived materialize and :accept after proposal -- test(events): assert :refresh does not fire on unchanged bytes -- feat(events): fire :refresh with change=:created/:updated -- chore(store): grep-friendly TODO marker for hook-error audit gap -- feat(events): fire :put and :delete after successful writes -- feat(refresh): wrap fetcher exceptions with fetcher-name context -- feat(refresh): in-process fetcher driver with 2s timeout -- refactor(store_view): WRITE_METHODS loop, accept coverage, drift guard -- feat(store_view): read-only proxy for extension code -- refactor(projection): extract REDUCER_TIMEOUT_SECONDS; default config to {} -- feat(projection): rename transform→reducer; route via store registry -- refactor(manifest): fetcher_config always defaults to {}; fetcher.nil? is the discriminator -- feat(manifest): source.fetcher/config/ttl with legacy-key rejection -- chore(cli): mark --parse= bridge as temporary; timeout returns in task 14 -- feat(fetchers): port built-in parsers to BuiltinFetchers in new registry -- feat(store): wrap extension load failures with filename context -- fix(store): deterministic extension load order via Dir.glob.sort -- feat(store): per-store ExtensionRegistry loaded from .textus/extensions/ -- refactor(dsl): private THREAD_REGISTRY_KEY; restructure DSL spec -- feat(dsl): add Textus.fetcher/reducer/hook with thread-scoped registry -- fix(registry): hooks() read should not auto-register the queried event -- feat(registry): add ExtensionRegistry with fetcher/reducer/hook slots -- scrub: replace 'envato' with 'acme' in fixtures and docs; sync README ruby versions -- ci: drop ruby 3.1/3.2, target 3.3 and 3.4 -- release prep: CHANGELOG, gemspec metadata URIs, CI workflow, Brewfile for lefthook -- quality: add rubocop config and lefthook git hooks; autocorrect existing code -- gemspec: exclude internal plan docs from packaged gem -- spec: align §6+ examples and keys with canon/working zone names -- readme: rewrite for 0.1.0 release with current zone model and v1.1 features -- entry: force UTF-8 on parsed content, reject invalid encoding -- example: wire lowercase parser into intake.upstream.notes -- cli: fix --zone=Z + --format=json flag combo on list/stale -- example: README tour demonstrating parser, calculator, hooks, schema ownership -- example: project schema with maintained_by per field -- example: rank-by-recency calculator wired into derived.claude.root -- example: register lowercase parser to demo .textus/parsers/*.rb auto-load -- example: add intake zone with releases.rss source and on_stale hook -- spec: §5.10 hooks declaration + §9 hooks verb -- hooks: list verb exposes declared hooks; runners execute, textus declares -- manifest: parse hooks: block on entries -- spec: §5.9 document calculators extension point -- projection: apply transform: NAME between pluck and sort -- calculators: registry + auto-load with 2s timeout (mirrors parsers) -- spec: document role_authority override (human always wins) -- validate-all: cross-check field last-writer against schema.maintained_by -- audit-log: expose last_writer_for(key) -- spec: document schema fields.maintained_by and evolution block -- schema-migrate: auto-apply evolution.migrate_from when --rename omitted -- schema: parse maintained_by per field + evolution block -- examples: ship refresh-intake.sh proving the runner-textus boundary -- examples: 50-line MCP server wrapping textus get/list/put -- examples: claude-plugin reference flow with templates, Rakefile, lefthook -- schema-tools: init from entry, diff against entries, migrate field renames -- init: textus init --profile= scaffolds .textus/ from bundled profiles -- proposal: accept verb copies pending patch into target, deletes pending -- put: --parse=NAME runs registered parser on stdin, stamps last_refreshed_at -- stale: detect intake entries past their source.ttl -- parsers: auto-load .textus/parsers/*.rb with 2s timeout bound -- deps/rdeps/published: walk projection.select + generator.sources -- builder: textus build materializes derived entries + publishes symlinks -- symlink: publish_to with copy-mode fallback and sentinel -- parsers: json, csv, markdown-links, ical-events, rss (stdlib-only) -- projection: select/pluck/sort_by/limit engine, capped at 1000 -- mustache: vendor minimal stdlib-only engine with depth bound -- list/stale: accept --zone=Z filter -- cli: wire --as flag, delete verb, validate-all verb -- store: validate-all walks every entry and reports violations -- store: role-gated put, delete verb, audit log on every write -- audit-log: append-only tsv with flock on every write -- role: resolve from flag > env > .textus/role > default human -- manifest: parse zones block with writable_by; synthesize legacy zones if absent -- errors: add InvalidRole/InvalidProjection/TemplateError/PublishError/ProposalError -- layout: move user content under .textus/zones/; manifest paths stay short -- spec: rewrite CLI surface and conformance fixtures for v1.0 -- spec: add compute, publish, intake, pending, audit, security-bounds sections -- spec: rewrite §1-5 for v1.0 general-purpose framing and role-based zones -- Initial commit: textus/1 reference Ruby implementation +### Features + +- textus intro verb + inject_intro builder flag +- declare all five zones and pre-create their directories +- health-check verb + actionable error hints +- publish_each for nested entries; example mirrors agents/skills/commands automatically +- stable Textus UID + textus mv preserves identity across moves +- normalize fetcher results into {frontmatter, body, content} +- per-format build pipelines + _meta injection for structured outputs +- textus migrate-keys helper for key-grammar migration +- per-entry format field + strict key grammar enforcement +- add per-format storage strategies (markdown, json, yaml, text) +- scaffold .textus/extensions/ with README +- add refresh + extensions list; rename --parse to --fetcher; remove hooks list +- validate event names against ExtensionRegistry::EVENTS +- include pending_key/target_key in event_error extras +- optional JSON extras column for event/error records +- fire :build after derived materialize and :accept after proposal +- fire :refresh with change=:created/:updated +- fire :put and :delete after successful writes +- wrap fetcher exceptions with fetcher-name context +- in-process fetcher driver with 2s timeout +- read-only proxy for extension code +- rename transform→reducer; route via store registry +- source.fetcher/config/ttl with legacy-key rejection +- port built-in parsers to BuiltinFetchers in new registry +- wrap extension load failures with filename context +- per-store ExtensionRegistry loaded from .textus/extensions/ +- add Textus.fetcher/reducer/hook with thread-scoped registry +- add ExtensionRegistry with fetcher/reducer/hook slots + +### Bug Fixes + +- pass suggestions on UnknownKey from get/put/mv raise sites +- drop duplicate generated_at on Hash reducer results +- deterministic extension load order via Dir.glob.sort +- hooks() read should not auto-register the queried event From 4da35f2416e91896859dba04c1e69edf5fd57170 Mon Sep 17 00:00:00 2001 From: Patrick Date: Mon, 6 Jul 2026 00:38:35 +0700 Subject: [PATCH 4/6] chore: bump to v0.56.0 --- CHANGELOG.md | 14 ++++++++++++++ lib/textus/version.rb | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b45e9519..5902f3c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,20 @@ All notable changes to this project are documented in this file. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## v0.56.0 — 2026-07-06 + +### Features + +- web surface: POST /api/:verb via Sinatra + Puma, GET /api/status +- shared Dispatch.call: all three surfaces (CLI, MCP, Web) route through it +- EntryConstraint: naming/schema/format enforcement at propose, accept, put +- CLI group/verb: 'textus web serve' starts the web server + +### Bug Fixes + +- workflow TTL reader (alias ttl every) destroyed @ttl on read, preventing reseeding +- changelog now filters to feat:/fix: commits only, grouped by type + ## Unreleased ### Features diff --git a/lib/textus/version.rb b/lib/textus/version.rb index 40f9c8ad..5d2277fc 100644 --- a/lib/textus/version.rb +++ b/lib/textus/version.rb @@ -1,4 +1,4 @@ module Textus - VERSION = "0.55.2" + VERSION = "0.56.0" PROTOCOL = "textus/4" end From 77bed41e818361e6a3e001b62d4308d3be1e5e27 Mon Sep 17 00:00:00 2001 From: Patrick Date: Mon, 6 Jul 2026 00:40:56 +0700 Subject: [PATCH 5/6] chore: commit Gemfile.lock for v0.56.0 --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 2c5c9a2e..cb9ae46c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - textus (0.55.2) + textus (0.56.0) concurrent-ruby (~> 1.3) csv (>= 3.0) dry-schema (~> 1.13) @@ -244,7 +244,7 @@ CHECKSUMS sqlite3 (2.9.5-arm64-darwin) sha256=d0cf444a70fc9395d513cfbcc1e6719e224aa645314e3824cb0474c721425aa2 sqlite3 (2.9.5-x86_64-linux-gnu) sha256=233dbcb6714148dd23bc5aeb33e8efd6eac974969564ddd5794c23d5f52b231e stringio (3.2.0) sha256=c37cb2e58b4ffbd33fe5cd948c05934af997b36e0b6ca6fdf43afa234cf222e1 - textus (0.55.2) + textus (0.56.0) tilt (2.7.0) sha256=0d5b9ba69f6a36490c64b0eee9f6e9aad517e20dcc848800a06eb116f08c6ab3 tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f unicode-display_width (3.2.0) sha256=0cdd96b5681a5949cdbc2c55e7b420facae74c4aaf9a9815eee1087cb1853c42 From a46f1b330944457c10bd9e1b66a04e593cbfa8da Mon Sep 17 00:00:00 2001 From: Patrick Date: Mon, 6 Jul 2026 00:41:26 +0700 Subject: [PATCH 6/6] chore: regenerate changelog for v0.56.0 --- .textus/data/artifacts/changelog.json | 6 +++--- .textus/data/artifacts/feeds/skills.json | 2 +- CHANGELOG.md | 14 -------------- 3 files changed, 4 insertions(+), 18 deletions(-) diff --git a/.textus/data/artifacts/changelog.json b/.textus/data/artifacts/changelog.json index 79d60038..38318095 100644 --- a/.textus/data/artifacts/changelog.json +++ b/.textus/data/artifacts/changelog.json @@ -1,12 +1,12 @@ { "_meta": { - "generated_at": "2026-07-05T17:36:20Z", + "generated_at": "2026-07-05T17:41:05Z", "uid": "60e228765192c063" }, "entries": [ { - "tag": "Unreleased", - "date": null, + "tag": "v0.56.0", + "date": "2026-07-06", "groups": { "Features": [ { diff --git a/.textus/data/artifacts/feeds/skills.json b/.textus/data/artifacts/feeds/skills.json index 82569074..28fc500e 100644 --- a/.textus/data/artifacts/feeds/skills.json +++ b/.textus/data/artifacts/feeds/skills.json @@ -1,6 +1,6 @@ { "_meta": { - "generated_at": "2026-07-05T17:36:20Z", + "generated_at": "2026-07-05T17:41:05Z", "uid": "36318f45b6fec691" }, "skills": [ diff --git a/CHANGELOG.md b/CHANGELOG.md index 5902f3c9..a0fa3e4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,20 +10,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features -- web surface: POST /api/:verb via Sinatra + Puma, GET /api/status -- shared Dispatch.call: all three surfaces (CLI, MCP, Web) route through it -- EntryConstraint: naming/schema/format enforcement at propose, accept, put -- CLI group/verb: 'textus web serve' starts the web server - -### Bug Fixes - -- workflow TTL reader (alias ttl every) destroyed @ttl on read, preventing reseeding -- changelog now filters to feat:/fix: commits only, grouped by type - -## Unreleased - -### Features - - add Knowledge/Scratchpad handlers, remove old Human/Agent stubs - add HandlerContext — restricted container view for handlers - add Session class with method_missing, replace Store in CLI/MCP surfaces