Skip to content

v0.1.0 readiness — pre-plugin design notes

Status: Draft. Not normative. Captures the v0.1.0 long-horizon work that ADR-2 (Extension API) and the v0.1.0 row of ROADMAP describe at a high level, and locates each item against today’s v0.0.7 working tree so the next implementer can see what is reusable, what needs stabilisation, and what must be invented before the first public plugin contract can ship.

This document is a working sketch. The binding contracts continue to live in ADR-2, ADR-4 (inference engine), docs/internal-spec/inference-engine.md, and docs/internal-spec/internal-type-api.md. When this draft disagrees with any of those, the spec / ADR binds and this draft is out of date.

ROADMAP.md reserves v0.1.0 for two cross-cutting infrastructure surfaces:

  1. Persistent on-disk caches for parsed RBS environments, scope indexes, and catalog data so warm rigor check runs are fast.
  2. Plugin API (ADR-2) — the capability-role / fact-contribution / mutation-summary surface plugin authors will attach to.

The current v0.0.x slices are the substrate that the plugin API has to be designed against. v0.0.7’s “pre-plugin coverage push” closed the largest type-language and built-in coverage gaps that v0.1.0 plugins would otherwise stumble on day one.

Today’s analyser surface in plugin-API terms

Section titled “Today’s analyser surface in plugin-API terms”

ADR-2 describes the public objects plugins will receive. Today’s v0.0.7 engine has internal counterparts of every one of them, but none has been declared “public” in the gem-API sense. The table below maps ADR-2’s vocabulary to the current Rigor module:

ADR-2 surfacev0.0.7 implementationPlugin-readiness gap
Scope (immutable, edge-aware)Rigor::Scope (39 public methods, immutable per-instance, threaded through StatementEvaluator and ExpressionTyper).Surface is stable internally but not yet annotated as public. Public-namespace declaration + a drift test (similar to the RbsLoader shape contract) needed before exposure.
Type system value objectsRigor::Type::* carriers + Rigor::Type::Combinator. Specs at internal-type-api.md.Already documented as the public type-object surface. Combinator additions (e.g. v0.0.7’s key_of, value_of, int_mask, indexed_access) flow through the same factory contract. Ready as-is for plugins reading types.
Reflection layerMixed: Rigor::Environment (class registry + RBS loader), Rigor::Inference::ScopeIndexer (discovered classes / methods / instance vars), Rigor::Builtins::*_CATALOG (per-class method catalogs).Three sources of reflection data with no unified facade. v0.1.0 needs a Rigor::Reflection (or similar) facade that joins these into a stable per-class / per-method query surface.
Trinary certaintyRigor::Trinary + Rigor::Type::AcceptsResult. Used pervasively by Type::*#accepts.Public-ready. The yes / maybe / no semantics already match ADR-2’s policy that maybe does not narrow as a positive proof.
Flow-effect bundlesRigor::RbsExtended::*Effect structs (predicate / assert / param / return) + Rigor::Analysis::FactStore::Fact.Effect structs are RBS-side; a unified plugin-side bundle (return_type, truthy_facts, falsey_facts, post_return_facts, mutations, invalidations, exceptional, role_conformance, provenance) is not yet built. ADR-2 § “Flow Contribution Bundle” is the spec; v0.1.0 implements it.
Diagnostic provenanceRigor::Analysis::Diagnostic (path / line / column / message / severity / rule).Per-rule identifier exists; the source-family prefix (plugin.<id>.<name> etc.) is not yet supported. The Diagnostic struct grows one provenance field.
Cache descriptorsNone at runtime; every rigor check re-parses, re-loads RBS, re-computes scope indexes, re-loads catalogs.Schema fixed at 20260505-cache-slice-taxonomy.md (per-slot entry shapes, composition rules, cache-key derivation, granularity guidance). Persistence layer (storage backend, locking, eviction) ships in v0.1.0.
Plugin manifest / configurationNone.New surface. Trusted-gem trust model from ADR-2 § “Plugin Trust and I/O Policy” — start with Gemfile-listed gems + a config schema.
Extension testingRSpec + the integration FixtureHarness (spec/integration/fixtures/*.rb).The fixture harness is reusable for plugin tests; the v0.1.0 release should ship a published Rigor::Testing::PluginHarness so plugin authors get the same shape.
Capability rolesNone. ADR-2 reserves an opinionated core catalog (_Closable, _Rewindable, …); the v0.0.x engine has structural-interface infrastructure under docs/type-specification/structural-interfaces-and-object-shapes.md but no concrete role catalog yet.Modest pre-work needed — roles are essentially named structural interfaces with conformance metadata.

The infrastructure surfaces have a natural order. Each row is a prerequisite for the rows below:

  1. Reflection facade. Until source declarations / RBS / catalogs are reachable through one stable facade, plugin reflection contributions cannot be merged deterministically. Today’s ScopeIndexer + Environment + Builtins::*_CATALOG triad needs a unified read-side API. Estimate: medium. No new analysis logic — purely a facade over existing data.
  2. Cache slice schema. Caches are essential before plugin contributions are persisted, because plugin facts (dynamic members, dynamic returns, role conformance) must be invalidated when their inputs change. The typed-slot schema (files, gems, plugins, configs) goes in first; persistence (file-based store
    • integrity check) follows. Estimate: large. The hard part is designing slice boundaries that compose with plugin contributions without exploding cache cardinality.
  3. Flow-contribution bundle. ADR-2’s bundle struct is the public shape plugins return. Internal effect structs (PredicateEffect etc.) must be reconciled with the bundle’s superset shape, then the dispatch path (MethodDispatcher + Narrowing) consumes the bundle through the same merge rules built-in rules already use. Estimate: medium. Most internals are already in place — the merge and dispatch sites already accept multiple sources.
  4. Plugin manifest + registration. A plugin gem declares protocols it implements; Rigor loads the gem and instantiates services. Estimate: small if the trusted-gem model holds — essentially a Gemfile inspection plus constructor-injection of a service container.
  5. Narrow extension protocols. Dynamic return type, type- specifying, dynamic reflection are the three highest-leverage protocols. Each needs an interface module, a registration site, and a dispatch hook in the existing tier chain. Estimate: medium per protocol but each is independent, so they can land one at a time.
  6. Diagnostic provenance prefix. Add the source-family field to Rigor::Analysis::Diagnostic and update the formatter. Estimate: small.
  7. Plugin testing harness. A published wrapper around the existing FixtureHarness plus rule-test helpers. Estimate: small.

The caches and flow-contribution bundle are the heaviest items. Everything else is mostly mechanical assembly once those two are in place.

Open questions resolved by v0.0.7 work (and questions still open)

Section titled “Open questions resolved by v0.0.7 work (and questions still open)”

ADR-2’s “Open Questions” section lists nine items. The v0.0.7 push narrows several of them:

  • Initial plugin manifest format / configuration schema language. Still open. Probable answer: YAML manifest + Ruby DSL hybrid, matching the data/builtins/ruby_core/*.yml precedent.
  • Synthetic / virtual AST nodes for rules. Still open. Today’s StatementEvaluator operates on raw Prism::Nodes; introducing a virtual node layer is a real architectural decision.
  • Fixture marker spelling for inferred-type assertions. v0.0.x already uses assert_type("...", expr) calls in fixture files (spec/integration/fixtures/*_catalog.rb). That syntax round-trips through Prism and stays Ruby-valid; it should be the published spelling for plugin tests too.
  • Capability-role conformance payload (full / partial / excluded / maybe). Still open. Needs a worked example before the shape freezes — probably driven by a concrete Stream / Closable role proposal.
  • Diagnostic identifier taxonomy for conflicting plugin contributions. Still open. v0.0.x uses rule: prefix on Diagnostic; expanding to source-family.plugin-id.rule-id is a small struct change.
  • Reflection cache key schema. Still open. Depends on the cache slice design (item 2 above).
  • Compatibility ranges for public extension namespaces. Still open. Cannot answer until the first protocol is published; the ADR-2 working response (gem version dependencies + conformance test suite) holds.
  • Broad expression / operator hooks if enabled later. Still deferred per ADR-2 working response. v0.0.7’s “narrow tier” pattern (MethodDispatcher::ConstantFolding / BlockFolding / ShapeDispatch / KernelDispatch) demonstrates that narrow hooks cover most real-world cases without broad dispatch.
  • Dynamic-return matching by structural interface / object shape (vs nominal only). Still open. The structural-interface infrastructure exists (structural-interfaces-and-object-shapes.md) but is not exercised by the dispatcher yet. v0.1.0 likely starts with nominal-only and adds structural matching as a follow-up.

Pre-v0.1.0 work that does not require ADR-2 to ship

Section titled “Pre-v0.1.0 work that does not require ADR-2 to ship”

Several items can land independently of the plugin API and materially shorten v0.1.0:

  • Public-API declaration of Rigor::Scope, Rigor::Type, and Rigor::Environment — namespace policy + drift tests. No new code, just a contract declaration.
  • Reflection facade scaffolding — a thin Rigor::Reflection module that exposes class_definition_for(name), method_definitions_for(class_name, method_name), constants_for(class_name), etc. Reads from existing sources; publishes a stable shape.
  • Cache slice taxonomy — landed at 20260505-cache-slice-taxonomy.md. Fixes the per-slot entry shapes, comparator semantics, composition rules, cache-key derivation, granularity guidance, and schema-versioning policy. No code; the prerequisite contract for the persistence layer that ships in v0.1.0.
  • Flow-effect bundle struct — define the bundle as a Rigor::FlowContribution struct with the eight slots in ADR-2. The internal PredicateEffect etc. structs convert into bundles at the boundary of RbsExtended and Narrowing; built-in rules produce bundles too. Plugins follow.
  • Diagnostic provenance field — extend Diagnostic with source_family (default :builtin). Update the formatter to optionally include the prefix in the rule id.

Any of these can ship as a v0.0.x dot release; none requires ADR-2 to be finalised.

  • The Rigor::Type::* carriers are sufficient. v0.0.7’s additions (key_of, value_of, int_mask, indexed_access) covered the spec table.
  • The catalog tier (MethodDispatcher::ConstantFolding + BlockFolding + ShapeDispatch + KernelDispatch) is in good shape. Each tier has a small, well-defined surface area; plugins can register through the same tier-chain pattern by adding a new precision tier or a generic dispatcher hook.
  • RBS::Extended directives (return:, param:, assert*, predicate-if-*) cover the user-side authoring surface. The remaining conforms-to directive is the only spec-listed directive without an implementation, and it depends on a structural-conformance checker that is itself an open v0.1.0 question.
  • Rigor::Inference::ScopeIndexer already has a clear contract: read-only declarations table, written once, queried during inference. No restructuring needed.
  • Rigor::Trinary and Type::AcceptsResult are the canonical certainty surfaces. Plugins consume them directly.
Section titled “Recommended next slice (for a future implementer)”

Status update (v0.0.7 batch 6, 2026-05-05): the reflection facade scaffolding has landed. Rigor::Reflection is published as the unified read-side facade (specs at docs/internal-spec/reflection.md; RBS at sig/rigor/reflection.rbs; specs at spec/rigor/reflection_spec.rb). The public API covers:

  • class_known? / class_ordering / nominal_for_name / singleton_for_name,
  • constant_type_for (joining in-source and RBS-side constants; in-source wins on collision),
  • instance_method_definition / singleton_method_definition,
  • discovered_class? / discovered_method?.

The facade is read-only and additive: existing call sites that read directly from Scope or RbsLoader continue to work unchanged and migrate to the facade at their own pace.

Status update (v0.0.7 batch 7, 2026-05-05): the consumer migration also landed. Five engine-internal callers now read through the facade instead of scope.environment.rbs_loader: Analysis::CheckRules (four rule-impl call sites + the definition_available? / lookup_method helpers), Inference::Narrowing#resolve_rbs_extended_method, Inference::StatementEvaluator#resolve_call_method, Inference::MethodDispatcher#user_class_fallback_receiver, Inference::MethodParameterBinder#lookup_rbs_method, and Inference::MethodDispatcher::RbsDispatch’s lookup_method / build_type_vars helpers. Two raw environment.rbs_loader presence-checks remain in RbsDispatch#try_dispatch / #block_param_types — they ask “is the loader present?” and the facade wrapper would add noise without simplifying the call sites.

The facade gained rbs_class_known?, instance_definition / singleton_definition, class_type_param_names, and an environment: kwarg alternative to scope: to support the migration without a one-shot Scope-construction tax.

Status update (v0.0.7 batch 8, 2026-05-05): the cache slice taxonomy design doc has landed at 20260505-cache-slice-taxonomy.md. It fixes the per-slot entry shapes (FileEntry with :digest / :mtime / :exists comparators, GemEntry, PluginEntry, ConfigEntry), the composition rules across producers (union by key per slot, stricter-comparator-wins for files, conflicts invalidate the slice), the canonical cache-key derivation, the granularity guidance per producer family (reflection / inference / catalog / plugin), and the schema- versioning policy.

Status update (v0.0.8 planning, 2026-05-05): the cache persistence backend choice is fixed. ADR-6 records the working decision: a sharded directory of binary entries written through a custom canonical format, zero new gem dependencies, per-file flock for write atomicity, no eviction in the first implementation. The persistence layer ships as the v0.0.8 release theme; see the v0.0.8 row of ROADMAP.md.

Next pre-v0.1.0 slice after the cache layer: Rigor::FlowContribution bundle struct — the eight-slot shape ADR-2 § “Flow Contribution Bundle” specifies. Internal effect structs (PredicateEffect, etc.) convert into bundles at the boundary; built-in rules and plugins both produce bundles. That is the next substrate work after cache; it can land in v0.0.8 as a companion if scope allows, or stay for v0.0.9.

This document is a snapshot of the v0.0.7 working tree’s readiness for v0.1.0. It will be updated when:

  • v0.0.x dot releases close items in the “Pre-v0.1.0 work that does not require ADR-2 to ship” list.
  • ADR-2’s “Open Questions” are resolved into concrete schemas.
  • The reflection facade lands and changes the underlying reflection surface area.

After v0.1.0 ships, this document is superseded by the public plugin API documentation and stays in docs/design/ as a historical record.

© 2026 TypedDuck. Licensed under CC BY-SA 4.0.