partition_gardener 0.3.5 → 0.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +21 -0
  3. data/README.md +2 -2
  4. data/docs/THIRD_PARTY_LICENSE_MANIFEST.tsv +18 -19
  5. data/docs/application_contract.md +29 -3
  6. data/docs/audit_reference.md +1 -1
  7. data/docs/changelogs/20260709120900_partition-engines-guide.md +1 -1
  8. data/docs/changelogs/20260813142300_rebalance-before-archive-attach.md +19 -0
  9. data/docs/changelogs/20260813144516_relock-appraisal-advisory-json.md +21 -0
  10. data/docs/changelogs/20260902114500_align-child-columns-before-attach.md +19 -0
  11. data/docs/changelogs/20260902124500_cut-patch-release-0-3-4.md +24 -0
  12. data/docs/changelogs/20260909143500_occupied-window-current-origin.md +19 -0
  13. data/docs/changelogs/20260909154900_tail-slot-rfc.md +21 -0
  14. data/docs/changelogs/20260909162554_high-end-future-occupancy.md +21 -0
  15. data/docs/changelogs/20260909164000_shipped-contract-rfcs.md +21 -0
  16. data/docs/changelogs/20260909165500_cut-patch-release-0-3-5.md +24 -0
  17. data/docs/changelogs/20260916144600_public-identifiers-contract.md +23 -0
  18. data/docs/changelogs/20261005135700_checkpoint-table-name.md +23 -0
  19. data/docs/changelogs/20261005141621_cut-patch-release-0-3-6.md +23 -0
  20. data/docs/changelogs/20261021220000_cut-patch-release-0-3-7.md +24 -0
  21. data/docs/configuration.md +3 -1
  22. data/docs/dependencies/20260813144516_json-cve-2026-71847.md +27 -0
  23. data/docs/issues/20260909143500_occupied-window-current-origin.md +25 -0
  24. data/docs/issues/20260909164000_shipped-contract-rfcs.md +25 -0
  25. data/docs/issues/20260916113300_opaque-composite-public-ids.md +55 -0
  26. data/docs/issues/20260917123253_ci-drop-automatic-dependency-audit.md +28 -0
  27. data/docs/operations.md +1 -1
  28. data/docs/partition_landscape.md +6 -4
  29. data/docs/reviews/20261021220000_engineering-audit-0-3-7.md +30 -0
  30. data/docs/reviews/20261028075216_engineering-audit-release-preparation.md +35 -0
  31. data/docs/reviews/20261029150855_engineering-audit-release-preparation.md +43 -0
  32. data/docs/schemas/partition_garden.schema.json +16 -0
  33. data/lib/partition_gardener/archive_retention.rb +7 -3
  34. data/lib/partition_gardener/config_document.rb +4 -0
  35. data/lib/partition_gardener/configuration.rb +9 -1
  36. data/lib/partition_gardener/date_calendar.rb +2 -5
  37. data/lib/partition_gardener/gap_detection.rb +1 -1
  38. data/lib/partition_gardener/migration/hot_switch_concern.rb +24 -15
  39. data/lib/partition_gardener/plan_applier.rb +17 -3
  40. data/lib/partition_gardener/sql_run_record_store.rb +40 -5
  41. data/lib/partition_gardener/version.rb +1 -1
  42. data/sig/partition_gardener.rbs +2 -2
  43. metadata +22 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: abde0192951f5cc668d53ca847f119c3d9dcf5c994404504119cfb1d54c6be17
4
- data.tar.gz: 47eab4f81f45acf7a59730e2a04ac48769e655557da78c0db12be355a85ce6b2
3
+ metadata.gz: e5f9084c2b05bdad581f87e5b137cc7321106f2a4a39e3d52def523acd74f0b7
4
+ data.tar.gz: 86ba83786f0cf06729e041e758e8034253e549677237160bb3dc0c09716ec071
5
5
  SHA512:
6
- metadata.gz: 51677ad1c0b6d9513a86e574e9254776a7b9cecbd83f230a38b61d1a3f04be5313987901658aa9aebbf78e47a0f5ddd9b9573c6b3396c599ac6c60fdc932ab96
7
- data.tar.gz: f1f4271c57f3a1ed9eaf2ca2f47d351cb50f729377f21bfac7657ce9d5342067b8dbc4c005d3db926d9cbf25186fbf0f431d287daf8139111758c2d647e7ac8d
6
+ metadata.gz: c3c13183c16a47fc4b479a7c4311e689a2081e9dddc0c60f8a590c5e4cdffa4e56db1f8e270e7cf4119772398f1f286baaa4ab985b20ef743b8fa2854a38b47e
7
+ data.tar.gz: 702c2a0f9d29d5c0a63894172ced8ab62cf47f70ab84f0fbcd02a9e648455cfed78927d187ca41538612140c7271b51363937fa60a1be501a24e4c3bb3ac42a5
data/CHANGELOG.md CHANGED
@@ -2,6 +2,27 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.3.7 (2026-10-29)
6
+
7
+ - Fix rebalance resume so phase `detach` still runs detach and drain until the record advances to `segments`
8
+ - Refuse `CREATE TABLE IF NOT EXISTS … PARTITION OF` when a detached table already owns the planned child name
9
+ - Drop detached remove candidates only when they hold zero rows
10
+ - Rename hot-switch children when the live table name is a suffix of the shadow prefix (for example `p_events` → `events`)
11
+ - Repoint serial sequences after hot-switch by setting `nextval` default and `setval` on the live table
12
+ - Fail closed in `sync_delta_data` when `sync_stale_column` is missing from source or target
13
+ - Sort gap detection bounds with native `Comparable` order so integer windows are not lexicographic
14
+ - Advance `DateCalendar.add_years` through leap days via `Date#>>`
15
+ - Notify detach versus drop for archive retention when `retention_keep_table` is set
16
+ - Export and accept `active_id_lo`, `active_id_width`, `current_band_size`, and `archive_band_size` in JSON registry documents
17
+ - Align README Ruby requirement and RBS `statement_timeout` types with the gemspec
18
+
19
+ ## 0.3.6 (2026-10-05)
20
+
21
+ - Store rebalance checkpoints in `partition_gardener_checkpoints` by default (previously `partition_gardener_run_records`)
22
+ - Add `run_record_table_name` so a host can keep another checkpoint table name
23
+ - Rename leftover `partition_gardener_runs` or `partition_gardener_run_records` on first use of the default table name
24
+ - Document public identifiers for partitioned rows: four contracts, Rails signed_id versus scalar primary_key, fail-closed decode ([application_contract.md](docs/application_contract.md#public-identifiers))
25
+
5
26
  ## 0.3.5 (2026-09-09)
6
27
 
7
28
  - Keep attached monthly children that already cover the start of the sliding-window active range, and fill the remainder as `_open` rather than overlapping `_current`
data/README.md CHANGED
@@ -6,7 +6,7 @@ PostgreSQL partition lifecycle: archive, current, and future zones, heat-driven
6
6
 
7
7
  ## Requirements
8
8
 
9
- - Ruby >= 3.2
9
+ - Ruby >= 3.4
10
10
  - PostgreSQL with declarative partitioning
11
11
  - `pg` gem (runtime dependency)
12
12
  - Rails >= 7.1 optional — railtie loads when Rails is present and sets Active Record connection plus `Time.zone.today`
@@ -37,7 +37,7 @@ Complements migration gems (e.g. [pg_party](https://github.com/rkrage/pg_party))
37
37
  ### Application and migration
38
38
 
39
39
  - [docs/cutover.md](docs/cutover.md) — hot-switch playbook and template upgrades
40
- - [docs/application_contract.md](docs/application_contract.md) — writes, moves, routing when the key is missing, bulk load, replicas
40
+ - [docs/application_contract.md](docs/application_contract.md) — writes, moves, routing when the key is missing, public identifiers, bulk load, replicas
41
41
  - [docs/naming.md](docs/naming.md) — child partition naming catalog
42
42
  - [docs/host_testing.md](docs/host_testing.md) — CI and staging for host apps
43
43
 
@@ -1,27 +1,27 @@
1
1
  bundle gem version licenses
2
- root activemodel 8.1.3.1 MIT
3
- root activerecord 8.1.3.1 MIT
4
- root activesupport 8.1.3.1 MIT
2
+ root activemodel 8.1.4 MIT
3
+ root activerecord 8.1.4 MIT
4
+ root activesupport 8.1.4 MIT
5
5
  root appraisal 2.5.0 MIT
6
6
  root ast 2.4.3 MIT
7
7
  root base64 0.3.0 Ruby|BSD-2-Clause
8
- root bigdecimal 4.1.2 Ruby|BSD-2-Clause
8
+ root bigdecimal 4.1.3 Ruby|BSD-2-Clause
9
9
  root bundler 4.0.16 MIT
10
10
  root concurrent-ruby 1.3.8 MIT
11
11
  root connection_pool 3.0.2 MIT
12
12
  root diff-lcs 1.6.2 MIT|Artistic-1.0-Perl|GPL-2.0-or-later
13
13
  root drb 2.2.3 Ruby|BSD-2-Clause
14
14
  root i18n 1.15.2 MIT
15
- root json 2.21.2 Ruby
15
+ root json 3.1.0 Ruby
16
16
  root language_server-protocol 3.17.0.6 MIT
17
17
  root lint_roller 1.1.0 MIT
18
18
  root logger 1.7.0 Ruby|BSD-2-Clause
19
19
  root minitest 6.0.6 MIT
20
- root parallel 2.2.0 MIT
21
- root parser 3.3.12.0 MIT
22
- root partition_gardener 0.3.5 MIT
20
+ root parallel 2.3.0 MIT
21
+ root parser 3.3.13.0 MIT
22
+ root partition_gardener 0.3.7 MIT
23
23
  root pg 1.6.3 BSD-2-Clause
24
- root polyrun 2.2.4 MIT
24
+ root polyrun 2.2.5 MIT
25
25
  root prism 1.9.0 MIT
26
26
  root prosopite 2.2.0 Apache-2.0
27
27
  root racc 1.8.1 Ruby|BSD-2-Clause
@@ -29,33 +29,32 @@ root rack 3.2.7 MIT
29
29
  root rainbow 3.1.1 MIT
30
30
  root rake 13.4.2 MIT
31
31
  root rbs 4.2.0 BSD-2-Clause|Ruby
32
- root regexp_parser 2.12.0 MIT
32
+ root regexp_parser 2.13.1 MIT
33
33
  root rspec 3.13.2 MIT
34
34
  root rspec-core 3.13.6 MIT
35
35
  root rspec-expectations 3.13.5 MIT
36
36
  root rspec-mocks 3.13.8 MIT
37
37
  root rspec-support 3.13.7 MIT
38
- root rspec_junit_formatter 0.6.0 MIT
39
- root rubocop 1.88.2 MIT
38
+ root rubocop 1.91.0 MIT
40
39
  root rubocop-ast 1.50.0 MIT
41
40
  root rubocop-capybara 2.23.0 MIT
42
41
  root rubocop-factory_bot 2.28.0 MIT
43
42
  root rubocop-performance 1.26.1 MIT
44
43
  root rubocop-rails 2.34.3 MIT
45
44
  root rubocop-rspec 3.10.2 MIT
46
- root rubocop-rspec_rails 2.32.0 MIT
47
- root rubocop-thread_safety 0.7.3 MIT
45
+ root rubocop-rspec_rails 2.33.0 MIT
46
+ root rubocop-thread_safety 0.8.0 MIT
48
47
  root ruby-progressbar 1.13.0 MIT
49
48
  root securerandom 0.4.1 Ruby|BSD-2-Clause
50
- root standard 1.56.0
49
+ root standard 1.57.0
51
50
  root standard-custom 1.0.2 MIT
52
51
  root standard-performance 1.9.0 MIT
53
52
  root standard-rails 1.6.0 MIT
54
- root standard-rspec 0.5.0 MIT
53
+ root standard-rspec 0.5.1 MIT
55
54
  root thor 1.5.0 MIT
56
55
  root timeout 0.6.1 Ruby|BSD-2-Clause
57
56
  root tsort 0.2.0 Ruby|BSD-2-Clause
58
57
  root tzinfo 2.0.6 MIT
59
- root unicode-display_width 3.2.0 MIT
60
- root unicode-emoji 4.2.0 MIT
61
- root uri 1.1.1 Ruby|BSD-2-Clause
58
+ root unicode-display_width 3.3.0 MIT
59
+ root unicode-emoji 4.3.0 MIT
60
+ root uri 1.1.2 Ruby|BSD-2-Clause
@@ -1,6 +1,6 @@
1
1
  # Application contract
2
2
 
3
- How host applications should behave around partitioned tables: queries, writes, maintenance side effects, bulk load, and replicas. For UI scoping and aggregate snapshots see [partition_landscape.md](partition_landscape.md). For operator runbooks see [operations.md](operations.md).
3
+ How host applications should behave around partitioned tables: queries, writes, public identifiers, maintenance side effects, bulk load, and replicas. For UI scoping and aggregate snapshots see [partition_landscape.md](partition_landscape.md). For operator runbooks see [operations.md](operations.md).
4
4
 
5
5
  ## Query and write basics
6
6
 
@@ -8,6 +8,7 @@ How host applications should behave around partitioned tables: queries, writes,
8
8
  - `conflict_key` columns match the parent unique index; updates and deletes use `query_constraints` when the logical id is not globally unique ([partition_landscape.md](partition_landscape.md#rails-application-contract)).
9
9
  - Inserts supply a routable partition key value so rows land in named children, not only in `default`.
10
10
  - When only a logical id or parent reference is available, follow the recovery ladder in [partition_landscape.md](partition_landscape.md#routing-hints-when-the-key-is-not-in-hand).
11
+ - Public URLs and API ids follow [Public identifiers](#public-identifiers).
11
12
  - After maintenance with high `rows_moved`, refresh `id → partition_key` mappings when the partition key can change.
12
13
 
13
14
  ## During maintenance (row moves)
@@ -31,6 +32,31 @@ Duplicate key across children — overlapping manual DDL. Application response:
31
32
 
32
33
  Applications should not catch routing errors and retry without the partition key.
33
34
 
35
+ ## Public identifiers
36
+
37
+ Treat database uniqueness, public identifier, routing hint, and authorization as four contracts. PostgreSQL uniqueness on a partitioned parent is composite because the partition key must sit in every unique index. That does not by itself decide what a person sees in a URL or API.
38
+
39
+ When a single sequence or UUIDv7 keeps `id` unique across children, keep that logical id as the application identifier: `self.primary_key = :id` so `to_param` and JSON stay a scalar, not a Rails underscore composite such as `42_100`. Still attach a routing hint on point lookups so `SELECT` can prune. Do not invent that hint from current month.
40
+
41
+ When `id` is unique only inside a child, the public identifier for point lookups must carry every uniqueness column. Prefer one opaque path segment over Rails `extract_value` underscore form (`4_2`) or a separate query parameter the caller can edit independently.
42
+
43
+ Prefer Rails APIs over a homemade encoding:
44
+
45
+ - True composite `primary_key` (array): `signed_id` / `find_signed` or `to_sgid` / `SignedGlobalID`. Both uniqueness columns travel; `find` can prune. `signed_id` uses HMAC SHA256, JSON, and `url_safe: true`. The payload is encoded, not encrypted.
46
+ - Scalar `primary_key = :id` plus `query_constraints`: `signed_id` and `generates_token_for` still sign and find by `id` alone. `query_constraints` never feeds `SELECT`. Sign `[id, partition_key]` with a host `ActiveSupport::MessageVerifier` (`url_safe: true`), or resolve through an unpartitioned mapping table.
47
+ - `has_secure_token` stores a random Base58 mapping value. A unique index on that column alone is illegal on a partitioned parent unless the partition key is in the index. Put the mapping off the partitioned fact table, or keep a globally unique logical id on the fact row.
48
+ - Default `MessageVerifier` is not URL-safe. `signed_id` and `GlobalID::Verifier` opt into `url_safe`. Do not encrypt identifiers with `MessageEncryptor`.
49
+ - Unsigned GlobalID (`gid://app/Event/123/2026-03-15`) and `to_param` underscore composites are tunable. Keep them off public point-lookup URLs.
50
+ - Extra JSON in `generates_token_for` is compared after fetch. It is not a `WHERE` predicate and is plaintext in the token.
51
+
52
+ Active Record 7.1 is this gem's development floor. Composite `find_by_token_for` needs 7.2 or 8.0. Composite `find_signed` wrapping `primary_key => [id]` needs 8.1. Hosts on 7.1 should verify finders before relying on signed composite lookup.
53
+
54
+ After decode, query with both columns and fail closed. Do not fall back to id-only scan. A bad or unsigned token is 404, not a bounded retry across children. Encoding is packaging, not authorization. Lookups still go through an ownership set.
55
+
56
+ List screens still show period, account, or branch in product language ([partition_landscape.md](partition_landscape.md#ui-and-product-surfaces)). Tenant and list keys for hot paths come from session or parent context, not from a tunable URL field. Pagination cursors already belong in the same family as opaque tokens.
57
+
58
+ Gardener does not ship an encoder. Host applications own secrets, token version, and decode.
59
+
34
60
  ## Bulk import and backfill
35
61
 
36
62
  - Chunk `COPY` or `insert_all` by bucket window within partition bounds (month, week, day).
@@ -69,7 +95,7 @@ Registry JSON may differ per shard only if layouts differ (unusual).
69
95
  ## Admin and operator surfaces
70
96
 
71
97
  - Default filters: current month or selected tenant, not all history.
72
- - Global id search is an advanced, slow path; require date or tenant hint, or resolve through a mapping table before scanning children.
98
+ - Global id search is an advanced, slow path; require date or tenant hint, or resolve through a mapping table before scanning children ([Public identifiers](#public-identifiers)).
73
99
  - Export flows chunk by bucket; show progress per period.
74
100
  - Totals read from snapshot tables with `computed_at`, not live `SUM` across all children or a stale materialized view over the full fact table ([partition_landscape.md](partition_landscape.md#materialized-views)).
75
101
 
@@ -79,6 +105,6 @@ See [host_testing.md](host_testing.md) for CI registry fixtures and integration
79
105
 
80
106
  ## Related
81
107
 
82
- - [partition_landscape.md](partition_landscape.md) — pruning, routing layers and hints, UI, snapshots
108
+ - [partition_landscape.md](partition_landscape.md) — pruning, routing layers and hints, UI, snapshots; `query_constraints` versus `SELECT`
83
109
  - [cutover.md](cutover.md) — backfill and switch
84
110
  - [naming.md](naming.md) — child table names
@@ -98,7 +98,7 @@ Per-table metrics (`RunMetrics#to_h`):
98
98
 
99
99
  ## Run record table
100
100
 
101
- When `run_record_enabled`, PostgreSQL table `partition_gardener_run_records` stores:
101
+ When `run_record_enabled`, PostgreSQL table `partition_gardener_checkpoints` (or `run_record_table_name`) stores:
102
102
 
103
103
  `table_name` — parent.
104
104
 
@@ -1,6 +1,6 @@
1
1
  ## Participants
2
2
 
3
- Andrei Makarov, agent-assisted release prep.
3
+ Andrei Makarov (@amkisko)
4
4
 
5
5
  ## Decisions
6
6
 
@@ -0,0 +1,19 @@
1
+ # Rebalance before archive attach
2
+
3
+ ## Participants
4
+
5
+ Andrei Makarov (@amkisko)
6
+
7
+ ## Decisions
8
+
9
+ Date-range maintenance rebalances the sliding-window tail before it finalizes monthly archives or applies retention. That lets one run detach a current partition that still starts on 1 July, attach a new current partition from 1 August, send July rows to default, and attach a non-overlapping monthly child from default.
10
+
11
+ Archive attach is skipped when the bucket start is at or after the attached current partition lower bound. Those buckets wait until rebalance moves the current span forward. The lower bound is read with Connection.current_partition_lower_bound.
12
+
13
+ ## Effects
14
+
15
+ A July-to-August maintenance sequence attaches the July monthly child and a current partition from 1 August.
16
+
17
+ ## Source
18
+
19
+ Requested in the partition_gardener sliding-window rollover fix. Specs: spec/integration/sliding_window_maintenance_spec.rb and spec/partition_gardener/attach_retry_spec.rb.
@@ -0,0 +1,21 @@
1
+ # Relock appraisal graphs for json advisory
2
+
3
+ ## Participants
4
+
5
+ Andrei Makarov (@amkisko)
6
+
7
+ ## Decisions
8
+
9
+ Appraisal lockfiles are part of the CI install graph. After 0.3.3 they must pin partition_gardener 0.3.3 and json 2.21.2 so frozen bundle install and bundle-audit both pass. The dependency audit workflow scans Gemfile.lock and *.gemfile.lock.
10
+
11
+ ## Effects
12
+
13
+ Relocked gemfiles/rails72.gemfile.lock, gemfiles/rails8ruby34.gemfile.lock, and gemfiles/rails8ruby4.gemfile.lock. Widened the find in .github/workflows/dependency-audit.yml.
14
+
15
+ ## Next
16
+
17
+ Merge the patch so main test and dependency audit jobs install a consistent graph.
18
+
19
+ ## Source
20
+
21
+ Triggered by failing Dependency audit and test workflows after the 0.3.3 merge. Advisory note: usr/docs/dependencies/20260813144516_json-cve-2026-71847.md.
@@ -0,0 +1,19 @@
1
+ # Align child columns before attach
2
+
3
+ ## Participants
4
+
5
+ Andrei Makarov (@amkisko)
6
+
7
+ ## Decisions
8
+
9
+ ATTACH PARTITION now runs additive column align first (default on). CREATE TABLE LIKE and DETACH snapshot columns; a later parent ADD COLUMN does not reach that child. Align adds all missing ordinary columns in one ALTER TABLE statement (skip identity and generated), preserving type, non-default collation, default, and NOT NULL. PostgreSQL rejects the complete statement when existing rows cannot satisfy NOT NULL, so standalone execution does not leave a partial alignment. Extra child columns stay. Existing types and other constraints are not rewritten.
10
+
11
+ Audit warns when an unattached managed child still lags the parent. Built-in strategies filter unrelated prefixed tables and load all candidate column metadata in one catalog query. Custom partition name formatters retain prefix discovery because detached tables have no parent provenance. align_child_columns: false keeps fail-fast attach. JSON registry import rejects non-boolean align_child_columns values.
12
+
13
+ ## Effects
14
+
15
+ LIKE and keep-table reattach paths attach after a parent ADD COLUMN without a host workaround. A missing conflict-key column is aligned before its child index is created.
16
+
17
+ ## Source
18
+
19
+ https://github.com/amkisko/partition_gardener.rb/issues/18
@@ -0,0 +1,24 @@
1
+ # Cut patch release 0.3.4
2
+
3
+ ## Participants
4
+
5
+ Andrei Makarov (@amkisko)
6
+
7
+ ## Decisions
8
+
9
+ Cut patch release 0.3.4 for child column align before ATTACH PARTITION, shipped after 0.3.3.
10
+
11
+ ## Effects
12
+
13
+ Moved Unreleased notes into CHANGELOG.md under 0.3.4. Bumped lib/partition_gardener/version.rb and path-gem lockfiles to 0.3.4.
14
+
15
+ ## Next
16
+
17
+ Merge trunk/0.3.4 to main, then run make release.
18
+
19
+ ## Source
20
+
21
+ https://github.com/amkisko/partition_gardener.rb/issues/18
22
+ https://github.com/amkisko/partition_gardener.rb/pull/19
23
+
24
+ Feature note: usr/docs/changelogs/20260902114500_align-child-columns-before-attach.md
@@ -0,0 +1,19 @@
1
+ # Occupied window current origin
2
+
3
+ ## Participants
4
+
5
+ Andrei Makarov (@amkisko)
6
+
7
+ ## Decisions
8
+
9
+ Date-range planning keeps attached monthly children whose ranges overlap the active window. The remainder after a contiguous occupying prefix from the window start is named open. Current is reserved for a filler that starts at the window origin. OccupiedWindow reads Connection.attached_partitions bounds. ZoneSegments walks those catalog segments before heat splits and fillers. Monthly name parse also accepts an optional first-of-month suffix so those child names resolve as that month.
10
+
11
+ A later pass changed the remainder name from current to open so the slot matches the documented origin rule.
12
+
13
+ ## Effects
14
+
15
+ A monthly catalog through the current month no longer plans a current partition that overlaps the occupying child. Postgres attach of the remainder filler starts at the next month as open.
16
+
17
+ ## Source
18
+
19
+ Follows usr/docs/issues/20260909143500_occupied-window-current-origin.md. Specs: spec/partition_gardener/date_range_occupied_window_spec.rb, spec/partition_gardener/sliding_window_spec.rb, spec/integration/sliding_window_maintenance_spec.rb.
@@ -0,0 +1,21 @@
1
+ # Tail slot RFC
2
+
3
+ ## Participants
4
+
5
+ Andrei Makarov (@amkisko)
6
+
7
+ ## Decisions
8
+
9
+ RFC 0001 records the project RFC process. RFC 0002 specifies occupancy-first tail slot names. A date-named child that covers a range is that range. Current, open, and future fill only uncovered ranges. Future starts after premade months that occupy keys at or after the window end. Registry flags are out of this RFC. A later pass dropped ensure_current, ensure_future, and ensure_default from the draft.
10
+
11
+ ## Effects
12
+
13
+ rfcs/0001-rfc-process.md and rfcs/0002-tail-slots.md are Proposed. Occupied-window naming (open after an occupying origin) matches RFC 0002 current-zone occupancy. A later pass put high-end occupancy for future into the planner. Registry flags are not in this RFC.
14
+
15
+ ## Next
16
+
17
+ Discuss RFC 0002 unresolved questions. Implementation PRs cite RFC-0002.
18
+
19
+ ## Source
20
+
21
+ Follows usr/docs/issues/20260909143500_occupied-window-current-origin.md and rfcs/0002-tail-slots.md.
@@ -0,0 +1,21 @@
1
+ # High-end occupancy for future
2
+
3
+ ## Participants
4
+
5
+ Andrei Makarov (@amkisko)
6
+
7
+ ## Decisions
8
+
9
+ A date-named child that covers keys at or after the active window end stays in the plan. Future starts after the latest finite end among those occupants. Future is omitted when an occupant already ends at MAXVALUE. Holes between those occupants stay gaps. Default remains residual routing. No registry flags.
10
+
11
+ ## Effects
12
+
13
+ The sliding-window planner keeps a premade month that begins at the window end and starts future from the next month. A child that straddles the window end pushes future to that child's end. Noncontiguous premade months past the window stay attached. Apply no longer attaches future over a month that already occupies that range.
14
+
15
+ ## Next
16
+
17
+ Discuss RFC 0002 unresolved questions. Integer-window occupancy is unchanged.
18
+
19
+ ## Source
20
+
21
+ Follows rfcs/0002-tail-slots.md and usr/docs/issues/20260909143500_occupied-window-current-origin.md.
@@ -0,0 +1,21 @@
1
+ # Shipped contract RFCs
2
+
3
+ ## Participants
4
+
5
+ Andrei Makarov (@amkisko)
6
+
7
+ ## Decisions
8
+
9
+ Stable RFCs 0003 through 0015 specify contracts that already ship: sliding window layout, registry JSON, plan and audit reports, archive names, retention, CLI, maintenance backends, keyset rebalance, advisory lock and run records, hot switch, child column align, integer window, and hash list composite. RFC 0001 and RFC 0002 stay Proposed. Integer occupancy stays unresolved inside RFC 0014. Registry flags for ensure slots stay out.
10
+
11
+ ## Effects
12
+
13
+ rfcs/ids/0003 through rfcs/ids/0015 are claimed. Matching rfcs/NNNN-slug.md files are Stable. rfcs/README.md Current set lists them. CHANGELOG 0.3.5 records the set. Operator guides gained Related links. docs/cli.md apply --all example now includes --confirm.
14
+
15
+ ## Next
16
+
17
+ Implementation PRs cite RFC-NNNN. Discuss RFC 0002 unresolved questions. Integer occupancy is a follow-on, not this set.
18
+
19
+ ## Source
20
+
21
+ Follows the RFC 0002 occupancy-first work and the request to specify remaining shipped contracts under rfcs/.
@@ -0,0 +1,24 @@
1
+ # Cut patch release 0.3.5
2
+
3
+ ## Participants
4
+
5
+ Andrei Makarov (@amkisko)
6
+
7
+ ## Decisions
8
+
9
+ Cut patch release 0.3.5 on main for occupancy-first sliding-window plans and RFCs 0001 through 0015, shipped after 0.3.4. Stay on main. No trunk branch.
10
+
11
+ ## Effects
12
+
13
+ Moved Unreleased notes into CHANGELOG.md under 0.3.5. Bumped lib/partition_gardener/version.rb and path-gem lockfiles to 0.3.5.
14
+
15
+ ## Next
16
+
17
+ Commit the version cut, then run make release.
18
+
19
+ ## Source
20
+
21
+ Occupancy: usr/docs/changelogs/20260909143500_occupied-window-current-origin.md
22
+ High-end future: usr/docs/changelogs/20260909162554_high-end-future-occupancy.md
23
+ RFC 0002: usr/docs/changelogs/20260909154900_tail-slot-rfc.md
24
+ Stable RFCs: usr/docs/changelogs/20260909164000_shipped-contract-rfcs.md
@@ -0,0 +1,23 @@
1
+ # Public identifiers contract
2
+
3
+ ## Participants
4
+
5
+ Andrei Makarov (@amkisko)
6
+
7
+ ## Decisions
8
+
9
+ Host applications treat database uniqueness, public identifier, routing hint, and authorization as four contracts. When id is unique across children, keep a scalar application id and attach a routing hint on point lookups. When id is unique only inside a child, the public point-lookup identifier carries every uniqueness column as one opaque path segment.
10
+
11
+ Prefer Rails signed_id or SignedGlobalID when primary_key is the composite array. When primary_key stays :id, sign [id, partition_key] with a host MessageVerifier or use an unpartitioned mapping table. query_constraints still only helps writes. Do not use to_param underscore form or unsigned GlobalID for public point lookups. Fail closed after decode. Browse screens still show period or tenant. Gardener does not ship an encoder.
12
+
13
+ ## Effects
14
+
15
+ docs/application_contract.md gained a Public identifiers section. docs/partition_landscape.md Rails application contract, routing hints, and UI surfaces point to it. README application_contract blurb names public identifiers.
16
+
17
+ ## Next
18
+
19
+ No RFC unless public identifiers become a gem runtime contract.
20
+
21
+ ## Source
22
+
23
+ usr/docs/issues/20260916113300_opaque-composite-public-ids.md
@@ -0,0 +1,23 @@
1
+ # Checkpoint table name
2
+
3
+ ## Participants
4
+
5
+ Andrei Makarov (@amkisko)
6
+
7
+ ## Decisions
8
+
9
+ SqlRunRecordStore default table is partition_gardener_checkpoints. Hosts set run_record_table_name when they already own another relation. First use with the default name renames partition_gardener_runs or partition_gardener_run_records when that catalog relation exists and the default table does not.
10
+
11
+ RFC 0011 records the table, the config key, and the rename.
12
+
13
+ ## Effects
14
+
15
+ lib/partition_gardener/sql_run_record_store.rb and configuration.rb. Docs in configuration, operations, and audit_reference. Specs in sql_run_record_store_spec.rb.
16
+
17
+ ## Next
18
+
19
+ Hosts that already created partition_gardener_run_records keep resume rows after the first enabled run, or set run_record_table_name to keep the old name.
20
+
21
+ ## Source
22
+
23
+ RFC 0011
@@ -0,0 +1,23 @@
1
+ # Cut patch release 0.3.6
2
+
3
+ ## Participants
4
+
5
+ Andrei Makarov (@amkisko)
6
+
7
+ ## Decisions
8
+
9
+ Cut patch release 0.3.6 on main for the default checkpoint table name, `run_record_table_name`, leftover table rename, and public identifier docs, shipped after 0.3.5. Stay on main. No trunk branch.
10
+
11
+ ## Effects
12
+
13
+ Moved Unreleased notes into CHANGELOG.md under 0.3.6. Bumped lib/partition_gardener/version.rb and path-gem lockfiles to 0.3.6.
14
+
15
+ ## Next
16
+
17
+ Run make release.
18
+
19
+ ## Source
20
+
21
+ Checkpoint table: docs/changelogs/20261005135700_checkpoint-table-name.md
22
+ Public identifiers: docs/changelogs/20260916144600_public-identifiers-contract.md
23
+ RFC 0011
@@ -0,0 +1,24 @@
1
+ # Cut patch release 0.3.7
2
+
3
+ ## Participants
4
+
5
+ Andrei Makarov (@amkisko)
6
+
7
+ ## Decisions
8
+
9
+ - Bump `PartitionGardener::VERSION` to `0.3.7`.
10
+ - Record user-visible correctness and contract fixes in `CHANGELOG.md` under `## 0.3.7 (2026-10-29)`.
11
+
12
+ ## Effects
13
+
14
+ - Resume, hot-switch, gap detection, calendar leap day, retention notify, and integer-window JSON registry fixes landed on `patch/engineering-audit-0-3-7`.
15
+ - Release script now uses `git status --porcelain` and loads `PartitionGardener::VERSION` directly.
16
+
17
+ ## Next
18
+
19
+ - Commit the prepared tree on `main`, tag `0.3.7`, push, and run `make release` (gem push, tag push, GitHub release). See `docs/reviews/20261029150855_engineering-audit-release-preparation.md`.
20
+
21
+ ## Source
22
+
23
+ - Review: `docs/reviews/20261021220000_engineering-audit-0-3-7.md`
24
+ - `CHANGELOG.md`, `lib/partition_gardener/version.rb`
@@ -47,6 +47,8 @@ When the gem loads inside Rails, the railtie sets `connection_resolver` and `tod
47
47
 
48
48
  `run_record_enabled` (default: `true`) — persist phase checkpoints for resume (global default).
49
49
 
50
+ `run_record_table_name` (default: `partition_gardener_checkpoints`) — PostgreSQL table used by `SqlRunRecordStore`. On first use with the default name, the store renames `partition_gardener_runs` or `partition_gardener_run_records` when that relation exists and the default table does not.
51
+
50
52
  `run_record_store` (default: `SqlRunRecordStore` when a database connection is available, else in-memory) — where rebalance checkpoints are stored.
51
53
 
52
54
  `strict_maintenance_backend_validation` (default: `false`) — when `true`, raise `MaintenanceBackend::ValidationError` on register if `maintenance_backend` disagrees with `partman.parent_config`; when `false`, notify only.
@@ -67,7 +69,7 @@ Do not run two full `run!` jobs against the same table concurrently. Host apps s
67
69
 
68
70
  ### Run records
69
71
 
70
- When a database connection is configured (Active Record or `DATABASE_URL`), `SqlRunRecordStore` creates `partition_gardener_run_records` on first use. Run records let incremental rebalance resume after partial failure. Disable with `config.run_record_enabled = false` or per-table `run_record_enabled: false`.
72
+ When a database connection is configured (Active Record or `DATABASE_URL`), `SqlRunRecordStore` creates `partition_gardener_checkpoints` on first use. Checkpoints let incremental rebalance resume after partial failure. Disable with `config.run_record_enabled = false` or per-table `run_record_enabled: false`. Set `config.run_record_table_name` when the host already owns another table name.
71
73
 
72
74
  ## Per-table registration
73
75
 
@@ -0,0 +1,27 @@
1
+ # json CVE-2026-71847
2
+
3
+ ## Dependency
4
+
5
+ json, pulled in through the development graph (rubocop / activesupport). Root Gemfile.lock already had 2.21.2 after the Dependabot bundler group bump. Appraisal locks gemfiles/rails72.gemfile.lock, gemfiles/rails8ruby34.gemfile.lock, and gemfiles/rails8ruby4.gemfile.lock still resolved 2.20.0.
6
+
7
+ ## Symptom
8
+
9
+ bundle-audit check reports GHSA-9hj4-r449-hfvc / CVE-2026-71847 on json 2.20.0 through 2.21.1. The published fix is >= 2.21.2. Dependency audit CI on the 0.3.3 merge failed against the root lock while it still had 2.21.1. After Dependabot updated only Gemfile.lock, later audit jobs on main passed while the appraisal graphs CI actually installs stayed on 2.20.0. The audit workflow find matched files named Gemfile.lock only, so gemfiles/*.gemfile.lock were never scanned.
10
+
11
+ Test CI on those same appraisal locks also failed in frozen mode because they still listed partition_gardener 0.3.2 after VERSION moved to 0.3.3.
12
+
13
+ ## Evidence
14
+
15
+ Local bundle-audit check --gemfile-lock on each appraisal lock reported json 2.20.0 and the advisory. GitHub advisory: https://github.com/advisories/GHSA-9hj4-r449-hfvc. Upstream advisory: https://github.com/ruby/json/security/advisories/GHSA-9hj4-r449-hfvc. Failed Dependency audit run on the 0.3.3 merge: GitHub Actions run 31696494175.
16
+
17
+ ## Suggested fix
18
+
19
+ Relock the three appraisal gemfiles so json is 2.21.2 and the path gem is 0.3.3. Teach .github/workflows/dependency-audit.yml to scan both Gemfile.lock and *.gemfile.lock.
20
+
21
+ ## Next
22
+
23
+ Land the relock on main. Dependabot still watches only the root bundler directory, so appraisal graphs will need a manual relock when the next advisory lands unless that config grows.
24
+
25
+ ## Source
26
+
27
+ Upstream: https://github.com/advisories/GHSA-9hj4-r449-hfvc and CVE-2026-71847. Downstream: gemfiles/*.gemfile.lock and .github/workflows/dependency-audit.yml on patch/relock-appraisal-advisory-json.
@@ -0,0 +1,25 @@
1
+ # Occupied monthly children at the active window start
2
+
3
+ ## Participants
4
+
5
+ Andrei Makarov (@amkisko)
6
+
7
+ ## Decisions
8
+
9
+ When attached monthly children already cover the start of the sliding-window active range, the plan keeps those children and fills the remainder as open, not current. Current is only the filler that starts at the active window origin. The planner reads occupancy from attached range bounds, not only from parsed monthly names.
10
+
11
+ Archive children whose range ends at or before the window start stay archive. Tail slot names current, open, and future are not occupancy. A first-month heat split that is not yet attached already named the later filler open; occupied prefix uses the same name.
12
+
13
+ RFC 0002 records occupancy-first placement. A date-named child that covers a range is that range. Current, open, and future fill only uncovered ranges. Default is residual routing. This patch does not add registry flags. A later RFC 0002 pass dropped ensure flags from the draft and added high-end occupancy for future versus premade months past the window.
14
+
15
+ ## Effects
16
+
17
+ A catalog of monthly children through the current month, with default only and no current or future slot, plans open from the next month and attaches without overlapping an existing child. A premade month that begins at the window end stays attached. Future starts after that month.
18
+
19
+ ## Next
20
+
21
+ RFC 0002 stays Proposed. Unresolved questions live in the RFC, including integer occupancy. Implementation PRs cite RFC-0002.
22
+
23
+ ## Source
24
+
25
+ Requested as a sliding-window plan fix after 0.3.3 skipped only the reverse overlap, archive attach when current already covers that month. Naming follow-up: current must not start after the occupying prefix.
@@ -0,0 +1,25 @@
1
+ # Specify shipped contracts as Stable RFCs
2
+
3
+ ## Participants
4
+
5
+ Andrei Makarov (@amkisko)
6
+
7
+ ## Decisions
8
+
9
+ Write Standards Track Stable RFCs for every shipped contract named in the RFC gap analysis, including layouts and execution paths that were previously operator-guide only. Skip whether-to-partition advice, engine recipes, monitoring SLOs, host CI, job wiring, and landscape UI notes. Skip registry ensure flags. Occupancy for integer catalogs stays an unresolved question, not a claimed walk.
10
+
11
+ RFC numbers: 0003 sliding-window-layout, 0004 registry-json, 0005 plan-audit-reports, 0006 archive-naming, 0007 retention, 0008 cli, 0009 maintenance-backends, 0010 keyset-rebalance, 0011 advisory-lock-run-records, 0012 hot-switch, 0013 child-column-align, 0014 integer-window, 0015 hash-list-composite.
12
+
13
+ Facts preserved: current is origin filler, not this month. Default is always created except hash. Apply always needs --confirm. Moves are insert then delete with ON CONFLICT. partition_key_column is trusted operator SQL.
14
+
15
+ ## Effects
16
+
17
+ The RFC files and id claims exist on this branch. Indexes and Related links point at the set.
18
+
19
+ ## Next
20
+
21
+ Review Proposed RFC 0002. Follow-on RFC if integer occupancy ships.
22
+
23
+ ## Source
24
+
25
+ Requested after RFC 0001 and RFC 0002. Related usr/docs/changelogs/20260909164000_shipped-contract-rfcs.md.