diff --git a/docs/current-status-card.md b/docs/current-status-card.md index f7f5e2e..a91b066 100644 --- a/docs/current-status-card.md +++ b/docs/current-status-card.md @@ -58,9 +58,11 @@ N13/N14/N15 are pre-business validation results. They can help identify UI eleme ## Current loop -Current loop: `R6e sandbox-only send connector shell and idempotency audit hardening implemented; next R6f fake/sandbox connector contract or corrected full evidence intake`. +Current loop: `Planning gate complete for continuous execution; awaiting business review before starting R6f`. -Plan file: `docs/superpowers/plans/2026-07-10-core-business-capabilities-roadmap.md`. +Active continuous execution plan: `docs/superpowers/plans/2026-07-10-r6f-r14-continuous-execution-plan.md`. + +Previous roadmap file: `docs/superpowers/plans/2026-07-10-core-business-capabilities-roadmap.md`. Loop rule: @@ -222,4 +224,10 @@ R6e sandbox-only send connector shell and idempotency audit hardening is complet - Duplicate same-key/same-target/same-content preview calls return structured metadata with `duplicate_detected=true` and no side effects. - Same idempotency key reused for different target/content returns `send_status="blocked"` with `conflict_detected=true` and no side effects. - `scripts\verify-go-mcp.ps1` and `cmd\isphere-capability-smoke` use fresh per-run idempotency state to keep repeatable smoke deterministic. -- Next node: R6f fake/sandbox connector contract or corrected full evidence intake. +- Next node after user approval: R6f fake/sandbox connector contract. + +Continuous execution plan R6f-R14 is drafted and awaiting user review: + +- Plan file: `docs/superpowers/plans/2026-07-10-r6f-r14-continuous-execution-plan.md`. +- Scope: 15 ordered rounds covering send-message connector/gate hardening, send-file preview/idempotency/package, receive-file download preview, receive-message reconciliation, end-to-end business smoke, and release-candidate report. +- Execution rule after approval: one round at a time, status-card update, verification, commit, push, then continue automatically until an evidence-only blocker requires user action. diff --git a/docs/superpowers/plans/2026-07-10-r6f-r14-continuous-execution-plan.md b/docs/superpowers/plans/2026-07-10-r6f-r14-continuous-execution-plan.md new file mode 100644 index 0000000..215bacf --- /dev/null +++ b/docs/superpowers/plans/2026-07-10-r6f-r14-continuous-execution-plan.md @@ -0,0 +1,903 @@ +# R6f-R14 Continuous Execution Plan Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 用 15 个连续轮次把 iSphere MCP 的四个业务目标推进到可验收状态:搜索联系人、搜索群组、收发消息、收发文件;如果生产写入证据不足,则产出明确阻断和下一包采集材料。 + +**Architecture:** Go MCP server 是唯一对数字员工暴露的产品面;C# WinHelper、MsgLibReadSidecar、录制包和本地脚本只作为证据采集、只读解析、或受控连接器边界。读能力优先走 PacketReader/MsgLib 已验证源;写能力先建立 fake/sandbox connector contract、审计/idempotency、生产门禁,再根据在线沙箱证据决定是否开启真实连接器。 + +**Tech Stack:** Go 1.23.4, `github.com/modelcontextprotocol/go-sdk`, PowerShell, x86 .NET Framework sidecar, C# WinHelper, Windows iSphere/IMPlatformClient evidence packages. + +## Global Constraints + +- Repository root: `E:\coding\codex\isphere-ai-bridge`. +- Remote name: `gitea`; active branch: `main` unless the user explicitly asks for a new branch. +- Code search rule from `AGENTS.md`: use `git ls-files`, `ag`, `grep -R`, or PowerShell file commands; do not use `rg`. +- Local machine cannot currently log in to iSphere; do not attempt local live login as an acceptance gate. +- Production `isphere_send_message`, `isphere_send_file`, and real file download stay blocked unless the corresponding evidence gate passes in this plan. +- Committed docs and script output must not include raw message body, raw file content, credentials, or unredacted local operator paths. +- Standard deterministic smoke must keep optional real MsgLib env cleared unless a script name explicitly says it is an optional real-evidence smoke. +- Every round ends with status-card update, `git diff --check`, `go test ./...`, `go build ./cmd/isphere-mcp`, relevant PowerShell smoke, commit, and push. +- Generated packages and run outputs remain under ignored `runs/`; commit only package builders, validators, docs, and source code. + +--- + +## Current Starting Point + +- `isphere_search_contacts`: core search exists, with deterministic ranking/de-dup and optional MsgLib enrichment. +- `isphere_search_groups`: core search exists, with deterministic ranking/de-dup and optional MsgLib enrichment. +- `isphere_receive_messages`: PacketReader default exists; explicit `source_preference="msglib_readonly"` exists; default merge/reconciliation is not yet enabled. +- `isphere_receive_files`: list mode exists; real download mapping is blocked by missing cache/download evidence. +- `isphere_send_message`: preview/dry-run exists; production remains blocked; R6e added sandbox-only connector metadata and idempotency state. +- `isphere_send_file`: not yet registered as a product tool; file upload evidence exists only as UIA/menu and DLL metadata, not as a send connector. + +## Continuous Execution Rules After Approval + +1. Execute exactly one round at a time in the order below. +2. At the start of each round, mark that round as `in_progress` in `docs/current-status-card.md`. +3. Use TDD for code rounds: add failing tests first, run the focused failing test, implement the minimum, then run the full verification gate. +4. If a round is blocked only by missing online evidence, write the blocker into `docs/source-discovery/...`, keep production behavior blocked, and move to the next local-safe round. +5. If all remaining rounds require missing online evidence, stop and ask the user for the exact returned package listed by the most recent package builder. +6. After every round, commit and push only the intended source/docs/scripts: + +```powershell +git status --short +git diff --check +go test ./... +go build ./cmd/isphere-mcp +powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-go-mcp.ps1 +git status --short +Write-Host "Use the concrete git add and git commit commands listed inside the active round below." +git push gitea main +``` + +## Planned File Structure + +- `internal/tools/isphere_send_message.go`: existing MCP tool; keeps request/response contract, idempotency use, audit use, and production gate decisions. +- `internal/tools/send_message_connector.go`: new focused connector interface, fake connector result types, and connector error mapping for message send. +- `internal/tools/send_message_gate.go`: new production gate loader and reason-code formatter for message send. +- `internal/tools/isphere_send_message_test.go`: message send tool contract tests for preview, fake accepted/failed, duplicate, conflict, gate blocked. +- `internal/tools/isphere_send_file.go`: new file-send preview MCP tool with allowed-directory validation and SHA256/size metadata. +- `internal/tools/isphere_send_file_test.go`: file-send preview, blocked production, path validation, idempotency tests. +- `internal/mcpserver/server.go` and `internal/mcpserver/server_test.go`: tool registration count and name coverage. +- `scripts/package-send-sandbox-gate.ps1`: existing package builder; will get v2 manifest/checklist coverage. +- `scripts/validate-returned-send-sandbox-package.ps1`: existing validator; will get strict v2 pass/fail fields. +- `scripts/package-send-file-sandbox-gate.ps1`: new package builder for upload/file-transfer evidence. +- `scripts/verify-file-cache-mapping.ps1`: existing diagnostic; will get v2 cache-root and fingerprint matching. +- `scripts/verify-business-goals-smoke.ps1`: new end-to-end MCP business smoke. +- `docs/current-status-card.md`: source of truth for active loop, blockers, and next round. +- `docs/go-mcp-runbook.md`: operator-facing run and env instructions. +- `docs/source-discovery/*.md`: evidence notes and decisions. + +--- + +## Round Queue + +### R6f: Fake/sandbox send connector contract + +**Purpose:** 在不接真实发送入口的前提下,把发送连接器接口、fake accepted、fake failed、审计映射、idempotency 映射一次性定型。 + +**Files:** +- Create: `internal/tools/send_message_connector.go` +- Modify: `internal/tools/isphere_send_message.go` +- Modify: `internal/tools/isphere_send_message_test.go` +- Modify: `docs/current-status-card.md` +- Modify: `docs/go-mcp-runbook.md` + +**Interfaces:** +- Produces: `type SendMessageConnector interface { ExecuteSendMessage(context.Context, SendMessageConnectorRequest) (SendMessageConnectorResult, error) }` +- Produces: `type SendMessageConnectorRequest struct { TargetType string; TargetID string; TargetRef string; ContentSHA256 string; ContentLength int; IdempotencyKeySHA256 string; ExecutionMode string }` +- Produces: `type SendMessageConnectorResult struct { Accepted bool; Status string; AckRef string; ErrorCode string; ErrorMessage string; ConnectorMode string }` +- Produces: `RegisterISphereSendMessageToolWithStateAndConnector(server, auditStore, idempotencyStore, connector)` for tests only. + +- [ ] **Step 1: Write failing fake-accepted test** + +Add `TestISphereSendMessageFakeConnectorAcceptedAudit` to `internal/tools/isphere_send_message_test.go`: + +```go +func TestISphereSendMessageFakeConnectorAcceptedAudit(t *testing.T) { + connector := fakeSendMessageConnector{result: SendMessageConnectorResult{ + Accepted: true, Status: "accepted", AckRef: "fake-ack-1", ConnectorMode: "fake", + }} + response := callSendMessageWithInjectedConnector(t, connector, map[string]any{ + "target_type": "contact", "target_id": "u1", "content_text": "hello", + "content_sha256": sha256Hex("hello"), "idempotency_key": "idem-1", "execution_mode": "production", + }) + assertToolBool(t, response, "ok", true) + assertToolString(t, response, "send_status", "accepted") + assertToolString(t, response, "connector_mode", "fake") + assertAuditOmits(t, response, []string{"hello", "idem-1"}) +} +``` + +Run: + +```powershell +go test ./internal/tools -run TestISphereSendMessageFakeConnectorAcceptedAudit -v +``` + +Expected before implementation: FAIL because `RegisterISphereSendMessageToolWithStateAndConnector` and connector types do not exist. + +- [ ] **Step 2: Write failing fake-failed test** + +Add `TestISphereSendMessageFakeConnectorFailureAudit` asserting `ok=false`, `send_status="failed"`, `error_code="fake_rejected"`, and `side_effect_flags.real_send_attempted=false`. + +- [ ] **Step 3: Implement connector contract** + +Create `internal/tools/send_message_connector.go` with the interface, request/result structs, and result normalization. Keep the fake connector in the test file. + +- [ ] **Step 4: Wire injected connector only for tests** + +Modify `internal/tools/isphere_send_message.go` so default `RegisterISphereSendMessageTool` still blocks production, while `RegisterISphereSendMessageToolWithStateAndConnector` can execute a fake connector in tests. + +- [ ] **Step 5: Verify and commit** + +```powershell +git diff --check +go test ./internal/tools -run "TestISphereSendMessageFakeConnector|TestISphereSendMessageIdempotency" -v +go test ./... +go build ./cmd/isphere-mcp +powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-go-mcp.ps1 +git add internal\tools\send_message_connector.go internal\tools\isphere_send_message.go internal\tools\isphere_send_message_test.go docs\current-status-card.md docs\go-mcp-runbook.md +git commit -m "feat: add fake send connector contract" +git push gitea main +``` + +**Acceptance gate:** Default MCP still reports `production_send_enabled=false`; fake connector is reachable only through test injection. + +--- + +### R6g: Send audit and idempotency replay diagnostics + +**Purpose:** 让后续连接器接入前可以独立验证审计、重复请求、冲突请求、重放读取,不依赖登录环境。 + +**Files:** +- Create: `scripts/verify-send-audit-idempotency.ps1` +- Create: `scripts/test-verify-send-audit-idempotency.ps1` +- Modify: `internal/tools/isphere_send_message_test.go` +- Modify: `docs/go-mcp-runbook.md` +- Modify: `docs/current-status-card.md` + +**Interfaces:** +- Consumes: `ISPHERE_SEND_AUDIT_PATH`, `ISPHERE_SEND_IDEMPOTENCY_PATH`. +- Produces script output keys: `audit_records`, `idempotency_records`, `duplicate_detected`, `conflict_detected`, `raw_body_present`, `raw_idempotency_key_present`. + +- [ ] **Step 1: Write fixture test script** + +`scripts/test-verify-send-audit-idempotency.ps1` creates temporary JSONL audit/idempotency files with one normal record, one duplicate, and one conflict. + +```powershell +powershell -NoProfile -ExecutionPolicy Bypass -File scripts\test-verify-send-audit-idempotency.ps1 +``` + +Expected before implementation: FAIL because `verify-send-audit-idempotency.ps1` does not exist. + +- [ ] **Step 2: Implement diagnostic script** + +`scripts/verify-send-audit-idempotency.ps1` accepts: + +```powershell +param( + [Parameter(Mandatory=$true)][string]$AuditPath, + [Parameter(Mandatory=$true)][string]$IdempotencyPath +) +``` + +It prints sanitized JSON with counts and booleans only; it exits non-zero if raw `content_text` or raw `idempotency_key` appears. + +- [ ] **Step 3: Add tool-level test for audit redaction regression** + +Add `TestISphereSendMessageAuditRedactionIsStable` asserting JSONL audit files do not contain the submitted message body or raw idempotency key. + +- [ ] **Step 4: Verify and commit** + +```powershell +git diff --check +powershell -NoProfile -ExecutionPolicy Bypass -File scripts\test-verify-send-audit-idempotency.ps1 +go test ./internal/tools -run TestISphereSendMessageAuditRedactionIsStable -v +go test ./... +go build ./cmd/isphere-mcp +powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-go-mcp.ps1 +git add scripts\verify-send-audit-idempotency.ps1 scripts\test-verify-send-audit-idempotency.ps1 internal\tools\isphere_send_message_test.go docs\go-mcp-runbook.md docs\current-status-card.md +git commit -m "test: add send audit idempotency diagnostics" +git push gitea main +``` + +**Acceptance gate:** Diagnostics can prove audit/idempotency shape without opening iSphere. + +--- + +### R6h: Central production gate policy for send message + +**Purpose:** 把“为什么不能生产发送”变成单独 gate 对象,避免后续连接器代码散落硬编码。 + +**Files:** +- Create: `internal/tools/send_message_gate.go` +- Create: `internal/tools/send_message_gate_test.go` +- Modify: `internal/tools/isphere_send_message.go` +- Modify: `docs/current-status-card.md` +- Modify: `docs/go-mcp-runbook.md` + +**Interfaces:** +- Produces: `type SendMessageGatePolicy struct { ProductionAllowed bool; ReasonCode string; ReasonMessage string; EvidenceGatePass bool; ConnectorConfigured bool }` +- Produces: `LoadSendMessageGatePolicy(env map[string]string, evidenceGatePass bool, connectorConfigured bool) SendMessageGatePolicy`. +- Valid reason codes: `send_evidence_missing`, `send_connector_missing`, `send_disabled_by_default`, `send_gate_passed`. + +- [ ] **Step 1: Write table tests** + +Create `TestLoadSendMessageGatePolicy` with cases for default disabled, env set but evidence missing, evidence pass but connector missing, and all pass. + +```powershell +go test ./internal/tools -run TestLoadSendMessageGatePolicy -v +``` + +Expected before implementation: FAIL because the gate type does not exist. + +- [ ] **Step 2: Implement gate policy** + +Create `send_message_gate.go` with the exact type/function and reason-code order from the test. + +- [ ] **Step 3: Route blocked production through the gate** + +Modify `isphere_send_message.go` so blocked production responses include `gate_reason_code` and `gate_reason_message` from `SendMessageGatePolicy`. + +- [ ] **Step 4: Verify and commit** + +```powershell +git diff --check +go test ./internal/tools -run TestLoadSendMessageGatePolicy -v +go test ./... +go build ./cmd/isphere-mcp +powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-go-mcp.ps1 +git add internal\tools\send_message_gate.go internal\tools\send_message_gate_test.go internal\tools\isphere_send_message.go docs\current-status-card.md docs\go-mcp-runbook.md +git commit -m "feat: centralize send message production gate" +git push gitea main +``` + +**Acceptance gate:** The default runtime stays blocked, but the response explains the exact reason in machine-readable fields. + +--- + +### R6i: B-route connector adapter shell + +**Purpose:** 为后续 IMPlatformClient/B-route sidecar 接入定义请求/响应 JSON 边界,但本轮不执行真实发送。 + +**Files:** +- Create: `internal/tools/send_message_broute_adapter.go` +- Create: `internal/tools/send_message_broute_adapter_test.go` +- Modify: `docs/source-discovery/2026-07-10-send-connector-preflight.md` +- Modify: `docs/current-status-card.md` + +**Interfaces:** +- Produces: `type BRouteSendAdapterConfig struct { SidecarPath string; TimeoutSeconds int; Mode string }`. +- Produces: `func NewBRouteSendMessageConnector(config BRouteSendAdapterConfig) SendMessageConnector`. +- In this round, accepted modes are `disabled` and `dry_run_contract`; any other mode returns `broute_mode_blocked`. + +- [ ] **Step 1: Write disabled-mode test** + +```go +func TestBRouteSendMessageConnectorDisabledMode(t *testing.T) { + connector := NewBRouteSendMessageConnector(BRouteSendAdapterConfig{Mode:"disabled"}) + result, err := connector.ExecuteSendMessage(context.Background(), SendMessageConnectorRequest{ + TargetType:"contact", TargetID:"u1", ContentSHA256:"abc", IdempotencyKeySHA256:"idem", + }) + if err == nil { t.Fatalf("expected disabled error") } + if result.ErrorCode != "broute_mode_blocked" { t.Fatalf("unexpected error code: %s", result.ErrorCode) } +} +``` + +- [ ] **Step 2: Write dry-run contract test** + +Add `TestBRouteSendMessageConnectorDryRunContract` asserting no external process starts and result contains `ConnectorMode="broute-dry-run-contract"` and `Accepted=false`. + +- [ ] **Step 3: Implement adapter shell** + +Implement a connector that validates request fields and returns contract-shaped blocked/dry-run results only. Do not call `Start-Process`, do not load DLLs, do not click/type. + +- [ ] **Step 4: Verify and commit** + +```powershell +git diff --check +go test ./internal/tools -run TestBRouteSendMessageConnector -v +go test ./... +go build ./cmd/isphere-mcp +powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-go-mcp.ps1 +git add internal\tools\send_message_broute_adapter.go internal\tools\send_message_broute_adapter_test.go docs\source-discovery\2026-07-10-send-connector-preflight.md docs\current-status-card.md +git commit -m "feat: add b-route send adapter shell" +git push gitea main +``` + +**Acceptance gate:** B-route contract exists; no real send entrypoint is invoked. + +--- + +### R6j: Online sandbox-send recording package v2 + +**Purpose:** 修正 R6d 缺失项,一次性要求 before/after recorder、人工时间戳、ack/sent-record、重复发送确认,减少用户反复跑包。 + +**Files:** +- Modify: `scripts/package-send-sandbox-gate.ps1` +- Modify: `scripts/verify-send-sandbox-gate-package.ps1` +- Modify: `scripts/validate-returned-send-sandbox-package.ps1` +- Modify: `docs/source-discovery/2026-07-10-send-sandbox-gate.md` +- Modify: `docs/current-status-card.md` + +**Required returned fields:** +- `operator_started_at_local` +- `operator_clicked_send_at_local` +- `operator_observed_success_at_local` +- `before_recorder_present=true` +- `after_recorder_present=true` +- `success_ack_or_sent_record_present=true` +- `duplicate_second_send_attempted=false` + +- [ ] **Step 1: Add v2 manifest assertions to package verifier** + +```powershell +powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-send-sandbox-gate-package.ps1 +``` + +Expected before implementation: FAIL because v2 required returned fields are not enforced. + +- [ ] **Step 2: Add v2 checklist to package builder** + +Update package contents to include `RETURN-CHECKLIST-V2.md`, `SANDBOX-SEND-INPUTS.schema.json`, and a stricter `CREATE-RETURN-ZIP.ps1` that refuses to package if after-recorder output is missing. + +- [ ] **Step 3: Add validator strict mode** + +Add parameter: + +```powershell +param([string]$ZipPath, [switch]$StrictV2) +``` + +`-StrictV2` exits non-zero if any required returned field is absent. + +- [ ] **Step 4: Verify and commit** + +```powershell +git diff --check +powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-send-sandbox-gate-package.ps1 +powershell -NoProfile -ExecutionPolicy Bypass -File scripts\test-validate-returned-send-sandbox-package.ps1 +go test ./... +go build ./cmd/isphere-mcp +powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-go-mcp.ps1 +git add scripts\package-send-sandbox-gate.ps1 scripts\verify-send-sandbox-gate-package.ps1 scripts\validate-returned-send-sandbox-package.ps1 scripts\test-validate-returned-send-sandbox-package.ps1 docs\source-discovery\2026-07-10-send-sandbox-gate.md docs\current-status-card.md +git commit -m "chore: harden send sandbox evidence package v2" +git push gitea main +``` + +**Acceptance gate:** A generated package can no longer omit the R6d missing after-recorder/timestamp fields. + +--- + +### R6k: Returned sandbox evidence intake v2 + +**Purpose:** 自动检查 Downloads 是否已有新返回包;有则按 strict v2 intake,没则写清楚生产发送仍阻断并继续本地可推进工作。 + +**Files:** +- Create or modify: `docs/source-discovery/2026-07-10-returned-send-sandbox-analysis-v2.md` +- Modify: `docs/current-status-card.md` +- Modify: `docs/source-discovery/capability-source-matrix.md` + +- [ ] **Step 1: Look for a new returned package** + +```powershell +Get-ChildItem C:\Users\zhaoy\Downloads -Filter *.zip | Sort-Object LastWriteTime -Descending | Select-Object -First 10 FullName,LastWriteTime,Length +``` + +Prefer a zip whose contents include `SANDBOX-SEND-INPUTS.json`, after-recorder output, and return manifest. + +- [ ] **Step 2A: If a new candidate exists, validate strict v2** + +```powershell +$candidate = Get-ChildItem C:\Users\zhaoy\Downloads -Filter *.zip | Sort-Object LastWriteTime -Descending | Select-Object -First 1 +powershell -NoProfile -ExecutionPolicy Bypass -File scripts\validate-returned-send-sandbox-package.ps1 -ZipPath $candidate.FullName -StrictV2 +``` + +Expected pass output must include `r6d_gate_pass=true` and `strict_v2_pass=true` before production send can move beyond blocked state. + +- [ ] **Step 2B: If no candidate exists, document no-intake state** + +Create `docs/source-discovery/2026-07-10-returned-send-sandbox-analysis-v2.md` stating no strict-v2 returned package was found, `r6d_gate_pass=false`, and production `isphere_send_message` remains blocked. + +- [ ] **Step 3: Verify and commit** + +```powershell +git diff --check +go test ./... +go build ./cmd/isphere-mcp +powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-go-mcp.ps1 +git add docs\source-discovery\2026-07-10-returned-send-sandbox-analysis-v2.md docs\source-discovery\capability-source-matrix.md docs\current-status-card.md +git commit -m "docs: record send sandbox evidence v2 intake" +git push gitea main +``` + +**Acceptance gate:** The status card explicitly says whether production send is still blocked or evidence-gated for the next round. + +--- + +### R6l: Send-message production path gate closure + +**Purpose:** 根据 R6k 决策收口:如果 strict v2 通过,只打开 gated fake/sandbox connector path;如果没通过,保持生产阻断并让响应/文档完全一致。 + +**Files:** +- Modify: `internal/tools/isphere_send_message.go` +- Modify: `internal/tools/isphere_send_message_test.go` +- Modify: `scripts/verify-go-mcp.ps1` +- Modify: `docs/go-mcp-runbook.md` +- Modify: `docs/current-status-card.md` + +**Branching rule:** +- If `r6d_gate_pass=false`: add regression tests proving production remains blocked with `gate_reason_code="send_evidence_missing"`. +- If `r6d_gate_pass=true`: add tests that production can only run through an injected connector with `ISPHERE_SEND_PRODUCTION_ENABLED=1`, evidence gate pass, and idempotency key present. + +- [ ] **Step 1: Write default-blocked regression test** + +Add `TestISphereSendMessageProductionGateBlockedWithoutStrictEvidence`, asserting `ok=false`, `send_status="blocked"`, `gate_reason_code` is not empty, `production_send_enabled=false`, and `real_send_attempted=false`. + +- [ ] **Step 2: Write gated fake-production test if strict v2 passed** + +Add `TestISphereSendMessageProductionRequiresEvidenceAndConnector`. If strict v2 did not pass, keep this test skipped with explicit skip reason: `strict v2 send evidence not present in this repository state`. + +- [ ] **Step 3: Update verification smoke** + +`verify-go-mcp.ps1` must continue printing `production_send_enabled=false` on the default path unless strict evidence and env gate are configured. + +- [ ] **Step 4: Verify and commit** + +```powershell +git diff --check +go test ./internal/tools -run "TestISphereSendMessageProduction" -v +go test ./... +go build ./cmd/isphere-mcp +powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-go-mcp.ps1 +git add internal\tools\isphere_send_message.go internal\tools\isphere_send_message_test.go scripts\verify-go-mcp.ps1 docs\go-mcp-runbook.md docs\current-status-card.md +git commit -m "feat: close send message production gate behavior" +git push gitea main +``` + +**Acceptance gate:** No ambiguous state remains: send-message is either gated by validated evidence or explicitly blocked with a machine-readable reason. + +--- + +### R7: `isphere_send_file` preview tool + +**Purpose:** 补齐“发文件”的 MCP 产品面,先支持 preview/dry-run:校验目标、文件存在性、允许目录、大小、SHA256,不上传不点击。 + +**Files:** +- Create: `internal/tools/isphere_send_file.go` +- Create: `internal/tools/isphere_send_file_test.go` +- Modify: `internal/mcpserver/server.go` +- Modify: `internal/mcpserver/server_test.go` +- Modify: `scripts/verify-go-mcp.ps1` +- Modify: `cmd/isphere-capability-smoke/main.go` +- Modify: `docs/go-mcp-runbook.md` +- Modify: `docs/current-status-card.md` + +**Interfaces:** +- Tool name: `isphere_send_file`. +- Args: `target_type`, `target_id`, `file_path`, `file_sha256`, `idempotency_key`, `execution_mode`, `preview`. +- Env: `ISPHERE_SEND_FILE_ALLOWED_DIR` required for previewing real local file paths. +- Response fields: `ok`, `send_status`, `file_sha256`, `file_size_bytes`, `target_ref`, `production_send_enabled`, `side_effect_flags`. + +- [ ] **Step 1: Write file-preview test** + +Add `TestISphereSendFilePreviewComputesHashAndSize`, creating a temp allowed directory and `hello.txt`, then asserting preview returns `ok=true`, `send_status="planned"`, `file_size_bytes=5`, and `real_send_attempted=false`. + +- [ ] **Step 2: Write path rejection test** + +Add `TestISphereSendFileRejectsPathOutsideAllowedDir` asserting paths outside `ISPHERE_SEND_FILE_ALLOWED_DIR` return `ok=false` and `send_status="blocked"`. + +- [ ] **Step 3: Implement tool and registration** + +Register the tenth MCP tool. Production execution returns blocked with `production_send_enabled=false`. + +- [ ] **Step 4: Update smoke scripts** + +`verify-go-mcp.ps1` and `isphere-capability-smoke` must assert tool count includes `isphere_send_file`, preview works with a temp fixture file, and production remains blocked. + +- [ ] **Step 5: Verify and commit** + +```powershell +git diff --check +go test ./internal/tools -run TestISphereSendFile -v +go test ./... +go build ./cmd/isphere-mcp +powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-go-mcp.ps1 +git add internal\tools\isphere_send_file.go internal\tools\isphere_send_file_test.go internal\mcpserver\server.go internal\mcpserver\server_test.go scripts\verify-go-mcp.ps1 cmd\isphere-capability-smoke\main.go docs\go-mcp-runbook.md docs\current-status-card.md +git commit -m "feat: add send file preview tool" +git push gitea main +``` + +**Acceptance gate:** 数字员工能看到“发文件”工具,但默认路径只做 preview,不上传。 + +--- + +### R8: Send-file idempotency and audit hardening + +**Purpose:** 把发文件的审计/idempotency 与发消息保持一致,后续接上传连接器时不再重做控制面。 + +**Files:** +- Modify: `internal/tools/isphere_send_file.go` +- Modify: `internal/tools/isphere_send_file_test.go` +- Modify: `scripts/verify-go-mcp.ps1` +- Modify: `docs/go-mcp-runbook.md` +- Modify: `docs/current-status-card.md` + +**Interfaces:** +- Env: `ISPHERE_SEND_FILE_AUDIT_PATH`. +- Env: `ISPHERE_SEND_FILE_IDEMPOTENCY_PATH`. +- Duplicate same target/file/idempotency returns `duplicate_detected=true` with no second send. +- Reusing the same idempotency key for a different target or file hash returns `conflict_detected=true`. + +- [ ] **Step 1: Write duplicate test** + +Add `TestISphereSendFileDuplicateIdempotencyDetected`: same target, same file hash, same idempotency key called twice; second response has `duplicate_detected=true` and `real_send_attempted=false`. + +- [ ] **Step 2: Write conflict test** + +Add `TestISphereSendFileIdempotencyConflictBlocked`: same idempotency key with a different file hash returns `send_status="blocked"` and `conflict_detected=true`. + +- [ ] **Step 3: Implement audit and idempotency state** + +Reuse the message idempotency pattern or extract a shared small helper only if it reduces duplication without changing message behavior. + +- [ ] **Step 4: Verify and commit** + +```powershell +git diff --check +go test ./internal/tools -run TestISphereSendFile -v +go test ./... +go build ./cmd/isphere-mcp +powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-go-mcp.ps1 +git add internal\tools\isphere_send_file.go internal\tools\isphere_send_file_test.go scripts\verify-go-mcp.ps1 docs\go-mcp-runbook.md docs\current-status-card.md +git commit -m "feat: harden send file idempotency audit" +git push gitea main +``` + +**Acceptance gate:** File upload remains blocked, but duplicate/conflict behavior is deterministic. + +--- + +### R9: Send-file upload evidence package + +**Purpose:** 为“真实发文件”采集一次性材料:文件菜单、上传路径、大小限制、发送确认、上传/文件传输 DLL 状态。 + +**Files:** +- Create: `scripts/package-send-file-sandbox-gate.ps1` +- Create: `scripts/verify-send-file-sandbox-gate-package.ps1` +- Create: `scripts/validate-returned-send-file-sandbox-package.ps1` +- Create: `docs/source-discovery/2026-07-10-send-file-sandbox-gate.md` +- Modify: `docs/current-status-card.md` + +**Required package contents:** +- `RUN-FILE-RECORDING-SUITE.bat` +- `FILE-SEND-INPUTS.json` +- `RETURN-FILE-CHECKLIST.md` +- `CREATE-FILE-RETURN-ZIP.ps1` +- WinHelper recorder executable and dependencies +- Capability smoke executable if already built + +- [ ] **Step 1: Build verifier first** + +```powershell +powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-send-file-sandbox-gate-package.ps1 +``` + +Expected before implementation: FAIL because package builder does not exist. + +- [ ] **Step 2: Implement package builder** + +The builder outputs `runs/send-file-sandbox-gate-package.zip` and includes only executables/scripts/docs needed by an online operator. + +- [ ] **Step 3: Implement returned-package validator** + +Validator requires file send attempt timestamps, before/after recorder output, file SHA256, observed upload/sent status, and duplicate-send confirmation. + +- [ ] **Step 4: Verify and commit** + +```powershell +git diff --check +powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-send-file-sandbox-gate-package.ps1 +go test ./... +go build ./cmd/isphere-mcp +powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-go-mcp.ps1 +git add scripts\package-send-file-sandbox-gate.ps1 scripts\verify-send-file-sandbox-gate-package.ps1 scripts\validate-returned-send-file-sandbox-package.ps1 docs\source-discovery\2026-07-10-send-file-sandbox-gate.md docs\current-status-card.md +git commit -m "chore: add send file sandbox evidence package" +git push gitea main +``` + +**Acceptance gate:** 用户可以把一个包带到可登录环境采集发文件证据。 + +--- + +### R10: Receive-file download resolver v2 diagnostic + +**Purpose:** 继续推进“收文件”的下载/落地问题,验证 MsgLib 记录、cache/archive 文件、文件名/source-id/大小/时间窗口是否能形成可信映射。 + +**Files:** +- Modify: `scripts/verify-file-cache-mapping.ps1` +- Create: `scripts/test-verify-file-cache-mapping.ps1` +- Create: `docs/source-discovery/2026-07-10-file-download-mapping-v2.md` +- Modify: `docs/current-status-card.md` + +**Diagnostic inputs:** +- `-CacheRoot` accepts an absolute operator-local cache root selected at runtime; fixture tests create the cache root under `$env:TEMP`. +- `-MsgLibDb` and `-SQLiteDll` accept copied DB/runtime paths when real copied DB is available; fixture tests do not require real DB files. +- `-SanitizedOutputPath` writes committed-safe JSON summary to a caller-selected output file. + +- [ ] **Step 1: Add fixture test for resolver scoring** + +Fixture contains three DB-like file records and four cache-like file entries. Expected scoring: + +```text +exact filename + exact size + close timestamp => score 100 +exact size + source id in name/ref => score 80 +filename only => score 40 and not accepted +``` + +- [ ] **Step 2: Implement scoring and sanitized output** + +Output fields: `records_checked`, `cache_candidates_checked`, `accepted_matches`, `ambiguous_matches`, `raw_paths_printed=false`, `file_contents_read=false`. + +- [ ] **Step 3: Write decision note** + +`docs/source-discovery/2026-07-10-file-download-mapping-v2.md` records whether accepted matches exist. If not, download remains blocked and UI/client download connector becomes the next route. + +- [ ] **Step 4: Verify and commit** + +```powershell +git diff --check +powershell -NoProfile -ExecutionPolicy Bypass -File scripts\test-verify-file-cache-mapping.ps1 +go test ./... +go build ./cmd/isphere-mcp +powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-go-mcp.ps1 +git add scripts\verify-file-cache-mapping.ps1 scripts\test-verify-file-cache-mapping.ps1 docs\source-discovery\2026-07-10-file-download-mapping-v2.md docs\current-status-card.md +git commit -m "test: add file download resolver v2 diagnostic" +git push gitea main +``` + +**Acceptance gate:** 文件下载要么得到可实现映射,要么得到明确阻断证据。 + +--- + +### R11: Receive-file download preview contract + +**Purpose:** 即使真实下载还不能做,也让 `isphere_receive_files` 对 download 请求返回可机器解析的 preview/blocked 结果,而不是只有泛化拒绝。 + +**Files:** +- Modify: `internal/tools/isphere_receive_files.go` +- Modify: `internal/tools/isphere_receive_files_test.go` +- Modify: `scripts/verify-go-mcp.ps1` +- Modify: `docs/go-mcp-runbook.md` +- Modify: `docs/current-status-card.md` + +**Interfaces:** +- Args accepted: `mode="download"`, `preview=true`, `file_ref`, `output_dir`. +- If resolver lacks accepted mapping, response: `ok=false`, `download_status="blocked"`, `blocked_reason_code="file_cache_mapping_missing"`. +- If resolver has accepted mapping in fixture tests, response: `ok=true`, `download_status="planned"`, no file copy during preview. + +- [ ] **Step 1: Write blocked-preview test** + +Add `TestISphereReceiveFilesDownloadPreviewBlockedWithoutMapping`, asserting `blocked_reason_code="file_cache_mapping_missing"` and `file_contents_read=false`. + +- [ ] **Step 2: Write planned-preview fixture test** + +Add `TestISphereReceiveFilesDownloadPreviewPlannedWithFixtureMapping`, using an injected fixture resolver and asserting `download_status="planned"`, `file_contents_read=false`, and `file_copied=false`. + +- [ ] **Step 3: Implement preview contract** + +Keep non-preview real download blocked unless R10 accepted mapping exists and a separate production-copy gate is added. + +- [ ] **Step 4: Verify and commit** + +```powershell +git diff --check +go test ./internal/tools -run TestISphereReceiveFilesDownloadPreview -v +go test ./... +go build ./cmd/isphere-mcp +powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-go-mcp.ps1 +git add internal\tools\isphere_receive_files.go internal\tools\isphere_receive_files_test.go scripts\verify-go-mcp.ps1 docs\go-mcp-runbook.md docs\current-status-card.md +git commit -m "feat: add receive file download preview contract" +git push gitea main +``` + +**Acceptance gate:** 数字员工可以理解“为什么现在不能下载”,并在有映射时得到 planned preview。 + +--- + +### R12: Receive-message reconciliation helper + +**Purpose:** 给收消息建立 PacketReader/MsgLib 对账能力,用于后续提升默认 `auto` 的可信度,但本轮不改变默认路由。 + +**Files:** +- Create: `internal/isphere/message_reconciliation.go` +- Create: `internal/isphere/message_reconciliation_test.go` +- Create: `scripts/verify-receive-source-reconciliation.ps1` +- Modify: `docs/source-discovery/2026-07-10-receive-message-reconciliation-precheck.md` +- Modify: `docs/current-status-card.md` + +**Interfaces:** +- Produces: `type MessageReconciliationCandidate struct { Source string; MessageID string; ConversationID string; SenderID string; TimestampUnixMilli int64; BodyPresent bool; AttachmentPresent bool }`. +- Produces: `func ReconcileMessageSources(left, right []MessageReconciliationCandidate) MessageReconciliationSummary`. +- Summary fields: `strong_matches`, `medium_matches`, `unmatched_left`, `unmatched_right`, `auto_merge_recommended`. + +- [ ] **Step 1: Write reconciliation tests** + +Add `TestReconcileMessageSourcesStrongAndMediumMatches`: same message id is strong; same conversation/sender and timestamp within 3000ms is medium; auto merge remains false until a strong-ratio threshold is met. + +- [ ] **Step 2: Implement pure Go helper** + +No DB/log IO in the helper. It only compares sanitized candidates. + +- [ ] **Step 3: Add script wrapper** + +Script runs against fixture JSON first; optional real evidence mode prints counts and booleans only. + +- [ ] **Step 4: Verify and commit** + +```powershell +git diff --check +go test ./internal/isphere -run TestReconcileMessageSources -v +powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-receive-source-reconciliation.ps1 -UseFixture +go test ./... +go build ./cmd/isphere-mcp +powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-go-mcp.ps1 +git add internal\isphere\message_reconciliation.go internal\isphere\message_reconciliation_test.go scripts\verify-receive-source-reconciliation.ps1 docs\source-discovery\2026-07-10-receive-message-reconciliation-precheck.md docs\current-status-card.md +git commit -m "feat: add receive message reconciliation helper" +git push gitea main +``` + +**Acceptance gate:** 对账能力存在,但默认 receive routing 不改变。 + +--- + +### R13: End-to-end business goals smoke + +**Purpose:** 用一个脚本从数字员工视角验证四个业务目标的当前状态:能搜联系人、搜群、收消息、列文件、预览发消息、预览发文件,并列出阻断项。 + +**Files:** +- Create: `scripts/verify-business-goals-smoke.ps1` +- Create: `docs/reports/2026-07-10-business-goals-smoke.md` +- Modify: `docs/current-status-card.md` +- Modify: `docs/go-mcp-runbook.md` + +**Output keys:** +- `search_contacts_ready` +- `search_groups_ready` +- `receive_messages_ready` +- `send_message_preview_ready` +- `send_message_production_ready` +- `receive_files_list_ready` +- `receive_files_download_ready` +- `send_file_preview_ready` +- `send_file_production_ready` +- `remaining_business_blockers` + +- [ ] **Step 1: Implement fixture-backed smoke** + +The script starts `cmd/isphere-mcp` with temp fixture env and calls MCP requests using the same pattern as `verify-go-mcp.ps1`. + +- [ ] **Step 2: Assert business booleans** + +Default expected state after R12: + +```text +search_contacts_ready=true +search_groups_ready=true +receive_messages_ready=true +send_message_preview_ready=true +receive_files_list_ready=true +send_file_preview_ready=true +send_message_production_ready=false unless strict send evidence passed +receive_files_download_ready=false unless R10 accepted mapping exists +send_file_production_ready=false unless file sandbox evidence passed +``` + +- [ ] **Step 3: Create report** + +`docs/reports/2026-07-10-business-goals-smoke.md` summarizes the current business state in plain Chinese. + +- [ ] **Step 4: Verify and commit** + +```powershell +git diff --check +powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-business-goals-smoke.ps1 +go test ./... +go build ./cmd/isphere-mcp +powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-go-mcp.ps1 +git add scripts\verify-business-goals-smoke.ps1 docs\reports\2026-07-10-business-goals-smoke.md docs\current-status-card.md docs\go-mcp-runbook.md +git commit -m "test: add end-to-end business goals smoke" +git push gitea main +``` + +**Acceptance gate:** 业务目标完成度可以用脚本输出,而不是口头判断。 + +--- + +### R14: Operator packaging, runbook, and release-candidate report + +**Purpose:** 把当前可交付能力、包、用法、阻断项整理成用户能执行、业务能审核、下一轮能继续的最终材料。 + +**Files:** +- Modify: `docs/go-mcp-runbook.md` +- Create: `docs/reports/2026-07-10-r6f-r14-release-candidate.md` +- Modify: `docs/source-discovery/capability-source-matrix.md` +- Modify: `docs/current-status-card.md` +- Modify: package scripts touched in earlier rounds only if packaging verification finds missing required files. + +**Required report sections:** +- `## Business capability status` +- `## MCP tools available to digital employees` +- `## Packages generated under runs` +- `## Production blockers` +- `## Exact next evidence request if blocked` +- `## Verification commands and latest results` + +- [ ] **Step 1: Rebuild local packages** + +```powershell +powershell -NoProfile -ExecutionPolicy Bypass -File scripts\package-send-sandbox-gate.ps1 +powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-send-sandbox-gate-package.ps1 +powershell -NoProfile -ExecutionPolicy Bypass -File scripts\package-send-file-sandbox-gate.ps1 +powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-send-file-sandbox-gate-package.ps1 +``` + +- [ ] **Step 2: Run full verification** + +```powershell +git diff --check +powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-business-goals-smoke.ps1 +go test ./... +go build ./cmd/isphere-mcp +powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-go-mcp.ps1 +``` + +- [ ] **Step 3: Write release-candidate report** + +The report must state the business status using these exact categories: + +```text +已完成:可直接给数字员工用 +可预览:数字员工可规划但不会真实发出/下载 +阻断:需要在线可登录环境返回证据 +``` + +- [ ] **Step 4: Commit and push** + +```powershell +git status --short +git add docs\go-mcp-runbook.md docs\reports\2026-07-10-r6f-r14-release-candidate.md docs\source-discovery\capability-source-matrix.md docs\current-status-card.md scripts\package-send-sandbox-gate.ps1 scripts\verify-send-sandbox-gate-package.ps1 scripts\package-send-file-sandbox-gate.ps1 scripts\verify-send-file-sandbox-gate-package.ps1 +git commit -m "docs: publish r6f r14 release candidate status" +git push gitea main +``` + +**Acceptance gate:** 用户可以审核一个清晰结论:哪些目标已经能给数字员工用,哪些只到 preview,哪些必须回到在线环境采证。 + +--- + +## Expected Business State After R14 + +| Goal | Expected state after local-only rounds | If strict online evidence arrives during R6k/R9 | +| --- | --- | --- | +| 搜索联系人 | 已完成,可给数字员工用 | unchanged | +| 搜索群组 | 已完成,可给数字员工用 | unchanged | +| 收消息 | 已完成基础能力;增加对账辅助 | 可进一步判断是否切换 default auto merge | +| 发消息 | preview/idempotency/audit/gate 完整;生产默认阻断 | 可进入 gated connector production path | +| 收文件 | list 完整;download preview 能解释阻断 | 若 R10 映射成立,可进入真实 download gate | +| 发文件 | preview/idempotency/audit/tool surface 完整;生产默认阻断 | 若 R9 返回包通过,可进入 upload connector path | + +## Stop Conditions That Require User Input + +- R6k strict-v2 returned send package missing, and remaining desired work is real production send. +- R9 returned send-file package missing, and remaining desired work is real upload/send file. +- R10 cannot map received-file metadata to cache/download and user wants real download rather than preview/blocked explanation. +- A future online package contains raw sensitive material that cannot be summarized into committed docs without user choosing a redaction policy. + +## Self-Review + +- Spec coverage: the plan covers search contacts, search groups, receive messages, send messages, receive files, and send files. +- Loop coverage: there are 15 ordered rounds from R6f through R14, each with files, commands, acceptance gates, commit messages, and push commands. +- Placeholder scan: the plan does not use placeholder markers; evidence-dependent branches have exact pass/fail behavior and concrete next actions. +- Type consistency: send-message connector types introduced in R6f are consumed by R6h, R6i, and R6l; file-send tool fields introduced in R7 are consumed by R8, R9, and R13.