52 KiB
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:mainunless the user explicitly asks for a new branch. - Code search rule from
AGENTS.md: usegit ls-files,ag,grep -R, or PowerShell file commands; do not userg. - 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; explicitsource_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
- Execute exactly one round at a time in the order below.
- At the start of each round, mark that round as
in_progressindocs/current-status-card.md. - Use TDD for code rounds: add failing tests first, run the focused failing test, implement the minimum, then run the full verification gate.
- 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. - 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.
- After every round, commit and push only the intended source/docs/scripts:
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.goandinternal/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:
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:
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 no real side-effect flags such as sent_message or typed_text.
- 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
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.
R6f Result:
- Added
internal/tools/send_message_connector.gowithSendMessageConnector,SendMessageConnectorRequest,SendMessageConnectorResult, and result normalization. - Added
RegisterISphereSendMessageToolWithStateAndConnectorfor injected test-only connector execution. - Added fake connector accepted/failed tests proving accepted/failed connector outcomes map into structured response and redacted audit metadata.
- Default server registration still uses no connector;
execution_mode="production"remains blocked withproduction_send_enabled=false. - No helper send operation, UI click/type, network replay, hook, upload, or real production send was introduced.
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 -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:
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
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.
R6g Result:
- Added
scripts/test-verify-send-audit-idempotency.ps1and confirmed the red test failed before the verifier existed. - Added
scripts/verify-send-audit-idempotency.ps1to summarize redacted audit/idempotency JSONL files with sanitized counts and booleans. - Diagnostic detects duplicate same-key/same-target/content replay, conflict same-key/different-content replay, and raw field leaks.
- Added
TestISphereSendMessageAuditRedactionIsStableto prove file-backed audit JSONL does not store raw message body or raw idempotency key. - No iSphere login, real send, helper action, hook, upload, or network replay was introduced.
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.
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
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.
R6h Result:
- Added
internal/tools/send_message_gate.gowithSendMessageGatePolicyandLoadSendMessageGatePolicy. - Added table tests for
send_disabled_by_default,send_evidence_missing,send_connector_missing, andsend_gate_passed. - Default
isphere_send_messageproduction-blocked responses and audit events now includegate_reason_codeandgate_reason_message. ISPHERE_SEND_PRODUCTION_ENABLED=1is recognized by policy tests, but the default MCP runtime still has no real connector and reportsproduction_send_enabled=false.
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
disabledanddry_run_contract; any other mode returnsbroute_mode_blocked. -
Step 1: Write disabled-mode test
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
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.
R6i Result:
- Added
internal/tools/send_message_broute_adapter.gowithBRouteSendAdapterConfigandNewBRouteSendMessageConnector. - Added disabled-mode and dry-run-contract tests.
disabledreturnsbroute_mode_blocked;dry_run_contractvalidates request shape and returnsbroute_dry_run_only.- The adapter shell does not start a sidecar, load DLLs, click/type UI, replay network traffic, or upload files.
- Production
isphere_send_messageremains blocked in the default MCP runtime.
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 -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:
param([string]$ZipPath, [switch]$StrictV2)
-StrictV2 exits non-zero if any required returned field is absent.
- Step 4: Verify and commit
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.
R6j Result:
scripts\verify-send-sandbox-gate-package.ps1first failed against the old package becauseRETURN-CHECKLIST-V2.mdwas missing.scripts\package-send-sandbox-gate.ps1now emitsRETURN-CHECKLIST-V2.md,SANDBOX-SEND-INPUTS.schema.json,CREATE-RETURN-ZIP.ps1, and strict-v2 fields inSANDBOX-SEND-INPUTS.template.json.CREATE-RETURN-ZIP.ps1refuses to build a return zip when after-recorder output, strict-v2 timestamps, success evidence, or no-duplicate confirmation are missing.scripts\validate-returned-send-sandbox-package.ps1now supports-ZipPathand-StrictV2.- Existing partial package validation remains
partial_use_only; strict-v2 validation exits non-zero for that partial package.
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
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
$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
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.
R6k Result:
- Scanned
C:\Users\zhaoy\Downloadsfor recent zip files. - No new strict-v2 returned send-sandbox package was found.
- Re-validated
C:\Users\zhaoy\Downloads\1631.zipwith-ZipPathand-StrictV2; strict-v2 failed as expected. - Created
docs/source-discovery/2026-07-10-returned-send-sandbox-analysis-v2.md. - Decision:
r6d_gate_pass=false,strict_v2_pass=false; default production send remains blocked.
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 withgate_reason_code="send_evidence_missing". -
If
r6d_gate_pass=true: add tests that production can only run through an injected connector withISPHERE_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
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.
R6l Result:
- R6k found
strict_v2_pass=false, so R6l used the blocked branch. - Added
TestISphereSendMessageProductionGateBlockedWithoutStrictEvidence, proving production remains blocked withgate_reason_code="send_evidence_missing"whenISPHERE_SEND_PRODUCTION_ENABLED=1but strict evidence is missing. - Added
TestISphereSendMessageProductionRequiresEvidenceAndConnectoras an explicit skipped test with reasonstrict v2 send evidence not present in this repository state. - Standard MCP smoke continues to report
production_send_enabled=false.
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_DIRrequired 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
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,不上传。
R7 Result:
- Added
internal/tools/isphere_send_file.goandinternal/tools/isphere_send_file_test.go. - Registered
isphere_send_fileas the tenth MCP tool. - Preview requires
ISPHERE_SEND_FILE_ALLOWED_DIR, validates target/idempotency/file existence/path boundary/SHA256/size, and returnssend_status="planned"withreal_send_attempted=false. - Production
execution_mode="production"remains structured blocked withproduction_send_enabled=falseand all send/upload/UI/network side-effect flags false. - Updated
scripts/verify-go-mcp.ps1andcmd/isphere-capability-smoketo assert 10 tools, send-file preview readiness, and send-file production disabled. - Verification passed:
git diff --check, focused send-file tests, 10-tool registration test,go test ./...,go build ./cmd/isphere-mcp,scripts\verify-go-mcp.ps1, andgo run ./cmd/isphere-capability-smoke.
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=truewith 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
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.
R8 Result:
- Added send-file audit and idempotency state with
ISPHERE_SEND_FILE_AUDIT_PATHandISPHERE_SEND_FILE_IDEMPOTENCY_PATH. - Duplicate same target/file/idempotency key now returns
duplicate_detected=trueand remains planned/no-send. - Reusing the same idempotency key for a different target or file hash returns
send_status="blocked"andconflict_detected=true. - Send-file audit/idempotency JSONL stores file hash, size, target ref, result, side-effect flags, and hashed idempotency key; it does not store raw file content, raw local file path, or raw idempotency key.
scripts/verify-go-mcp.ps1now assertssend_file_duplicate_detected=trueandsend_file_conflict_blocked=truewhile keepingsend_file_production_enabled=false.- Verification passed:
git diff --check,go test ./internal/tools -run TestISphereSendFile -v,go test ./...,go build ./cmd/isphere-mcp,scripts\verify-go-mcp.ps1, andgo run ./cmd/isphere-capability-smoke.
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 -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
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: 用户可以把一个包带到可登录环境采集发文件证据。
R9 Result:
- Added
scripts/package-send-file-sandbox-gate.ps1, which buildsruns/send-file-sandbox-gate-package.zip. - Added
scripts/verify-send-file-sandbox-gate-package.ps1; the first run failed before the builder existed, then passed after implementation. - Added
scripts/validate-returned-send-file-sandbox-package.ps1for future returned online/internal sandbox packages. - Added
docs/source-discovery/2026-07-10-send-file-sandbox-gate.md. - The package includes recorder binaries, capability smoke executable,
FILE-SEND-INPUTS.template.json, operator instructions,CREATE-FILE-RETURN-ZIP.ps1, andRUN-FILE-RECORDING-SUITE.bat. - Local verifier generated and validated the package; production
isphere_send_fileremains blocked. - Returned-package validator was checked with a temporary strict-pass fixture.
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:
-
-CacheRootaccepts an absolute operator-local cache root selected at runtime; fixture tests create the cache root under$env:TEMP. -
-MsgLibDband-SQLiteDllaccept copied DB/runtime paths when real copied DB is available; fixture tests do not require real DB files. -
-SanitizedOutputPathwrites 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:
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
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: 文件下载要么得到可实现映射,要么得到明确阻断证据。
R10 Result:
- Added v2 fixture scoring to
scripts/verify-file-cache-mapping.ps1. - Updated
scripts/test-verify-file-cache-mapping.ps1to prove exact filename/size/time gets score 100, source-id/size gets score 80, and filename-only gets score 40 without acceptance. - Fixture verification produced
records_checked=3,cache_candidates_checked=4,accepted_matches=2,ambiguous_matches=0,score_100_matches=1,score_80_matches=1,score_40_only_matches=1,raw_paths_printed=false, andfile_contents_read=false. - Created
docs/source-discovery/2026-07-10-file-download-mapping-v2.md. - Real production file download remains blocked because this proves resolver logic, not a real iSphere cache mapping from an online package.
R10a: Returned send-sent record diagnostic intake
Purpose: 用户在 R10 后提供了两个 send-sent-record-diagnostic 返回包。本插入轮次只做安全复核:判断是否能打开生产发消息门禁;不能则记录明确阻断。
Files:
-
Create:
scripts/validate-send-sent-record-diagnostic-package.ps1 -
Create:
scripts/test-validate-send-sent-record-diagnostic-package.ps1 -
Create:
docs/source-discovery/2026-07-10-returned-send-sent-record-diagnostic.md -
Modify:
docs/current-status-card.md -
Step 1: Write validator test first
Test covers a blocked fixture and a strict-pass fixture. Initial run failed before the validator existed.
- Step 2: Implement safe validator
Validator accepts -ZipPath, emits booleans/counts only, and supports -Strict non-zero exit when strict sent-record evidence is missing.
- Step 3: Validate returned packages
Both returned packages produced decision="manual_success_without_machine_sent_record" and strict_send_record_pass=false.
- Step 4: Document decision
Created docs/source-discovery/2026-07-10-returned-send-sent-record-diagnostic.md. Production send remains blocked.
R10a Result:
- Package 1 and package 2 both have before/after runs and operator success.
- Neither package has machine-verifiable after sent-record/content-hash evidence.
- Both show
after_offline_blocked=true. production_send_unlock_recommended=false; defaultisphere_send_messageremains production-blocked.
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
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。
R11 Result:
- Actual implementation files are
internal/tools/isphere_files.goandinternal/tools/isphere_files_test.go. - Added structured download preview blocked response for missing mapping.
- Added injected fixture resolver test for planned preview.
- Standard MCP smoke now reports
receive_file_download_preview_blocked=true. - Real file copy/download remains blocked; preview reads no file contents and returns no raw paths.
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
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 不改变。
R12 Result:
- Added pure Go reconciliation helper and tests.
- Added
scripts/verify-receive-source-reconciliation.ps1 -UseFixture. - Fixture output is sanitized and keeps
default_receive_routing_changed=false. - Default receive routing remains PacketReader/log-backed; no
automerge was enabled.
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:
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
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 -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
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:
已完成:可直接给数字员工用
可预览:数字员工可规划但不会真实发出/下载
阻断:需要在线可登录环境返回证据
- Step 4: Commit and push
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.