Hand-written. ../harvested/data-modeling.md is what the documents say; this file is what the
implementation found, and it wins. Each entry names the harvested entry it answers by that
entry's stable key.
- Value-object base type: the design wins —
Data.defineis the base for every core domain type, andT::Structis not available to this port at all. Resolvesdata-modeling/35fde90f. The styleguide's rule is conditional on Sorbet (T::Structis the# typed: strictdefault because Sorbet cannot typeData.definemembers), and this SDK has no Sorbet:T::Structis defined bysorbet-runtime, a third-party gem that would have to appear as anadd_dependencyindexpace-core.gemspec, whichSEAM-1/NFR-1forbid and the gemspec audit mechanically rejects. The premise the styleguide rule rests on is therefore absent, not overruled. What the SDK does instead: every core model isData.definewith aninitializeoverride that validates and callssuper, collectionsduped and frozen exactly once at construction,private_class_method :newplus a validating.build, andsig/**/*.rbscarrying the per-member types RBS can express and Sorbet was wanted for. The styleguide's own reasoning forData.define— frozen on construction,==/hash/withfor free, keyword constructor enforced — is adopted in full. The styleguide-amendment alternative (scoping theT::Structpreference to "projects using Sorbet") was considered and not taken; this is recorded as an SDK deviation instead. review ·docs/work/mvp/2026-09-05-ruby-sdk-v1-roadmap-design.md· high · sha:manual-roadmap-conflict-1 - A seam is a duck type plus a
.conforms?predicate plus an RBS interface type, never a Sorbet abstract module. Resolvesdata-modeling/a13e9ffe. The rule is conditional on Sorbet in its mechanism —sig { abstract }stubs aresorbet-runtime, which would be anadd_dependencyline indexpace-core.gemspecthatSEAM-1/NFR-1forbid andrake gates:gemspec_auditmechanically rejects — so the premise is absent rather than overruled, exactly as../notes/type-system.mdrecords for the wider Sorbet question. But the substitution is not simply "drop the module": the rule also says concrete implementations shouldincludethe interface, and this port deliberately does not ask that either, for a reason that is about libraries rather than about Sorbet. A seam here is implemented by a gem this project does not control and, in the common case, by an object that already has the shape — a barelambda, aDexpace::Pipeline, a Rack-style#call-able.docs/sdk-design-ruby/00-porting-method.md, working principle P14, makes that a principle: where the host ecosystem has converged on one API shape, the seam is defined as a structural subset of it so existing infrastructure duck-types in with zero adapter code. A nominalincludewould put aDexpace::constant into every adapter's ancestry, would make a two-line lambda non-conformant, and would buy nothing Ruby actually enforces. What the SDK does instead, perdocs/sdk-design-ruby/03-seam-by-seam-idiomatic-mapping.md§3.2 and §3.4: each seam is three artifacts and no fourth — a documented duck type (#call(request, options, cancellation)for a transport;#media_type/#dump_string/#dump_bytes/#dump_to/#dump_into/#loadfor a codec); a.conforms?predicate on the seam's own module, which is the runtime check the registry runs before accepting a factory and which is also howSEAM-2is satisfied, because core names the shape and never an implementation; and an RBSinterface _Transport-style type in that seam'ssig/file, which is what a consumer's ownsteep checksees at a parameter. Assignability and runtime identity are different questions in Ruby and each artifact answers one — the same split phase 1 found forSEAM-29, where the RBS interface_Builder[T]and the moduleDexpace::Builderare both needed. What is lost and is not replaced: a type checker cannot verify that an adapter implements the seam, only that it is passed where one is expected;.conforms?anddexpace-conformance's per-adapter assertions are what stand in. One residual gap is admitted rather than hidden: the synchronous and asynchronous transport seams have the same structural shape and differ only in return type, so no.conforms?can tell them apart before the first call — they are two registries and the adapter names which it registers into. The styleguide-amendment alternative (making chapter 6's interface rule read "a documented duck type, an abstract module where Sorbet is in use") was considered and not taken; this is recorded as an SDK deviation instead (phase 2). review ·docs/work/mvp/phase2/2026-09-06-phase2-seam-foundations-design.md· high · sha:manual-phase2-seam-duck-types - The whole wire model is
Ractor.shareable?as built,RequestandResponseincluded: design §4's claim stands in full, and the phase-1 design's narrowing of it (ledger rowP1-9) did not survive the build. Two documents disagreed.data-modeling/996c0b12— design §4 — says deep-freezing at construction makes the whole wire model Ractor-shareable, and the phase-1 design'sP1-9said the two models holding aURI::Genericare not. Design §4 won: this entry confirmsdata-modeling/996c0b12and overrules nothing harvested. What the phase-1 design got right, verified on 2026-09-05 and again on 2026-09-15 against 3.2.11 and 4.0.6:URI::RFC3986_PARSER.parse("https://example.test/a?x=1").freezeisfrozen?but notRactor.shareable?, becauseURI::Generic#freezeis shallow and its@hostand@pathStrings stay unfrozen — and that same fact is anXCUT-15alias rather than a shareability curiosity:URI#dupshares those Strings with the source, so the plan'sinput.dup.freezeleftrequest.url.host << "x"able to change a model in place through a reader. What it got wrong: it tookRactor.make_shareable(uri)for the only route to a shareable URI and warned that the call deep-freezesURI::RFC3986_PARSER, a process-global object, in place — the in-place hazarddata-modeling/fa3eaf6eanddata-modeling/59efaae6name, one level further out than they state it. That warning is moot:URI::RFC3986_PARSER.frozen?andRactor.shareable?(URI::RFC3986_PARSER)are already true on both interpreters, because the uri gem freezes the parser at definition, and the SDK never callsmake_shareableon a URI in any case. What the SDK does:Dexpace::URL.parse!re-parses a URI input from its text (neverdup, which aliases) and freezes each String-valued instance variable of the URI it now owns, skipping the parser reference;Model.ownkeeps every collection member a deep-frozen copy;Model.frozen_stringcopies and freezes every caller-supplied String member,Response#reasonincluded; and so every model —RequestandResponsegiven anilor frozen body, which is opaque in phase 1 and carried as given — isRactor.shareable?with no global touched.request_test.rbandresponse_test.rbassert it, so a later change toURL.parse!cannot lose the property silently.P1-9is retired as built andP1-13records the ownership rule (docs/work/mvp/phase1/2026-09-05-phase1-core-http-domain-model-design.md).Ractoris still load-bearing nowhere (docs/sdk-design-ruby/09-toolchain-and-quality-gates.md's runtime floor keeps it out of the supported surface). This entry stands where the phase-1 design's planning-time entry stood under## Superseded(sha:manual-phase1-ractor-uri), which claimed the narrowing and was withdrawn once the build disproved it; the phase 3 to 8 design documents that cite that entry's former key as "narrowing the shareability claim" describe the planning-time reading, and each phase's start-of-phase note query surfaces this entry instead. review ·docs/work/mvp/phase1/2026-09-05-phase1-core-http-domain-model-checklist.md· high · sha:manual-phase1-ractor-uri-as-built
Data#withdoes not call aninitializeoverride on Ruby 3.2, so it does not validate there; it does on 3.4 and 4.0. Supersedesdata-modeling/c4fe4732, whose "#with produces a copy with changes" was verified on 3.4.10 only. Re-verified on 2026-09-05 against three installed interpreters with oneDatasubclass whoseinitializeraises on anilmember: on 3.2.11instance.with(member: nil)returns#<data … member=nil>and the override is never invoked; on 3.4.10 and 4.0.6 it raises. Why it matters rather than being a curiosity:docs/sdk-design-ruby/04-domain-model-construction.mdmakes#withthe only derivation path for every value type with no builder —MediaType,Status,Protocol,Method,HeaderNameand the conditional-request helpers — so following the harvested wording ships unvalidated derivation on the declared floor (required_ruby_version >= 3.2) while everyHTTP-4/SEAM-29check passes on the developer's Ruby. That is the same shape as theURI::DEFAULT_PARSERsplit design §3.5 pins against: it passes where you look and fails where you do not. What the SDK does instead:Dexpace::Model, the module every coreDatatype includes, overrides#withto route through the type's own validating.build—changes.empty? ? self : self.class.build(**to_h, **changes)— so derivation re-validates identically on 3.2, 3.3, 3.4 and 4.0, and a value type added by any later phase inherits it by including the module rather than by remembering this entry. The CI matrix runs the real suite on every row, which is what keeps the guarantee standing; locally the check ismise exec ruby@3.2.11 -- bundle exec rake test:gems, and deleting the override turns that run red while the 4.0 run stays green. Two neighbouring facts in the same entry are unaffected and still hold on all three interpreters: aDatainstance is frozen on construction, and==/eql?/hashare generated over all members. review ·docs/work/mvp/phase1/2026-09-05-phase1-core-http-domain-model-design.md· high · sha:manual-phase1-data-with