feat(bridge): lease deadlines and the participation ledger (Phase 11b)

Protocol 6 amended in place. Two mechanisms behind one new default-off gate,
`Bridge.EventsEnabled` -- deliberately not `AdminWriteEnabled`, because enabling
the admin plane is consenting to staff moderation from a screen a human is
looking at, and this is consenting to the world being changed and watched on a
schedule, unattended.

BridgeLeases: a live config value held for a bounded time, with the deadline
honoured on the shard whether or not the website is heard from again, and a
compare-and-set restore that reports `drifted` rather than overwriting a GM's
deliberate change. Memory-only -- nothing calls Config.Save() -- so a restart is
a free restore.

BridgeParticipation: presence in a declared area plus kill credit inside it,
keyed by character serial, persisted in the world save. The Bridge's first
persisted state, because a run spans hours and an in-memory tally would regress
every attendee's score after one restart. Its snapshot is also the first handler
that DEFERS, which makes `bridge.busy` reachable for the first time.

And it immediately found a defect in 11a: BridgeIdempotency.Busy built its frame
with Begin("bridge.busy") and then appended a diagnostic `.Str("kind", ...)`, so
the object carried two `kind` fields and every JSON parser takes the last. The
sidecar answered 200 instead of 425. Renamed `busyKind`.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-09-04 19:31:20 -05:00
parent d2a12c46e2
commit 63a7dc4374
10 changed files with 2181 additions and 7 deletions

View File

@@ -199,6 +199,46 @@ RequireIpForCreate=true
AccountNameMaxLength=16
AccountPasswordMaxLength=30
# ── The event plane (docs/link/v6.md 8) ──────────────────────────────────────
#
# Leases and the participation ledger: the website holding a live config value for a bounded
# time, and this shard counting who took part in a run. Both are driven on a SCHEDULE, by an
# event the website starts unattended.
#
# This is deliberately NOT AdminWriteEnabled. Turning the admin plane on is consenting to
# staff moderation driven from a screen a human is looking at; turning this on is consenting
# to the website changing and watching your world at four in the morning. One switch could
# not honestly express both.
#
# A lease always carries its own deadline and this shard restores the baseline when it
# passes, whether or not the website is ever heard from again -- and a lease is never written
# to disk, so a restart puts every leased value back too.
EventsEnabled=false
# The longest this shard will hold a lease, whatever the website asks for. Thirty days.
# A longer request is REFUSED rather than shortened: a silently-clamped lease would leave the
# two halves disagreeing about when the world comes back.
LeaseMaxDurationSec=2592000
# How long a finished lease stays listed after its deadline restored it, so a teardown that
# arrives late still gets a definite verdict instead of finding nothing.
LeaseGraceSec=86400
# How often the participation sweep credits everyone standing in a run's area, and what one
# kill inside it is worth against one minute of being there.
ParticipationSweepSeconds=30
ParticipationKillWeight=5.0
# Bounds. Runs counted at once, members per run, and the widest area an event may declare.
ParticipationMaxRuns=8
ParticipationMaxMembers=2000
ParticipationMaxRadius=300
# How long a closed run's tally stays readable before this shard forgets it, and how many
# members one snapshot resolves before yielding the Core thread.
ParticipationGraceSec=86400
ParticipationSnapshotChunk=100
# The test scaffolding in tools/scaffolding/ reads its own flags from this file
# (SeedOnStart, CensusOnStart, ProbeOnStart). They are absent here on purpose:
# Config.Get returns the default of false when a key is missing, so a deployed