From ada41f5a4fe2ea8028f904898d151bc98fd63135 Mon Sep 17 00:00:00 2001 From: Lucas Carlson Date: Sun, 18 Oct 2026 08:31:58 -0700 Subject: [PATCH 1/4] docs: add a maintenance and continuity page A reader who checks maturity asks who maintains the gem, how a release is published, and what they keep if maintenance stops. One page now answers these questions and asks for more maintainers. The README, the versioning page, and the comparison pages link to the new page and no longer state a count of maintainers. A new test fails when a page states one. --- README.md | 3 +- docs/compare/good-job.md | 2 +- docs/compare/solid-queue.md | 2 +- docs/comparisons.md | 2 +- docs/maintenance.md | 65 +++++++++++++++++++++++++ docs/versioning.md | 4 +- test/unit/maintenance_documents_test.rb | 34 +++++++++++++ 7 files changed, 106 insertions(+), 6 deletions(-) create mode 100644 docs/maintenance.md create mode 100644 test/unit/maintenance_documents_test.rb diff --git a/README.md b/README.md index 83bb3d1..6fee179 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ model and when to use it. **Status.** Version 1.0: the public API follows semantic versioning and does not break before 2.0. [Versioning](docs/versioning.md) gives the limits of the -promise. First public release: August 6, 2026. One maintainer. +promise. First public release: August 6, 2026. **Production use.** One first-party application with more than 100,000 users runs it in production. That is a count of users, not a measure of load. No @@ -199,6 +199,7 @@ every claim in [`test/guides/`](test/guides/): - [Reminders](docs/reminders.md) - [Reactive ERB](docs/realtime.md) - [Detailed architecture](docs/architecture.md) +- [Maintenance and continuity](docs/maintenance.md) - [Detailed documentation](docs/) The dashboard, benchmarks, migration cookbook, schema, and exhaustive API diff --git a/docs/compare/good-job.md b/docs/compare/good-job.md index 035b57c..410c01c 100644 --- a/docs/compare/good-job.md +++ b/docs/compare/good-job.md @@ -210,7 +210,7 @@ How it works: - First public release: August 6, 2026. - Version 1.0.0: October 13, 2026. The public API follows [semantic versioning](../versioning.md). -- One maintainer. +- [Maintenance and continuity](../maintenance.md) tells you who maintains the project and how a release is made. - One first-party application with more than 100,000 users runs it in production. That is a count of users, not a measure of load. No production use by other people is known. - The [benchmarks](../benchmarks.md) are development measurements. Longer soak tests are open work. diff --git a/docs/compare/solid-queue.md b/docs/compare/solid-queue.md index 1a9bc8a..40af371 100644 --- a/docs/compare/solid-queue.md +++ b/docs/compare/solid-queue.md @@ -205,7 +205,7 @@ How it works: - First public release: August 6, 2026. - Version 1.0.0: October 13, 2026. The public API follows [semantic versioning](../versioning.md). -- One maintainer. +- [Maintenance and continuity](../maintenance.md) tells you who maintains the project and how a release is made. - One first-party application with more than 100,000 users runs it in production. That is a count of users, not a measure of load. No production use by other people is known. - The [benchmarks](../benchmarks.md) are development measurements. Longer soak tests are open work. diff --git a/docs/comparisons.md b/docs/comparisons.md index 779c741..2b3abf6 100644 --- a/docs/comparisons.md +++ b/docs/comparisons.md @@ -23,4 +23,4 @@ The problem guides start from the same choices: ## Status -Solid Objects 1.0.0 shipped on October 13, 2026. The first public release was on August 6, 2026. It has one maintainer. One first-party application with more than 100,000 users runs it in production. That is a count of users, not a measure of load. No production use by other people is known. See [versioning](versioning.md). +Solid Objects 1.0.0 shipped on October 13, 2026. The first public release was on August 6, 2026. One first-party application with more than 100,000 users runs it in production. That is a count of users, not a measure of load. No production use by other people is known. See [versioning](versioning.md) and [maintenance and continuity](maintenance.md). diff --git a/docs/maintenance.md b/docs/maintenance.md new file mode 100644 index 0000000..aa3b576 --- /dev/null +++ b/docs/maintenance.md @@ -0,0 +1,65 @@ +# Maintenance and continuity + +This page tells you who maintains Solid Objects, how a release is made, and +what you keep if maintenance stops. It also tells you how to help maintain the +project. + +## Who maintains it + +Lucas Carlson ([@cardmagic](https://github.com/cardmagic)) is the lead +maintainer of the Ruby gem `solid_objects` and of the npm package +[`solid-objects`](https://github.com/cardmagic/solid-objects-js). The two +packages use the same model and the same correctness contract. A change to the +model goes into both packages, and the +[parity ledger](https://github.com/cardmagic/solid-objects-js/blob/main/docs/parity.md) +records each difference between them. + +The same maintainer sells [Solid Objects Pro](https://solidobjects.pro), a +commercial package that batches operations at scale. Pro depends on this gem. +This gem does not depend on Pro, and it needs no account, license key, or +service from the maintainer. + +## How a change is checked + +CI runs on each push and each pull request: + +- The full test suite on SQLite, on each pair of Ruby 3.3, 3.4, or 4.0 and + Rails 7.1, 7.2, 8.0, or 8.1. +- The full test suite on PostgreSQL and on MySQL, with both MySQL clients. +- Standard, RuboCop, RBS, Steep, and Brakeman. + +[Compatibility and maturity](virtual-actors.md#compatibility-and-maturity) +lists the supported versions. The [correctness contract](correctness.md) lists +the guarantees that the tests check. + +## How a release is made + +1. The maintainer pushes a version tag, for example `v1.0.3`. +2. GitHub Actions runs all test jobs again on that tag. +3. If all test jobs pass, GitHub Actions builds the gem and pushes it to + RubyGems with trusted publishing. The repository keeps no RubyGems key. +4. The same job makes a GitHub release and attaches the gem file. + +[Versioning](versioning.md) tells you what a release can change. +`CHANGELOG.md` lists each release. + +## What you keep if maintenance stops + +- **Your data.** Actor state, mailboxes, reminders, and effects are rows in + your application's database. [Database schema](database-schema.md) describes + each table. No service of the maintainer holds your data. +- **The code.** The [MIT License](../MIT-LICENSE) lets anyone use, change, + fork, and publish the code. +- **The checks.** The tests and the CI configuration are in this repository. A + fork can check its changes with the same suite. + +## Help maintain Solid Objects + +The project wants more maintainers. Experience with Rails, with locks and +transactions in PostgreSQL or MySQL, or with TypeScript and Node.js helps. + +1. Read [CONTRIBUTING.md](../CONTRIBUTING.md). +2. Send a small pull request: a fix, a test, or a correction to a guide. +3. After some of your pull requests are merged, open an + [issue](https://github.com/cardmagic/solid-objects-ruby/issues) to ask to + become a maintainer. diff --git a/docs/versioning.md b/docs/versioning.md index 7953a53..d0d4403 100644 --- a/docs/versioning.md +++ b/docs/versioning.md @@ -41,8 +41,8 @@ lists the current minimum versions. Version 1.0 is a promise about the API. It is not a claim of measured scale. The [benchmarks](benchmarks.md) are development measurements and do not -guarantee capacity. Longer soak tests are open work. The project has one -maintainer, and no production use by other people is known. +guarantee capacity. Longer soak tests are open work. No production use by +other people is known. ## Releases before 1.0 diff --git a/test/unit/maintenance_documents_test.rb b/test/unit/maintenance_documents_test.rb new file mode 100644 index 0000000..5e5f6a7 --- /dev/null +++ b/test/unit/maintenance_documents_test.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require "test_helper" + +class MaintenanceDocumentsTest < ActiveSupport::TestCase + ROOT = File.expand_path("../..", __dir__) + MAINTAINER_COUNT = /\b(?:one|only|sole|single)\s+maintainer\b/i + + def guide(path) + File.read(File.join(ROOT, path)) + end + + def documentation_paths + [ "README.md", "CONTRIBUTING.md", *Dir.glob("docs/**/*.md", base: ROOT).sort ] + end + + test "the README links to the maintenance page" do + assert_includes guide("README.md"), "(docs/maintenance.md)" + end + + test "the maintenance page names the lead maintainer" do + assert_match(/is the lead\s+maintainer/, guide("docs/maintenance.md")) + end + + test "no page states a count of maintainers" do + pages = documentation_paths.select { |path| guide(path).match?(MAINTAINER_COUNT) } + + assert_empty pages + end + + test "the maintenance page tells a contributor how to start" do + assert_includes guide("docs/maintenance.md"), "(../CONTRIBUTING.md)" + end +end From 4a0fc71d1ba0328bc0dc18194c677b8c84e650fb Mon Sep 17 00:00:00 2001 From: Lucas Carlson Date: Sun, 18 Oct 2026 08:31:58 -0700 Subject: [PATCH 2/4] chore: prepare version 1.0.3 --- CHANGELOG.md | 8 ++++++++ Gemfile.lock | 4 ++-- lib/solid_objects/version.rb | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0afce2e..35d746f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.0.3 - 2026-10-18 + +- Add `docs/maintenance.md`. It tells you who maintains the gem, how CI checks + each change, how a release is published, what you keep if maintenance + stops, and how to become a maintainer. The README, `docs/versioning.md`, and + the comparison pages now link to this page and no longer state a count of + maintainers. A new test fails when a page states one. + ## 1.0.2 - 2026-10-15 - Add two comparison pages in `docs/compare/`: Solid Queue diff --git a/Gemfile.lock b/Gemfile.lock index d70dd17..a1391f8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - solid_objects (1.0.2) + solid_objects (1.0.3) actioncable (>= 7.1) actionpack (>= 7.1) actionview (>= 7.1) @@ -392,7 +392,7 @@ CHECKSUMS rubocop-rails-omakase (1.1.0) sha256=2af73ac8ee5852de2919abbd2618af9c15c19b512c4cfc1f9a5d3b6ef009109d ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33 securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1 - solid_objects (1.0.2) + solid_objects (1.0.3) sqlite3 (2.9.5-aarch64-linux-gnu) sha256=78075b6337d3d182c6d2b4691049ed45cd220826160c9ea18946bf6a1de200dc sqlite3 (2.9.5-aarch64-linux-musl) sha256=18c801185deb4adc01ddb281e8f672a39e3d1729979ca91e39439cd3eac0402d sqlite3 (2.9.5-arm-linux-gnu) sha256=1bdfca0c7d63998c60b0f4a8e3c8df2d33800ccc4abd2d612eddbbbc92a4c48b diff --git a/lib/solid_objects/version.rb b/lib/solid_objects/version.rb index 5feb868..9e25219 100644 --- a/lib/solid_objects/version.rb +++ b/lib/solid_objects/version.rb @@ -1,5 +1,5 @@ # rbs_inline: enabled module SolidObjects - VERSION = "1.0.2" + VERSION = "1.0.3" end From f1fa32a36f044f49a3770eadc84e311a24fe125a Mon Sep 17 00:00:00 2001 From: Lucas Carlson Date: Sun, 18 Oct 2026 08:46:07 -0700 Subject: [PATCH 3/4] test: catch each form of a maintainer count The count pattern now finds numerals and plural forms, and a test checks the pattern against examples. The maintenance page test now checks the name and profile link of the lead maintainer. The changelog no longer says that the versioning page links to the maintenance page. --- CHANGELOG.md | 6 +++--- test/unit/maintenance_documents_test.rb | 23 ++++++++++++++++------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35d746f..f25aaa6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,9 @@ - Add `docs/maintenance.md`. It tells you who maintains the gem, how CI checks each change, how a release is published, what you keep if maintenance - stops, and how to become a maintainer. The README, `docs/versioning.md`, and - the comparison pages now link to this page and no longer state a count of - maintainers. A new test fails when a page states one. + stops, and how to become a maintainer. The README and the comparison pages + now link to this page. They and `docs/versioning.md` no longer state a count + of maintainers. A new test fails when a page states one. ## 1.0.2 - 2026-10-15 diff --git a/test/unit/maintenance_documents_test.rb b/test/unit/maintenance_documents_test.rb index 5e5f6a7..4b773fe 100644 --- a/test/unit/maintenance_documents_test.rb +++ b/test/unit/maintenance_documents_test.rb @@ -4,26 +4,35 @@ class MaintenanceDocumentsTest < ActiveSupport::TestCase ROOT = File.expand_path("../..", __dir__) - MAINTAINER_COUNT = /\b(?:one|only|sole|single)\s+maintainer\b/i + MAINTAINER_COUNT = /\b(?:\d+|one|two|three|four|five|six|seven|eight|nine|ten|only|sole|single)\s+maintainers?\b/i def guide(path) File.read(File.join(ROOT, path)) end - def documentation_paths - [ "README.md", "CONTRIBUTING.md", *Dir.glob("docs/**/*.md", base: ROOT).sort ] - end - test "the README links to the maintenance page" do assert_includes guide("README.md"), "(docs/maintenance.md)" end test "the maintenance page names the lead maintainer" do - assert_match(/is the lead\s+maintainer/, guide("docs/maintenance.md")) + assert_match( + %r{Lucas Carlson \(\[@cardmagic\]\(https://github\.com/cardmagic\)\) is the lead\s+maintainer}, + guide("docs/maintenance.md") + ) + end + + test "the count pattern finds a count of maintainers in each form" do + [ "One maintainer.", "1 maintainer", "two maintainers", "the only\nmaintainer" ].each do |text| + assert_match MAINTAINER_COUNT, text + end + [ "the lead maintainer", "wants more maintainers", "become a maintainer" ].each do |text| + refute_match MAINTAINER_COUNT, text + end end test "no page states a count of maintainers" do - pages = documentation_paths.select { |path| guide(path).match?(MAINTAINER_COUNT) } + paths = [ "README.md", "CONTRIBUTING.md", *Dir.glob("docs/**/*.md", base: ROOT).sort ] + pages = paths.select { |path| guide(path).match?(MAINTAINER_COUNT) } assert_empty pages end From 80dae167bde2fdf09577c764cbafaf330c000d5d Mon Sep 17 00:00:00 2001 From: Lucas Carlson Date: Sun, 18 Oct 2026 08:55:19 -0700 Subject: [PATCH 4/4] ci: pull service images from the mirror.gcr.io cache ECR Public refused the PostgreSQL, MySQL, and Redis image pulls with "toomanyrequests: Data limit exceeded", so the database jobs failed before a test ran. mirror.gcr.io serves the same Docker Hub library images. The JavaScript repository moved to it in cardmagic/solid-objects-js#79. --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6e76fd9..b97d352 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -66,7 +66,7 @@ jobs: timeout-minutes: 15 services: redis: - image: public.ecr.aws/docker/library/redis:7 + image: mirror.gcr.io/library/redis:7 ports: - 6379:6379 options: >- @@ -87,7 +87,7 @@ jobs: timeout-minutes: 15 services: postgres: - image: public.ecr.aws/docker/library/postgres:18 + image: mirror.gcr.io/library/postgres:18 env: POSTGRES_USER: solid_objects POSTGRES_PASSWORD: solid_objects @@ -121,7 +121,7 @@ jobs: client: [ mysql2, trilogy ] services: mysql: - image: public.ecr.aws/docker/library/mysql:8.4 + image: mirror.gcr.io/library/mysql:8.4 env: MYSQL_USER: solid_objects MYSQL_PASSWORD: solid_objects