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.
What v0.1.0 commits to
Section titled “What v0.1.0 commits to”ROADMAP.md reserves v0.1.0 for two cross-cutting infrastructure surfaces:
- Persistent on-disk caches for parsed RBS environments, scope
indexes, and catalog data so warm
rigor checkruns are fast. - 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 surface | v0.0.7 implementation | Plugin-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 objects | Rigor::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 layer | Mixed: 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 certainty | Rigor::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 bundles | Rigor::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 provenance | Rigor::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 descriptors | None 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 / configuration | None. | New surface. Trusted-gem trust model from ADR-2 § “Plugin Trust and I/O Policy” — start with Gemfile-listed gems + a config schema. |
| Extension testing | RSpec + 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 roles | None. 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. |
Sequencing: what blocks what
Section titled “Sequencing: what blocks what”The infrastructure surfaces have a natural order. Each row is a prerequisite for the rows below:
- 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::*_CATALOGtriad needs a unified read-side API. Estimate: medium. No new analysis logic — purely a facade over existing data. - 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.
- Flow-contribution bundle. ADR-2’s bundle struct is the public
shape plugins return. Internal effect structs (
PredicateEffectetc.) 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. - 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.
- 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.
- Diagnostic provenance prefix. Add the source-family field to
Rigor::Analysis::Diagnosticand update the formatter. Estimate: small. - Plugin testing harness. A published wrapper around the
existing
FixtureHarnessplus 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/*.ymlprecedent. - Synthetic / virtual AST nodes for rules. Still open. Today’s
StatementEvaluatoroperates on rawPrism::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 onDiagnostic; expanding tosource-family.plugin-id.rule-idis 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, andRigor::Environment— namespace policy + drift tests. No new code, just a contract declaration. - Reflection facade scaffolding — a thin
Rigor::Reflectionmodule that exposesclass_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::FlowContributionstruct with the eight slots in ADR-2. The internalPredicateEffectetc. structs convert into bundles at the boundary ofRbsExtendedandNarrowing; built-in rules produce bundles too. Plugins follow. - Diagnostic provenance field — extend
Diagnosticwithsource_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.
What does NOT need pre-v0.1.0 work
Section titled “What does NOT need pre-v0.1.0 work”- 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::Extendeddirectives (return:,param:,assert*,predicate-if-*) cover the user-side authoring surface. The remainingconforms-todirective 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::ScopeIndexeralready has a clear contract: read-only declarations table, written once, queried during inference. No restructuring needed.Rigor::TrinaryandType::AcceptsResultare the canonical certainty surfaces. Plugins consume them directly.
Recommended next slice (for a future implementer)
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.
Status of the design doc itself
Section titled “Status of the design doc itself”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.