Opinion Wars: week-long two-faction battles on posts #173
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "blindxfish/devplacepy:OpinionWar"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
A new post attachment type beside polls, in the spirit of the old eRepublik battles: the composer gains a Start Opinion War button (next to Add poll, same disabled-inputs opt-in mechanism) that names exactly two factions, and the battle runs for exactly 7 days from post creation.
Mechanics
100 + 10 * min(level, 20)HP (110 for a newcomer, capped at 300). No randomness anywhere - deliberate, so the purchasable-randomness app-store conditional stays untriggered.award_rewards: participation for every fighter with at least one fight, a winner bonus, a top-damage-dealer bonus; draws pay participation only. Four badges (Instigator, First Blood, War Veteran, Champion) and a newbattlenotification type (lead changes, the result, fight-ready).Architecture
opinion_warsrow keyed bypost_uid, created insidePOST /posts/create(malformed input silently creates no war, thecreate_pollprecedent), batch-attached at the four poll-map call sites, rendered by a partial on the post card. Wars are create-time only - no builder in the edit modal, sinceends_atanchors to creation.conditional_update_row. The fight sequence is cooldown-first with compensation (claim cooldown -> spend coins -> land damage; each later refusal compensates the earlier steps), so a crash can cost a turn but never coins.resultevent and notifies fighters.OpinionWarService(BaseService, lock owner) is the backstop for unviewed wars and sends the fight-ready notifications via a marker CAS that can never repeat.opinion_war_eventstrail (per-war atomic seq allocation in one INSERT..SELECT) relayed to pub/subpublic.battle.{uid}on the lock owner (handlers never publish - multi-worker frame loss), client pairs the subscription with an incremental?after=poller and seq dedupe.dp-opinion-warweb component adopts server-rendered markup (real forms, full no-JS path); the battlefield is CSS pixel-art (box-shadow sprites: castles, flags, marching soldiers, campfire;steps()animation, off under reduced motion) inopinionwar.csswith a file-scoped--war-*palette.GET /battlesis a quizzes-style listing (active/ended/mine, search, pagination) linked from the topnav; there is no battle detail page - the post is the surface.Fan-out
HTML+JSON faces (
WarOut/BattlesOut), Devii actions (list_battles/get_battle/battle_eventspublic,join_battle/fight_battleconfirm-gated), API docs group Opinion Wars + war params onposts-create, docs prose pageopinion-wars, sitemap entry,REPORTABLE_TARGETS["battle"]+ cascades + soft-delete registration, audit keysbattle.*, README and nestedservices/opinionwar/CLAUDE.md.Verification
The four-layer procedure ran against a scratch DB: property checks over the full damage domain, a 1200-step stateful fuzz (war hp always equals the fighter sum, coins never negative, resolved totals frozen), and real 8-process races - a concurrent fight lands exactly once with exactly one fee, 37 coins across two wars allows exactly one fight, concurrent resolution awards XP exactly once, concurrent joins create one fighter row. Persisted tests:
tests/unit/services/opinionwar/,tests/api/battles/(listing, actions, events, resolution), war-creation cases intests/api/posts/create.py, andtests/e2e/battles/(composer toggle + poll exclusivity, join/fight flow with live HP updates, switch confirm, guest state). Full three-tier suite run in the project Docker image before pushing.🤖 Generated with Claude Code
A new post attachment type beside polls: the composer gains a Start Opinion War builder (same disabled-inputs opt-in as the poll builder) that names exactly two factions; the battle runs for exactly 7 days from post creation. Members join a side, may defect at any time (damage already dealt stays with the faction it was dealt to), and fight once per 24 hours per battle. A fight spends 25 Code Farm coins and deals deterministic level-weighted damage: 100 + 10 * min(level, 20) HP, so a newcomer deals 110 and a veteran caps at 300 - no randomness anywhere. The battle renders on the post card as a CSS pixel-art battlefield (box-shadow sprites: castles, faction flags, marching soldiers, a flickering campfire; steps() animation, disabled under reduced motion) with live HP bars, a countdown, the viewer's faction strip, top contributors and an event ticker. Live frames ride pub/sub on public.battle.{uid} via a relay on the service-lock owner, with the durable opinion_war_events trail (per-war atomic seq) as the source of truth and a 15s incremental poller as fallback. /battles lists battles with active/ended/mine filters, search and pagination. Every mutation is a conditional UPDATE via conditional_update_row: the fight sequence claims the cooldown first, then spends coins, then lands the damage, compensating earlier steps on any later refusal so a crash costs a turn, never coins. Resolution is lazy on read (no cron): an exactly-once CAS computes the winner in the statement, awards XP (participation, winner bonus, top damage dealer bonus; draws pay participation only), emits the result event and notifies fighters. The OpinionWarService backstop resolves unviewed wars and sends fight-ready notifications, exactly-once via a marker CAS. Fan-out: battle notification type, four badges, audit keys (battle.create/join/switch/fight/resolve), Devii actions (join/fight confirm-gated), API docs group, docs prose page, sitemap and topnav entries, REPORTABLE_TARGETS registration, post-delete cascades, README and nested CLAUDE.md documentation. Verified with the four-layer procedure: property checks over the full damage domain, 1200-step stateful fuzz (hp-sum invariant, coins never negative, resolved totals frozen), and real 8-process races proving exactly-once semantics for concurrent fights, double-spends across two wars, resolution XP and double-joins. Persisted tests in tests/unit/services/opinionwar, tests/api/battles, tests/e2e/battles and tests/api/posts/create.py. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>