chore: package send sandbox evidence gate

This commit is contained in:
zhaoyilun
2026-07-10 14:19:15 +08:00
parent ac3f10f53c
commit 8cdaa3c0bc
8 changed files with 569 additions and 17 deletions

View File

@@ -58,7 +58,7 @@ N13/N14/N15 are pre-business validation results. They can help identify UI eleme
## Current loop ## Current loop
Current loop: `R6b B-route preview/dry-run contract complete; next R6c online sandbox-send evidence gate`. Current loop: `R6c local online-sandbox evidence package prepared; next R6d returned sandbox evidence intake after package is run and returned`.
Plan file: `docs/superpowers/plans/2026-07-10-core-business-capabilities-roadmap.md`. Plan file: `docs/superpowers/plans/2026-07-10-core-business-capabilities-roadmap.md`.
@@ -115,6 +115,7 @@ Current loop output so far:
42. R5b/C30-live: analyzed returned live-probe package `isphere-live-probe-20260710-121958`; confirmed one x86 `IMPlatformClient.exe` `4.1.2.6842`, live server endpoint evidence, installed B-route DLL presence, active local `MsgLib.db` path shape, and A-route UIA selectors for search/draft/send/file menu. Production send remains blocked because the probe did not prove runtime entrypoint reachability or send success/ack mapping and the captured chat panels report an offline-send blocker. 42. R5b/C30-live: analyzed returned live-probe package `isphere-live-probe-20260710-121958`; confirmed one x86 `IMPlatformClient.exe` `4.1.2.6842`, live server endpoint evidence, installed B-route DLL presence, active local `MsgLib.db` path shape, and A-route UIA selectors for search/draft/send/file menu. Production send remains blocked because the probe did not prove runtime entrypoint reachability or send success/ack mapping and the captured chat panels report an offline-send blocker.
43. R6a/C31: added WinHelper `0.4.0` read-only `probe_send_entrypoints` and `probe_send_uia_controls`, wired the live recorder to emit `send_entrypoints_preflight.json` and `send_uia_controls_preflight.json`, and verified that copied install metadata reports all 5 required B-route entrypoints available while the synthetic UIA classifier reports `A_ROUTE_OFFLINE_BLOCKED`. Production send/file upload remains blocked pending preview/dry-run, dynamic observation, idempotency/audit, and one approved sandbox send. 43. R6a/C31: added WinHelper `0.4.0` read-only `probe_send_entrypoints` and `probe_send_uia_controls`, wired the live recorder to emit `send_entrypoints_preflight.json` and `send_uia_controls_preflight.json`, and verified that copied install metadata reports all 5 required B-route entrypoints available while the synthetic UIA classifier reports `A_ROUTE_OFFLINE_BLOCKED`. Production send/file upload remains blocked pending preview/dry-run, dynamic observation, idempotency/audit, and one approved sandbox send.
44. R6b: added `isphere_send_message` preview/dry-run only. The tool validates `target_type`, `target_id`, `content_sha256`, and `idempotency_key`, returns connector metadata `implatform-sidecar`/`preview`, appends redacted JSONL audit events without raw message body or raw idempotency key, and returns `send_status="blocked"` for production with all side-effect flags false. `verify-go-mcp.ps1` now verifies the 9-tool surface and proves `send_preview_tool_present=true` and `production_send_enabled=false`. 44. R6b: added `isphere_send_message` preview/dry-run only. The tool validates `target_type`, `target_id`, `content_sha256`, and `idempotency_key`, returns connector metadata `implatform-sidecar`/`preview`, appends redacted JSONL audit events without raw message body or raw idempotency key, and returns `send_status="blocked"` for production with all side-effect flags false. `verify-go-mcp.ps1` now verifies the 9-tool surface and proves `send_preview_tool_present=true` and `production_send_enabled=false`.
45. R6c: because the local machine cannot log in, prepared an online/internal sandbox evidence package instead of attempting local send. Added `scripts/package-send-sandbox-gate.ps1`, `scripts/verify-send-sandbox-gate-package.ps1`, and `docs/source-discovery/2026-07-10-send-sandbox-gate.md`; local verification generates `runs/send-sandbox-gate-package.zip` with the read-only recorder, before/after scripts, sandbox input template, SHA256 helper, expected-return checklist, and return-zip helper.
## Recompiled business roadmap ## Recompiled business roadmap
@@ -185,3 +186,14 @@ R6b B-route preview/dry-run contract is complete:
- `ISPHERE_SEND_AUDIT_PATH` can override the local audit JSONL path; the default path is ignored under `runs/send-audit/send-message-preview.jsonl`. - `ISPHERE_SEND_AUDIT_PATH` can override the local audit JSONL path; the default path is ignored under `runs/send-audit/send-message-preview.jsonl`.
- Standard MCP smoke now verifies 9 tools and confirms production send is not enabled. - Standard MCP smoke now verifies 9 tools and confirms production send is not enabled.
- Decision: next node is R6c online sandbox-send evidence gate. Do not implement production send until one approved sandbox send proves success/ack or sent-record evidence and duplicate idempotency protection. - Decision: next node is R6c online sandbox-send evidence gate. Do not implement production send until one approved sandbox send proves success/ack or sent-record evidence and duplicate idempotency protection.
R6c local preparation for online sandbox-send evidence is complete:
- Local login is still unavailable, so no local live send was attempted.
- Created `docs/source-discovery/2026-07-10-send-sandbox-gate.md`.
- Added package script `scripts/package-send-sandbox-gate.ps1`.
- Added verification script `scripts/verify-send-sandbox-gate-package.ps1`.
- Verified package output: `runs/send-sandbox-gate-package.zip`.
- Package requires an online/internal sandbox operator to run before/after read-only recorder captures around exactly one manual sandbox send.
- Production `isphere_send_message` and `isphere_send_file` remain blocked until the returned package proves success/ack or sent-record evidence and no second send.
- Next node: R6d returned sandbox evidence intake after the package is run and returned.

View File

@@ -354,7 +354,52 @@ powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\validate-n12r-retu
Keep the validation report outside `$captureRoot`; the returned package is evidence input and should remain unchanged. Keep the validation report outside `$captureRoot`; the returned package is evidence input and should remain unchanged.
## 13. Troubleshooting ## 13. R6c online sandbox send evidence package
The local development machine cannot log in to iSphere. Do not attempt to prove production send locally.
To prepare the online/internal sandbox evidence package:
```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\package-send-sandbox-gate.ps1
```
Verify the package locally:
```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-send-sandbox-gate-package.ps1
```
Expected output includes:
```json
{
"ok": true,
"local_login_required": false,
"online_sandbox_required": true,
"production_send_enabled": false
}
```
Generated package:
```text
runs\send-sandbox-gate-package.zip
```
Send that zip to the online/internal sandbox operator. The operator must follow `OPERATOR-STEPS.md` inside the package:
1. fill `SANDBOX-SEND-INPUTS.json`;
2. run `run-before-recorder.bat`;
3. manually send exactly one sandbox message in the official client;
4. fill `success_ack_or_sent_record`;
5. run `run-after-recorder.bat`;
6. run `make-return-zip.ps1`;
7. return the generated zip.
Production `isphere_send_message` remains blocked until the returned package is analyzed.
## 14. Troubleshooting
- If C# helper build fails, run `scripts\build-win-helper.ps1` directly and check for missing .NET Framework reference assemblies. - If C# helper build fails, run `scripts\build-win-helper.ps1` directly and check for missing .NET Framework reference assemblies.
- If `win_helper_version` fails, rerun `powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-win-helper.ps1` first. - If `win_helper_version` fails, rerun `powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-win-helper.ps1` first.

View File

@@ -0,0 +1,109 @@
# Send Sandbox Gate
Date: 2026-07-10
Node: R6c online sandbox-send evidence gate
## Local constraint
The local development environment cannot log in to iSphere / IMPlatformClient because it has no reachable service endpoint. Therefore R6c cannot be completed by a local live send.
Decision for this loop: prepare a returnable online sandbox evidence package instead of attempting any local send.
## Package produced
Script:
```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\package-send-sandbox-gate.ps1
```
Verification:
```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-send-sandbox-gate-package.ps1
```
Generated package:
```text
runs/send-sandbox-gate-package/
runs/send-sandbox-gate-package.zip
```
The package embeds the existing read-only live probe recorder under:
```text
recorder/ISphereLiveProbeRecorder.exe
recorder/variants/ISphereLiveProbeRecorder-x86.exe
```
## Package files
| File | Purpose |
| --- | --- |
| `README.txt` | Explains local login limitation, online sandbox requirement, and one-send rule. |
| `OPERATOR-STEPS.md` | Step-by-step operator procedure. |
| `SANDBOX-SEND-INPUTS.template.json` | Template for sandbox target, content hash, idempotency key, and observed result. |
| `EXPECTED-RETURN-FILES.txt` | Return package acceptance checklist. |
| `run-before-recorder.bat` | Runs read-only recorder before the manual sandbox send. |
| `run-after-recorder.bat` | Runs read-only recorder after the manual sandbox send. |
| `compute-content-hash.ps1` | Computes SHA256 for the exact test message body. |
| `make-return-zip.ps1` | Creates the return zip after before/after recordings and input JSON are present. |
## Operator rules
The package encodes these explicit markers:
```text
LOCAL_LOGIN_UNAVAILABLE
ONLINE_SANDBOX_REQUIRED
MANUAL_ONE_SEND_ONLY
DO_NOT_SEND_SECOND_TIME
content_sha256
idempotency_key
success_ack_or_sent_record
```
Required online procedure:
1. Log in normally on an online/internal sandbox machine.
2. Fill `SANDBOX-SEND-INPUTS.json` from the template.
3. Run `run-before-recorder.bat`.
4. Manually send exactly one message to the declared sandbox target.
5. Record `success_ack_or_sent_record`.
6. Do not send a second copy.
7. Run `run-after-recorder.bat`.
8. Run `make-return-zip.ps1`.
9. Return the generated zip.
## Safety boundary
The package does not:
- send messages automatically;
- upload files;
- click or type;
- inject or hook;
- capture network traffic;
- enable production send.
The only send action is the operator's one manual sandbox send inside the official client.
## Acceptance gate
R6c can pass only after a returned package proves all of the following:
- explicit sandbox target;
- exactly one content hash;
- exactly one idempotency key;
- observed success/ack or local sent-record evidence;
- confirmation that no second send was made;
- before/after read-only recorder outputs are present.
## Current decision
R6c local-preparation slice is complete, but the full online evidence gate is not passed yet.
Production `isphere_send_message` remains blocked.
Next node: **R6d returned sandbox evidence intake** after the user returns the generated package.

View File

@@ -344,17 +344,17 @@ connector_stage = "preview" | "sandbox_send" | "production"
## Next implementation loop ## Next implementation loop
R6b B-route preview/dry-run contract is now complete. `isphere_send_message` is registered as preview/dry-run only, validates target/content-hash/idempotency input, appends redacted audit JSONL, and blocks production with all side-effect flags false. R6c local preparation is now complete. Because the local development machine cannot log in, the online sandbox-send gate was converted into a returnable evidence package rather than a local live-send attempt.
Next node should be **R6c online sandbox-send evidence gate**: Next node should be **R6d returned sandbox evidence intake** after the package is run in an online/internal sandbox and returned:
1. Prepare or collect one operator-approved sandbox send evidence package. 1. Check completed `SANDBOX-SEND-INPUTS.json`.
2. Use one explicit sandbox target, one content hash, and one idempotency key. 2. Check before/after read-only recorder outputs.
3. Observe success/ack or local sent-record evidence. 3. Confirm one explicit sandbox target, one content hash, and one idempotency key.
4. Prove duplicate retry with the same idempotency key is locally rejected before any second send. 4. Confirm `success_ack_or_sent_record`.
5. Capture method names, argument shape, IDs/hashes, status booleans, and refs only. 5. Confirm no second send was made.
6. Do not enable production `send_message_after_approval` until the evidence gate passes. 6. Do not enable production `send_message_after_approval` until returned evidence passes.
## Current status ## Current status
B is selected as the primary send/file-send route. Runtime discovery, entrypoint metadata preflight, and preview/dry-run audit are in place. A remains the fallback and now has concrete UIA selector/offline-blocker classifier support. Production send remains blocked until dynamic observation and one approved sandbox send prove success/ack behavior. B is selected as the primary send/file-send route. Runtime discovery, entrypoint metadata preflight, preview/dry-run audit, and the online sandbox evidence package are in place. A remains the fallback and now has concrete UIA selector/offline-blocker classifier support. Production send remains blocked until the returned sandbox package proves success/ack behavior.

View File

@@ -6,6 +6,7 @@ Evidence index: `docs/source-discovery/2026-07-09-n12-pre-zyl-index.md`
Schema notes: `docs/source-discovery/2026-07-09-n12-pre-zyl-schema-notes.md` Schema notes: `docs/source-discovery/2026-07-09-n12-pre-zyl-schema-notes.md`
Returned live probe: `docs/source-discovery/2026-07-10-returned-live-probe-analysis.md` Returned live probe: `docs/source-discovery/2026-07-10-returned-live-probe-analysis.md`
Send connector preflight: `docs/source-discovery/2026-07-10-send-connector-preflight.md` Send connector preflight: `docs/source-discovery/2026-07-10-send-connector-preflight.md`
Send sandbox gate: `docs/source-discovery/2026-07-10-send-sandbox-gate.md`
## Source priority ## Source priority
@@ -24,8 +25,8 @@ Send connector preflight: `docs/source-discovery/2026-07-10-send-connector-prefl
| `isphere_search_contacts` | bare sender/receiver JIDs extracted from normalized log-backed messages loaded from configured PacketReader file or directory source | validated copied `MsgLib.db` tables: `TD_Roster`, `TD_CustomEffigy`, `tblRecent`, `tblChatLevel`, `tblPersonMsg`; decrypted roster/contact stanzas from `Smark.SendReceive`; UIA helper source if display names are still missing | `docs/source-discovery/2026-07-10-msglib-readonly-schema-extraction.md`; `internal/isphere/contacts_test.go`; `internal/tools/isphere_contacts_test.go` | C34 documents optional MsgLib contact display enrichment; R2 adds deterministic exact-match-first ranking, case-insensitive de-duplication across log/MsgLib candidates, and preserves `source`/`raw_ref` | core contact search complete; optional richer fields later | | `isphere_search_contacts` | bare sender/receiver JIDs extracted from normalized log-backed messages loaded from configured PacketReader file or directory source | validated copied `MsgLib.db` tables: `TD_Roster`, `TD_CustomEffigy`, `tblRecent`, `tblChatLevel`, `tblPersonMsg`; decrypted roster/contact stanzas from `Smark.SendReceive`; UIA helper source if display names are still missing | `docs/source-discovery/2026-07-10-msglib-readonly-schema-extraction.md`; `internal/isphere/contacts_test.go`; `internal/tools/isphere_contacts_test.go` | C34 documents optional MsgLib contact display enrichment; R2 adds deterministic exact-match-first ranking, case-insensitive de-duplication across log/MsgLib candidates, and preserves `source`/`raw_ref` | core contact search complete; optional richer fields later |
| `isphere_search_groups` | decrypted `PacketReader.ProcessPacket` `type="groupchat"` stanzas and conference/MUC JIDs loaded from configured PacketReader file or directory source | validated copied `MsgLib.db` tables: `tblMsgGroupPersonMsg`, `TD_WorkGroupAuth`, `tblRecent`; UIA helper source if group display names are still missing | `docs/source-discovery/2026-07-10-msglib-readonly-schema-extraction.md`; `internal/isphere/groups_test.go`; `internal/tools/isphere_groups_test.go`; C9 ignored-log scan: PacketReader 86 groupchat/86 conference hits; Smark 24 groupchat/658 conference/631 muc hits | C34 documents optional MsgLib group display enrichment; R2 adds deterministic exact-match-first ranking, case-insensitive de-duplication across log/MsgLib candidates, and preserves `source`/`raw_ref` | core group search complete; optional member/owner hierarchy later | | `isphere_search_groups` | decrypted `PacketReader.ProcessPacket` `type="groupchat"` stanzas and conference/MUC JIDs loaded from configured PacketReader file or directory source | validated copied `MsgLib.db` tables: `tblMsgGroupPersonMsg`, `TD_WorkGroupAuth`, `tblRecent`; UIA helper source if group display names are still missing | `docs/source-discovery/2026-07-10-msglib-readonly-schema-extraction.md`; `internal/isphere/groups_test.go`; `internal/tools/isphere_groups_test.go`; C9 ignored-log scan: PacketReader 86 groupchat/86 conference hits; Smark 24 groupchat/658 conference/631 muc hits | C34 documents optional MsgLib group display enrichment; R2 adds deterministic exact-match-first ranking, case-insensitive de-duplication across log/MsgLib candidates, and preserves `source`/`raw_ref` | core group search complete; optional member/owner hierarchy later |
| `isphere_receive_files` | decrypted `PacketReader.ProcessPacket` file-transfer message stanzas via `internal/isphere.ListFilesFromMessages` and `isphere_receive_files` list mode; configured PacketReader file or directory source; `zyl\importal\<hash>` cache entries remain unlinked | MsgLib `TD_ReceiveFileRecord` safe metadata through explicit copied-DB path; decrypted `Smark.SendReceive` and `SaveToDB` traces for file-reference reconciliation; future UIA/client connector for download | `docs/source-discovery/2026-07-09-n12-pre-zyl-schema-notes.md#c12-file-transfer-evidence-precheck`; `docs/source-discovery/2026-07-10-file-download-mapping-precheck.md`; `docs/source-discovery/2026-07-10-file-cache-mapping-diagnostic.md` | C22 verifies safe file-list contract args; R3 confirms list metadata is available; R3b diagnostic finds 0 current cache/archive matches, so R4 remains blocked | blocked pending better cache evidence or UI/client download connector | | `isphere_receive_files` | decrypted `PacketReader.ProcessPacket` file-transfer message stanzas via `internal/isphere.ListFilesFromMessages` and `isphere_receive_files` list mode; configured PacketReader file or directory source; `zyl\importal\<hash>` cache entries remain unlinked | MsgLib `TD_ReceiveFileRecord` safe metadata through explicit copied-DB path; decrypted `Smark.SendReceive` and `SaveToDB` traces for file-reference reconciliation; future UIA/client connector for download | `docs/source-discovery/2026-07-09-n12-pre-zyl-schema-notes.md#c12-file-transfer-evidence-precheck`; `docs/source-discovery/2026-07-10-file-download-mapping-precheck.md`; `docs/source-discovery/2026-07-10-file-cache-mapping-diagnostic.md` | C22 verifies safe file-list contract args; R3 confirms list metadata is available; R3b diagnostic finds 0 current cache/archive matches, so R4 remains blocked | blocked pending better cache evidence or UI/client download connector |
| `isphere_send_message` | B-route selected: running-client sidecar / in-process connector, preferring `AppContextManager.SendTxtMessageByJid(...)` or `MessageScheduling.SendChatMessage(...)` over the unimplemented `SendP2PMessage(...)` interface path; current MCP tool exposes preview/dry-run only | A-route fallback: constrained UI/RPA wrapper only if B cannot pass runtime probe, entrypoint availability, dynamic observation, and idempotent dry-run gates; network/protocol replay remains deferred | `docs/source-discovery/2026-07-10-send-message-source-precheck.md`; `docs/source-discovery/2026-07-10-send-message-connector-selection.md`; `docs/source-discovery/2026-07-10-send-sidecar-b-first-plan.md`; `docs/source-discovery/2026-07-10-returned-live-probe-analysis.md`; `docs/source-discovery/2026-07-10-send-connector-preflight.md`; `internal/tools/isphere_send_message_test.go` | R6b registers `isphere_send_message` preview/dry-run only. Preview validates target/content hash/idempotency, returns `send_status="planned"`, connector metadata, redacted audit metadata, and all side-effect flags false. Production returns `send_status="blocked"` and `production_send_enabled=false` in verification. No real send, success/ack, or production idempotency behavior is proven. | next: R6c online sandbox-send evidence gate; production send remains blocked | | `isphere_send_message` | B-route selected: running-client sidecar / in-process connector, preferring `AppContextManager.SendTxtMessageByJid(...)` or `MessageScheduling.SendChatMessage(...)` over the unimplemented `SendP2PMessage(...)` interface path; current MCP tool exposes preview/dry-run only | A-route fallback: constrained UI/RPA wrapper only if B cannot pass runtime probe, entrypoint availability, dynamic observation, and idempotent dry-run gates; network/protocol replay remains deferred | `docs/source-discovery/2026-07-10-send-message-source-precheck.md`; `docs/source-discovery/2026-07-10-send-message-connector-selection.md`; `docs/source-discovery/2026-07-10-send-sidecar-b-first-plan.md`; `docs/source-discovery/2026-07-10-returned-live-probe-analysis.md`; `docs/source-discovery/2026-07-10-send-connector-preflight.md`; `docs/source-discovery/2026-07-10-send-sandbox-gate.md`; `internal/tools/isphere_send_message_test.go` | R6c prepares the online sandbox evidence package because local login is unavailable. `runs/send-sandbox-gate-package.zip` can collect before/after read-only recorder outputs around one manual sandbox send, but no returned evidence has been analyzed yet. Production remains blocked. | next: R6d returned sandbox evidence intake after user returns the package |
| `isphere_send_file` | B-route selected after send-message probe: managed offline-file path through `OffLineFileSend.sendFile()`, `IMPP.Service*.dll` `IFileTransfer.FileUpload(...)`, and `Chat.sendFileMessage(...)` | A-route fallback: constrained UI/RPA upload/send-file flow; native `TcpFileTransfer.dll` is a later candidate only after managed path is rejected | `docs/source-discovery/2026-07-10-send-message-source-precheck.md`; `docs/source-discovery/2026-07-10-send-message-connector-selection.md`; `docs/source-discovery/2026-07-10-send-sidecar-b-first-plan.md`; `docs/source-discovery/2026-07-10-returned-live-probe-analysis.md`; `docs/source-discovery/2026-07-10-send-connector-preflight.md` | R6a/C31 metadata confirms `FileTransfer.FileUpload`, `FileTransfer.AsynFileUpload`, `OffLineFileSend.sendFile`, and `Chat.sendFileMessage` candidates. File-send remains blocked behind message-send sandbox evidence plus later upload/file-message dry-run evidence. | next: wait for R6c sandbox-send evidence, then file-send upload preflight | | `isphere_send_file` | B-route selected after send-message probe: managed offline-file path through `OffLineFileSend.sendFile()`, `IMPP.Service*.dll` `IFileTransfer.FileUpload(...)`, and `Chat.sendFileMessage(...)` | A-route fallback: constrained UI/RPA upload/send-file flow; native `TcpFileTransfer.dll` is a later candidate only after managed path is rejected | `docs/source-discovery/2026-07-10-send-message-source-precheck.md`; `docs/source-discovery/2026-07-10-send-message-connector-selection.md`; `docs/source-discovery/2026-07-10-send-sidecar-b-first-plan.md`; `docs/source-discovery/2026-07-10-returned-live-probe-analysis.md`; `docs/source-discovery/2026-07-10-send-connector-preflight.md`; `docs/source-discovery/2026-07-10-send-sandbox-gate.md` | R6a/C31 metadata confirms `FileTransfer.FileUpload`, `FileTransfer.AsynFileUpload`, `OffLineFileSend.sendFile`, and `Chat.sendFileMessage` candidates. File-send remains blocked behind returned message-send sandbox evidence plus later upload/file-message dry-run evidence. | next: wait for R6d, then file-send upload preflight |
## Stage C entry rule ## Stage C entry rule
@@ -42,4 +43,4 @@ Start Stage C with a narrow log-backed `isphere_receive_messages` source abstrac
## Deferred source work ## Deferred source work
`MsgLib.db` has a validated copied read-only open path through the bundled 32-bit `System.Data.SQLite.dll` and password `123`. C27 adds `MsgLibReadSidecar` as the bounded x86 .NET reader boundary, C28 adds the Go `internal/msglib` process client, C29 adds bounded `display_entities` extraction, C30 wires it as optional contact/group MCP enrichment, C31 proves real copied-DB MCP enrichment with sanitized output, C32 reuses it for receive-message display fields, C33 proves that receive display path through a real copied-DB MCP smoke without printing entity values, C34 documents the operator setup/verification path, C35 maps MsgLib message tables to receive-message contract fields without reading row values, C36 adds metadata-only `message_sources` readiness, C37 defines the bounded DB-backed receive-source design, C38 implements sidecar/Go-wrapper `list_messages` with sanitized verification, C39 adds a Go adapter from MsgLib list results to the receive-message domain model, C40 adds explicit tool-level `msglib_readonly` source selection, C41 wires env-configured explicit MsgLib receive with optional sanitized smoke, R1 documents source reconciliation keys, and R2 hardens contact/group search ranking and de-duplication. The active roadmap is `docs/superpowers/plans/2026-07-10-core-business-capabilities-roadmap.md`; next step is R6c online sandbox-send evidence gate. `MsgLib.db` has a validated copied read-only open path through the bundled 32-bit `System.Data.SQLite.dll` and password `123`. C27 adds `MsgLibReadSidecar` as the bounded x86 .NET reader boundary, C28 adds the Go `internal/msglib` process client, C29 adds bounded `display_entities` extraction, C30 wires it as optional contact/group MCP enrichment, C31 proves real copied-DB MCP enrichment with sanitized output, C32 reuses it for receive-message display fields, C33 proves that receive display path through a real copied-DB MCP smoke without printing entity values, C34 documents the operator setup/verification path, C35 maps MsgLib message tables to receive-message contract fields without reading row values, C36 adds metadata-only `message_sources` readiness, C37 defines the bounded DB-backed receive-source design, C38 implements sidecar/Go-wrapper `list_messages` with sanitized verification, C39 adds a Go adapter from MsgLib list results to the receive-message domain model, C40 adds explicit tool-level `msglib_readonly` source selection, C41 wires env-configured explicit MsgLib receive with optional sanitized smoke, R1 documents source reconciliation keys, and R2 hardens contact/group search ranking and de-duplication. The active roadmap is `docs/superpowers/plans/2026-07-10-core-business-capabilities-roadmap.md`; next step is R6d returned sandbox evidence intake after the online package is returned.

View File

@@ -28,9 +28,9 @@
| 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 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 | | 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 | | 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 and A-route UIA/offline-state classifier support. | Need dynamic observation and one approved sandbox send before production. | next R6c online sandbox-send evidence gate | | Send messages | `isphere_send_message` preview/dry-run is registered; R6a confirms B-route entrypoint metadata; R6c local package for online sandbox evidence is prepared. | Need returned online sandbox evidence proving success/ack and no duplicate send before production. | next R6d returned sandbox evidence intake |
| 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 | | 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 send-message sandbox evidence plus upload/file-transfer dry-run. | wait for R6c, then file-send upload preflight | | 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 |
## Node Sequence ## Node Sequence
@@ -474,11 +474,45 @@ powershell -NoProfile -ExecutionPolicy Bypass -File scripts\verify-go-mcp.ps1
**Stop condition:** **Stop condition:**
- If the environment cannot log in or cannot use a sandbox target, stop and request a returned sandbox evidence package rather than implementing production send. - If the environment cannot log in or cannot use a sandbox target, stop and request a returned sandbox evidence package rather than implementing production send.
**R6c Result:**
- Created `docs/source-discovery/2026-07-10-send-sandbox-gate.md`.
- Added `scripts/package-send-sandbox-gate.ps1`.
- Added `scripts/verify-send-sandbox-gate-package.ps1`.
- Generated and locally verified `runs/send-sandbox-gate-package.zip`.
- The package includes the read-only live probe recorder plus before/after recorder batch files, sandbox input template, SHA256 helper, expected-return checklist, and return-zip script.
- This local slice does not pass the online evidence gate because the local machine cannot log in. It only prepares the exact package needed for an online/internal sandbox operator.
- Decision: next node is R6d returned sandbox evidence intake after the package is run and returned. Production `isphere_send_message` and `isphere_send_file` remain blocked.
---
### R6d: Returned sandbox evidence intake
**Purpose:** Analyze the returned R6c online sandbox-send evidence package and decide whether production send implementation can start.
**Files:**
- Create: `docs/source-discovery/2026-07-10-returned-send-sandbox-analysis.md`
- Modify: this roadmap, `docs/current-status-card.md`, and `docs/source-discovery/capability-source-matrix.md`.
**Evidence to check:**
- Completed `SANDBOX-SEND-INPUTS.json`.
- Before recorder output.
- After recorder output.
- `success_ack_or_sent_record`.
- Confirmation that no second send was made.
**Acceptance gate:**
- If success/ack or sent-record evidence and duplicate-send prevention are proven, allow the later R6 production implementation node to start.
- If any item is missing, keep production send blocked and request a corrected returned package.
**Stop condition:**
- Do not infer success from screenshots or vague operator notes alone. Require the structured returned package fields and before/after recorder output.
--- ---
### R6: Send-message preview and production path ### R6: Send-message preview and production path
**Purpose:** Implement production `isphere_send_message` after R6c proves success/ack behavior. **Purpose:** Implement production `isphere_send_message` after R6d analyzes returned sandbox evidence and proves success/ack behavior.
**Files:** **Files:**
- Create or modify: `internal/tools/isphere_send_message.go` - Create or modify: `internal/tools/isphere_send_message.go`

View File

@@ -0,0 +1,271 @@
param(
[string]$OutputDir = "runs/send-sandbox-gate-package",
[switch]$NoZip
)
$ErrorActionPreference = "Stop"
Set-StrictMode -Version Latest
$repo = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot "..")).Path
$packageDir = Join-Path $repo $OutputDir
$recorderRelative = Join-Path $OutputDir "recorder"
$recorderDir = Join-Path $packageDir "recorder"
$zipPath = "$packageDir.zip"
if (Test-Path -LiteralPath $packageDir) {
Remove-Item -LiteralPath $packageDir -Recurse -Force
}
if (Test-Path -LiteralPath $zipPath) {
Remove-Item -LiteralPath $zipPath -Force
}
New-Item -ItemType Directory -Force -Path $packageDir | Out-Null
& powershell -NoProfile -ExecutionPolicy Bypass -File (Join-Path $repo "scripts\package-live-probe-recorder.ps1") -OutputDir $recorderRelative -NoZip
if ($LASTEXITCODE -ne 0) {
throw "package-live-probe-recorder.ps1 failed with exit code $LASTEXITCODE"
}
if (-not (Test-Path -LiteralPath (Join-Path $recorderDir "ISphereLiveProbeRecorder.exe"))) {
throw "recorder package was not created under $recorderDir"
}
@'
R6c iSphere Send Sandbox Gate Package
LOCAL_LOGIN_UNAVAILABLE: the development machine cannot log in to iSphere.
ONLINE_SANDBOX_REQUIRED: run this package only on a machine/network where iSphere/IMPlatformClient can log in.
Goal:
- collect one operator-approved sandbox-send evidence package;
- keep production send disabled in the repository;
- return enough evidence to decide whether production send work can start later.
What this package does:
- runs the read-only live probe recorder before the manual sandbox send;
- asks the operator to manually send exactly one sandbox message in the official client;
- runs the same read-only recorder after the manual send;
- zips the returned evidence folder.
What this package does not do:
- it does not send messages automatically;
- it does not click or type;
- it does not upload files;
- it does not hook or inject into the client;
- it does not enable production send.
Required rule:
- MANUAL_ONE_SEND_ONLY: send exactly one sandbox message manually.
- DO_NOT_SEND_SECOND_TIME: do not send a second message with the same content or idempotency key.
- Record content_sha256 and idempotency_key in SANDBOX-SEND-INPUTS.json.
- Fill success_ack_or_sent_record after the operator sees the sent message, ack, or local sent record.
Return:
- run make-return-zip.ps1 and send back the generated zip.
'@ | Set-Content -LiteralPath (Join-Path $packageDir "README.txt") -Encoding UTF8
@'
# R6c Online Sandbox Send Gate - Operator Steps
This package is for an online/internal sandbox only.
Markers:
- LOCAL_LOGIN_UNAVAILABLE: do not run this on the local development machine for proof.
- ONLINE_SANDBOX_REQUIRED: run on a machine where iSphere/IMPlatformClient can log in.
- MANUAL_ONE_SEND_ONLY: send exactly one test message manually.
- DO_NOT_SEND_SECOND_TIME: do not repeat the send with the same idempotency_key.
## Step 1 - Prepare inputs
1. Copy `SANDBOX-SEND-INPUTS.template.json` to `SANDBOX-SEND-INPUTS.json`.
2. Fill:
- `sandbox_target_type`
- `sandbox_target_id`
- `content_text_to_send`
- `idempotency_key`
3. Run:
```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File .\compute-content-hash.ps1 -Text "<exact content_text_to_send>"
```
4. Copy the printed SHA256 into `content_sha256`.
## Step 2 - Before-send recording
1. Open and log in to iSphere/IMPlatformClient normally.
2. Open the sandbox target conversation.
3. Do not send anything yet.
4. Run `run-before-recorder.bat`.
## Step 3 - Manual one-send action
1. In the official iSphere client, manually send exactly the `content_text_to_send`.
2. Send it only to the declared sandbox target.
3. Do not send a second copy.
4. Fill these fields in `SANDBOX-SEND-INPUTS.json`:
- `manual_send_started_at_local`
- `manual_send_finished_at_local`
- `operator_observed_success`
- `success_ack_or_sent_record`
- `do_not_send_second_time_confirmed`
## Step 4 - After-send recording
Run `run-after-recorder.bat`.
## Step 5 - Return package
Run:
```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File .\make-return-zip.ps1
```
Send back the generated zip file.
## Required returned evidence
The returned evidence should contain:
- before recorder output;
- after recorder output;
- completed `SANDBOX-SEND-INPUTS.json`;
- success_ack_or_sent_record text;
- confirmation that there was no second send.
The repository must keep production send disabled until this evidence is reviewed.
'@ | Set-Content -LiteralPath (Join-Path $packageDir "OPERATOR-STEPS.md") -Encoding UTF8
@'
{
"gate": "R6c online sandbox-send evidence gate",
"LOCAL_LOGIN_UNAVAILABLE": true,
"ONLINE_SANDBOX_REQUIRED": true,
"MANUAL_ONE_SEND_ONLY": true,
"DO_NOT_SEND_SECOND_TIME": true,
"sandbox_target_type": "direct-or-group",
"sandbox_target_id": "<sandbox target id only>",
"content_text_to_send": "<exact one-time sandbox message text>",
"content_sha256": "<sha256 hex of content_text_to_send>",
"idempotency_key": "<one unique idempotency key>",
"manual_send_started_at_local": "",
"manual_send_finished_at_local": "",
"operator_observed_success": false,
"success_ack_or_sent_record": "",
"do_not_send_second_time_confirmed": false,
"notes": ""
}
'@ | Set-Content -LiteralPath (Join-Path $packageDir "SANDBOX-SEND-INPUTS.template.json") -Encoding UTF8
@'
Expected return files for R6c:
1. SANDBOX-SEND-INPUTS.json
- content_sha256 is filled.
- idempotency_key is filled.
- success_ack_or_sent_record is filled.
- MANUAL_ONE_SEND_ONLY is true.
- DO_NOT_SEND_SECOND_TIME is true.
- do_not_send_second_time_confirmed is true.
2. return-evidence/before/*
- Read-only recorder output before manual send.
3. return-evidence/after/*
- Read-only recorder output after manual send.
4. The generated zip from make-return-zip.ps1.
Decision rule:
- If success_ack_or_sent_record is missing, production send remains blocked.
- If duplicate/idempotency confirmation is missing, production send remains blocked.
- If the target was not an explicit sandbox target, production send remains blocked.
'@ | Set-Content -LiteralPath (Join-Path $packageDir "EXPECTED-RETURN-FILES.txt") -Encoding UTF8
@'
@echo off
setlocal
cd /d "%~dp0"
mkdir return-evidence 2>nul
mkdir return-evidence\before 2>nul
echo Running BEFORE read-only recorder...
recorder\ISphereLiveProbeRecorder.exe --record-live-probe --out "%~dp0return-evidence\before" --no-pause
echo Done. Check return-evidence\before.
pause
'@ | Set-Content -LiteralPath (Join-Path $packageDir "run-before-recorder.bat") -Encoding ASCII
@'
@echo off
setlocal
cd /d "%~dp0"
mkdir return-evidence 2>nul
mkdir return-evidence\after 2>nul
echo Running AFTER read-only recorder...
recorder\ISphereLiveProbeRecorder.exe --record-live-probe --out "%~dp0return-evidence\after" --no-pause
echo Done. Check return-evidence\after.
pause
'@ | Set-Content -LiteralPath (Join-Path $packageDir "run-after-recorder.bat") -Encoding ASCII
@'
param(
[Parameter(Mandatory=$true)]
[string]$Text
)
$bytes = [System.Text.Encoding]::UTF8.GetBytes($Text)
$sha = [System.Security.Cryptography.SHA256]::Create()
try {
$hash = $sha.ComputeHash($bytes)
-join ($hash | ForEach-Object { $_.ToString("x2") })
}
finally {
$sha.Dispose()
}
'@ | Set-Content -LiteralPath (Join-Path $packageDir "compute-content-hash.ps1") -Encoding ASCII
@'
$ErrorActionPreference = "Stop"
Set-StrictMode -Version Latest
$root = Split-Path -Parent $MyInvocation.MyCommand.Path
$inputs = Join-Path $root "SANDBOX-SEND-INPUTS.json"
if (-not (Test-Path -LiteralPath $inputs)) {
throw "missing SANDBOX-SEND-INPUTS.json; copy the template and fill it first"
}
$before = Join-Path $root "return-evidence\before"
$after = Join-Path $root "return-evidence\after"
if (-not (Test-Path -LiteralPath $before)) {
throw "missing before recorder output: $before"
}
if (-not (Test-Path -LiteralPath $after)) {
throw "missing after recorder output: $after"
}
$stamp = Get-Date -Format "yyyyMMdd-HHmmss"
$zip = Join-Path $root ("isphere-send-sandbox-return-" + $stamp + ".zip")
$items = @(
$inputs,
(Join-Path $root "return-evidence"),
(Join-Path $root "EXPECTED-RETURN-FILES.txt")
)
Compress-Archive -Path $items -DestinationPath $zip -Force
[ordered]@{
ok = $true
zip = $zip
production_send_enabled = $false
online_sandbox_required = $true
} | ConvertTo-Json -Depth 4 -Compress
'@ | Set-Content -LiteralPath (Join-Path $packageDir "make-return-zip.ps1") -Encoding ASCII
if (-not $NoZip) {
Compress-Archive -Path (Join-Path $packageDir "*") -DestinationPath $zipPath -Force
}
[ordered]@{
ok = $true
package_dir = (Resolve-Path -LiteralPath $packageDir).Path
recorder_dir = (Resolve-Path -LiteralPath $recorderDir).Path
zip = if (Test-Path -LiteralPath $zipPath) { (Resolve-Path -LiteralPath $zipPath).Path } else { $null }
local_login_required = $false
online_sandbox_required = $true
production_send_enabled = $false
} | ConvertTo-Json -Depth 4 -Compress

View File

@@ -0,0 +1,80 @@
param(
[string]$OutputDir = "runs/send-sandbox-gate-package"
)
$ErrorActionPreference = "Stop"
Set-StrictMode -Version Latest
$repo = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot "..")).Path
$packageScript = Join-Path $repo "scripts\package-send-sandbox-gate.ps1"
if (-not (Test-Path -LiteralPath $packageScript)) {
throw "missing package script: $packageScript"
}
$packageRoot = Join-Path $repo $OutputDir
$zipPath = "$packageRoot.zip"
if (Test-Path -LiteralPath $packageRoot) {
Remove-Item -LiteralPath $packageRoot -Recurse -Force
}
if (Test-Path -LiteralPath $zipPath) {
Remove-Item -LiteralPath $zipPath -Force
}
& powershell -NoProfile -ExecutionPolicy Bypass -File $packageScript -OutputDir $OutputDir
if ($LASTEXITCODE -ne 0) {
throw "package-send-sandbox-gate.ps1 failed with exit code $LASTEXITCODE"
}
$requiredFiles = @(
"README.txt",
"OPERATOR-STEPS.md",
"SANDBOX-SEND-INPUTS.template.json",
"EXPECTED-RETURN-FILES.txt",
"run-before-recorder.bat",
"run-after-recorder.bat",
"make-return-zip.ps1",
"recorder\ISphereLiveProbeRecorder.exe",
"recorder\variants\ISphereLiveProbeRecorder-x86.exe"
)
foreach ($relative in $requiredFiles) {
$path = Join-Path $packageRoot $relative
if (-not (Test-Path -LiteralPath $path)) {
throw "missing package file: $relative"
}
}
$readme = Get-Content -LiteralPath (Join-Path $packageRoot "README.txt") -Raw
$steps = Get-Content -LiteralPath (Join-Path $packageRoot "OPERATOR-STEPS.md") -Raw
$expected = Get-Content -LiteralPath (Join-Path $packageRoot "EXPECTED-RETURN-FILES.txt") -Raw
foreach ($needle in @(
"LOCAL_LOGIN_UNAVAILABLE",
"ONLINE_SANDBOX_REQUIRED",
"MANUAL_ONE_SEND_ONLY",
"DO_NOT_SEND_SECOND_TIME",
"content_sha256",
"idempotency_key",
"success_ack_or_sent_record"
)) {
if ($readme -notmatch [regex]::Escape($needle) -and $steps -notmatch [regex]::Escape($needle) -and $expected -notmatch [regex]::Escape($needle)) {
throw "package instructions missing required phrase: $needle"
}
}
if (-not (Test-Path -LiteralPath $zipPath)) {
throw "missing package zip: $zipPath"
}
$result = [ordered]@{
ok = $true
package_dir = (Resolve-Path -LiteralPath $packageRoot).Path
zip = (Resolve-Path -LiteralPath $zipPath).Path
required_file_count = $requiredFiles.Count
local_login_required = $false
online_sandbox_required = $true
production_send_enabled = $false
}
$result | ConvertTo-Json -Depth 4 -Compress