param( [string]$IlDir = "runs/offline-evidence-intake/zyl-qqfile-20260709/metadata/c28-il", [string]$OutputDir = "runs/send-upload-deep-dive", [string]$ReportPath = "docs/source-discovery/2026-07-12-send-upload-deep-dive.md", [string]$ValidationPath = "docs/source-discovery/2026-07-12-login-reachability-validation-items.md", [int]$WindowRadius = 5, [int]$MaxEvidencePerItem = 20 ) $ErrorActionPreference = "Stop" $repo = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot "..")).Path $ilDirFull = Join-Path $repo $IlDir $outDir = Join-Path $repo $OutputDir $reportFullPath = Join-Path $repo $ReportPath $validationFullPath = Join-Path $repo $ValidationPath New-Item -ItemType Directory -Force -Path $outDir | Out-Null New-Item -ItemType Directory -Force -Path (Split-Path -Parent $reportFullPath) | Out-Null New-Item -ItemType Directory -Force -Path (Split-Path -Parent $validationFullPath) | Out-Null $sources = @( @{ path = "IMPlatformClient.exe.il"; role = "main client; high-level send, file-send orchestration, runtime auth/service state" }, @{ path = "smack.dll.il"; role = "Chat/XMPP send implementation and file-message primitive" }, @{ path = "IMPP.Service.dll.il"; role = "file upload HTTP/service implementation" }, @{ path = "IMPP.ServiceBase.dll.il"; role = "FileUploadPara/FileUploadResult/FileTransferParaBase DTO contracts" }, @{ path = "IMPP.Interface.dll.il"; role = "IAppContextManager/IFileTransfer interface contracts" }, @{ path = "IMPP.Common.dll.il"; role = "ServiceManager/Auth DTOs and common runtime state" } ) $items = @( @{ key = "text_entry_app_context" title = "Text send high-level entry: AppContextManager.SendTxtMessageByJid" stage = "text_send" meaning = "Best business-level text-send candidate if reachable in logged-in runtime." must_verify = @("Reach AppContextManager instance", "Confirm target JID/chat type mapping", "Invoke dry/probe path without send first", "Correlate sent record/ack after real send test") patterns = @("AppContextManager::SendTxtMessageByJid", "\bSendTxtMessageByJid\b", "IAppContextManager::SendTxtMessageByJid") }, @{ key = "text_entry_scheduling" title = "Text send scheduling fallback: MessageScheduling.SendChatMessage" stage = "text_send" meaning = "Fallback text-send path if AppContextManager is hard to reach but Chat/Roster args exist." must_verify = @("Locate or construct RosterContactsArgs", "Locate Chat instance", "Confirm direct/group branch", "Correlate sent record/ack") patterns = @("MessageScheduling::SendChatMessage", "SendChatMessage\(") }, @{ key = "chat_low_level_send" title = "Low-level text primitive: Chat.SendMessage -> XMPPConnection.send" stage = "text_send" meaning = "Confirms final XMPP send primitive; not enough alone without authenticated Chat/XMPP state." must_verify = @("Locate Chat instance", "Locate authenticated XMPPConnection", "Confirm direct/group message payload", "Capture ack/sent-record") patterns = @("com\.vision\.smack\.Chat::SendMessage", "com\.vision\.smack\.XMPPConnection::send", "callvirt instance void com\.vision\.smack\.XMPPConnection::send") }, @{ key = "file_upload_para_base" title = "Upload parameter base: FileTransferParaBase" stage = "file_upload" meaning = "Defines host, port, domain, local path, timeout/cache and token fields required by upload/download DTOs." must_verify = @("Host", "Port", "Domain", "LocalFilePath", "Token", "Cache/timeout flags if present") patterns = @("FileTransferParaBase", "get_Host\(\)", "get_Port\(\)", "get_Domain\(\)", "get_LocalFilePath\(\)", "get_Token\(\)") }, @{ key = "file_upload_para" title = "Upload DTO: FileUploadPara" stage = "file_upload" meaning = "Concrete upload request DTO; adds IsImg/IsPublic and invokes FileTransferParaBase constructor." must_verify = @("Constructor arguments", "IsImg", "IsPublic", "Token value", "Local file path normalization") patterns = @("FileUploadPara", "FileUploadPara::\.ctor", "get_IsImg\(\)", "get_IsPublic\(\)", "set_IsImg", "set_IsPublic") }, @{ key = "file_upload_service" title = "Upload service: IFileTransfer/FileTransfer.FileUpload" stage = "file_upload" meaning = "Managed upload entry; returns FileUploadResult and internally writes multipart token/form data." must_verify = @("Reach IFileTransfer instance", "Call FileUpload with safe test file", "Capture FileUploadResult", "Capture HTTP server/token fields without leaking token") patterns = @("FileTransfer::FileUpload", "IFileTransfer::FileUpload", 'Content-Disposition: form-data; name="token"', "FileUploadResult") }, @{ key = "file_upload_auth" title = "Upload auth/server state: HTTPFileServer + Auth token" stage = "file_upload" meaning = "File upload depends on runtime server/auth state, not only a local file path." must_verify = @("HTTPFileServer host/port", "UnifiedAuthentication/Auth token", "Domain/resource", "Access token/client id if present") patterns = @("get_HTTPFileServer\(\)", "UnifiedAuthentication", "Auth::get_token", "accessToken", "clientId", "HTTPFileServer") }, @{ key = "file_upload_result" title = "Upload result: FileUploadResult.FileID and remote metadata" stage = "file_upload_result" meaning = "Server-issued FileID is required before a chat file message is meaningful." must_verify = @("FileID", "URL or remote folder if present", "size/name metadata", "failure code/message") patterns = @("FileUploadResult::get_FileID", "FileUploadResult::set_FileID", "FileUploadResult", "RemoteFolder", "get_Url", "get_URL") }, @{ key = "file_message_finalizer" title = "File-message finalization: trans_UploadCompleted / SendFileMessage / Chat.sendFileMessage" stage = "send_file_message" meaning = "After upload succeeds, the client constructs offline file-transfer content and sends chat file message." must_verify = @("trans_UploadCompleted receives FileID", "OFFLINE_FILE_TRANSFER payload fields", "Target Chat/JID", "Sent file record/ack") patterns = @("OffLineFileSend::trans_UploadCompleted", "MessageScheduling::SendFileMessage", "Chat::sendFileMessage", "OFFLINE_FILE_TRANSFER") }, @{ key = "target_chat_resolution" title = "Target resolution: JID -> Chat / ChatRoom" stage = "runtime_context" meaning = "Both text and file send need a target JID mapped to Chat or ChatRoom; group handling may differ." must_verify = @("Contact JID", "Group/conference JID", "ChatManager.GetChat", "ChatRoom branch and permissions") patterns = @("ChatManager::GetChat", "GetChat\(", "ChatRoom", "get_RosterJid", "get_ChatRoomJid", "get_HasSpeechAuthJid") }, @{ key = "runtime_owner" title = "Runtime owner: frmMain / IMPPManager / MessageCenter" stage = "runtime_context" meaning = "Likely owners of initialized logged-in state and message center callbacks." must_verify = @("Running frmMain", "IMPPManager.UserInfo", "MessageCenter", "Reconnect success state", "loaded assemblies") patterns = @("class IMPP\.Client\.frmMain", "IMPPManager::get_UserInfo", "GetMessageCenter\(\)", "MessageCenter", "OnReConnectionSucceed") } ) function Get-RelativePath([string]$Path) { if ($Path.StartsWith($repo, [System.StringComparison]::OrdinalIgnoreCase)) { return $Path.Substring($repo.Length).TrimStart([char[]]@('\', '/')) } return $Path } function Write-Utf8NoBomLf([string]$Path, [string]$Text) { $utf8NoBom = [System.Text.UTF8Encoding]::new($false) $normalized = $Text -replace "`r`n", "`n" $normalized = $normalized -replace "`r", "`n" $normalized = $normalized.TrimEnd() + "`n" [System.IO.File]::WriteAllText($Path, $normalized, $utf8NoBom) } function Get-EvidenceKind([string]$Line) { if ($Line -match "^\s*\.class\b") { return "type_definition" } if ($Line -match "^\s*\.method\b") { return "method_header" } if ($Line -match "^\s*\.field\b") { return "field" } if ($Line -match "^\s*\.property\b") { return "property" } if ($Line -match "\bend of method\b") { return "method_end" } if ($Line -match "\bnewobj\b") { return "constructor_call" } if ($Line -match "\bcall(?:virt)?\b") { return "callsite" } if ($Line -match "\bldstr\b") { return "string_literal" } if ($Line -match "^\s*(instance|static)\b.*\(") { return "signature_continuation" } return "reference" } function Get-MethodContext($Lines, [int]$Index) { $start = [Math]::Max(0, $Index - 120) for ($i = $Index; $i -ge $start; $i--) { if ($Lines[$i] -match "^\s*\.method\b") { $header = $Lines[$i].Trim() if (($i + 1) -lt $Lines.Count -and $Lines[$i + 1] -match "^\s+(instance|static|default)\b") { $header = ($header + " " + $Lines[$i + 1].Trim()) } return "L$($i + 1): $header" } } return "" } function Find-Evidence($Item) { $evidence = New-Object System.Collections.Generic.List[object] foreach ($source in $sources) { $path = Join-Path $ilDirFull $source.path if (-not (Test-Path -LiteralPath $path)) { continue } $lines = [System.IO.File]::ReadAllLines($path) for ($i = 0; $i -lt $lines.Count; $i++) { $line = $lines[$i] foreach ($pattern in $Item.patterns) { if ($line -match $pattern) { $evidence.Add([pscustomobject]@{ source = Get-RelativePath $path source_role = $source.role line = $i + 1 kind = Get-EvidenceKind $line pattern = $pattern snippet = $line.Trim() context = Get-MethodContext $lines $i }) break } } if ($evidence.Count -ge $MaxEvidencePerItem) { break } } if ($evidence.Count -ge $MaxEvidencePerItem) { break } } return $evidence.ToArray() } $sourceSummary = foreach ($source in $sources) { $path = Join-Path $ilDirFull $source.path $exists = Test-Path -LiteralPath $path $lineCount = 0 $size = 0 if ($exists) { $file = Get-Item -LiteralPath $path $size = $file.Length $lineCount = ([System.IO.File]::ReadLines($path) | Measure-Object).Count } [pscustomobject]@{ path = Get-RelativePath $path role = $source.role exists = $exists lines = $lineCount size_bytes = $size } } $itemResults = foreach ($item in $items) { $evidence = Find-Evidence $item $status = if ($evidence.Count -gt 0) { "static_evidence_found" } else { "missing_static_evidence" } [pscustomobject]@{ key = $item.key title = $item.title stage = $item.stage meaning = $item.meaning status = $status evidence_count = $evidence.Count must_verify = $item.must_verify evidence = $evidence } } $newValidationItems = @( [pscustomobject]@{ priority = "P0"; category = "test_rule"; item = "Define one-shot test targets and operator confirmation"; evidence = "direct JID, optional group JID, self-send/sandbox-account flag, operator confirmation, one-send-only flag, test content/file hash seed"; unlocks = "reliable ack and sent-record correlation" }, [pscustomobject]@{ priority = "P0"; category = "snapshot"; item = "Capture before/after sent-record snapshots"; evidence = "before count/last summary, after count/last summary, target ref, content hash or file hash, timestamp window"; unlocks = "machine-verifiable send result" }, [pscustomobject]@{ priority = "P0"; category = "sent_record_source"; item = "Identify sent-record and ack source paths"; evidence = "MessageCenter::OnMessageSended availability, PacketReader/SaveToDB/MsgLib source availability, MCP audit ref, ack/ref id if present"; unlocks = "production gate evidence" }, [pscustomobject]@{ priority = "P0"; category = "adapter_reachability"; item = "Confirm in-process adapter reachability mode"; evidence = "process bitness, CLR/.NET version, user/session id, loaded managed assemblies, same-process/managed-harness/injection requirement, stop-condition reason if blocked"; unlocks = "B-route implementation decision" }, [pscustomobject]@{ priority = "P0"; category = "runtime"; item = "Confirm logged-in IMPlatformClient.exe process and loaded assemblies"; evidence = "process id, module list, loaded managed assemblies, login/user presence without leaking credentials"; unlocks = "all B-route adapter tests" }, [pscustomobject]@{ priority = "P0"; category = "runtime"; item = "Confirm AppContextManager/IAppContextManager reachability"; evidence = "type visible, instance path, SendTxtMessageByJid callable shape, direct/group chat type mapping"; unlocks = "text send high-level path" }, [pscustomobject]@{ priority = "P0"; category = "runtime"; item = "Confirm Chat/XMPPConnection authenticated state"; evidence = "Chat instance, XMPPConnection state, target JID mapping, group ChatRoom branch if target is group"; unlocks = "text and file message finalization" }, [pscustomobject]@{ priority = "P0"; category = "file_upload"; item = "Capture FileUploadPara required values without leaking token"; evidence = "host, port, domain, local path presence, IsImg/IsPublic, token-present boolean/hash only, timeout/cache flags if visible"; unlocks = "upload_file probe" }, [pscustomobject]@{ priority = "P0"; category = "file_upload"; item = "Confirm IFileTransfer/FileTransfer.FileUpload reachability"; evidence = "service instance or constructor path, method signature, safe call readiness, HTTP status/failure channel mapping"; unlocks = "file upload" }, [pscustomobject]@{ priority = "P0"; category = "file_upload_result"; item = "Confirm FileUploadResult.FileID after safe test upload"; evidence = "FileID present, size/name or URL/remote folder metadata, HTTP status, service error code/message if upload fails"; unlocks = "send_file_message" }, [pscustomobject]@{ priority = "P0"; category = "send_file_message"; item = "Confirm trans_UploadCompleted/SendFileMessage consumes FileID"; evidence = "FileID enters payload, Chat.sendFileMessage invoked, sent file record/ack, failure reason if target/permission blocks"; unlocks = "production file send" }, [pscustomobject]@{ priority = "P0"; category = "failure_reason"; item = "Capture structured failure reasons for every probe step"; evidence = "step name, exception type, error code, message, offline/permission/target-not-found/token/file-size/file-type/network-timeout classification"; unlocks = "actionable next decision when probe fails" }, [pscustomobject]@{ priority = "P0"; category = "ack"; item = "Correlate sent record/ack with content hash or file hash"; evidence = "sent record source, timestamp, target ref, body/file metadata hash, ack/ref id, before/after snapshot diff"; unlocks = "production gate" }, [pscustomobject]@{ priority = "P1"; category = "group"; item = "Confirm group ChatRoom branch and permissions"; evidence = "ChatRoom type, speech/upload permission state, group JID, mute/permission failure reason"; unlocks = "group send/file send" }, [pscustomobject]@{ priority = "P1"; category = "fallback"; item = "If B-route reachability fails, capture A-route UI evidence"; evidence = "window hwnd, rtbSendMessage, btnSend, screenshots, failure reason"; unlocks = "demo fallback only" } ) $result = [pscustomobject]@{ ok = $true generated_at = (Get-Date).ToUniversalTime().ToString("o") decision = "continue_offline_deep_dive_then_login_reachability_probe" sources = $sourceSummary items = $itemResults new_validation_items = $newValidationItems originals_copied = $false originals_modified = $false } $jsonPath = Join-Path $outDir "send-upload-deep-dive.json" Write-Utf8NoBomLf $jsonPath ($result | ConvertTo-Json -Depth 8) $md = New-Object System.Collections.Generic.List[string] $md.Add("# Send / Upload Deep Dive") $md.Add("") $md.Add("Date: 2026-07-12") $md.Add("") $md.Add("## Business summary") $md.Add("") $md.Add("Offline deep dive confirms the remaining work is not generic sending code. The business blocker is logged-in runtime reachability plus online proof. Text send needs reachable AppContextManager/MessageScheduling/Chat. File send needs upload parameters and server-issued FileUploadResult.FileID before Chat.sendFileMessage can be meaningful.") $md.Add("") $md.Add("## Decision") $md.Add("") $md.Add("| Question | Answer |") $md.Add("| --- | --- |") $md.Add("| Should work stop before login? | No |") $md.Add("| Highest-value offline work | Deepen send/upload decomposition and prepare login validation items |") $md.Add("| Can production send be enabled offline? | No |") $md.Add("| Next package | login reachability probe package |") $md.Add("") $md.Add("## Sources") $md.Add("") $md.Add("| Source | Role | Exists | Lines | Size bytes |") $md.Add("| --- | --- | --- | ---: | ---: |") foreach ($source in $sourceSummary) { $md.Add("| ``$($source.path)`` | $($source.role) | $($source.exists) | $($source.lines) | $($source.size_bytes) |") } $md.Add("") $md.Add("## Deep-dive findings") $md.Add("") $md.Add("| Item | Stage | Status | Evidence | Business meaning |") $md.Add("| --- | --- | --- | ---: | --- |") foreach ($item in $itemResults) { $md.Add("| ``$($item.title)`` | ``$($item.stage)`` | ``$($item.status)`` | $($item.evidence_count) | $($item.meaning) |") } $md.Add("") $md.Add("## What this adds to tomorrow's login validation") $md.Add("") $md.Add("| Priority | Category | Validation item | Evidence to capture | Unlocks |") $md.Add("| --- | --- | --- | --- | --- |") foreach ($v in $newValidationItems) { $md.Add("| $($v.priority) | $($v.category) | $($v.item) | $($v.evidence) | $($v.unlocks) |") } $md.Add("") $md.Add("## Evidence by item") foreach ($item in $itemResults) { $md.Add("") $md.Add("### $($item.title)") $md.Add("") $md.Add("Stage: ``$($item.stage)``. Status: ``$($item.status)``. Evidence count: $($item.evidence_count).") $md.Add("") $md.Add("Business meaning: $($item.meaning)") $md.Add("") $md.Add("Must verify tomorrow:") foreach ($verify in $item.must_verify) { $md.Add("- $verify") } $md.Add("") if ($item.evidence_count -eq 0) { $md.Add("No static evidence found in configured IL sources.") continue } $md.Add("| Source | Kind | Snippet | Context |") $md.Add("| --- | --- | --- | --- |") foreach ($e in $item.evidence) { $snippet = ($e.snippet -replace "\|", "\\|") if ($snippet.Length -gt 240) { $snippet = $snippet.Substring(0, 237) + "..." } $context = ($e.context -replace "\|", "\\|") if ($context.Length -gt 180) { $context = $context.Substring(0, 177) + "..." } $md.Add("| ``$($e.source):$($e.line)`` | ``$($e.kind)`` | ``$snippet`` | ``$context`` |") } } $md.Add("") $md.Add("## Updated stop / continue rules") $md.Add("") $md.Add("Continue B-route tomorrow if P0 runtime, text-send, upload, FileID, and ack probes produce positive or actionable evidence. Stop B-route and escalate if logged-in runtime cannot expose AppContextManager/MessageScheduling/Chat/IFileTransfer, or if upload cannot produce FileUploadResult.FileID. Keep A-route/RPA backup-only for demo continuity.") Write-Utf8NoBomLf $reportFullPath ($md -join "`n") $validation = New-Object System.Collections.Generic.List[string] $validation.Add("# Login Reachability Validation Items") $validation.Add("") $validation.Add("Date: 2026-07-12") $validation.Add("") $validation.Add("Purpose: this is the operator-facing checklist for the 2026-07-13 logged-in environment. It is generated from the offline send/upload deep dive and should be used to build the one-shot reachability probe package. P0 items are blocking; production send remains disabled until all relevant P0 evidence is collected and correlated.") $validation.Add("") $validation.Add("## Operator rules") $validation.Add("") $validation.Add("- Run the probe only after the target iSphere/IMPlatformClient session is visibly logged in.") $validation.Add("- Use a pre-agreed direct test JID and optional group test JID; record them in the output JSON.") $validation.Add("- Use one-shot test content and one safe small test file; record SHA256 hashes, not raw sensitive content.") $validation.Add("- Capture before/after sent-record snapshots around the one-shot send/upload test.") $validation.Add("- Do not mark production ready unless ack/sent-record correlation is machine-verifiable.") $validation.Add("") $validation.Add("## P0 items") $validation.Add("") foreach ($v in $newValidationItems | Where-Object { $_.priority -eq "P0" }) { $validation.Add("### $($v.item)") $validation.Add("") $validation.Add("- Category: $($v.category)") $validation.Add("- Evidence to capture: $($v.evidence)") $validation.Add("- Unlocks: $($v.unlocks)") $validation.Add("") } $validation.Add("## P1 items") $validation.Add("") foreach ($v in $newValidationItems | Where-Object { $_.priority -eq "P1" }) { $validation.Add("### $($v.item)") $validation.Add("") $validation.Add("- Category: $($v.category)") $validation.Add("- Evidence to capture: $($v.evidence)") $validation.Add("- Unlocks: $($v.unlocks)") $validation.Add("") } $validation.Add("## Required output schema") $validation.Add("") $validation.Add("Every probe run should emit JSON with these top-level sections:") $validation.Add("") $validation.Add("- ``run``: timestamp, machine, operator confirmation, one-shot flags.") $validation.Add("- ``targets``: direct JID, optional group JID, target type, source of target selection.") $validation.Add("- ``runtime``: process id, bitness, CLR/.NET version, user/session, loaded assemblies.") $validation.Add("- ``reachability``: AppContextManager, MessageScheduling, Chat, XMPPConnection, IFileTransfer, FileUploadPara, FileUploadResult, Chat.sendFileMessage.") $validation.Add("- ``server_auth``: host, port, domain, token_present, token_hash_prefix only, accessToken/clientId presence when visible.") $validation.Add("- ``snapshots``: before/after counts and last-record summaries for sent-record sources.") $validation.Add("- ``attempts``: probe-only, optional one-shot text, optional safe upload, optional file-message finalization.") $validation.Add("- ``failures``: structured failure code, exception type, message, classification, next action.") $validation.Add("- ``correlation``: content/file hash match, target match, timestamp window, ack/ref id.") $validation.Add("") $validation.Add("## Pass criteria") $validation.Add("") $validation.Add("- Text send can move forward when logged-in runtime exposes AppContextManager or MessageScheduling plus Chat/XMPPConnection and sent-record/ack correlation is possible.") $validation.Add("- File send can move forward when logged-in runtime exposes IFileTransfer/FileUpload, FileUploadPara values, FileUploadResult.FileID, and Chat.sendFileMessage finalization evidence.") $validation.Add("- Production remains blocked if any P0 item is missing, if failure reasons are unstructured, or if only static evidence is available.") $validation.Add("- Text production can move forward only when one-shot target, before/after snapshot, sent-record source, and content-hash/ack correlation all pass.") $validation.Add("- File production can move forward only when FileUploadPara values, IFileTransfer reachability, FileUploadResult.FileID, file-message finalization, and file sent-record/ack correlation all pass.") Write-Utf8NoBomLf $validationFullPath ($validation -join "`n") [pscustomobject]@{ ok = $true json_path = $jsonPath report_path = $reportFullPath validation_path = $validationFullPath items = $itemResults.Count validation_items = $newValidationItems.Count decision = $result.decision originals_copied = $false originals_modified = $false } | ConvertTo-Json -Depth 5