feat: pinned floor — static values are permanent in authoritative mode#108
Merged
Conversation
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).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
In
authoritativemode 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)
current − remote − pinned. Pins are exempt unconditionally — a pin the endpoint once served and later dropped stays.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).millpond_include_values_pinned/_pinned_onlysurvive 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_removals2→0 (the two pinned legacy teams leave the countdown),sizebecomes the union.Testing
TestPinnedFloor+ build wiring + the reversed bulk-removal assertion, which now also pins that a world-replacement response never wins through the additions path)