Skip to content

Since #294 a single date column stops the spinel emit, and --allow-unsupported does not write the tree as the error says #303

Description

@eddygarcas

Probed at main e5f8dd3, Linux x86-64.

Since #294 (which carries #231, "Preserve date-only columns in the native Ruby target"), an app with a single t.date column no longer emits for --target spinel at all, and --allow-unsupported does not change that, although the error says it would.

db/schema.rb:

ActiveRecord::Schema[8.1].define(version: 2026_01_01_000000) do
  create_table "widgets", force: :cascade do |t|
    t.string "name"
    t.date "shipped_on"
  end
end

(plus a Widget model and a WidgetsController#index that renders Widget.count; nothing reads the date column).

$ roundhouse --target spinel -o out .
roundhouse: error[unsupported]: Date not supported (spinel): no date-only runtime wired for this target yet (native Ruby is supported)
roundhouse: 1 unsupported/syntax error(s), 0 type error(s) — rerun with --allow-unsupported to write the output anyway

$ roundhouse --target spinel --allow-unsupported -o out .
roundhouse: warning[unsupported]: Date not supported (spinel): no date-only runtime wired for this target yet (native Ruby is supported)
roundhouse: spinel: Date-only values are not supported; use the native Ruby target
$ echo $?
1        # and no `out/` is written
roundhouse --target spinel --allow-unsupported on this fixture
fc49a94 (before #294) writes the tree, exit 0
e5f8dd3 exit 1, nothing written

I understand the boundary is deliberate (the PR says so for JRuby, Spinel and Roda). The two things that look unintended: the --allow-unsupported hint is not honoured, and an app whose date columns are never read by the code paths it compiles loses the target entirely, where before the tree was written and built (I did not check how fc49a94 typed the column). A per-column gap (or the old mapping under --allow-unsupported) would keep such apps building.

Found while compiling a Rails API app with --target spinel.

Activity

  1. eddygarcas commented on Oct 3, 2026

    @eddygarcas
    CollaboratorAuthor

    Follow-up after reading docs/guide/transpile.md, which documents this as intended: Spinel and the other non-ruby targets "reject Date at the project boundary before emitting files, even with --allow-unsupported". Thanks, that settles the boundary itself. Three questions so I can plan around it:

    1. Is a date-only runtime for the Spinel target planned? If it is on the roadmap, I'll wait for it. If nobody has it scheduled, I'd be glad to contribute it. A pointer to the contract the ruby target's date-only adapter follows, and to the tests a Spinel version would have to pass, would be enough to start on a PR.
    2. Is there an interim you'd accept for apps whose date columns are never read by the code they compile? For example, an explicit opt-in that keeps the pre-Meta PR: enum/Date semantics, relation finders, test typing, Alba ledger and CI #294 timestamp mapping for Spinel. If it is not wanted, I'll handle it on my side.
    3. The error message. It still ends with "rerun with --allow-unsupported to write the output anyway", which the boundary never honours. Would you take a small PR that drops that hint for this error?

    Found while compiling a Rails API app with --target spinel.

  2. eddygarcas commented on Oct 3, 2026

    @eddygarcas
    CollaboratorAuthor

    @thomasklemm, you shaped this boundary in #231/#294, so you're probably the best person to answer the questions above. The short version: is a date-only runtime for the Spinel target planned? If not, would you accept one as a contribution (a plain-Ruby date-only class in the Spinel runtime that meets the same contract as the ruby target's adapter)? Or would you rather Spinel ship a date package first? I'm happy to do the work either way. I'd just like to build it the way you'd want to merge it.

  3. thomasklemm commented on Oct 3, 2026

    @thomasklemm
    Collaborator

    @eddygarcas Very flexible on this. Feel free to move this in any direction you see fit when pairing with your agent and explore what's best. Guess the solution might also change over time as both roundhouse and spinel move along and mature

  4. thomasklemm commented on Oct 3, 2026

    @thomasklemm
    Collaborator

    @eddygarcas Following up with a bit more of what I found, since you asked which way I'd want this built. Still flexible on the shape. This is context, not a spec.

    Thanks for the careful repro, and for checking the guide before treating the boundary as a bug. The refusal itself is the boundary from #294, and your fixture is enough to hit it: reject_unsupported_dates in src/project.rs keys off the schema, so an unread t.date column rejects the Spinel project before any method is looked at. --allow-unsupported only downgrades diagnostics in src/bin/roundhouse.rs. This gate returns an error from target_files before any file exists, which is why the run prints the hint and then exits 1 with nothing written. The guide already says that. The hint is the part that doesn't match.

    On your three questions, as I see them today:

    1. I don't know of a date-only Spinel runtime on our side, and upstream doesn't look close either. Current matz/spinel has no packages/date. docs/require.md still uses require "date" as the example of a require Spinel cannot satisfy, and matz/spinel#1102 is closed as out of scope. MatheusRich asked on 2026-10-01 whether to reopen it, now that other stdlib packages exist. No reply, and no package. CRuby's date is still the C extension (on the order of ten thousand lines). The pure-Ruby rewrite, ruby/date#155, is a similar size. So a full date package would be a real upstream project, not a missing method, and I wouldn't wait on it for this issue. If you also want to nudge that conversation upstream, that's welcome. It just doesn't unblock the apps you're compiling.

      What did land, and what makes a contribution here realistic, is matz/spinel#6399. Spinel now compiles a program-defined class Date instead of treating it as a reopening of a builtin it doesn't have. A bounded date in our Spinel runtime looks like the practical path. How you lay that out is up to you: one class, a tiny local package, whatever fits the runtime you find. I'd rather it not claim to be the stdlib, and require "date" in emitted Spinel code will still fail the require gate, so the overlay file that already calls Date.iso8601 can't be copied over as-is. It lives in runtime/spinel/scaffold/ruby_overlay/ and is documented as CRuby/JRuby-only.

    2. An opt-in that puts the old timestamp mapping back is the one direction I'd rather we didn't take, even for columns nothing reads. Ty::Date exists so a date isn't quietly a Time or a String. A clock and a zone the column doesn't have is the mixup Meta PR: enum/Date semantics, relation finders, test typing, Alba ledger and CI #294 closed. Supporting the column properly makes that opt-in unnecessary, including for the unread-column case in your fixture. If you find a narrower interim that stays honest, I'm open to it.

    3. Yes. The "rerun with --allow-unsupported" line is misleading for this error, and a small PR that drops the hint here is welcome on its own. It doesn't have to wait for the runtime.

    The behavior we already promise on native Ruby, and that I'd hope a Spinel version agrees with, is pinned by tests/date_columns_runtime.rb (run from tests/emit_and_run.rs):

    • storage stays YYYY-MM-DD, with no clock and no zone
    • parse_db_date / format_db_date are the seam (temporal_seam in src/lower/model_to_library/schema.rs)
    • SQL NULL, and the adapter's empty string for NULL, both come back as nil
    • >> / << are calendar shifts, clamped once at month end (2024-01-31 >> 1 is 2024-02-29, >> 2 is 2024-03-31)
    • a shift doesn't mutate the receiver, and an index shift doesn't mutate storage
    • JSON is the ISO date, or JSON null, not a zoned timestamp
    • 2023-02-29 raises. A non-date write raises TypeError
    • changing the zone doesn't move the calendar day

    The analyzer already accepts a bounded surface, pinned in tests/date_columns.rs: new / civil / parse / strptime / iso8601 / today, >> / <<, to_date / to_time, the calendar components, iso8601 / to_s / strftime, comparisons, leap?, and the weekday predicates. Known-bad arguments stay errors, and Time doesn't gain >>. Scheduling::Date is a different nominal class, not Ty::Date. Outside that table, a call is still an analysis error, which is the right edge until there's a body for it. You don't have to grow the table to match whatever the new class can do.

    One project rule that's worth keeping in view: dropping the diagnostic is a claim that the emitted program runs, not just that check is quiet. The Ruby runtime test above is the kind of pin that makes that claim. How you arrange the Spinel run is your call.

    A few edges I noticed while reading, none of them executed, because the gate returns before emit. Worth a look when the boundary opens, and the fix shape is yours:

    • encoded_value in src/lower/as_json_writer.rs sends a date column through JsonBuilder.encode_datetime. The specialized DateColumn path in the same file already formats an ISO date. The unspecialized writer doesn't. From the primitive, a 10-character date looks like it would be quoted as raw text rather than given a clock, but I haven't run it.
    • schema_time_columns lists DateTime and Time, not Date. _as_json_only would then emit the stored text from []. Rails' answer for as_json(only:) on a date is the ISO date or null. attributes staying stored text is intentional, and the runtime test pins that. I wouldn't change that one.
    • The jbuilder lowerer routes a date column through encode_datetime on <col>_raw, same as a timestamp. A date probably shouldn't grow a T and a Z.

    Also separate from a first runtime, and fine to leave diagnosed rather than stubbed: ActiveSupport date extensions (beginning_of_month, advance, and the rest of the Time table), form.date_field / date_select (typed as strings, but not in the form-builder lowering table; I haven't emitted a view), and where(due_on: some_date). I didn't find a date-predicate fold. If a query can't be emitted as a comparison against YYYY-MM-DD text, I'd rather it diagnose than bind the value as a timestamp. Schema defaults like default: Date.current, and nonliteral strict-local defaults, are an existing ingestion gap. And a require "date" in the app under compilation is still Spinel's refusal. Rewriting that into our class would be claiming the stdlib loaded.

    JRuby is the same project-boundary rejection, and the guide says its adapter path is unverified. I wouldn't mark JRuby, Roda, or the non-Ruby targets supported because Spinel grew a class. Each of those needs its own runtime.

    If you take this on, I'm glad to review. The Ruby runtime test is the acceptance check I'd trust. The hint fix can land first, on its own, whenever you like.

  5. eddygarcas commented on Oct 4, 2026

    @eddygarcas
    CollaboratorAuthor

    Thanks for the map, @thomasklemm. The hint fix is up on its own as #410. It stops suggesting --allow-unsupported whenever target_files refuses the project, with no special case for Date. I'll start on the date-only Spinel runtime next, beginning with the JSON paths, and keep tests/date_columns_runtime.rb as the acceptance check.

  6. thomasklemm commented on Oct 5, 2026

    @thomasklemm
    Collaborator

    Spinel date-only runtime work is stacking on Hill climb #451 (bounded program-defined Date, load only when the app uses dates so Campfire stays safe; hint half already merged as #410). Remaining edges (AS date extensions, form helpers, schema Date.current defaults, other targets) stay ledgered — not claiming this issue fully closed until those are honest or explicitly out of scope.

  7. thomasklemm commented on Oct 5, 2026

    @thomasklemm
    Collaborator

    Update: Spinel Date / remaining #303 work is owned by #423 (Add bounded Date runtime for Spinel), not Hill-climb #451.

    Early Date commits may still appear in #451’s history; do not stack further Date work on #451. Deferred Date ledger (AS date extensions, form date helpers, schema Date.current defaults, require "date" as stdlib, non-Spinel date runtimes, matz/spinel#7334, Campfire omit-Date verification) → track against #423.

  8. 13 remaining items

  9. eddygarcas commented on Oct 6, 2026

    @eddygarcas
    CollaboratorAuthor

    Checked on main 37bb4c5: with #454 merged, the original repro (an app with a single t.date column) now emits for --target spinel with exit 0. I'll leave this issue open or closed as you prefer, @thomasklemm, since you're tracking the remaining Date items (ActiveSupport extensions, form helpers, schema defaults) against #454.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions