diff --git a/openspec/changes/add-namespace-invite-links/specs/namespace-invitations/spec.md b/openspec/changes/add-namespace-invite-links/specs/namespace-invitations/spec.md
index c384d4d6..da305791 100644
--- a/openspec/changes/add-namespace-invite-links/specs/namespace-invitations/spec.md
+++ b/openspec/changes/add-namespace-invite-links/specs/namespace-invitations/spec.md
@@ -116,3 +116,8 @@ API SHALL 使用 Issue #911 的路径与响应合同。
- **THEN** 按原方式安全捕获并清除片段,保持兼容
- **WHEN** 新的具名片段格式无效
- **THEN** 清除先前暂存凭证,不能误用上一条邀请;无片段的登录回跳仍恢复合法暂存邀请
+
+#### Scenario: 他人消费邀请后生成新链接
+- **WHEN** 他人已消费邀请,而管理员页面还缓存旧的可兑换状态
+- **THEN** 点击生成先刷新服务端状态,已消费或过期的邀请不误弹替换确认,仍可兑换的邀请必须确认
+- **AND** 状态刷新失败时不沿用旧缓存生成或替换,显示可重试错误
diff --git a/web/src/features/namespace/invitation-controls.test.tsx b/web/src/features/namespace/invitation-controls.test.tsx
index 9397b03e..df80128e 100644
--- a/web/src/features/namespace/invitation-controls.test.tsx
+++ b/web/src/features/namespace/invitation-controls.test.tsx
@@ -34,6 +34,10 @@ describe('InvitationControls', () => {
api.createInvitation.mockRejectedValue(new Error('unavailable'))
api.revokeInvitation.mockRejectedValue(new Error('unavailable'))
const dialog = await openConfirmation(action)
+ if (action === 'replace') {
+ expect(api.invitationStatus).toHaveBeenCalledTimes(2)
+ expect(api.createInvitation).not.toHaveBeenCalled()
+ }
fireEvent.click(within(dialog).getByRole('button', { name: 'dialog.confirm' }))
@@ -41,4 +45,37 @@ describe('InvitationControls', () => {
expect(screen.getByRole('alert').textContent).toBe('invitation.error')
expect(action === 'replace' ? api.createInvitation : api.revokeInvitation).toHaveBeenCalledTimes(1)
})
+
+ it.each(['consumed', 'expired'])('creates without a replacement prompt when the cached link was %s', async () => {
+ api.invitationStatus.mockResolvedValueOnce({ active: true }).mockResolvedValue({ active: false })
+ api.createInvitation.mockImplementation(() => new Promise(() => {}))
+ const client = new QueryClient({ defaultOptions: { queries: { retry: false } } })
+ render(