Connect live Nexus offer withdrawal and named alliance history
An unallied sender can withdraw its unanswered offer during an ordinary Nexus. The table names the outgoing recipient and keeps this distinct from breaking a formed alliance. The chronicle now records named proposals, withdrawals and breaks. Existing authoritative quote, timing locks, readiness, physical custody, Nexus-card forfeiture and request/version handling remain unchanged. Full-table rendering checks cover owner/recipient/observer controls, replacement, withdrawal, incoming offers, break, blocked timing and JSON continuation. A production SQLite race permits exactly one acceptance or withdrawal at a version; a stale withdrawal cannot break an alliance formed by the accepted request. Private restored views and seat credentials remain intact. All four current AI profiles retain legal participation; no strategy or calibration changes. Independent review found no material issue. Types, lint, all 4973 offline tests, production build and 49 HTTP tests passed on stable source. Browser QA used a fresh three-human Basic lobby, genuine setup and a controlled later-Storm boundary with existing Spice Cards reordered; actual Storm/Worm resolution opened the Nexus. This is a focused position, not a full-game sample. Browser proposal, refresh, keyboard withdrawal, retained incoming invitation, acceptance, break and named chronicle entries passed. At390px the page375px and withdraw339px by46px fit, with16px text. Final refresh restored no offers or alliances. No screenshot was captured; full visual/assistive and all-configuration acceptance remain open. Browser lost-response injection was not performed. All 1459 original rooms, 3081 seats and auxiliary rows are unchanged. SQLite integrity and foreign keys pass. The healthy server was reused without restart, migration, reset or recurring maintenance. Full rules and expansion completion remain gated. The goal retains deferred full AI development, three adjacent approximate75% targets, no usage cutoff, selective subagents and verified Git pushes. Private evidence: 20260921-nexus-offer. Verified source tree: e44e61b651e66cad23326bcca3ba2eecd6bb5a779a16e0273db8d0039f453511
This commit is contained in:
1 parent
91dcca46fb
commit
2b071a44a2
11 files changed
+301
-17
No files matched your search
+10
-1
@@ -22825,6 +22825,9 @@ function applyActionInner(
|
||||
'Alliances change only during a Nexus.',
|
||||
);
|
||||
const target = action.target ? stringField(action.target) : null;
|
||||
const formerAlly = p.ally ? getPlayer(g, p.ally).name : null;
|
||||
const formerOffer = g.allianceOffers[id];
|
||||
const formerOfferName = g.players.find((player) => player.id === formerOffer)?.name;
|
||||
let other: Player | null = null;
|
||||
if (target !== null) {
|
||||
other = getPlayer(g, target);
|
||||
@@ -22848,12 +22851,18 @@ function applyActionInner(
|
||||
for (const player of g.players) player.ally = quote.allies[player.id];
|
||||
g.allianceOffers = quote.offers;
|
||||
if (target === null) {
|
||||
log(g, `${p.name} is unallied.`);
|
||||
log(g, formerAlly
|
||||
? `${p.name} broke the alliance with ${formerAlly}; both are unallied.`
|
||||
: formerOffer
|
||||
? `${p.name} withdrew the alliance offer to ${formerOfferName ?? 'a player no longer seated'} and remains unallied.`
|
||||
: `${p.name} is unallied.`);
|
||||
} else if (quote.formed) {
|
||||
requireRule(other, 'The alliance partner is no longer seated.');
|
||||
p.allySinceTurn = other.allySinceTurn = g.turn;
|
||||
discardAllianceNexusCards(g, p, other);
|
||||
log(g, `${p.name} and ${other.name} formed an alliance.`);
|
||||
} else {
|
||||
log(g, `${p.name} offered an alliance to ${other!.name}; both players must agree before an alliance forms.`);
|
||||
}
|
||||
g.ready = [];
|
||||
return g;
|
||||
|
||||
@@ -2083,6 +2083,30 @@ export const RULE_TOPICS: RuleTopic[] = [
|
||||
{ area: 'Verification', status: 'Partial', detail: 'Focused checks cover private forecast authorization, public reveal records, saved continuation, original versus modified distance and malformed component rejection. Broader rules and visual acceptance remain separate.', evidence: ['tests/storm-card-inspection.test.ts'] },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'nexus-offers',
|
||||
title: 'Propose, withdraw and break alliances',
|
||||
category: 'Getting started',
|
||||
coverage: 'Partial',
|
||||
developmentStage: 'Prototyped',
|
||||
summary: 'Manage ordinary alliance offers during a Nexus; each partner must agree before an alliance forms.',
|
||||
steps: [
|
||||
'When a Nexus opens, choose a partner and use Propose / accept alliance. A player who has invited you is marked in the partner list. Both players must be unallied and the current rules must permit the pairing.',
|
||||
'Your outgoing offer names its recipient. You remain unallied until the other player agrees. Choose another partner to replace the offer, or use Withdraw your alliance offer to cancel it.',
|
||||
'Withdrawal removes only your outgoing offer. Another player’s invitation may remain, and you can still accept it while the Nexus is open. Once allied, use the separate Break alliance action to leave during a Nexus.',
|
||||
'The table chronicle records proposals, formation, withdrawals and breaks. Changing an offer clears Nexus readiness so the table can review the updated negotiations.',
|
||||
'Refresh restores saved offers and alliances. If acceptance and withdrawal arrive together, only one action is committed; refresh or reconcile a changed table before choosing again. Pending responses and decisions must finish first.',
|
||||
'Allied forces, cards and spice keep their owners. Alliance funding and individual faction powers use their own controls; special alliances and expansion restrictions remain separate.',
|
||||
],
|
||||
related: ['spice-blow', 'alliance-funding', 'table-discussion', 'privacy'],
|
||||
checklist: [
|
||||
{ area: 'Implementation', status: 'Partial', detail: 'Existing ordinary reciprocal offers, withdrawal and break preserve readiness and configured alliance restrictions. Broader special diplomacy remains unfinished.', evidence: ['game/nexus-alliance.ts', 'game/engine.ts'] },
|
||||
{ area: 'Player controls', status: 'Implemented', detail: 'Named outgoing offers now have live withdrawal, alongside existing proposal, acceptance and break controls.', evidence: ['components/game-table.tsx'] },
|
||||
{ area: 'AI', status: 'Partial', detail: 'All four existing profiles can continue legally after withdrawal. Full diplomacy and difficulty tuning remain deferred.' },
|
||||
{ area: 'Documentation', status: 'Implemented', detail: 'Offer versus alliance, incoming invitations, readiness, refresh and concurrent choices are explained.', evidence: ['docs/NEXUS_OFFERS.md'] },
|
||||
{ area: 'Verification', status: 'Partial', detail: 'Full-table rendering, JSON continuation, pending-window rejection and SQLite acceptance/withdrawal races are covered. All-configuration and full visual acceptance remain open.', evidence: ['tests/nexus-offer-controls.test.tsx', 'tests/nexus-offer-recovery.test.ts', 'tests/nexus-alliance.test.ts'] },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'alliance-funding',
|
||||
title: 'Fund an ally’s bid or shipment',
|
||||
|
||||
Reference in new issue
Block a user