Skip to content

마스터 계약 공식 이름 및 STEP3 활성화 대시보드 구현#57

Open
Jaymyong66 wants to merge 24 commits into
mainfrom
feature/master-agreement-step3-impl
Open

마스터 계약 공식 이름 및 STEP3 활성화 대시보드 구현#57
Jaymyong66 wants to merge 24 commits into
mainfrom
feature/master-agreement-step3-impl

Conversation

@Jaymyong66
Copy link
Copy Markdown
Contributor

@Jaymyong66 Jaymyong66 commented Apr 27, 2026

요약

이 PR은 Master Agreement의 공식 이름 기능과 STEP3 풀 활성화 대시보드 전환을 함께 구현합니다.

핵심 변경:

  • MasterAgreement.name을 온체인에 저장
  • leader / operator가 마스터 계약 이름을 수정할 수 있는 rename instruction 추가
  • 백엔드 파서 및 프론트 fetch 경로에 공식 이름 전파
  • STEP1 생성 플로우에서 공식 이름 입력 및 실제 on-chain create path 반영
  • 계약 선택 드롭다운, 리뷰 패널, 이름 수정 UI 추가
  • STEP3를 단순 확인 화면이 아니라 활성화 대시보드로 전환
  • 대충 이런 마스터계약 활성화 후 풀 현황 관련 대시보드
스크린샷 2026-04-28 오전 2 01 23

주요 변경 사항

1. 컨트랙트 / 온체인

  • MasterAgreement 계정에 name: String 필드 추가
  • 이름 정규화/검증 로직 추가
    • 공백-only 금지
    • 40자 초과 금지
  • update_master_agreement_name instruction 추가
  • 이름 검증 로직을 shared helper로 분리해 create / rename 경로에서 공용 사용

2. 백엔드

  • MasterAgreement 파서가 master_id 뒤의 name을 읽도록 변경
  • API 응답을 통해 공식 이름이 프론트까지 전달되도록 수정
  • 관련 테스트 fixture 업데이트

3. 프론트 - 이름 UX

  • STEP1에 공식 이름 입력 필드 추가
  • 실제 생성 트랜잭션이 name을 보내도록 수정
  • 드롭다운에서 계약명 · 상태 · 역할 · 종료일 형식으로 표시
  • rename editor 추가
  • optimistic/local state와 authoritative refresh를 함께 다루도록 보강
  • operator 역할도 rename 가능하도록 selection / gating 경로 정리

4. 프론트 - STEP3 대시보드

  • STEP3를 MasterActivationDashboard로 전환
  • 표시 항목:
    • 계약 이름
    • readiness KPI
    • pool health
    • blocker
    • premium / claim / net snapshot
  • STEP2는 확인 완료 후 STEP3로 넘어가는 전이 단계로 정리
  • 실제 activation CTA는 STEP3에만 남기도록 수정
  • simulation / on-chain 모두에서 의미 있는 STEP3가 보이도록 보강
  • loading / error 상태에서 가짜 0 잔액이나 가짜 deficit를 보이지 않도록 수정

사용 방법

새 마스터 계약 생성

  1. STEP1 기본 설정에서 공식 이름을 입력합니다.
  2. 나머지 조건을 입력하고 계약을 생성합니다.
  3. 생성 직후 드롭다운/리뷰 패널/STEP3에서 이름이 표시됩니다.

이름 수정

  1. 기존 마스터 계약을 선택합니다.
  2. leader 또는 operator 권한으로 이름 수정 UI를 엽니다.
  3. 새 이름 저장 후 드롭다운/리뷰 패널/대시보드에 반영됩니다.

STEP3 활성화

  1. STEP2에서 확인을 완료합니다.
  2. 활성화 대시보드로 이동 버튼으로 STEP3로 이동합니다.
  3. STEP3에서 readiness / pool 상태 / blocker / 자금 스냅샷을 확인합니다.
  4. 실제 activation은 STEP3의 CTA에서 실행합니다.

주의사항

1. 기존 legacy Master Agreement 호환성

이번 구현은 새로 생성되는 계약부터 공식 이름을 저장하지만, 운영 중인 기존 계약이 즉시 사라지지 않도록 백엔드에 old/new layout fallback parser를 추가했습니다.

즉:

  • 새 layout 계약: name을 정상적으로 읽습니다.
  • 기존 old-layout 계약: name=""으로 fallback 파싱합니다.

제약:

  • 기존 계약 자체가 온체인에서 이름을 가지게 되는 것은 아닙니다.
  • 기존 계약까지 이름을 영구적으로 채우려면 별도 migration 전략이 필요합니다.

2. activation 권한

  • 프론트는 on-chain mode에서 실제 master.operator 기준으로 activation CTA를 gating 하도록 맞췄습니다.
  • leader != operator인 계약에서는 leader가 CTA를 눌렀다가 런타임에서 거절당하는 dead-end를 피하도록 정리했습니다.

3. optimistic 이름 표시

  • 생성/수정 직후에는 local optimistic state로 이름을 먼저 보여주고,
  • 이후 authoritative refresh 결과와 다시 동기화합니다.

즉 일시적 backend refresh 지연이 있어도 이름이 바로 사라지지 않도록 처리했습니다.

4. STEP3 데이터 로딩

  • unresolved / error 상태에서 가짜 0.00 USDC, 가짜 deficit, 가짜 blocker를 보여주지 않도록 수정했습니다.
  • readiness 로딩과 policy snapshot 로딩을 분리해, policy fetch가 느려도 activation readiness 자체는 계속 볼 수 있습니다.

테스트 / 검증

Contract

  • cargo test
    • 결과: 133 passed / 0 failed

Backend

  • cargo test
    • 결과: 31 passed / 0 failed / 4 ignored

Frontend

환경상 yarn workspace/lockfile 경로가 불안정해서, repo-local binary 기준으로 검증했습니다.

  • ./node_modules/.bin/vitest run src/components/tabs/tab-contract/__tests__ src/hooks/__tests__/useMasterAgreementSnapshot.test.ts
    • 결과: 7 files / 29 tests passed
  • ./node_modules/.bin/tsc -b --pretty false
    • 결과: passed
  • git diff --check
    • 결과: clean

리뷰 포인트

  • on-chain name 추가가 legacy 계정과 공존해야 하는지
  • leader / operator 권한 모델이 현재 제품 요구와 맞는지
  • STEP2 -> STEP3 전이와 guide 흐름이 사용자 기대와 맞는지
  • STEP3 snapshot 표시 범위가 현재 운영 목적에 충분한지

The new spec fixes two UX gaps at once: contracts need an official, durable name, and Step 3 needs to become an operational readiness dashboard instead of repeating participant confirmation.

The design chooses on-chain storage for the master agreement name because backend persistence is not durable enough for the user's requirement. It also scopes Step 3 to a leader/operator snapshot dashboard, keeping time-series analytics out of the first implementation slice.

Constraint: Master agreement names must survive backend loss and remain the official shared identifier
Rejected: Backend-only metadata storage | name would disappear if backend state is lost
Rejected: Existing-contract backfill | not required for this first rollout
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Treat the contract field as the single source of truth for master agreement names; do not reintroduce backend-owned naming
Tested: Spec self-review for placeholders, ambiguity, and scope
Not-tested: Implementation feasibility against exact Anchor account sizing and migration details
Persist a trimmed master agreement name at creation time and reject
blank or overlong values before the account is initialized. The account
space now reserves an explicit UTF-8 budget for the stored name.

Constraint: Follow the task's TDD sequence with focused contract tests first
Rejected: Accept raw input without trimming | would persist avoidable whitespace differences on-chain
Confidence: high
Scope-risk: narrow
Directive: Keep create-time name validation aligned with MASTER_AGREEMENT_NAME_MAX_LEN if the limit changes
Tested: cargo test create_master_agreement_test
Tested: cargo test
Shared master agreement name normalization is reused for the new rename path, and the frontend IDL artifacts are synced to expose the instruction and account shape.

Constraint: Keep scope limited to Task 2 instruction, tests, and regenerated IDL artifacts
Rejected: Duplicating name normalization in the rename instruction | would fork a shared invariant
Confidence: high
Scope-risk: narrow
Directive: Keep rename authorization limited to leader/operator unless the master agreement role model changes
Tested: cargo test update_master_agreement_name_test; anchor build
Not-tested: yarn test src/lib/__tests__/programEnv.test.ts (blocked by Yarn 1 vs packageManager yarn@4.1.0/Corepack requirement)
The regenerated JSON already included the rename instruction and added name fields, but the hand-maintained OpenParametric type block was still stale.

Constraint: Keep the fix limited to the stale frontend TS IDL export
Rejected: Regenerate or rewrite the full TS helper file | broader than the review item and would mix unrelated drift
Confidence: high
Scope-risk: narrow
Directive: When JSON IDL is refreshed, update both helper interfaces and the exported OpenParametric type block together
Tested: rg -n 'updateMasterAgreementName|"name": "name"' frontend/src/lib/idl/open_parametric.ts frontend/src/lib/idl/open_parametric.json; npx tsc --noEmit -p tsconfig.json
Not-tested: Full frontend test suite
This follow-up closes the Task 2 review gaps by sharing name normalization between create and rename, extending rename-path tests to cover trimming and rejection cases, and making the frontend TS helper surface require the same name field the JSON IDL already defines.

Constraint: Keep the fix limited to Task 2 follow-up review items without regenerating unrelated frontend artifacts
Rejected: Leave normalization in create_master_agreement.rs | keeps a shared invariant hidden behind one instruction boundary
Rejected: Update only the TS helper type without the typed hook consumer | would leave the frontend surface internally inconsistent
Confidence: high
Scope-risk: narrow
Directive: If master agreement name rules change, update the shared helper and both create/rename tests together
Tested: cargo test update_master_agreement_name_test; cargo test create_master_agreement_test; cargo test; npx tsc --noEmit -p tsconfig.json; rg -n 'export interface CreateMasterAgreementParams|name: string;|name: updateMasterAgreementName|name: name' frontend/src/lib/idl/open_parametric.ts frontend/src/lib/idl/open_parametric.json frontend/src/hooks/useCreateMasterAgreement.ts
Not-tested: Interactive frontend create-master flow
Parse the on-chain master agreement name into backend responses and carry it
through the frontend agreement summary/account mappers so downstream UI work can
consume it. The backend test fixture now serializes the inserted string field,
and related backend test constructors include placeholder names so the requested
parser test command still compiles after the struct shape change.

Constraint: The on-chain account layout inserts `name` immediately after `master_id`, so parser offsets had to shift without touching unrelated fields
Constraint: Existing backend unit tests construct `MasterAgreementInfo` directly, so they needed the new field for the requested cargo test command to pass
Rejected: Limit changes to the five requested files only | `cargo test program_accounts` failed to compile due to direct struct initializers in existing backend tests
Confidence: high
Scope-risk: narrow
Directive: Keep backend/frontend field ordering aligned with the on-chain account layout when adding future master agreement fields
Tested: `cd backend && cargo test program_accounts`
Tested: Frontend TypeScript diagnostics clean for modified hooks/store and `src/store/__tests__/pure-functions.test.ts` via `npx tsc --noEmit --project frontend/tsconfig.json`
Not-tested: `cd frontend && yarn test src/store/__tests__/pure-functions.test.ts` blocked by Yarn/Corepack workspace setup and lockfile state
Keep the workbench and dropdown aligned with the on-chain master agreement name so leaders and operators can create, review, and rename agreements without falling back to opaque identifiers.

Constraint: The live STEP1 on-chain creation flow still batches raw instructions, so the create params had to stay inline while becoming typed and name-complete
Rejected: Route creation through useCreateMasterAgreement | it only exposes an rpc path and cannot compose the existing multi-transaction setup flow
Directive: Keep the on-chain create params and rename hook aligned with the IDL name field; do not drop the name/oracleFeed fields when the setup flow changes
Confidence: high
Scope-risk: narrow
Tested: Dropdown rendering test via ./node_modules/.bin/vitest run src/components/layout/__tests__/MasterAgreementDropdown.test.tsx src/components/tabs/tab-contract/__tests__/MasterAgreementWorkbench.test.tsx; frontend typecheck via ./node_modules/.bin/tsc -b --pretty false
Not-tested: Live wallet-backed create and rename transactions against a running backend/indexer
The workbench rename UI was gated on operator access, but the fetched agreement summaries never surfaced operator ownership, so selecting an operator-owned agreement could not set the store role correctly.

Constraint: Operator access must continue to respect existing wallet-role precedence instead of overriding leader or participant ownership
Rejected: Make the workbench bypass store role and inspect account ownership directly | it would hide the broken summary-selection path instead of fixing it
Directive: Keep backend summary role detection and dropdown selection role sync in step when new roles are added
Confidence: high
Scope-risk: narrow
Tested: ./node_modules/.bin/vitest run src/components/layout/__tests__/MasterAgreementDropdown.test.tsx src/hooks/__tests__/useMasterAgreements.test.ts; ./node_modules/.bin/tsc -b --pretty false
Not-tested: Live backend response with a real operator-owned agreement in a connected browser session
The workbench could briefly fall back to stale or placeholder naming after create and rename because visible state depended on list/account refetch timing. This adds a narrow selected-name store surface so the active agreement keeps its official name immediately while reconciliation runs in the background.

Constraint: The optimistic name must not be overwritten by the stale policy list during the same selection cycle
Rejected: Wait for list/account refetch before updating the UI | it preserves the timing gap the review flagged
Directive: Treat selectedMasterAgreementName as the immediate UI source for the active agreement only; refresh paths should reconcile it, not block on it
Confidence: high
Scope-risk: narrow
Tested: ./node_modules/.bin/vitest run src/components/layout/__tests__/MasterAgreementDropdown.test.tsx src/components/tabs/tab-contract/__tests__/MasterAgreementWorkbench.test.tsx; ./node_modules/.bin/vitest run src/components/tabs/tab-contract/__tests__/MasterAgreementNameEditor.test.tsx; ./node_modules/.bin/tsc -b --pretty false
Not-tested: Live wallet-backed create/rename with delayed backend index updates in a browser session
The review panel could still show a stale backend name even after the dropdown and editor had fresher local state, and the rename flow collapsed reconciliation into a single success state. This aligns the review surface with the optimistic selected name and makes refresh failures explicit while keeping the successful local update visible.

Constraint: Keep the fix minimal and limited to precedence/toast semantics rather than reworking the refresh architecture
Rejected: Wait for account refresh before showing any success state | it reintroduces the stale-name lag already fixed elsewhere in Task 4
Directive: When optimistic selected-name state exists for the active agreement, prefer it over lagging account data in read surfaces
Confidence: high
Scope-risk: narrow
Tested: ./node_modules/.bin/vitest run src/components/tabs/tab-contract/__tests__/MasterAgreementNameEditor.test.tsx src/components/tabs/tab-contract/__tests__/MasterAgreementReviewPanel.test.tsx; ./node_modules/.bin/tsc -b --pretty false
Not-tested: Live browser session with delayed account refresh after rename
The rename warning branch relied on rejected promises, but both master-agreement refresh hooks swallowed failures and resolved normally. This changes those refetch surfaces to return explicit booleans so the editor can distinguish successful reconciliation from local-only optimistic updates.

Constraint: Preserve existing state updates and call patterns while adding a real success/failure signal
Rejected: Infer failure from Promise.allSettled shapes in the editor | the hooks resolve on failure today, so that signal is false confidence
Directive: Any UI that needs refresh-health semantics from these hooks should use their boolean return values rather than settlement shape
Confidence: high
Scope-risk: narrow
Tested: ./node_modules/.bin/vitest run src/components/tabs/tab-contract/__tests__/MasterAgreementNameEditor.test.tsx src/components/tabs/tab-contract/__tests__/MasterAgreementReviewPanel.test.tsx; ./node_modules/.bin/tsc -b --pretty false
Not-tested: Live browser session with backend fetch failures during rename reconciliation
Replace the duplicated STEP3 confirmation surface with a leader/operator snapshot that shows activation readiness, collateral coverage, and live money flow in one place.

Constraint: Existing Yarn workspace resolution is broken in this worktree, so verification used direct local vitest/tsc binaries
Rejected: Reuse ParticipantConfirm for STEP3 | it kept the activation surface as a duplicate of STEP2 instead of a snapshot dashboard
Confidence: high
Scope-risk: narrow
Directive: Keep STEP3 focused on current activation readiness; do not expand it into historical analytics without a separate task
Tested: ./node_modules/.bin/vitest run src/hooks/__tests__/useMasterAgreementSnapshot.test.ts src/components/tabs/tab-contract/__tests__/MasterAgreementWorkbench.test.tsx
Tested: ./node_modules/.bin/tsc -b --pretty false
Not-tested: yarn test in this worktree remains blocked by lockfile/workspace resolution
Use the selected or parsed master agreement name for the leader row in pool collateral status instead of always falling back to a generic label.

Constraint: Keep the fix inside the existing pool-collateral label path without redesigning store-backed naming
Rejected: Introduce a broader party-label abstraction | unnecessary scope for a single missing leader path
Confidence: high
Scope-risk: narrow
Directive: Preserve the current fallback order for leader labels unless a dedicated leader-display source is added later
Tested: ./node_modules/.bin/vitest run src/hooks/__tests__/useMasterAgreementSnapshot.test.ts src/components/tabs/tab-contract/__tests__/MasterAgreementWorkbench.test.tsx
Tested: ./node_modules/.bin/tsc -b --pretty false
The activation review step became non-actionable after the dashboard swap, and unresolved policy fetches were rendering misleading zero-value money metrics. This shares the activation path across the participant and dashboard surfaces, exposes policy fetch state for the dashboard, and adds focused regressions for the step swap and loading/error handling.

Constraint: Keep the Task 5 fix pass scoped to the existing activation and snapshot surfaces

Rejected: Re-embed ParticipantConfirm inside the activation step | would duplicate the participant checklist UI instead of sharing the activation behavior

Rejected: Null out the full snapshot during policy loading/errors | would hide valid collateral KPIs alongside the money snapshot regression

Confidence: high

Scope-risk: narrow

Directive: Keep activation business logic shared between participant confirmation and activation dashboard surfaces

Tested: vitest run src/hooks/__tests__/useMasterAgreementSnapshot.test.ts src/components/tabs/tab-contract/__tests__/MasterAgreementWorkbench.test.tsx src/components/tabs/tab-contract/__tests__/ParticipantConfirm.test.tsx src/components/tabs/tab-contract/__tests__/MasterActivationDashboard.test.tsx

Tested: ./node_modules/.bin/tsc -b --pretty false (from frontend/)

Tested: lsp_diagnostics_directory frontend
Simulation mode now derives its Step 3 dashboard from local agreement terms and pool state, while on-chain mode withholds collateral readiness until token balances are actually loaded.

This preserves the dashboard handoff in simulation without routing users back to confirmation semantics, and it removes the synthetic zero-balance path that was producing false deficits during unresolved balance fetches.

Constraint: Task 5 scope is limited to Step 3 dashboard correctness and directly related tests
Rejected: Send simulation users back to the confirmation panel | breaks the Step 3 dashboard product intent
Rejected: Keep zero-balance placeholders until fetch completes | shows false Action Needed and deficit states
Confidence: high
Scope-risk: narrow
Directive: Do not treat missing collateral balances as authoritative funded data without an explicit resolved state
Tested: vitest run src/components/tabs/tab-contract/__tests__ src/hooks/__tests__/useMasterAgreementSnapshot.test.ts; tsc --noEmit --pretty false -p tsconfig.json; lsp diagnostics on modified files; lsp_diagnostics_directory frontend
Task 5 still conflated policy fetch latency with readiness state, and token balance RPC failures could collapse into zero balances that rendered false blockers. This pass keeps readiness KPIs active while policy data loads, isolates policy-specific money-panel failures, and leaves collateral readiness unresolved when live balance reads fail.

Constraint: Scope limited to the Task 5 Step 3 dashboard hook/component path and directly related regressions
Rejected: Preserve a single loading/error channel for readiness and policies | it still hides ready collateral state behind policy fetch latency
Rejected: Continue coercing token balance RPC failures to zero | it produces synthetic underfunded states from transport failures
Confidence: high
Scope-risk: narrow
Directive: Do not fold policy loading/error back into readiness state without a separate unresolved UI path for missing collateral data
Tested: ./node_modules/.bin/vitest run src/components/tabs/tab-contract/__tests__ src/hooks/__tests__/useMasterAgreementSnapshot.test.ts (from frontend)
Tested: ./node_modules/.bin/tsc --noEmit --pretty false -p tsconfig.json (from frontend)
Tested: lsp_diagnostics on modified frontend files; lsp_diagnostics_directory frontend
Not-tested: Live Solana RPC integration behavior beyond mocked/unit coverage
Step 2 now stops at confirmation state and only advances into the Step 3 dashboard when approvals are complete. The dashboard also treats the live-balance handoff as loading so on-chain users do not briefly see an empty activation snapshot before balance reads begin.

Constraint: Task 5 fix pass must preserve simulation and on-chain activation behavior once Step 3 loads
Rejected: Leave activation wired in ParticipantConfirm | bypasses Step 3 ownership and guide target
Confidence: high
Scope-risk: narrow
Directive: Keep \ attached to MasterActivationDashboard unless Step ownership changes again
Tested: frontend vitest run src/components/tabs/tab-contract/__tests__ src/hooks/__tests__/useMasterAgreementSnapshot.test.ts
Tested: frontend tsc -b --pretty false
Tested: frontend lsp_diagnostics_directory + modified-file diagnostics
Not-tested: Manual browser walkthrough
The guided tour still targeted the old single-step activation path, and the new dashboard CTA could be clicked before the on-chain account fetch finished. This patch adds a dedicated tour stop for the Step 2 transition, shifts the downstream step wiring by one slot, and disables on-chain activation until the required account data is present.

Constraint: Keep scope limited to Task 5 activation flow guidance and readiness checks
Rejected: Reuse snapshot data for activation wallets | snapshot path does not expose the full account payload needed for on-chain activation
Confidence: high
Scope-risk: narrow
Directive: If the activation flow changes again, update both GUIDE_STEPS sequencing and GuideTour auto-advance/manual-step mappings together
Tested: frontend vitest run src/components/tabs/tab-contract/__tests__ src/hooks/__tests__/useMasterAgreementSnapshot.test.ts
Tested: frontend tsc --noEmit --pretty false -p tsconfig.json
Tested: lsp_diagnostics_directory frontend
Not-tested: Interactive browser walkthrough of the guide overlay
Task 5's activation path was fetching the same master-agreement account through the dashboard, snapshot, and collateral hooks. This change centralizes the account fetch in the dashboard, threads the resolved master state into the dependent hooks, and gives the Step 2 transition button its own label so it is distinct from the real Step 3 activation CTA.

Constraint: Keep the fix local to the Task 5 activation dashboard path and directly related tests/i18n
Rejected: Introduce a broader shared context/provider for master agreement data | too broad for a fix pass
Rejected: Leave snapshot and collateral hooks to fetch the same PDA independently | preserves duplicate backend fetch and SSE chains
Confidence: high
Scope-risk: narrow
Tested: ./node_modules/.bin/vitest run src/components/tabs/tab-contract/__tests__ src/hooks/__tests__/useMasterAgreementSnapshot.test.ts
Tested: ./node_modules/.bin/tsc -b --pretty false
Tested: lsp_diagnostics_directory frontend and per-file diagnostics on modified files
The workbench was still treating confirmation readiness as equivalent to a user-entered activation transition, which hid the transition CTA and let the guide advance on the wrong signal.

Constraint: Keep scope limited to the Task 5 Step 3 transition and directly related regression tests
Rejected: Broader workbench step-state refactor | unnecessary for the verified regression surface
Directive: Do not auto-select the activation dashboard from readiness state alone; only activation or an explicit user transition should cross that boundary
Confidence: high
Scope-risk: narrow
Tested: vitest run src/components/tabs/tab-contract/__tests__ src/hooks/__tests__/useMasterAgreementSnapshot.test.ts; tsc -b --pretty false; lsp_diagnostics_directory frontend
The UI was trusting the derived store role even though the program only
accepts the wallet stored in master.operator. This narrows on-chain
actionability to the loaded operator key while leaving simulation behavior
role-based for local previews.

Constraint: On-chain activation authority is defined by master.operator, not the frontend role mirror
Rejected: Keep leader/operator role gating and handle Unauthorized after click | preserves the dead-end CTA
Confidence: high
Scope-risk: narrow
Directive: Do not widen on-chain activation eligibility without comparing against the loaded operator account
Tested: vitest run src/hooks/__tests__/useMasterAgreementActivation.test.ts; vitest run src/components/tabs/tab-contract/__tests__/MasterActivationDashboard.test.tsx; ./node_modules/.bin/tsc -b --pretty false (frontend)
Selected agreement names were staying pinned to the optimistic store value
long after the backend/account refresh had published a newer name. This
adds a narrow sync path that keeps the optimistic label until the
authoritative value actually changes, then updates the selected name
without redesigning the store.

Constraint: Keep optimistic rename feedback immediate without a broader store rewrite
Rejected: Always prefer fetched names over selectedMasterAgreementName | would erase the optimistic rename before refresh completes
Confidence: high
Scope-risk: narrow
Directive: Preserve the optimistic name only until the authoritative source changes; do not make selectedMasterAgreementName permanently dominant again
Tested: vitest run src/components/layout/__tests__/MasterAgreementDropdown.test.tsx src/components/tabs/tab-contract/__tests__/MasterAgreementNameEditor.test.tsx src/components/tabs/tab-contract/__tests__/MasterAgreementReviewPanel.test.tsx; ./node_modules/.bin/tsc -b --pretty false (frontend)
@Jaymyong66 Jaymyong66 self-assigned this Apr 27, 2026
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