Repository navigation
Releases: patrick204nqh/textus
Releases · patrick204nqh/textus
Release list
v0.55.2
v0.55.1
Fixed
- publish_tree prune spec —
converge_nownow purges "done" jobs before reseeding. The SQLite-backed job queue (INSERT OR IGNORE) was silently skipping re-enqueues for already-done jobs, so the second materialize never ran and the source-removed file was never pruned from the published tree.
v0.54.2
Added
- Content-hash dedup in
Action::Ingest: re-ingesting the same URL or file bytes silently creates a supersede chain — new entry with full content, old stripped to tombstone with asuperseded_bypointer. Ports::RawIndex: lightweight YAML-based O(1) index for raw lane content-hash/URL lookups, stored at.textus/.state/indexes/raw.yaml.content_hashfield: every new raw entry stores asha256:content hash for fast dedup lookups.- Asset file move on re-ingest: asset files are moved (not copied) to the new date path on supersede.
Changed
.run→.staterename: the runtime artifact directory is renamed from.textus/.run/to.textus/.state/. Thestate/subdirectory becomescursors/. Layout path methods follow automatically. Manual migration step:mv .textus/.run .textus/.state.
v0.54.1
Added
Textus::Typesmodule: shareddry-typesconstrained types (RoleName,Cursor,FormatName) used across all dry-struct structs.
Changed
- MCP surface → official
mcpSDK (PR #223): the hand-rolled JSON-RPC 2.0 stdio loop is replaced by the officialmcpgem (~> 0.20). Protocol version auto-negotiates up to2025-11-25(was hardcoded2024-11-05).Catalog.build_toolsgeneratesMCP::Toolinstances from verb contracts;Servermanages session lifecycle and delegates dispatch.routing.rb, theMCP::Sessionalias, andToolSchemasare deleted. Session,Envelope,Container→Dry::Struct(PR #223):Data.definereplaced withDry::Structfor typed, loudly-failing construction at object creation time.SessionenforcesRoleNameand non-negativeCursor;Envelopeenforces knownFormatName;Containerhas typed port references.- Manifest validator → dry-schema + Semantics (PR #223): the 318-line programmatic validator is split into
contract.rb(dry-schema structural contract) andsemantics.rb(cross-field rules,walk-based unknown-key detection, ADR migration hints). Public interfaceValidator.validate!unchanged. - Mustache → ERB (PR #223):
mustachegem removed; all 8.mustachetemplates converted to.erbusingERB#result_with_hash.Produce::Rendernow usesERB.new(template, trim_mode: "-").result_with_hash(ctx).
Removed
mustacheruntime dependency (replaced by Ruby stdlib ERB).MCP::Routingmodule,MCP::Sessionalias,MCP::ToolSchemasmodule — superseded by the SDK.
textus 0.54.0 — Workflow system, raw ingest pipeline, MCP resources
Added
- Workflow system (PR #218): Ruby DSL files in
.textus/workflows/replace thestep/extension model.Workflow::Registry(loaded from.textus/workflows/**/*.rb) is resolved byProduce::Engineat produce time;Workflow::Runnerthreads data through each named step and persists. Core components:Workflow::Context,Workflow::Pattern(glob matching),Workflow::DSL,Workflow::Collector,Workflow::Registry,Workflow::Loader,Workflow::Runner. artifacts.indexproduced workflow: pre-computed full-store catalog (artifacts.index) served as a produced artifact — enumerates every manifest entry with its key, lane, format, etag, andgenerated_attimestamp.- MCP
resources/listandresources/read(PR #221): machine-laneproducedentries are now exposed as MCP resources attextus://<key>URIs with correct MIME types. Clients discover them via theresourcescapability advertised in theinitializehandshake. - Raw ingest pipeline (PR #219, ADR 0114, ADR 0116): new
rawzone-kind with awrite-oncegate and aningestcapability/verb (Action::Ingest). Doctor checks and an asset sentinel guard the lane. Therawbijection entry mapsraw → ingest.
Changed
artifacts.derived.*→artifacts.*: thederivedkey segment is dropped — all machine-lane entries flatten toartifacts.<name>in both the manifest and the on-disk store. Workflows, boot, and all other callers updated.bootreads fromartifacts.index: inline computation removed from the boot action; the pre-computed catalog drives the response.--leanflag removed (PR #220).pulseslimmed:stale,doctor, andnext_due_atremoved from the pulse envelope;index_etagadded so clients can cheaply detect catalog drift without a full list.ContractDriftmoved to core (PR #220):Textus::ContractDriftextracted from the MCP surface into the core library so it is reusable across surfaces.- MCP
Serverrefactored per Sandi Metz rules (PR #220): routing extracted toRoutingmodule; methods decomposed to ≤ 5 lines each. - Plugin manifest wiring (PR #220):
claude_pluginuses the installedtextusbinary (external use);mcp_configkeepsbundle exec(dev environment).--leanreferences removed from the plugin manifest spec. - Namespace renames (PR #218, internal):
entry/→format/,envelope/io/→envelope/,Ports::Queue→Ports::JobStore,background/→jobs/,pipeline/→produce/. - Workflows reorganised: workflow files split into
artifacts/andconfig/subdirectories under.textus/workflows/.
Removed
- Step system (PR #218): entire
lib/textus/step/directory deleted along with all step-related specs,container.steps, deadhooks.rbdoctor check, andports/audit_subscriber.rb. Migration: rewrite step files as.textus/workflows/*.rbusing the Workflow DSL. Jobs::Refresh(ADR 0115): workflow-based periodic re-derivation replaces the background refresh job.handler_permitmanifest field (PR #218): doctor-only field with no runtime enforcement deleted along with its policy class and schema key.- Dead guards and constants:
derived_write?no-op guard fromWriteVerb#cascade_to_rdeps;publish_eachruntime guard fromEntry::Publish; duplicate inlinecheck_action!fromput/key_delete/reject;BURN = :syncconstant; cross-lane notebook side-effect fromAction::Ingest. source.from: fetchandsource.from: derivenarrowed toexternalonly — fetch/derive methods removed fromProduced; freshness/pulse use retention rules; boot drops intake/derived keys.
Fixed
- MCP
resourcescapability missing frominitializehandshake (PR #221): the server only advertisedtools, so MCP clients never calledresources/list. Adding"resources" => {}makes all 9 produced artifacts discoverable as resources. adr_logworkflow:include_keyless: trueadded for tree-publish ofknowledge.decisions.- SpecLayout violations (PR #221):
workflow_index_specandworkflow/errors_spec(string-described) moved tospec/conformance/workflow/.
v0.53.0
Added
bin/dev: local script for syncing skills from GitHub during development.
Changed
- Gate dispatch consolidated (PR #215): removed redundant
container:param fromGate#dispatch(always@container); removed deadTextus::Dispatchmodule; removed double auth call inAccept; removed deadelsebranch androle_filterfromRoleScopeverb dispatch. Background::Planner::Plannerrenamed toPlanner::Planto resolve the double-name antipattern.Writer#putdecomposed (38→12 lines): extracted into 8 single-purpose sub-methods (resolve, uid, serialize, schema-validate, etag-check, persist, envelope, audit).Gate::Auth#check!/check_action!deduplicated: shared predicate-evaluation logic extracted intoevaluate_predicates.Init.runsplit (30→10 lines): 8 focused class methods (check_target!,create_directories,write_steps_readme,write_manifest,scaffold_agent,setup_state_dirs,write_gitignore,build_result).auth/writer/readerhelpers pulled intoWriteVerb: removed 4-file duplication fromPut,KeyMv,KeyDelete,Reject.Action::Base#argsauto-generated frominitializeparams: ~115 lines of boilerplate removed across 25 action classes.AuditLog#verify_integrity: per-line checking extracted intocheck_line_integrity+iterate_with_prev_seq.Error#initialize: 5-param constructor replaced withErrorInfoparameter object (backward-compatible).EventBus#invoke: replacedThread.new/killwithTimeout.timeout.- Hand-rolled Mustache replaced with
mustachegem: 117 lines of template engine deleted. - RuboCop formatting applied across the codebase.
Changed (breaking)
- The proposal payload key
frontmatter→_meta(ADR 0113). A proposal entry's proposed metadata moved from the top-levelfrontmatter:key to_meta:, so a proposal now carries the exact{ _meta, body }envelope shapeacceptreplays — retiring the only place the on-disk word "frontmatter" appeared as a runtime data key.acceptreadsenv.meta["_meta"]and theschema_validspecial case shrinks to a dig-with-fallback. No shim (house style): a proposal authored with the old key accepts with empty metadata and fails schema validation loudly. Migration: re-author any in-flight proposal with_meta:instead offrontmatter:— proposals are transientqueueentries, not durable canon.
v0.52.0
The "who may write what" tables stop being hand-copied across the canon docs and become a fourth generated reference doc, projected from the source of truth on every drain.
Added
docs/reference/authority.mdis a produced reference doc (ADR 0112). A newauthorityregistry handler projects theartifacts.derived.authorityentry — the zone-kind↔capability bijection (fromSchema::Vocabulary::LANES), this manifest's zones, and its roles with the zone-kinds each can write — rendered throughauthority.mustache. It joinsverbs.md/schema.md/adr-log.mdon the ADR 0097/0102 produced-docs pattern;GeneratorDrift+HandlerAllowlistguard it (no new doctor check).
Changed
- The authority model's SSoT splits along the produce/guard seam (ADR 0112, refining ADR 0098). The current-values tables move to the generated
authority.md;reference/zones.mddrops its three hand-maintained tables, keeps what each capability means as prose, and links to the generated values.explanation/concepts.mdand the docs index re-point accordingly; the orientation template now names four generated reference docs.
v0.51.0
build folds into reconcile, materialization becomes system-pushed, the producer kinds and machine zones collapse onto one source + retention grammar, and the machine-derivable reference docs graduate to produced artifacts.
Changed (breaking)
buildis removed;tendis nowreconcile(ADR 0087). One verb runs the full two-phase produce → destructive-sweep under a shared lock, and every canon write reactively rebuilds its dependent produced entries (source.on_write: sync | async). Migration: drop allbuildcalls (CLI + MCP); rename scripted/crontendtoreconcile— the verb, the MCP tool id, and the audit-log verb string.- Capabilities collapse to four —
author,keep,propose,reconcile(ADR 0090). Thefetchandbuildcapabilities fold intoreconcile;automationdefaults to[reconcile]. Migration: a role withcan: [fetch]/can: [build](or the interimingest) becomescan: [reconcile]; the old spellings are rejected at load with a hint. - One
machinezone-kind and oneproducedentry-kind (ADR 0091, 0095). Zone-kinds becomecanon | workspace | machine | queue— the formerquarantine+derivedzone-kinds fold into onemachinekind (the kind ⟺ capability mapping is a bijection again; at most onemachinezone). Entry-kinds becomeleaf | nested | produced— the formerderived+intakeentry-kinds fold into oneproducedkind, with the produce-method (intake / derived / external) read offsource.from. Migration:kind: quarantine/kind: derivedon a zone →kind: machine;kind: derived/kind: intakeon an entry →kind: produced. Both are rejected at load with a fold hint; no shim. source:+retention:replaceupkeep/lifecycle/materialize/intake/compute/template(ADR 0093). The old slots conflated production (how an entry's bytes are made) with retention (when an aged entry is retired). They split into an entry-levelsource: { from: project | handler | command }— one "acquire data from upstream" concept, wherefrommust agree withkind:— and a glob-matchedretention: { ttl, action: drop | archive }rule; both run through one produce engine. This is strictly more expressive (re-pull hourly and archive at 90 days is now sayable). Thelifecycle: { on_expire: warn }form is removed (warnwas dead since 0.50's pure-readget). Migration: rewriteupkeep:/lifecycle:/materialize:(and the oldintake:/compute:/template:blocks) intosource:+retention:. Old manifests fail at load with mechanical fold hints; no shim.- A
sourceproduces data;publish:is a list of targets (ADR 0094). A produced entry's stored form is data (e.g..json); rendering moves entirely to the publish path.publish:is now a list — each element a{ to:, template?:, inject_boot?: }file target (copies the data verbatim, or renders it through that target's own template) or a{ tree: }subtree mirror — so one dataset can render to differently-shaped targets (CLAUDE.mdvsAGENTS.md) without bespoke handler code. Published artifacts are clean content; textus's_metaprovenance stays in the stored entry. Migration: the mappublish: { to: [...] }/publish: { tree: }forms (and the olderpublish_to:/publish_tree:) are rejected at load —publish:is a list. - Hook events renamed (ADR 0094). RPC
:resolve_intake→:resolve_handler; pub-sub:entry_put→:entry_written,:build_completed→:entry_produced(plus:entry_publishedandproduce_failed). Migration: update hook subscriptions to the new names; old names fail at registration.
Added
- Reference docs are produced, not hand-authored (ADR 0097, 0098).
docs/reference/verbs.mdanddocs/reference/schema.md(projected from the live verb/schema registry) and the newdocs/reference/adr-log.md(projected from the ADR files) arekind: producedentries published out, with a CI gate that fails whenreconcileis not a no-op. Do not hand-edit them — edit the upstream source (the verb/schema code, or an ADR) and runreconcile;doctorflags a stale hand-edit asgenerator_drift. - The root
README.md,CONTRIBUTING.md, andSECURITY.mdare canon, published out (ADR 0103, 0104). They are authored under.textus/zones/knowledge/and published verbatim to the repo root with an editor banner (the same pattern asdocs/); a hand-edit to a front-door doc is clobbered byreconcileand caught by the CI no-op gate.
Changed
- Docs SSoT/DRY cleanup (ADR 0098). The duplicated ADR index is single-sourced — the mechanical status board lives in
docs/reference/adr-log.md, and the curated decisions README becomes an annotated reading guide; the verb producer depends onRead::Capabilitiesrather than reaching intoDispatcherinternals; a conformance guard keeps theevents/zones/mcpprojections covered.
Internal
- The architecture is now executable, and the produce / schema / port layers are decomposed (ADR 0092, 0099–0101, 0105–0109). The hexagonal layering is enforced by a conformance guard with the layer map written to
lib/textus/ARCHITECTURE.md(0106); the verb token gains a build-time route ⟺ contract bijection guard (0105); the divergent staleness comparisons collapse into oneDomain::Freshness::Evaluator(0099); the produce pipeline is gathered underlib/textus/produce/with anacquire÷rendersplit and thefetch_*fossils renamed (0100); two interface fossils are removed (0101);manifest/schema.rbsplits its validation walk intoSchema::Validatorand its constants intoSchema::Vocabulary+Schema::Keys(0107, 0109); every port becomes one shape — an instantiable class (0108, 0109); and the conformance spec tier is consolidated (67→19 loose) and coupled to the contract rather than to fixture spelling (0092). No behaviour, manifest-grammar, or verb-surface change.
v0.50.0
freshness collapses into pulse, the contract-drift guard stops deadlocking, and boot gains a lean session-start projection shipped via a plugin.
Added
boot --lean(ADR 0084) — a compact orientation projection (protocol,store_root,zones,agent_quickstart,contract_etag) for cheap session-start injection. The fullbootenvelope now also carriescontract_etag.--leanis a contract arg, so it threads to every surface (CLI flag, MCPleantool arg, Ruby kwarg).- textus ships as a Claude Code plugin (ADR 0084) — a single self-contained
.claude-plugin/plugin.jsonwith an inlineSessionStarthook (startup/clear/compact) that runsboot --lean, so enabling the plugin auto-orients each session. The agent-invokableboottool is unchanged; the hook is additive. Plugin data is kept inside.claude-plugin/(a separate concern from the gem), not a top-levelhooks/dir. - Agent-integration config is textus-managed, projected from canon (ADR 0086). This repo's
.mcp.jsonand.claude-plugin/plugin.jsonare now build artifacts —textus buildprojects them fromknowledge.project+Textus::VERSION(so the plugin version can never drift off the gem) the same way it projectsCLAUDE.md. The plugin manifest also gains an inlinemcpServersstanza (enable the plugin → MCP server and orientation hook in one). A newprovenance: falsederived-entry flag lets the JSON renderer emit config without a_metablock. Downstream consumers are unaffected —init --with-agentstill scaffolds a write-once.mcp.json(a build must never clobber a user's config).
Changed
- The contract-drift guard applies to mutating verbs only;
bootself-heals (ADR 0083). A mid-session manifest/hook/schema edit no longer deadlocks every MCP verb behind "re-run boot": pure reads andbootbypass the guard,bootre-arms the session's cachedcontract_etag, and only mutating verbs (theWrite::family + the destructiveMaintenance::verbstend/zone_mv/key_mv_prefix/key_delete_prefix) enforce it. "Re-run boot" now actually recovers instead of looping. Refines ADR 0074. tendreturns a health summary, not fullissues[](ADR 0085) — matchingpulse;doctorstays the sole owner of detailed health output.
Removed (breaking)
- The public
freshnessverb (ADR 0085). Observability is now two verbs on orthogonal axes —pulse(transient/temporal:changed+stale+next_due_at) anddoctor(structural correctness). The lifecycle scan that backedfreshnessbecomes a Ruby-only internal (emptysurfaces, ADR 0073) consumed bypulseand the hook context; per-entry detail is reconstructable fromget(carriesstale/last_fetched_at) +rule_explain(thelifecycle:ttl +on_expire). Migration: replacetextus freshnesswithtextus pulse(for thestalelist /next_due_at) orget+rule_explain(for one entry's verdict). SPEC's generator-drift attribution is corrected todoctor'sgenerator_driftcheck.
v0.49.0
The single-key mutation verbs gain the key_ family stem, and the migrate orchestrator is removed.
Changed (breaking)
mv→key_mvanddelete→key_delete, renamed everywhere the token is load-bearing: the verb / MCP-tool id, the guard transition symbol, the manifestguard:transition key, and the audit-log verb string. The single-key verbs now share thekey_stem with their bulk cousinskey_mv_prefix/key_delete_prefix;zone_mvis unchanged. The CLI is unchanged —key mv,key delete,key mv-prefix,key delete-prefixkeep their spelling. Migration: manifests withguard: { mv: … }/guard: { delete: … }blocks must rename those keys tokey_mv/key_delete; MCP clients pinned to themv/deletetool ids must update; audit rows written before this release keep theirmv/deleteverb strings (readers must accept both).
Removed (breaking)
- The
migrateverb (YAML-plan orchestrator). It was non-transactional and added a second input format for no capability the primitives lack — itszone_mv/key_mv_prefix/key_delete_prefixops remain individually callable, each with--dry-run. Removed from the CLI and the MCP catalog.