Three workstreams identified after the alpha shipped. Each is parallel-friendly via per-platform engineering sub-agents.
Created: 2026-06-24 (after v0.1.1-alpha shipped) Target release: v0.2.0
Installing the .deb succeeds but the app then "silently fails" — no clear log output, no actionable error, no obvious place to look. Diagnosing is currently hard because:
- The systemd user unit may not enable/start correctly post-install
- Daemon likely exits early when no
config.tomlexists, with no log capture tracingoutput goes nowhere by default (notracing-subscriberlog file)postinstdoesn't tell the user what to do next or where logs live- No
interlinedlist-sync --statusstyle command for self-diagnosis
-
File-based logging via
tracing-subscriberrolling appender- Log to
~/.local/share/interlinedlist-sync/logs/interlinedlist-sync.logwith daily rotation (keep 7 days) - Log level controlled by
RUST_LOGenv var (defaultinfo) - Format: structured JSON or compact text — choose compact for human grep-ability
- Initialize the logger BEFORE config loading so config-load failures are captured
- Print log path to stderr on startup so users can find it
- Log to
-
--statussubcommandinterlinedlist-sync --statusprints: config path + whether it exists, last sync time from state DB, log file path + last 10 lines, secret-store backend in use + whether a token is present (boolean, never the value), systemd unit state if running under systemd- Useful for the user, useful for bug reports
- Exit code 0 if everything's fine, 1 if anything's missing
-
systemd user unit improvements
StandardOutput=journalandStandardError=journalsojournalctl --user -u interlinedlist-syncworksRestart=on-failurewithRestartSec=10sandStartLimitBurst=3so transient failures don't loopAfter=network-online.targetso the daemon doesn't start before networking is up- Add a
Documentation=line pointing to the local README and the GitHub URL
-
postinstscript — make next steps obvious- After install, print to stdout:
InterlinedList Sync installed. Next steps: 1) Configure the sync folder + API base URL: $EDITOR ~/.config/interlinedlist-sync/config.toml (the daemon will create a default file on first run) 2) Store your account credentials: interlinedlist-sync --login 3) Enable + start the user service: systemctl --user enable --now interlinedlist-sync.service Status + logs: interlinedlist-sync --status journalctl --user -u interlinedlist-sync tail -F ~/.local/share/interlinedlist-sync/logs/interlinedlist-sync.log - Do NOT auto-enable the systemd unit in
postinst(requiresloginctl enable-lingeror per-user context which isn't safe for system-level package management)
- After install, print to stdout:
-
Early-exit clarity
- When the daemon exits because no config file / no credentials / etc., log a clear ERROR-level message saying why and what to do, then exit with a non-zero code
- Currently the daemon may silently sleep or exit 0 — change to be explicit
linux-ubuntu/crates/interlinedlist-sync/src/main.rs— wiretracing-subscriberfile appender; add--statussubcommand; clear early-exit messageslinux-ubuntu/crates/interlinedlist-sync/Cargo.toml— addtracing-appenderif not already a deplinux-ubuntu/packaging/systemd/interlinedlist-sync.service— journal output, Restart, After=network-onlinelinux-ubuntu/packaging/maintainer-scripts/postinst— print next-steps message- New:
linux-ubuntu/crates/interlinedlist-sync/src/status.rs—--statuscommand implementation - Tests: unit tests for status output formatting; integration test for log file creation
- After
sudo dpkg -i interlinedlist-sync_*.deb, the user sees a clear next-steps message interlinedlist-sync --statusproduces useful, sanitized output even before any sync has happened- Daemon early-exit produces an ERROR log line explaining the cause
- Log file appears at the documented path on first run
journalctl --user -u interlinedlist-syncshows the same output as the log file
engineering-linux-ubuntu-agent — single workstream, can be done in one session.
Today's Windows artifact is an unsigned .exe-in-zip. The user must extract it, run it manually, and there's no installer, no Start Menu entry, no startup registration, no uninstaller. Earlier MSIX attempts hit APPX3217 UAP-SDK-mismatch on the GitHub-hosted runner across three different version values, so MSIX is deferred.
Why Inno Setup over MSIX/MSI:
- Free, mature, widely used for desktop Windows apps
- No special SDK requirement on the build runner (CLI compiler
iscc.exeis small and installable via Chocolatey or direct download) - First-class support for: Start Menu shortcuts, registry-based startup, optional install tasks, code-sign hook
- Produces a single
InterlinedListSync-Setup-v*.exeinstaller — familiar UX to Windows users - WiX/MSI is "more correct" but heavier; Inno Setup matches our actual needs
Installer behavior:
- Welcome / License / Install Location pages — standard Inno wizard
- Tasks page — checkboxes:
- Create Start Menu shortcut (checked by default)
- Start automatically when Windows starts (checked by default)
- Launch InterlinedList Sync after installation (checked by default)
- Install — copies binaries to
%ProgramFiles%\InterlinedList Sync\, writes Start Menu shortcut, writesHKCU\Software\Microsoft\Windows\CurrentVersion\Runentry conditional on task selection - Finish — optional launch checkbox
App-side behavior on launch from Start Menu:
- On every launch, check if
HKCU\...\Run\InterlinedSyncregistry value exists and points to the current EXE - If not present:
- Show a one-time non-modal toast or dialog: "Run InterlinedList Sync automatically when you sign in? [Yes / No / Don't ask again]"
- "Yes" → write the registry value
- "No" → don't write, but ask again next launch
- "Don't ask again" → write a
HKCU\Software\InterlinedSync\StartupPromptSuppressed = 1flag so the prompt stays hidden
- App then minimizes to the tray and starts sync as today
Uninstaller:
- Standard Inno-generated uninstall — removes app files, removes Start Menu shortcut, removes
Runregistry entry - Does NOT remove user data (sync folder, credentials in Credential Manager, settings JSON) — preserve user data on uninstall, document this behavior
Optional code-signing:
- Inno's
[Setup]section accepts aSignTooldirective — wire to a conditionalsigntool signstep whenWINDOWS_CERT_PFX_BASE64is present (same secret already documented) - Sign both the installer
.exeand the embeddedInterlinedSync.exe
- New:
windows/installer/InterlinedSync.iss— Inno Setup script - New:
windows/installer/README.md— how to build the installer locally .github/workflows/release.yml(Windows job) — add steps:- Install Inno Setup via
choco install innosetup -y - Run
iscc /Qp windows/installer/InterlinedSync.iss /DAppVersion=${{ github.ref_name }}to compile - Conditional signtool step (same pattern as the MSIX scaffold we wrote)
- Upload
InterlinedListSync-Setup-${{ github.ref_name }}.exeas a release artifact (alongside the existing.zipfor users who prefer no installer)
- Install Inno Setup via
windows/InterlinedSync/Storage/IAutoStartManager.cs+RegistryAutoStartManager.cs— already exist from Phase 7; addIsManagedByInstaller()to distinguish installer-set vs user-set, and a "don't ask again" preference- New:
windows/InterlinedSync/UI/Views/StartupPromptDialog.xaml— one-time prompt on launch from Start Menu - Modified:
windows/InterlinedSync/App.xaml.cs— startup-prompt check at launch - Tests: unit tests for the prompt logic with
MockAutoStartManager; manual smoke-test checklist for the installer - Update
windows/PACKAGING.mdto document the Inno-based flow (MSIX remains a future option)
- Downloading
InterlinedListSync-Setup-v0.2.0.exe, double-clicking, accepting defaults: app is installed, Start Menu shortcut created, autostart registry entry written, app launches and lives in the tray - Unchecking "Start automatically" in the wizard: no registry entry written; app launches once if "Launch after installation" was checked, but doesn't restart on next sign-in
- Launching from Start Menu when no registry entry exists: one-time prompt appears with Yes/No/Don't-ask-again
- Uninstall via Add/Remove Programs: removes app + shortcuts + registry entry; preserves sync folder + credentials
- Installer can be signed when cert secrets are present
engineering-windows-agent — Inno script + app-side prompt + workflow integration. Single session.
The app today assumes credentials are already in the platform credential store. If a user installs fresh and runs the app, there's no clear path to enter their email + password. The tray icon may appear but the app sits in an "AuthExpired" or similar state with no obvious next step. Linux is CLI-only for sign-in (interlinedlist-sync --login prompt).
The desired behavior across all three platforms:
- No credentials → tray icon shows a distinct "Sign in needed" state (warning badge or muted color)
- Click tray icon → menu includes a prominent "Sign in…" item at the top
- Sign in dialog — email + password fields, "Sign in" button, error display on failed auth
- Validate via
POST /api/auth/sync-tokenBEFORE saving — never write garbage tokens to the credential store - Success → store token in platform credential store, dismiss dialog, kick off first sync
- Status:
OnboardingViewalready exists and handles sign-in. Phase 5 added Preferences. Just need to verify the no-credentials → onboarding flow surfaces correctly. - Tasks:
- On launch, if
KeychainManager.loadToken()returns nil, automatically presentOnboardingViewas a modal (window, not sheet — app has no main window) - Tray icon shows
exclamationmark.trianglewith "Sign in needed" tooltip when no token - "Sign in…" menu item at top of
StatusItemController's menu when no token; greyed out when signed in - Existing Phase 5 work likely covers most of this — verify and patch gaps
- On launch, if
- Acceptance: Fresh install → click tray → "Sign in…" → enter creds → app moves to idle state and starts syncing
- Status:
OnboardingWindowexists from Phase 7. Tray icon exists. - Tasks:
- On launch, if no token in Credential Manager, automatically show
OnboardingWindow(already done per Phase 7 report — verify) - Tray icon:
tray-error.ico(already a state) when no token - Tray right-click menu: top item "Sign in…" when no token; otherwise greyed out
- "Sign in…" reuses
OnboardingViewModel— opens the same window as first-run
- On launch, if no token in Credential Manager, automatically show
- Acceptance: Fresh install → app launches → onboarding window appears → enter creds → minimize to tray and sync starts
- Status: Biggest gap. Sign-in is CLI-only (
interlinedlist-sync --login). The GTK tray app launches the daemon and shows a Settings dialog, but the daemon exits immediately when no token is present, and there's no GUI to enter credentials. - Tasks:
- New GTK sign-in dialog at
linux-ubuntu/crates/tray-app/src/signin.rs: AdwaitaWindowwith email Entry + password Entry (visibility = false) + "Sign in" button + error label - Wired to
api_client::ApiClient::login(email, password)directly; on success, write toSecretStoreand emit a signal that the daemon can subscribe to - Daemon needs to wait for credentials instead of exiting — if no token at startup, the daemon enters a "waiting for sign-in" state (still running), polls the secret store every 5s, and starts the sync engine once a token appears
- Tray icon: distinct state when no token (use the
ksniwarning icon or stamp a badge) - Tray menu: "Sign in…" item at top when no token; opens the GTK sign-in dialog (gated behind
#[cfg(feature = "gtk")]) - For CLI-only environments (servers, no display), the existing
interlinedlist-sync --loginflow stays as the fallback
- New GTK sign-in dialog at
- Acceptance: Fresh
dpkg -i, no credentials → tray icon appears with "Sign in" state → click → enter creds → daemon picks up the new token and starts syncing without restart
- Validate credentials BEFORE persisting — don't write a token if
POST /api/auth/sync-tokenreturns 401. Surface the error in the dialog. - Sign-out path — Preferences (macOS/Windows) and tray menu (Linux) already have or need a "Sign out" item that clears the credential store and returns the app to the no-credentials state, prompting for sign-in next launch.
- No password logging — credentials never appear in any log line. Sign-in HTTP requests don't dump request body to logs.
- HIG / platform conventions — sign-in dialogs follow native UX (macOS sheet/window, Windows modal, Linux Adwaita dialog).
macOS:
- Verify
App/AppDelegate.swiftshowsOnboardingViewwhen no token at launch (likely already does) - Update
MenuBar/StatusItemController.swiftto show "Sign in…" item when in unsigned state - Tests:
OnboardingFlowTestscovering no-token launch path
Windows:
- Verify
App.xaml.csshowsOnboardingWindowwhen no token - Update
SystemTray/TrayMenuBuilder.cs(or wherever the tray menu is built) to surface "Sign in…" when in unsigned state - Tests: extend
OnboardingViewModelTestsfor unsigned-state launch
Linux:
- New:
linux-ubuntu/crates/tray-app/src/signin.rs— GTK sign-in dialog - Modified:
linux-ubuntu/crates/tray-app/src/linux.rs— "Sign in…" tray menu item; tray state when no token - Modified:
linux-ubuntu/crates/interlinedlist-sync/src/main.rs— wait-for-credentials state, poll secret-store, hot-start engine on token appearance - Modified:
linux-ubuntu/crates/sync-engine/src/lib.rs— accept "start without engine running" mode that can be triggered later - Tests: integration test for wait-for-credentials → token-appears → engine-starts flow
- macOS:
swift-engineer(small — verify + patch gaps) - Windows:
engineering-windows-agent(small — verify + patch + tray menu) - Linux:
engineering-linux-ubuntu-agent(largest — new dialog + daemon state machine)
All three can run in parallel.
- Workstream A (Linux logging) — smallest, unlocks user-facing diagnosis immediately
- Workstream C (account setup UX) — three parallel agents, blocks usability of any installer
- Workstream B (Windows installer) — biggest single workstream, depends on Workstream C being done so the installer's "launch after install" actually presents a sign-in dialog
- Cut v0.2.0 — same release flow as v0.1.1-alpha; bump tag to
v0.2.0(orv0.2.0-betaif you want another prerelease)
ETA per workstream: 1 session each (3 sessions total if sequential, ~1.5 if parallel where possible).
- MSIX packaging — defer until we can pin a Windows SDK in CI or move to a self-hosted runner
- Snap packaging — defer until we have snapcraft remote-build credentials or move to a self-hosted runner
- Apple Developer ID signing / notarization — defer until creds available
- Windows code-signing cert — defer until cert procured
- App Store / Snap Store publication — separate manual processes