Skip to content

feat: pinned floor — static values are permanent in authoritative mode#108

Merged
jghoman merged 1 commit into
mainfrom
jakob/include-values-pinned
Jul 21, 2026
Merged

feat: pinned floor — static values are permanent in authoritative mode#108
jghoman merged 1 commit into
mainfrom
jakob/include-values-pinned

Conversation

@jghoman

@jghoman jghoman commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

What

In authoritative mode the served allowlist becomes polled ∪ static. Static config values are pins: always served, never in the removal countdown (regardless of endpoint state), removable only by a config deploy. The endpoint governs everything it serves; config governs the rest.

Motivation: two grandfathered design-partner teams are in the static events-nrt allowlist but unknown to the CP discovery endpoint (they predate provisioning). Under the previous seed-then-replace semantics, the shadow→authoritative flip would have confirm-removed them after the damping window — silent data drop for exactly the customers config was supposed to protect.

Semantics (shaped by two adversarial review rounds)

  • Countdown domain: current − remote − pinned. Pins are exempt unconditionally — a pin the endpoint once served and later dropped stays.
  • bulk_removal guard domain: the endpoint-managed slice current − (pinned − remote). Endpoint-invisible pins excluded (large pin set must not mask a managed wipe); endpoint-served pins count (dropping 2 of its 12 must not read as 2-of-2 and refuse forever).
  • Additions apply even on a bulk_removal refusal (countdown still not committed). Starving a new team behind a disputed removal silently drops its records with offsets committed. A genuine world-replacement stays refused — its removals remain a managed-slice majority — while genuine growth clears a disputed refusal. This deliberately reverses the additions-refused-too choice from feat: dynamic include-values source for the keep-filter #107.
  • Pins-only + empty endpoint syncs instead of crash-looping on startup timeout (the all-legacy deployment is the degenerate form of the motivating case). Empty remains refused whenever endpoint-managed values are held.
  • Constructor rejects pins ⊄ bootstrap — a first-sight poll would bypass the type-flip guard and string-coerce int pins.
  • New gauges millpond_include_values_pinned / _pinned_only survive the flip (shadow gauges don't); the pin set is logged at startup. README warns that a full-mirror static list freezes removals — prune to intentional pins before flipping.

The shadow prober carries the same pins, so the running prod-us-ws soak's gauges predict exactly the post-flip set. Expected discontinuity on deploy: pending_removals 2→0 (the two pinned legacy teams leave the countdown), size becomes the union.

Testing

  • 13 new/updated unit cases (TestPinnedFloor + build wiring + the reversed bulk-removal assertion, which now also pins that a world-replacement response never wins through the additions path)
  • Full unit suite: 618 passed; ruff check + format clean

Served set in authoritative mode becomes polled ∪ static. The static
config values are pins: always served, never in the removal countdown
(regardless of endpoint state), removable only by a config deploy.
Motivation: grandfathered teams the control-plane discovery endpoint
has never heard of must survive the shadow→authoritative flip.

Semantics changes shaped by two adversarial review rounds:

- bulk_removal guard domain is the endpoint-managed slice
  current − (pinned − remote): endpoint-invisible pins are excluded
  (a large pin set must not mask a managed wipe) but endpoint-served
  pins count (dropping 2 of its 12 must not read as 2-of-2 and refuse
  forever).
- a bulk_removal refusal now applies that poll's ADDITIONS, without
  committing countdown state. Starving a new team behind a disputed
  removal drops its records silently with offsets committed — the loss
  class this module exists to prevent. A genuine world-replacement
  stays refused (its removals remain a managed-slice majority); only
  genuine growth clears a disputed refusal. This reverses the
  additions-refused-too choice from the original include-values commit.
- empty-remote is ACCEPTED when the held set is pins-only: the
  all-legacy deployment (endpoint legitimately serves nothing) must
  sync, not crash-loop on startup timeout. Still refused whenever
  endpoint-managed values are held.
- __init__ rejects pins ⊄ bootstrap: a first-sight poll would bypass
  the type-flip guard and string-coerce the pins into values the
  filter's column cast rejects.
- new gauges millpond_include_values_pinned / _pinned_only survive the
  authoritative flip (the shadow gauges don't); pins are logged at
  startup. README warns that a full-mirror static list freezes
  removals dead — prune to intentional pins before flipping.

The shadow prober carries the same pins, so the running shadow soak's
gauges predict exactly the post-flip set (pending_removals drops to 0
for pinned legacy teams on deploy — expected discontinuity).
@jghoman
jghoman merged commit b2bdf20 into main Jul 21, 2026
17 checks passed
@jghoman
jghoman deleted the jakob/include-values-pinned branch July 21, 2026 16:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant