Skip to content

feat(config): add auto-purchase switch stamp columns (migration 000115) - #840

Merged
cristim merged 1 commit into
mainfrom
feat/631-r1a-switch-stamps
Oct 13, 2026
Merged

cristim merged 1 commit into
mainfrom
feat/631-r1a-switch-stamps

Conversation

@cristim

@cristim cristim commented Oct 13, 2026

Copy link
Copy Markdown
Member

Refs #631 (R1a of the kill-switch grant work; R1b adds the gates and sets the stamps).

What

  • Migration 000115: nullable global_config.auto_purchase_changed_by, _at, _via (ADD COLUMN IF NOT EXISTS, no backfill, down drops them).
  • getGlobalConfigFrom reads them and saveGlobalConfigWith writes them back ($26-$28), so the RI-exchange whole-config read-modify-write keeps the stamps.
  • GlobalConfig fields are json:"-": PUT /api/config unmarshals the body onto this struct, so a tagged field could be forged by a client.

Nothing sets the stamps yet (R1b). Behaviour is unchanged: switch stays OFF, caps NULL.

Tests

  • pgxmock: upsert writes back the stamps it read (fails if the upsert passes nil; verified by mutation); arg counts/columns updated (25 -> 28).
  • Integration (real PG17, throwaway cluster; evidence is a real database, no mocks): a fresh migrate leaves auto_purchase_enabled false with NULL stamps, and an UpdateGlobalConfigAtomic RI-style write preserves seeded stamps. Env-gated by AUTO_PURCHASE_STAMPS_TEST_DATABASE_URL (db name r1a_*), same pattern as the RI exchange reservation test.
  • Migration test (container style, as 000114): switch stays OFF across the migration, up is idempotent, down drops the columns. Docker is unavailable on this host, so I ran the same up/down/up SQL by hand against the PG17 cluster (3 columns after up, 0 after down, up twice OK); the Go test itself compiles (go vet -tags integration) but was not run here.

Notes

🤖 Generated with Claude Code

Add nullable global_config.auto_purchase_changed_by/at/via, read them in
getGlobalConfigFrom and write them in the whole-config upsert so the
RI-exchange read-modify-write path keeps them. The fields are json:"-" so
PUT /api/config cannot forge them. Nothing sets the stamps yet.

Refs #631

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@cristim cristim added urgency/this-sprint Within the current sprint triaged Item has been triaged priority/p1 Next up; this sprint severity/high Significant harm impact/many Affects most users effort/xl Multi-week / refactor type/feat New capability labels Oct 13, 2026
@coderabbitai

coderabbitai Bot commented Oct 13, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

  • Run on-demand review

This review includes 8 billable files and costs up to $2.00.

  • Ask an admin to make reviews automatic

Open in CodeRabbit

Reviews can continue after your included limit without a manual trigger. An admin must approve usage-based billing.

Or wait 20 minutes for your next included review.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available. Your 60 included PR review attempts over the past 7 days set your current allowance at 1 review per hour.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Repository: LeanerCloud/cloud-commitments-platform/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Essentials
  • Run ID: 282134c3-50fc-426e-bdd0-2e5977d61b9c
📥 Commits

Reviewing files that changed from the base of the PR and between 4cecfdb and bca07ec.

📒 Files selected for processing (8)
  • internal/config/store_postgres.go
  • internal/config/store_postgres_auto_purchase_stamps_integration_test.go
  • internal/config/store_postgres_coverage_test.go
  • internal/config/store_postgres_pgxmock_test.go
  • internal/config/types.go
  • internal/database/postgres/migrations/000115_auto_purchase_switch_stamps.down.sql
  • internal/database/postgres/migrations/000115_auto_purchase_switch_stamps.up.sql
  • internal/database/postgres/migrations/000115_auto_purchase_switch_stamps_test.go
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@cristim

cristim commented Oct 13, 2026

Copy link
Copy Markdown
Member Author

Gate verdict: APPROVE at bca07ec

I am an independent gate reviewer (Opus) and did not write this PR. I reviewed the full diff with skepticism because it touches the auto-purchase switch path.

Checks against the PR's claims

  • Migration 000115 is expand-only: ADD COLUMN IF NOT EXISTS, nullable TEXT/TIMESTAMPTZ/TEXT, no backfill. The down migration drops exactly those three columns (IF EXISTS). 000115 is free on origin/main (4cecfdb, highest is 000114), and no other open PR uses the number.
  • Writers: the only non-test writers of global_config are PUT /api/config (internal/api/handler_config.go:97) and PUT /api/ri-exchange/config (internal/api/handler_ri_exchange.go:2135). Both go through UpdateGlobalConfigAtomic (internal/config/store_postgres.go:192), which reads the row with the stamps (:96-98, :132-134) and upserts them back as $26-$28 (:269-299, :369-371). No production code calls SaveGlobalConfig directly. The no-rows default struct leaves the stamps nil, which matches NULL.
  • Forging: the stamps are json:"-" (internal/config/types.go:92-94), and PUT /api/config decodes with stdlib encoding/json. A throwaway probe showed that unmarshalling a body with auto_purchase_changed_*, AutoPurchaseChanged* and lowercase variants leaves all three nil, and that marshalling the struct does not emit them. No other code path decodes into GlobalConfig (the service-config PUT decodes into ServiceConfig).
  • Behaviour: R1a has no writer that sets the stamps, and auto_purchase_enabled handling is unchanged.

Local evidence (from a git archive of the head commit, GOTOOLCHAIN=go1.26.9, run under the test-suite lock)

  • go test ./internal/config/... ./internal/database/postgres/migrations/... ./internal/api/... exited 0, and go vet -tags integration exited 0.
  • Throwaway PostgreSQL 17 cluster: TestPostgresStoreDB_AutoPurchaseStamps_FreshMigrateAndRIWritePreserve PASS. A fresh migrate leaves the switch false with NULL stamps, and an RI-style atomic write keeps the seeded stamps. schema_migrations is at 115 and not dirty.
  • Manual SQL on the same cluster: down gives 0 columns, up twice (idempotent) gives 3, down twice is OK.
  • git-secrets (--register-aws) is clean on all 8 files with both BSD and GNU grep.

CI: TestMigration_AutoPurchaseSwitchStamps ran and passed in the CI integration job (job 115098112469). On the first attempt, Lint Code failed only because fetching the golangci jsonschema timed out (network). The rerun is green: 26/26 checks pass, and mergeStateStatus is CLEAN.

Mutation testing (5 mutants, run once)

  • Upsert passes nil stamps: killed (unit and integration).
  • _via dropped from SELECT and Scan: killed (unit and integration).
  • SET auto_purchase_changed_by = NULL: killed (integration only).
  • $28 bound to the By value: killed (unit and integration).
  • Non-blocking survivor: changing the json:"-" tag on AutoPurchaseChangedBy to a real JSON name fails no test. No test currently pins the anti-forging property through PUT /api/config. Suggested follow-up for R1b: a handler test that sends the stamp keys and asserts they are not persisted.

No blocking findings.

@cristim
cristim merged commit 9121725 into main Oct 13, 2026
45 of 47 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/xl Multi-week / refactor impact/many Affects most users priority/p1 Next up; this sprint severity/high Significant harm triaged Item has been triaged type/feat New capability urgency/this-sprint Within the current sprint

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant