Trigger: tag push v*.
- Compute version from tag (vMAJOR.MINOR.PATCH → 4-part for mage)
- Sign DLLs + exe with signtool (CODESIGN_THUMBPRINT secret)
- Build ClickOnce .dll.manifest + .vsto via mage.exe, sign both, rename
payload files with .deploy convention for static-file serving
- Stage protocol handler bin/ + install-protocol.ps1 alongside the
ClickOnce artifact
- Upload publish/ artifact to GH Actions for inspection
- Upload to platform.dev:/var/www/outlook-addin/ via rsync (WSL) with scp
fallback. PLATFORM_DEV_SSH_KEY secret expects a base64-encoded private
key. Skips gracefully if the secret is empty.
- POST to MATTERMOST_WEBHOOK_RELEASES on success and failure
(#git-verelasim channel). Skips gracefully if the secret is empty.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Strings.resx (Hebrew, default) + Strings.en-US.resx (English override)
cover every dialog/sidebar/ribbon string, plus filing summary format
templates (`Msg_FilingSummary_*`)
- LocalizationManager singleton wraps ResourceManager with Get() and
Format() helpers; SetCulture honors AppSettings.Locale
- LocExtension XAML markup ({l:Loc Key}) for bindings
- Themes/RtlFonts.xaml — global RightToLeft FlowDirection and Hebrew
font stack (Segoe UI / David / Frank Ruehl / Arial)
- AddInHost calls LocalizationManager.SetCulture(Settings.Locale) before
any UI thread reads a resource
Existing dialogs keep their hardcoded Hebrew for v1 — those are already
in the correct (he-IL) language. Future iterations can switch to
{l:Loc …} bindings; the resource files already enumerate every string so
the migration is mechanical.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Host:
- FolderResolver: walks Outlook Stores/Folders by stored StoreId +
backslash-separated FolderPath; RCWs released at each hop
- FolderWatcher: rooted Items references in a Subscription list
(preventing the GC from silently dropping the COM event sink),
HashSet<EntryID> dedup, marshals category stamping back to the WPF
dispatcher. Per-folder mode: AutoFile when MatchingService returns a
confident unique match AND confidence ≥ ConfidenceThreshold; otherwise
stamps "Needs filing"
- FolderTreeBuilder: one-shot STA traversal (max depth 4, max 500 entries)
into Core-side FolderTreeNode list
UI:
- FolderTreeNode in Core for cross-project transport
- FolderRowViewModel: per-row IsWatched / Mode / ConfidenceThreshold
- SettingsViewModel.LoadFolderTree(tree) merges existing
AppSettings.WatchedFolders into the loaded list
- Save now writes WatchedFolders alongside URL/creds
- SettingsDialog: new "תיקיות" tab with a DataGrid (checkbox + mode combo
per folder)
AddInHost:
- Builds FolderResolver + FolderWatcher inside TryInitializeCrm, applies
Settings.WatchedFolders on startup and after Save
- LaunchSettings: invokes FolderTreeBuilder, passes the flat list to the
ViewModel, and re-applies the watch list after the dialog closes
Core/UI/Tests green (38 tests).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
UI:
- ComposeFromCaseDialog (RTL, search-then-pick a single Case via
GlobalSearch + entityType=Case filter)
- ComposeFromCaseViewModel — debounced GlobalSearch, returns chosen
EspoEntityRef
Host:
- ComposeService.ApplyCaseToMailAsync:
* fetches Case + primary Contact
* stamps UserProperties EspoCaseId, EspoCaseName, OutlookAddinGuid
* pre-fills To/Subject and appends an RTL footer with a CRM deep link
- ItemSend hook (in AddInHost.HookItemSend): when the user sends a
tagged compose, register guid → target with SentItemsWatcher
- SentItemsWatcher: rooted Items reference (prevents GC), 30s expiry
sweep, 2-min default timeout per pending tag, MailMatched event
- AddInHost.OnSentMailMatched: extracts the sent MailItem, files via
FilingService, stamps "Filed: {name}" category
- LaunchComposeFromCaseAsync (Inspector ribbon) and
LaunchComposeFromCaseByIdAsync (URL protocol) entry points
- NamedPipeListener (\.\pipe\MarcusLaw.OutlookAddin) — listens for
"COMPOSE <caseId>" lines, dispatches on WPF Dispatcher
Ribbon:
- InspectorRibbon.xml — "כתוב מתיק" button on Mail Compose ribbon, hidden
for received/read mail via getVisible
- ExplorerRibbon class now serves both Explorer and Inspector ribbons
(single IRibbonExtensibility, dispatches by ribbonID)
URL protocol:
- New OutlookAddinProtocolHandler console project (net48 WinExe):
parses outlookaddin://compose?caseId=<id>, connects to named pipe,
writes "COMPOSE <id>\n"
- tools/install-protocol.ps1 + uninstall-protocol.ps1 register the
scheme under HKCU\Software\Classes\outlookaddin
Core/UI/Tests + protocol handler all build; 38 unit tests still green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- SettingsDialog (WPF, RTL): General tab (URL/username/API key/locale) +
About tab; PasswordBox-bound API key, Test Connection probe, Save/Cancel
- SettingsViewModel: loads from SettingsManager + DpapiCredentialStore,
Test Connection spins a transient HttpClient + EspoCrmClient and reports
401 vs network failures separately; Save persists then signals dialog
close
- Ribbon: second "הגדרות" button in Marcus-Law group; "File to EspoCRM"
now offers to open Settings inline when not configured
- AddInHost.LaunchSettings tears down and rebuilds CRM/filing/retry stack
so new credentials take effect immediately (CrmClientChanged event)
- Tag .taskmaster/tasks/tasks.json: Task #3 in-review with implementation
notes (was already locally modified)
Core/UI/Tests green (38 tests passing); VSTO host project still requires
VS Installer Repair to compile locally.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
VSTO csproj template disables binding redirects, but System.Text.Json's
transitive deps (System.Text.Encodings.Web, System.Runtime.CompilerServices.Unsafe,
System.Memory) ship as out-of-band NuGet packs whose versions diverge
from .NET Framework 4.8's facade assemblies. Without redirects, the
JIT loads the wrong Unsafe.dll for DefaultJavaScriptEncoder's static
init, throwing FileLoadException wrapped in TypeInitializationException
when SettingsManager.JsonOptions is touched.
GenerateBindingRedirectsOutputType=true ensures the generated
{MarcusLaw.OutlookAddin.dll}.config goes alongside the assembly so
the VSTO loader actually sees it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Legacy VSTO csproj doesn't follow PackageReferences transitively from
ProjectReferences — at runtime the host's bin\ ends up without the
satellite DLLs that Core depends on. JIT'ing SettingsManager throws
a type-initializer exception because JavaScriptEncoder.UnsafeRelaxedJsonEscaping
(in System.Text.Encodings.Web, transitive of System.Text.Json) is missing.
Pull all four runtime NuGet packs into the host directly. Versions
mirror Core.csproj exactly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The legacy VSTO csproj doesn't include System.Net.Http in its default
references. Adding it so HttpClient resolves in AddInHost.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The composition root was calling EspoCrmClient(baseUrl, user, key, logger)
which doesn't match the actual ctor signature
EspoCrmClient(HttpClient, string, string, string, ILogger). One missing
HttpClient argument = one compile error blocking the host build.
- AddInHost now owns a single HttpClient (Timeout=30s), passed to
EspoCrmClient on init, disposed alongside the retry processor and
Serilog on shutdown.
- baseUrl.ToString() satisfies the string parameter; the Uri.TryCreate
upstream still validates the URL.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The legacy VSTO csproj defaults to C# 7.3, but the host code (e.g.
AddInHost, MailItemExtractor) uses nullable reference types — which
require C# 8.0+. Building fails with ~12x
"Feature 'nullable reference types' is not available in C# 7.3".
Sync with the SDK-style Core/UI/Tests projects that already declare
LangVersion=latest + Nullable=enable.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
OutlookAddin host project:
- ExplorerRibbon (IRibbonExtensibility + ExplorerRibbon.xml embedded): adds
"תייק ל-EspoCRM" button to Mail Home tab, enabled only when 1+ MailItems
selected; invalidates on Explorer.SelectionChange
- MailItemExtractor: STA-side conversion of Outlook.MailItem to MailEnvelope
primitives (subject, from, to/cc/bcc, body, PR_INTERNET_MESSAGE_ID,
attachments base64), with COM RCW release on each iteration
- RetryQueueProcessor: 30s background drain, max 3 attempts per item,
401 short-circuits the loop, items abandoned (not deleted) after max
- AddInHost: composition root — Logger, SettingsManager, DpapiCredentialStore,
EspoCrmClient, FilingService, RetryQueueProcessor. LaunchFileToCaseAsync
reads selection, extracts, opens dialog, files, stamps "Filed: {name}"
category on success, promotes target into Settings.RecentEntities (max 10),
shows summary MessageBox.
- ThisAddIn: instantiate AddInHost in Startup; CreateRibbonExtensibilityObject
returns ExplorerRibbon
VSTO targets are missing on this machine — host project not built locally.
Core/UI/Tests still green (38 tests passing).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The hand-written legacy csproj at src/OutlookAddin/ is marked Unsupported
by VS 2022. Per PROJECT-BRIEFING.md (steps 5-11), the host project must
be regenerated from the VSTO template inside Visual Studio with the
Office workload — there is no CLI/dotnet template equivalent.
Removing now so the next VS session starts from a clean slate.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Self-contained briefing for a fresh Claude Code session picking up work
on this project from a Windows machine running VS 2022. Covers:
- TL;DR + 4 features
- MailRouter API contract + EspoCRM endpoints
- Tech stack (locked decisions)
- Repo layout + current state
- Step-by-step recipe to regenerate the VSTO host project from the
template (the hand-written csproj is incompatible with VS)
- Conventions + deferred work + open risks
Read this before doing anything: docs/PROJECT-BRIEFING.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Initial repo scaffold for a Windows VSTO/COM Outlook Add-in (C#/.NET FW 4.8)
that integrates Outlook desktop with EspoCRM via the existing MailRouter
v1.2.0 endpoint (POST /api/v1/MailRouter/file).
Includes:
- Solution + 4 csproj projects (host, Core, UI, Tests) under src/
- VSTO host project stub (ThisAddIn.cs, requires VS 2022 + Office workload to fully resolve)
- Core/UI/Tests as SDK-style net48 projects (NuGet: Polly, System.Text.Json,
Serilog, CommunityToolkit.Mvvm, xUnit, Moq, FluentAssertions)
- .gitea/workflows/build.yml for Windows runner CI (publish/sign placeholders)
- docs/ARCHITECTURE.md (component diagram, threading model, per-feature flow)
- docs/ONBOARDING.md (Hebrew lawyer-facing install guide)
Server-side already in place: MailRouter v1.2.0 — see plan
~/.claude/plans/resilient-sauteeing-feather.md for the 6-week implementation
plan.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>