Skip to content

Fix extra-language CI compare/smoke for HeaderStore typing - #33

Closed
thomasklemm wants to merge 92 commits into
mainfrom
cursor/extra-language-ci-a318
Closed

thomasklemm wants to merge 92 commits into
mainfrom
cursor/extra-language-ci-a318

Conversation

@thomasklemm

@thomasklemm thomasklemm commented Oct 6, 2026 •

Copy link
Copy Markdown
Owner

Summary

Makes compare / compare-extra / smoke green again for the remaining extra-language targets after the view_helpers / form_with / Value-union work landed on main.

Upstream PR: rubys#495 (this fork PR was opened by mistake and closed).

Last Full validation failure before those fixes: run 37455132329 on c49721be.

Changes

  • Shared: seed RBS @ivar: T decls into parse_library_with_rbs so HeaderStore @keys/@vals empty [] initializers stamp Array[String] (not Untyped) for Go/Kotlin/C#/Swift emit.
  • Go: value-position IIFEs clear void_method so ternary tails in void methods still return.
  • Kotlin / C#: cast Long list indexes to Int / (int) for Array/List access.
  • Rust: to_s on Array index reads prefers the field-table elem type.

Does not weaken Soft Bar B (still 299) or suppress diagnostics.

Test plan

Open in Web Open in Cursor 

bunnykong and others added 30 commits October 5, 2026 20:39
Expose the registered String comparison through a typed facade that
reuses MessageVerifier's bytewise comparison. Ship the facade and its
signature in Ruby, JRuby and Spinel output and require it from consumers.

Pin controller execution, empty/multibyte/binary comparisons, ordinary
non-string errors and all three package shapes. Reuse the same string
contract in the native Spinel harness.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`p text` followed by deeper-indented lines now keeps them in the same
tag, as Slim does. `doctype transitional` and `strict` emit their XHTML
1.0 DOCTYPE instead of being reported as a gap.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A concern macro like has_mobile_version(*actions) binds the call's
symbols to the rest parameter, takes the trailing Hash as options, and
reads options[:if]. The macro now folds to a scoped before_action; an
absent or nil if: is no guard, any other if:/unless: still refuses.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Review follow-up. `has_mobile_version *ACTIONS` expanded to a filter
whose `only` silently lost the splat, so it ran on every action. A
literal `*%i[a b]` now spreads; any other splat leaves the macro
unexpanded. `options[:if]` on a Hash with a computed or `**` key is no
longer folded to nil (that key might be `:if`), so the filter is
refused instead of losing its guard. A repeated key reads its last
value, as in Ruby.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review follow-up. `filter_from_send` dropped any `only:`/`except:`
element that was not a Symbol, so `has_mobile_version :index, x` (or a
spread `*[:index, x]`) narrowed the filter to `:index`, and a list with
nothing readable became an unscoped filter. Such a list now refuses the
macro. String actions, which Rails accepts, are read rather than dropped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every run left `/tmp/roundhouse-emit-and-run-<pid>-<n>` behind, about
two thousand per full suite, enough to exhaust a tmpfs's inodes after a
few suites. A `Run`, and the tree `emit()` returns, now remove theirs
when dropped unless the test is failing (the failure names the tree) or
`ROUNDHOUSE_KEEP_EMITTED=1` is set. A run expected to fail is a passing
test and is removed too. An `empty_app()` base is removed once copied.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…on CRLF

Co-authored-by: Thomas Klemm <github@tklemm.eu>
Fold rubys#459 (limit/offset/hash order as values) plus string order
parsed as col/table.col with ASC/DESC, and validate last_n/first_n
before mutating. Fold rubys#464 positional where/having binds as a
one-pass split so ? and backslashes in values stay data. Keep
main's named-bind dispatch and nested-hash order (campfire sidebar).

Sanitize head/render locations and headers[]= with a shared
HeaderStore (Puma drop). Share one HttpHeaders helper for Tep/CGI.
Escape CR/LF/NUL in URL filename components; re-allowlist disk
disposition at show.

redirect_to refuses an absolute URL whose host is not this request's
Host. Session/flash cookies get SameSite=Lax and Secure on HTTPS;
signed jar options are honored. HMAC-only session store remains a
named residual (no AES-GCM in this runtime).

CSRF: verify_authenticity_token on shared Base; empty session token
matches nothing. Write-once in runtime/ruby, not a close across
targets. Overlay/spinel still mint per-session tokens.
References #23.

Pin with emit_and_run overlays. Skip ActionText XSS, LIKE, path
decode, Tep parser PRs, and Campfire app patches.

Co-authored-by: Thomas Klemm <github@tklemm.eu>
A bare protect_from_forgery is :null_session and is not modeled as
422, so the implicit :exception filter stays gated off. Overlay
scripts that write with: :exception disable allow_forgery_protection
or park Current.request so GET query/header/redirect checks are not
422.

Co-authored-by: Thomas Klemm <github@tklemm.eu>
Class-level Widget.order is rewritten to Widget.all.order at emit,
so the runner script must use all. A tails action names first_n
and last_n so treeshake does not drop them.

References #23.

Co-authored-by: Thomas Klemm <github@tklemm.eu>
Relation[Widget] has no catalog method first_n; the overlay must
write first(n)/last(n) so lowering rewrites them. The runner still
calls last_n on Widget.all.order after emit.

Co-authored-by: Thomas Klemm <github@tklemm.eu>
Walk header bytes with [i, 1] instead of getbyte/bytesize, find
hosts without String#index, and keep HeaderStore writers void so
C#/Go/TypeScript emit. Class-level CSRF flag is explicit self
methods. String order allows LOWER(col) / LOWER(table.col) for
campfire sidebars while still rejecting (SELECT 1) fragments.

Shared form_authenticity_token stays empty; ruby/spinel overlays
still mint. References #23.

Co-authored-by: Thomas Klemm <github@tklemm.eu>
…ines; XHTML doctypes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ions with extract_options!

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…h trees

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Skip apt work when dpkg already has the package, try the image index
before apt-get update, and install without recommends. Unit shards
link with lld, install gems in one step, run each cargo batch once
instead of --no-run plus run, and invoke the already-built debug
emit_preview. Store-check uses the debug compiler; writebook builds
the bin in the same cargo test. Spinel compile jobs inherit MAKEFLAGS=-j4.

Co-Authored-By: Cursor Agent <noreply@cursor.com>

Co-authored-by: Thomas Klemm <github@tklemm.eu>
CARGO_PROFILE_TEST_RUSTFLAGS / CARGO_PROFILE_DEV_RUSTFLAGS need the
unsettled profile-rustflags Cargo feature. Stable 1.98 exits 101 on
every cargo test, which is why all four unit shards failed.

Co-Authored-By: Cursor Agent <noreply@cursor.com>

Co-authored-by: Thomas Klemm <github@tklemm.eu>
Store the CSRF flag in a one-slot array instead of a class ivar so
rust/C#/Go emit. Walk headers with size/key_at/val_at (no Hash#each
or yield). Type HeaderStore keys as String and keep []= void.
Normalize backslash/tab locations and reject /// hosts. DiskController
writes through headers[]= so Spinel layout matches Base.

References #23.

Co-authored-by: Thomas Klemm <github@tklemm.eu>
Bring in rubys#469/rubys#470/rubys#474 and the CI runner cuts so rubys#476 stays
based on current upstream main. No rebase; no force-push.

Co-authored-by: Thomas Klemm <github@tklemm.eu>
Measured 452 Ty::Untyped sites after typing HeaderStore and location
walks as String. The leftover rise is order allowlists and cookie
flag hashes. Do not treat a quieter count as support.

Co-authored-by: Thomas Klemm <github@tklemm.eu>
Bare cookie writes left flags unrecorded, so the Spinel dispatcher
emitted Set-Cookie without HttpOnly. Default flag_httponly? to on
unless explicitly opted out. Carry explicit secure: through the
Rack descriptor so run_rack does not drop Secure on HTTP.

References #23.

Co-authored-by: Thomas Klemm <github@tklemm.eu>
WHATWG URL parsers delete tab anywhere, so `/\t/evil` becomes
`//evil` in the browser. sanitize_location now drops tab/CR/LF/NUL
everywhere and strips leading/trailing C0 and space. Pin the tab
case next to the backslash and triple-slash probes.

References #23.

Co-authored-by: Thomas Klemm <github@tklemm.eu>
order_fn_term? required an identifier inside the parens, so the
documented zero-arg RANDOM()/random() raised ArgumentError. Keep
SLEEP() and splice fragments rejected.

References #23.

Co-authored-by: Thomas Klemm <github@tklemm.eu>
send_data writes only inline/attachment, so the filename-bearing
Content-Disposition must be set after that call.

References #23.

Co-authored-by: Thomas Klemm <github@tklemm.eu>
Carry each cookie's same_site flag through the Rack descriptor
instead of forcing Lax. Emit Secure whenever SameSite is None so
browsers do not drop the cookie.

References #23.

Co-authored-by: Thomas Klemm <github@tklemm.eu>
* Tighten Soft Bar B: Relation records as Base (435→393)

Loaded Relation cache / Enumerable blocks / first_n last_n detect
destroy_* as Base (interim before Relation[T]). Order parts, hash
conditions, page/per, hidden_field_tag name, and Base.first/take
narrowed. Spinel sidecar still widens Base → untyped.

Measured residual 443→393; never raise the ceiling.

Co-Authored-By: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>

* Tighten Soft Bar B keys and query unions (393→352)

Cookie/session/flash index keys are String | Symbol. Nested order
hashes, find_by/update_all/pluck, and class_names take the shapes
the bodies already branch on. Spinel still widens find_by to untyped.

Co-Authored-By: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>

* Tighten Soft Bar B SQL and Arel unions (352→322)

quote, where/not, sum, exec log-name, update_column, unique_by,
Arel column/subquery, and number_with_precision take the shapes
the bodies already dispatch on.

Co-Authored-By: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>

* Shorten Soft Bar B ceiling comment

Drop the date-by-date residual ledger above CEILING. Keep the ratchet
rule and the current residual note only.

Co-Authored-By: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>

* Fix Spinel Relation Base rewrite holes

Spawn stays an exact pair (bare untyped, not Array[untyped]?).
Remaining Base on def/ivar lines widens in one pass so Set[Base]
and each_with_object are not missed. Pin no Base on Spinel def
lines. Ceiling unchanged.

Co-Authored-By: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>

* Widen Spinel Base.first/take and nested includes

connection.rbs class-side first/take (and remaining def-line Base)
rewrite on the Spinel tree only. includes/preload/eager_load accept
Symbol or a one-level Hash. Ceiling unchanged.

Co-Authored-By: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>

* Tighten Soft Bar B helper and SQL unions (322→303)

Drop the merge-leftover duplicate @records declaration. Narrow
column_predicate / hash_conditions, form helpers, cache keys,
cookie value_of / CookieJar.build, and exec binds. Bar A still
passes. Never raise the ceiling.

Co-Authored-By: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>

* Drop spawn-records Spinel rewrite after rubys#462 restack

take_query_lists has no records param. Pin the spawn helper name
instead. Ceiling unchanged.

Co-Authored-By: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>

* Lock Soft Bar B ceiling at 298

Measured residual after rebasing onto current main. Never raise.

Co-Authored-By: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>

* Widen class_names and quote unions

Match the helpers: Integer/bool/String-key hashes for class_names,
and Symbol for quote. Soft Bar B stays 298.

Co-Authored-By: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>

* Accept nil in find_by signatures

find_by(nil) is supported (add_condition no-ops). Keep the Spinel
exact-pair replacements in lockstep. Soft Bar B stays 298.

Co-Authored-By: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>

* Fix Spinel Relation initialize and hash predicates

Apply the Base→untyped sidecar in the framework-test harness.
Relax hash_conditions values; excluding writes the pk predicate
directly. Compare ids via ids_of. Soft Bar B 299.

Co-Authored-By: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>

* Accept String keys on relation conditions

where/where!/not/add_condition/hash_conditions take
Hash[String | Symbol, untyped]. Soft Bar B stays 299.

Co-Authored-By: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Bring in the Soft Bar B ratchet tighten (299) so rubys#476 stays based
on current upstream main. Keep the security helpers and type them
on main's String|Symbol / Integer|Float|String unions instead of
untyped. No rebase; no force-push.

Co-authored-by: Thomas Klemm <github@tklemm.eu>
Compile-time class-body macros now expand a statically interpolatable
class_eval heredoc (and substituted has_one) the same way literal
define_method already did. Load-hook includes contribute class-method
macros without installing mixin instance methods. HasOne carries scope
and autosave; polymorphic builders set the interface type column.

Forcing tests are synthetic overlays on the blog fixtures (enum,
attribute, define_method, heredoc class_eval), not a named app macro.
Dynamic string eval stays unexpanded.

Refs #30

Co-Authored-By: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>
has_many already synthesized `assoc.each { |c| c.destroy }` on
before_destroy. has_one now cascades the single child when present
(`if reader; reader.destroy`) so a nil association does not raise.

Shape pin in model_lowerer; emit_and_run overlay on real-blog with a
synthetic Profile child. Autosave and preload stay unclaimed.

Co-Authored-By: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>
count_sql already counts the result set, not underlying rows. Overlay
distinct_index / grouped_index on the kaminari pagination harness so
page readers and total_count match three unique titles from five rows,
and metadata reads do not mutate SQL or loaded records.

Refs rubys#343

Co-Authored-By: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>
thomasklemm and others added 29 commits October 6, 2026 18:31
Unblock main unit shards after String#bytes (rubys#434)
Campfire ingest→lower spent ~40% of wall time in construct-to-library
because dump_ir and five emitters typed every view, then built a second
ViewLowerCtx and lowered again. Signatures for the model registry only
need untyped classes; body typing can mutate that same Vec.

Also cache view-name / dynamic-render ivar sets across analyzer
fixpoint rounds, sort inference fingerprints without extra String keys,
and time post-analyze plus dump_ir lower stages.

Co-Authored-By: Composer <noreply@cursor.com>

Co-authored-by: Thomas Klemm <github@tklemm.eu>
Twenty-five disjoint send/op rewrites now share one post-order walk over
hook bodies (and matching view/test surfaces) instead of twenty-five
for_each_hook_body passes. Controller lowering registers jbuilder
signatures without typing template bodies, hoists the permitted-fields
map, and retypes a method only when the in-place bracket/broadcast
rewrite fires.

Co-Authored-By: Composer <noreply@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>
Controller lowering retyped every action after Arel even when the rewrite
was a no-op. Return whether the walk changed the body and type only then.
Union canonicalization no longer allocates a Debug string per variant on
every analyzer join. Fuse each.with_index into the shared post-analyze
walk and time blank, mocha, kwsplat, send_dispatch, and relation_residue.

Co-Authored-By: Composer <noreply@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>
Analyzer fixpoint compared registries by Debug-formatting every method
type into one string each round. Compare HashMaps of types instead.
Fuse time_calendar into the early send walk, and fuse rails_cache,
capture_inline, and_return, case_lambda, system_exception,
perform_all_later, attachables_grep, and send_file into one walk after
kwsplat. Time remaining dump_ir construct-to-library stages.

Co-Authored-By: Composer <noreply@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>
Type each test method once, then retype only when an in-place rewrite
fires or harvested ivars are nonempty. Assoc-create, route-id
projection, and assertion inlining now walk in place and report
whether anything changed, matching the controller skip-retype pattern.
Ordinal and enumerable rewrites keep the ivar-typed pass they need.

Co-Authored-By: Composer <noreply@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>
time_current, webmock, global_id_locate, and assoc_pluck each walked
the app tree on their own. Collect their tables once and apply the
rewrites in one hook walk, one test walk, and one view walk, matching
the original surfaces. Method names stay disjoint so fusion is
equivalent to the sequential passes.

Co-Authored-By: Composer <noreply@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>
Nine sequential passes after tag_builder (request_index through
assoc_loaded) each walked overlapping trees. Split them into one
controller/library/model walk and one hook+view+test walk, matching
the original surfaces. Fold config_reader's three hook walks and
helper_kwargs' class/instance walks into one each.

Co-Authored-By: Composer <noreply@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>
Fold try_guard last into the fused context walk, preserving its extra
test constant and inner-class surfaces. After the STI predecessor
cluster, fuse attribute_aliases, sti_is_a, job_test_only,
test_cookie_jar, controller_class_render, and sum_symbol into one
pre-tag walk matching original surfaces. Fold symbolize_keys last into
the config_reader walk so it sees lifted group readers.

tag_builder stays sequential: it synthesizes capture/html_safe that
later passes must walk, and must not move after kwsplat. kwsplat stays
sequential so send_file still sees expanded keyword args. inquiry stays
pre-order. Controller first-type and analyzer full-app retype are
unchanged.

Co-Authored-By: Composer <noreply@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>
Dropping Debug-string sort made Bool|Int|Nil canonicalize as
Int, Bool, Nil. The pattern-matching test compared derived ==
against Debug order. Compare unions as sets, pin the structural
order in a lattice unit test, and scan sibling expected-union
tables — only this Bool/Int mix disagreed with ty_tag.

Co-Authored-By: Composer <noreply@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>
Clone module-method and parent-link tables once per analyze() instead
of every typing round. Borrow filter-target bodies into chained_bodies
instead of cloning ancestor trees. Skip jbuilder template walks when
the controller lowerer only needs signatures. Stop cloning every
action to resolve filter halt-checks and params-helper specs.

Inner ROUNDHOUSE_TIMINGS split typing (constants/controllers/models/
library/views) and controller construct-to-library (build/jbuilder
sigs/type). IR stays equivalent; first controller type and full-app
retype are unchanged.

Co-Authored-By: Composer <noreply@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>
Effect collection does not feed harvest or unify, so stamp per-node
effects once after the typing loop instead of walking every body on
every round. Intermediate fixpoint rounds type production only;
views and tests retype once against the converged registry, with one
more full pass if those call sites moved a parameter.

Campfire analyze 0.29s -> 0.26s. Message JSON unchanged.

Co-Authored-By: Composer <noreply@cursor.com>

Co-authored-by: Thomas Klemm <github@tklemm.eu>
compare rust compiles transpiled view_helpers.rb, not the handwritten
runtime. Heterogeneous RBS unions rendered as serde_json::Value but
nil? became is_none (E0599), method_override_input took &str from a
Hash#fetch Value (E0308), and turbo_stream_from took Value at string
call sites (E0308).

Treat those unions as Value for is_null / Value::from / as_str, leave
Option and stringish unions on their existing shapes, and pin the emit
against real-blog.

Co-Authored-By: Composer <noreply@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>
Models do not read controller_ivar_env. Type each model and harvest its
returns into the dispatch registry before controllers in the same
typing pass, so a round carries model facts two hops instead of one.

Not a call-graph rewrite. Campfire Message JSON is unchanged. Production
fixpoint drops one harvest/unify round (8 -> 7).

Co-Authored-By: Composer <noreply@cursor.com>

Co-authored-by: Thomas Klemm <github@tklemm.eu>
The initial typing pass was still walking every view and test against
an empty first registry, then ignoring those trees for seven production
rounds. Type production only at the start; retype views and tests once
after the fixpoint. The absorb pass after view unify is production-only
too, matching the comment.

Campfire Message JSON unchanged. Analyze 0.72s -> 0.63s on this box.

Co-Authored-By: Composer <noreply@cursor.com>

Co-authored-by: Thomas Klemm <github@tklemm.eu>
Test methods were typed up to four times (empty-ivar, rewrite follow-up,
ivar reseed, ordinal) and the finished LibraryClass was cloned into the
output vec. Harvest ivars from the first typed pass and share one
follow-up type for rewrites plus ivar reads; move the class instead of
cloning. Inner stand-ins skip the empty-ivar retype when harvest is empty.

Controllers share one follow-up type across bracket, broadcast, and arel.
Jbuilder uses the in-place route-id projection and skips the second type
when it is a no-op.

Co-Authored-By: Composer <noreply@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>
Skipping views/tests on the first analyzer pass left test helpers typed
once after production converged. Caller-before-callee chains and
default-parameter seeds need harvest/unify/retype rounds; main typed
tests every fixpoint round and tests/test_module_source_typing.rs
passes there.

After production, type views once, then retype tests only until
inference settles. Views stay out of those extra rounds. Campfire
Message JSON unchanged.

Co-Authored-By: Composer <noreply@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>
Wave 12 stopped typing views and tests during the production fixpoint
but still harvested untyped test helpers and unified from those trees.
Later test-only rounds also rebuilt inferred_params from scratch, which
re-walked unchanged view bodies.

Production harvest/unify now skip tests and views. After views type
once, later test rounds overlay test call sites onto a production+view
param snapshot. Campfire Message JSON unchanged.

Co-Authored-By: Composer <noreply@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>
Empty value-position Seqs now count as Arel hoist changes. Config writes
walk rewritten args with the fused cable/credentials pass. In-place
route-id and assoc-create rewrites use pre-order map_expr_mut. Test
helpers retype after sibling signatures lift. Filter-effect lookup walks
parent controllers. Union tests compare both directions. Rust emit no
longer calls as_str on Option<Value> or HashMap::get.

Co-Authored-By: Composer <noreply@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>
Skipping first-pass view typing left nested serializer constructors
untyped after one production absorb: AuthorResource.new lives in
ArticleResource.to_h, so unify WithViews must run again after the
retype. Compact-required unit (3) failed
view_only_constructors_are_checked_without_another_callers_evidence.

Heterogeneous RBS unions rust-emit as serde_json::Value; to_s used
Display JSON quotes on form titles. Route those recvs through
ruby_to_s like Untyped/Record.

Co-Authored-By: Composer <noreply@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>
A non-literal format: wraps the helper as call + ".ext". Sequential
walks still visited that inner recv:None send; the fused mid callback
only saw +, so host/position rewrites no-op'd. Run those follow-ups on
the wrapped helper.

Value-shaped interpolation now uses ruby_to_s (same JSON-quote class as
to_s). Hash#fetch(k, nil) at a string param materializes Null-on-miss
before as_str, matching HashMap#get.

Co-Authored-By: Composer <noreply@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>
Command::current_dir wants AsRef<Path>; Deref on the Emitted newtype
does not coerce through that generic bound. Same three-site fix as
rubys#487 so unit shards compile.

Co-Authored-By: Composer <noreply@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>
opts.fetch is Hash[Symbol, untyped]; method_override_input takes
String | Symbol (rust &str). Stringify at the Hash-fetch boundary
like mail_to. optional_value_attr nil? already uses is_null on
Value-shaped unions. No extra-language Cast lifting.

Co-Authored-By: Composer <noreply@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>
Keep primitive-to-Value wrapping in rust coerce (not the shared
lowerer). Union/ParamValue to_s uses fully-qualified RubyToS UFCS so
ActionController compiles without the trait in scope. form_with
stringify stays; nil? is already is_null. Ivar Array fallback restores
HeaderStore << / index. Unit rust_json_value_coerce expected stringify
not as_str after form_with.

Co-Authored-By: Composer <noreply@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>
Wave 12 types views once against production-only helper returns,
then unifies params from those templates. Highlight helpers such as
Writebook leafables/show highlight_searched_content stayed gradual
untyped because view trees were never restamped after harvest.
Retype helpers, harvest, then views. Do not ratchet the inventory.

Co-Authored-By: Composer <noreply@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>
Runtime files emit under an empty EmitCtx, so pin ActionController
Const param Tys rust-only. HeaderStore []= takes Option<String> not
Value; val_at prefers the ivar Array[Option] table; tail nil in an
untyped method is Value::Null. Family 3 wrapping stays rust-only.

Co-Authored-By: Composer <noreply@cursor.com>
Co-authored-by: Thomas Klemm <github@tklemm.eu>
Speed up ingest → lowered IR
* Speed up Swift compare-extra without changing coverage

Hosted compare-extra (swift) is 7–8 billable minutes. Recent logs spend
~150s on `swift build -c release` and ~30s rebuilding tools/compare in
release, plus a cold Hummingbird/NIO clone.

Use debug SPM for the DOM-diff server (same check as the README and
swift_toolchain compile gate), skip the index store, cache SwiftPM
checkouts, cache tools/compare/target, and compile emit_preview next to
the framework tests.

Co-Authored-By: Cursor Agent <noreply@cursor.com>

Co-authored-by: Thomas Klemm <github@tklemm.eu>

* Keep Swift CI speedups off the shared compare cache

compare (rust) / compare-extra (go, kotlin) compiled broken
view_helpers after rust-cache grew cache-directories (new miss key)
and scripts/compare started skipping Cargo when a binary existed.
Swift framework tests also fatalError'd: --disable-index-store left
SPM looking for an index store path that does not exist.

Restore the default rust-cache and the release comparator for every
compare target. Always cargo-build tools/compare and emit_preview so
restored artifacts cannot skip a source rebuild. Keep debug `swift
build -Xswiftc -gnone` and the SwiftPM checkout cache.

Co-Authored-By: Cursor Agent <noreply@cursor.com>

Co-authored-by: Thomas Klemm <github@tklemm.eu>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Seed RBS `@ivar` decls into parse_library_with_rbs so empty `[]`
initializers stamp Array[String] (not Untyped) for Go/Kotlin/C#/Swift.
Go value-position IIFEs clear void_method so ternary tails return.
Kotlin/C# cast Long list indexes to Int. Rust to_s on Array[String]
index reads prefers the field-table elem type so key_at does not
Option-map a plain String.

Co-Authored-By: Cursor Agent <noreply@cursor.com>

Co-authored-by: Thomas Klemm <github@tklemm.eu>
@thomasklemm thomasklemm closed this Oct 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants