feat: harden send preview idempotency

This commit is contained in:
zhaoyilun
2026-07-10 19:24:53 +08:00
parent 138615a29c
commit 93112d6d99
8 changed files with 482 additions and 22 deletions

View File

@@ -28,7 +28,7 @@
| Search contacts | Registered MCP tool; deterministic exact-match ranking and case-insensitive de-duplication across log/JID candidates and optional copied-DB MsgLib display enrichment. | Optional richer fields such as department/title remain future enrichment, not a core search blocker. | complete for core search; optional enrichment later |
| Search groups | Registered MCP tool; deterministic exact-match ranking and case-insensitive de-duplication across groupchat/conference candidates and optional copied-DB MsgLib display enrichment. | Optional member count, owner refs, and nested/group hierarchy remain future enrichment, not a core search blocker. | complete for core search; optional enrichment later |
| Receive messages | PacketReader source works; copied `MsgLib.db` explicit receive works with `source_preference="msglib_readonly"`; R1 precheck says default should remain PacketReader until a future reconciliation helper is implemented. | Future reconciliation helper, source confidence scoring, pagination policy. | future receive helper after R2/R3 |
| Send messages | `isphere_send_message` preview/dry-run is registered; R6a confirms B-route entrypoint metadata; R6d partial returned package is usable for sandbox-only connector-shell/idempotency work. | Full production still needs after-recorder output and manual send timestamps, or a later complete returned package. | next R6e sandbox-only connector shell |
| Send messages | `isphere_send_message` preview/dry-run is registered; R6a confirms B-route entrypoint metadata; R6e adds sandbox-only connector shell metadata and idempotency/audit state. | Full production still needs after-recorder output/manual timestamps or a controlled online sandbox connector pass. | next R6f fake/sandbox connector contract |
| Receive files | List mode exists from message-derived file metadata; R3/R3b confirm DB file metadata exists but current cache/archive evidence does not map to local files. | R4 download is blocked until better cache evidence or a UI/client download connector is available. | blocked; continue R5 |
| Send files | Contract exists; R6a/C31 metadata confirms file-upload/offline-file/file-message candidates and A-route file menu classifier support. | Blocked behind returned send-message sandbox evidence plus upload/file-transfer dry-run. | wait for R6d, then file-send upload preflight |
@@ -551,6 +551,41 @@ powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-go-mcp.ps1
**Stop condition:**
- Do not wire a real send entrypoint until a full returned package or controlled online sandbox connector test is available.
**R6e Result:**
- Added `SendMessageIdempotencyStore` plus file-backed `fileSendMessageIdempotencyStore`.
- Added `ISPHERE_SEND_IDEMPOTENCY_PATH` for JSONL idempotency state.
- Added `RegisterISphereSendMessageToolWithState` so tests and future connector work can inject fake audit/idempotency state.
- `isphere_send_message` now returns `connector_mode="sandbox-only-shell"` and `production_send_enabled=false`.
- Duplicate idempotency key reuse for the same target/content is reported with `duplicate_detected=true` and no side effects.
- Conflicting idempotency key reuse for different content/target is blocked with `conflict_detected=true`, `send_status="blocked"`, and no side effects.
- `scripts/verify-go-mcp.ps1` and `cmd/isphere-capability-smoke` now use fresh per-run send audit/idempotency state so repeated smokes remain deterministic.
- Decision: next node is R6f fake/sandbox connector contract. Production send remains blocked.
---
### R6f: Fake/sandbox send connector contract
**Purpose:** Define the connector interface and fake connector behavior needed before any real B-route send entrypoint is attached.
**Files:**
- Modify: `internal/tools/isphere_send_message.go`
- Modify tests: `internal/tools/isphere_send_message_test.go`
- Modify docs: this roadmap, `docs/current-status-card.md`, and `docs/go-mcp-runbook.md`
**Execution policy:**
- The fake connector may simulate accepted/ack/failed outcomes only inside tests.
- Runtime MCP `execution_mode="production"` remains blocked unless an explicit test-only/fake injection is used.
- No real helper send op, no UI click/type, no network/protocol send, no upload.
**Acceptance gate:**
- Fake connector tests cover accepted, failed, duplicate, and idempotency-conflict paths.
- The production MCP server still reports `production_send_enabled=false`.
- Audit records keep raw message body and raw idempotency key out of JSONL.
**Stop condition:**
- If a real connector is introduced before fake/sandbox contract tests pass, stop and revert that part.
---
### R6: Send-message preview and production path