Repository navigation
CRuby Db: rate-limit checkpoint warns + flock takeover test - #59
Closed
thomasklemm wants to merge 248 commits into
Closed
thomasklemm wants to merge 248 commits into
thomasklemm wants to merge 248 commits into
Conversation
Infer unsigned byte integers and share one no-block bridge classifier across targets. Preserve native Ruby/Spinel block behavior and report unsupported strict-target block forms explicitly. Add emitted execution regressions. Fixes rubys#433. Co-Authored-By: Codex <noreply@openai.com>
Use the shared byte-array classifier at the forwarded-proc guard and exercise explicit nil blocks, refusal boundaries and generated RBS seeds. Document the target entry points and regression intent. Co-Authored-By: Codex <noreply@openai.com>
Return an ingest error instead of silently omitting external engine mounts. Keep survey recovery explicit and preserve the runtime's built-in ActiveStorage routes. Cover strict and survey CLI behavior with a generic path-sourced engine for Ruby and Spinel, and correct the route documentation. Co-Authored-By: Codex <noreply@openai.com>
Preserve supported runtime cable mounts and recover sibling routes with attributed error diagnostics. Keep strict refusal, allow explicit unsupported recovery, and aggregate check errors across route and application sources. Co-Authored-By: Codex <noreply@openai.com>
Declare the emitted Rack test prerequisite in unit CI and contributor setup. Keep the real request assertions unchanged. Co-Authored-By: Codex <noreply@openai.com>
Seed method bodies and receiver dispatch with qualified RBS identities. Keep only unique short aliases and tighten the measured residual ceiling. Co-Authored-By: Codex <noreply@openai.com>
Fixes rubys#430. Co-Authored-By: Codex <noreply@openai.com>
Fixes rubys#429. Co-Authored-By: Codex <noreply@openai.com>
Address the additional-target regressions found on PR rubys#439. Co-Authored-By: Codex <noreply@openai.com>
Retain Rack for the required mount dispatch test alongside upstream ruby-vips and libvips42 prerequisites. Preserve both branches' source changes. Co-Authored-By: Codex <noreply@openai.com>
Fix the scoped Swift request error boundary and shared Router portability on PR rubys#439. Co-Authored-By: Codex <noreply@openai.com>
Preserve the canonical identity fix and strict 45-site probe ceiling. Retain upstream's measured legacy-probe history without treating it as the canonical probe's ceiling. Co-Authored-By: Codex <noreply@openai.com>
Preserve zero, maximum-byte and bounds-rejection expectations while avoiding Swift infix try syntax in the generated framework tests. Co-Authored-By: Codex <noreply@openai.com>
Preserve upstream changes and both probe explanations without changing the casting implementation or its assertions. Co-Authored-By: Codex <noreply@openai.com>
Forward flattened int_params to the production and test Route constructors. Cover multiple constrained keys and unconstrained controls with generic emission and native Swift request regressions. Co-Authored-By: Codex <noreply@openai.com>
`redirect(path: "/x")` keeps the request query string. The synthesized
action built that from `ActionController::Current.request` and a
`split("#", 2)` whose `parts[0]` is `String?`, and the analyzer rejected
both. Every app with a path-option redirect got errors with no file or
line: 3 on real-blog with such a route, and 10 on Jumpstart Pro for its
three Devise-era `/users/*` redirects. Nothing ran the emitted action,
so no test caught it.
The action now reads the query off its own `request`, as the
block-redirect actions already do. Only `path:` keeps the query, and
its location is a string literal, so the rest is decided at compile
time: the `&` or `?` separator, and where the fragment goes. A `%{name}`
filled from a path segment cannot bring a `?` or `#` of its own. The
emitted action is one conditional over two literals, for example
`q == "" ? "/a#top" : "/a?" + q + "#top"`. The behavior is unchanged:
- an empty query leaves the location alone
- a path that already has a `?` is joined with `&`
- the query goes ahead of a fragment
That behavior is a deliberate divergence from Rails, now written down
in the code. Measured under Rails 8.1.4 and main, OptionRedirect only
appends `"?" + query`. It gives `/a?b=1?x=1`, where `b` reads as
`1?x=1`, and `/a#top?x=1`, where the query is inside the fragment and
never reaches the server. Rails also re-encodes the query (keys
sorted, a space as `+`). This passes it through as received.
Pinned by `tests/emit_and_run.rs::a_path_option_redirect_keeps_the_request_query`.
It failed with the three errors before the change and now runs each
case through a request. The lowering tests in routes_redirect_lowering.rs
follow the new shape.
Co-Authored-By: Claude <noreply@anthropic.com>
Review on rubys#456 asked whether a `%{id}` holding a `#` can move the kept query into a fragment. In Rails it would, if Rails did not re-escape: it decodes `/old/a%23top` to `params[:id] == "a#top"` and then path-escapes it back. The emitted router does neither step. A capture keeps the request's own percent-encoding, and a request path cannot carry a literal `#` or `?` in a segment, so the segment passes through as sent. That is the location Rails builds, apart from Rails' normalizing (`%7e` becomes `~`). Escaping it here would double-encode it (`%2523`), which I tried and measured. The comments that claimed a capture "cannot bring a `?` or `#`" now say why that holds. The emitted test adds the reviewer's case and a positional redirect with an encoded space and `?`. Both expectations are Rails 8.1.4's answers for the same requests. Co-Authored-By: Claude <noreply@anthropic.com>
The shared router handed every capture over still percent-encoded.
`/articles/a%20b` gave `params[:id] == "a%20b"`, where Rails' Journey
router (`CGI.unescapeURIComponent`, 8.1.4 and main) gives `"a b"`. That
held on every target, for `:name` segments, `*glob` captures and
prefixed segments (`~:username`) alike.
`Router.decode_segment` now decodes each capture once:
- `%20` is a space, `%2F` a slash inside the segment, and `%25` a percent.
- A `+` stays a `+`. Form decoding belongs to the query, not the path.
- A `%` without two hex digits after it stays as written, as in Rails.
It is pure shared Ruby in the shapes the router's other helpers
already lower everywhere: one `while` per method, `==` fan-outs, and no
regex. So it covers printable ASCII only (`%20`..`%7E`), through a
lookup string. A non-ASCII sequence such as `%C3%A9`, or a control
byte, stays encoded. That is no worse than before, and the code says so.
Three servers decoded the path before routing, which would now decode
it twice. They hand the router the raw path instead:
- Go: `URL.EscapedPath()`
- Python: aiohttp `rel_url.raw_path`
- C#: the request feature's `RawTarget`, minus the query
The TypeScript, Crystal, Rust, Kotlin, Swift (Hummingbird's `uri.path`
is a substring of the request line), Elixir, Ruby and Spinel servers
already pass the raw path.
A routing redirect's `%{name}` is filled from the decoded capture, so
it is now path-escaped again as Rails does
(`Journey::Router::Utils.escape_path`), through `Router.escape_path`,
the decoder's inverse. Otherwise a decoded `#` or `?` would end the
path. It leaves alone a `%XX` that `decode_segment` left encoded, so a
non-ASCII name answers `/~jos%C3%A9` as Rails does, not `%25C3%25A9`.
The analyzer exposes that one router method to app code.
Pinned by:
- `tests/emit_and_run.rs::a_path_parameter_is_percent_decoded`: six
requests, each expecting what Rails 8.1.4 answers.
- The redirect test's encoded-capture cases: `a%23top`, `a%20b%3Fc`,
`jos%C3%A9`, `100%25`, `a%2Fb%7e` and `x%0Ay`, with Rails' answers.
- Two new runtime router tests for `decode_segment` and `escape_path`.
They pass under CRuby and under the TypeScript framework suite. In
the Rust framework suite they compile, and that suite fails on
upstream already.
Claude-Session: https://claude.ai/code/session_01AxRosKWn6BKiBwzju7wjrU
…orm target in C# `go_smoke::router_v2_shape` pinned the old `params[name] = ap`. The router now assigns `decode_segment(seg)`, so the test pins that. The C# server read the raw request target as the path, but a request sent in absolute form (`GET http://host/articles`, as to a proxy) carries a scheme and host there. It now takes `Uri.AbsolutePath`, which stays escaped, and still falls back to `Request.Path` when there is no raw target. Claude-Session: https://claude.ai/code/session_01AxRosKWn6BKiBwzju7wjrU
Preserve both byte contract tests and upstream view rendering regressions. Validate the complete default suite on the merged source and retain the independently reproduced upstream native boot limitation in PR evidence. Co-Authored-By: Codex <noreply@openai.com>
Preserve canonical RBS seeds and finder-specific zero-residual assertions. Record independently measured upstream residual growth from 45 to 57, with identical expression paths on upstream and the finder candidate. Ledger the independently measured unseeded probe growth from 497 to 505 after upstream membership/cardinality changes; retain fully typed RBS gates. Co-Authored-By: Codex <noreply@openai.com>
Retain both byte regressions and upstream view-rendering execution tests. Keep the route implementation and contract expectations unchanged while validating the resulting source against current upstream. Co-Authored-By: Codex <noreply@openai.com>
Validate Float bounds before truncation instead of parsing scientific notation as a decimal String prefix. Align finder and parser input types, retain String semantics, and exercise lowered calls under Ruby and Spinel. Record the measured seven-site unseeded inference delta while preserving the separate fully typed RBS finder gates. Co-Authored-By: Codex <noreply@openai.com>
Return a transient statement while an identical cached shape is owned. Reset and clear each released handle once, track replay promotions, and drain abandoned siblings at the lease boundary, including failed requests. Evict statements after failed cleanup, retain resources whose close failed, and quarantine their connections. Track missing pool capacity if replacement fails; wake waiters to retry or raise the opener error. An exhausted checkout can restore the slot when the opener recovers. Healthy checkout is unchanged. Preserve the original request exception when cleanup also fails. Extend the shared CRuby/compiled Spinel ownership gate and inject reset, clear, close, replacement and shutdown failures in the real drivers. Run the JDBC cleanup and unbound ownership probes in the existing JRuby CI job, and route their paths through the planner. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Concern that declares `class_attribute :x, default: D` in `included do` and writes it from `class_methods` (`self.x += [...]`) left every macro call in the includer unrecognized, and its effect dropped. The macro's arguments may be computed at run time, so it is not evaluated here: the emitted class makes the same call when it loads. Each direct includer gets the Concern's class methods as its own, the attribute as a class ivar seeded with its default at the `include`, and a reader. `self.x = v` becomes `@x = v`; `self.x op= v` becomes `@x = x op v`, read through the reader. A subclass that never writes reads its parent's value, as class_attribute inherits; a write replaces it on that class only. All or nothing per carrier: another `included` statement or a source `@x` leaves the carrier unexpanded. The Ruby emitter now writes class-body initializers after the methods they may call. Spinel refuses `==` on a local holding such a reader's result (matz/spinel#7602); the native contract reads off the class directly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The class methods `ingest::class_attribute` copies onto a controller were
typed with every parameter unresolved: nothing observed their calls.
- A class-body macro call is a call site, and one written in a subclass
reaches the controller that defines the method.
- `**{k: v}.merge(h)`, which ingest writes for `f(k: v, **h)`, places
the literal's keys and gives each other keyword `h`'s value type.
A bare `**h` stays unplaced, as before.
- `Kernel#Array(x)` takes its element from `x` instead of RBS untyped.
- `[] + b` (and `|`, `concat`) onto an unknown element takes `b`'s.
- The attribute's type is what its methods store; on the class that
includes the Concern it is never unset, the default being stored at
the `include`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- `Kernel#Array` of a tuple joins the tuple's element types instead of
wrapping the tuple.
- An app method named `Array` keeps its answer even when that answer is
untyped; only the Kernel fallback is refined.
- `**{k: v}.merge(h)`: `h` merges over the literal, so a named key's type
joins the splat's value type.
- Each copied class method is keyed to the class_attribute it writes,
not the first one declared; a method writing two refuses the carrier.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…a relation - A copied class_attribute method's signature declared every parameter with the attribute's type; it now declares what the call sites seeded (a finite writer still takes the slot's). - A merged splat's literal keys join its value type where the call site is recorded, so every placement path sees it. - `Kernel#Array` of a relation is its records, of a range its elements; a record is left unknown, like a Hash. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review follow-ups. - A subclass that set its attribute to nil read its parent's value: the reader fell back on nil. Each write now also sets `@name__written`, and a subclass reader falls back only when that flag is unset, so nil is the subclass's value as in Rails. Spinel has no `instance_variable_defined?` on a class, hence the flag. - A copied class method's parameter defaults are typed before its parameters are seeded, so `only: nil` declares the nil it takes when the argument is absent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Treat native SQLite bind and step return codes as errors rather than success or EOF. Reset failed native cursors and drop their query-cache captures, but retain checkout ownership until explicit finalize or lease cleanup. An old ensure-finalizer must never reset another reader using the same raw pointer. After CRuby/JDBC cleanup, re-raise the original driver exception with its class and identity intact, even when cleanup also fails. Keep JDBC failed statements evicted. JDBC bind setters remain in the later JDBC commit. Cover bad bind slots, real integer-overflow step errors, same-lease recovery, and old finalizers on CRuby and compiled Spinel. Assert actual driver error classes and identities on CRuby and JDBC, including cleanup failures. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Inherit a real controller-file span for AOT controller_name/path literals (main rubys#552 left them synthetic and broke span preservation). Mirror library survey-mode honesty: model expand_mattr_cattr uses ingest_expr_strict so nil substitution cannot become a claimed seed. Co-Authored-By: Cursor <cursoragent@cursor.com> Co-authored-by: Thomas Klemm <github@tklemm.eu>
Attribute controller_name literals to controller source spans
Resolve controller_to_library conflict by taking main's rubys#573 controller_provenance_span (action-body preference + Option span). Co-Authored-By: Cursor <cursoragent@cursor.com> Co-authored-by: Thomas Klemm <github@tklemm.eu>
ingest_expr_strict still recurses through ingest_expr, so unsupported nested subtrees can become synthetic nil under survey while the outer call returns Ok. Mirror library and model claim paths: if the survey collector grew during default ingest, leave the declaration unmodeled. Co-Authored-By: Cursor <cursoragent@cursor.com> Co-authored-by: Thomas Klemm <github@tklemm.eu>
Signed Locator verifies MessageVerifier envelopes under a purpose, narrows with literal only:, and monomorphizes to locate_signed_<model> the same way unsigned locate does — so strict targets never dispatch through a class object. Co-Authored-By: Cursor <cursoragent@cursor.com> Co-authored-by: Thomas Klemm <github@tklemm.eu>
`for: purpose` is a default expression, not a local alias — bind purpose from **opts[:for] instead so the generic Locator entry works. Co-Authored-By: Cursor <cursoragent@cursor.com> Co-authored-by: Thomas Klemm <github@tklemm.eu>
Mirror ActionText::RichText: push an ApplicationRecord model with polymorphic belongs_to :record and schema columns (including optional slug). Blob is reached via instance helpers (url/filename/content_type), not a belongs_to — Blob is a runtime class. Co-Authored-By: Cursor <cursoragent@cursor.com> Co-authored-by: Thomas Klemm <github@tklemm.eu>
Reader always returns ActiveStorage::AttachedMany; attach_blob appends; attachments answers Attachment rows. Array attachables lower to sequential attach_blob/from_attachable. Preload with_attached_<name> → <name>_attachments. Ratchet runtime untyped ceiling for the model-bridge Relation chain. Co-Authored-By: Cursor <cursoragent@cursor.com> Co-authored-by: Thomas Klemm <github@tklemm.eu>
When the hook reopens an app model with only literal include Module calls, push those includes onto the model body (pending across file order) so concern splice applies has_many_attached. Other reopens stay surveyed. Co-Authored-By: Cursor <cursoragent@cursor.com> Co-authored-by: Thomas Klemm <github@tklemm.eu>
Overlay: has_many_attached array + attachments.last, Attachment finders, locate_signed mint/find. Unit: synthesis, AttachedMany reader, on_load carry. Update unclaimed-DSL and only_finder locate_signed rewrite tests. Co-Authored-By: Cursor <cursoragent@cursor.com> Co-authored-by: Thomas Klemm <github@tklemm.eu>
After save!, append a read of the model so inlined update! answers the record, not Bool — clears Spinel AOT Page vs Base widening. Catalog save! ReturnKind unchanged. Co-Authored-By: Cursor <cursoragent@cursor.com> Co-authored-by: Thomas Klemm <github@tklemm.eu>
Analyze registers blob/url/filename/content_type on the synthesized ActiveStorage::Attachment model so redirect_to @attachment.url types. Keep has_many_attached out of writable_field_set until a Many writer exists (proxy attach only). Re-apply pending on_load includes after Markdown/Attachment synthesis so synthesis-only targets still splice. Co-Authored-By: Cursor <cursoragent@cursor.com> Co-authored-by: Thomas Klemm <github@tklemm.eu>
Check errors 34→29; survey gaps 11→10 (on_load Attachment→Markdown include carried). Cleared locate_signed, ActiveStorage::Attachment constant, upload @record/@attachment, and Attachment#url. Co-Authored-By: Cursor <cursoragent@cursor.com> Co-authored-by: Thomas Klemm <github@tklemm.eu>
Rails only class_variable_sets a nil default when the @@ slot is not already defined; unconditional @@attr = nil wiped inherited values. Emit conditional defined?(@@attr) seeds for nil/absent defaults and pin with a redeclaration runtime test. Co-Authored-By: Cursor <cursoragent@cursor.com> Co-authored-by: Thomas Klemm <github@tklemm.eu>
Second concern splice after Attachment/Markdown synthesis now targets only models touched by apply_pending — a full re-splice doubled every already-expanded concern body (Writebook where/order counts, Campfire unique-constraint failures). AttachedMany#attachments uses raw SQL + ManyAttachment (not Relation over the synthesized Attachment MODEL), restoring Soft Bar B ceiling 304. Co-Authored-By: Cursor <cursoragent@cursor.com> Co-authored-by: Thomas Klemm <github@tklemm.eu>
The campfire page compares deployed shapes, route by route, as
once-campfire-rust's bench/run does: room page, messages?before=,
sidebar, search, avatar, a stylesheet, /up and a POST. The avatar
token and stylesheet digest are each lane's own, scraped from its room
page into {avatar}/{css} placeholders so cells line up across lanes.
The -1p, JVM-family and nocache lanes stay requestable by name but
leave the default set, and their notes print only when they ran.
bench-report takes the harness's endpoint order and lays the latency
table out a row per route when routes outnumber lanes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…5998 mattr/cattr values are shared across the class hierarchy
Every file under each app/assets directory and app/javascript lands at static/assets/<path within it>, where Rack::Static serves it. The emitted campfire linked 26 stylesheets plus its images, sounds and JS modules this way and served none of them: /assets/_reset.css was a 404. A path the scaffold already builds (tailwind.css, application.css) keeps its task. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tobi runs tobi/campfire-once-ruby-ractor as its Dockerfile does (ruby --yjit bin/campfire, jemalloc, Rails' worker formula as Ractors in one process) against the same seeded database, signing in with header-only forgery protection like the rust and go lanes. The emit directories are removed before each emit: the emitter never deletes a file it stopped generating, and the box's spinel tree still held a 09-25 index.rbs beside index_turbo_stream.rbs, which spinel's --rbs now refuses as a duplicate declaration. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…estorage-abd0 Hill climb: Writebook as the forcing fixture for ActionText Markdown uploads / ActiveStorage attachments
GitHub can leave selected archive-results as abandoned on large ci:full PR graphs even when every producer succeeded (run 37652830796). Treat the packaging evidence job as REPORTING: still selected for completeness and required by Pages assembly, but never a CI-summary/required failure. Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Preserve the instance receiver for literal controller_name and controller_path implementations. Pin the generated signatures and execute both Base and concrete-controller calls in the emitted Rust application. Fixes rubys#575. Co-Authored-By: Codex <noreply@openai.com>
The spinel binary serves static/assets, which only `make assets` fills, and the harness ran `make build` alone: every stylesheet the room page links was a 404 on that lane. bench-report prints the per-route placeholders as :avatar_token, "a stylesheet" and "POST <path>". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Make a module parameter's call-site join order-independent (rubys#209)
Keep app scopes and dynamic finders on the relation receiver (rubys#558)
Queue and other stdlib constants resolve again in emitted Ruby
Tep uses registered reasons for conflicts and validation errors
Serving checkpointers kept failing silently after wal_autocheckpoint=0. Warn at most once per 30s so operators see stuck disk/permission paths without a Campfire-style 250ms log storm. Pin leader-exit takeover: a survivor's loop acquires the flock and copies the WAL. Honest Spinel comment: WORKERS=1 is the measured lane; do not add a second flock home. Co-Authored-By: Cursor <cursoragent@cursor.com> Co-authored-by: Thomas Klemm <github@tklemm.eu>
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: true
Comment |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Advances the post-rubys#548 hardening from the Campfire #319 vs Roundhouse review — keeps RH’s opt-in design (no global
wal_autocheckpoint: 0).Summary
runtime/spinel/db_cruby.rb:warn_checkpoint_failureemits at most once per 30s. The loop keeps its 250 ms cadence (no Campfire-style backoff / log storm).tests/db_sqlite_concurrency.rs: a foreign flock holder is killed; the survivor’s background checkpointer acquires the lock and copies the WAL whilewal_autocheckpoint=0.runtime/spinel/db.rb: measured lane isWORKERS=1; do not add a second flock home for--workers N.Intentionally not ported from Campfire rubys#319: global yml pragma,
WEB_CONCURRENCY=autogating, Resque hooks, Spinel flock, per-tick warn storms.Test plan
cargo test --test db_sqlite_concurrency— 12 passed, 1 ignored (Spinel)Refs