Tropical Update Publisher — Project Specification v2.0

Goal: Rebuild the Tropical Update Publisher Electron desktop application, preserving every existing feature while applying 5 targeted quality fixes (see Section 19).

Working Directory: C:/Users/keen4/WxManBran/tools/tropical-update-publisher/

Git Branch: tropical-publisher-v2 (off main in the WxManBran repo)

Purpose: Drag-and-drop GUI tool for publishing tropical weather update documents to the WxManBran website repository via git. Combines document creation, folder navigation, file validation, and automated git publishing into a single desktop workflow.

Change scope vs v1.0: All existing behavior from the current tool is preserved. In addition, 5 quality improvements are now REQUIRED (see Section 19 below). These address dead code, UI/data-model consistency, code/comment drift, timezone correctness, and per-user path portability.


1. Critical Rules (Must Preserve)

  1. Do NOT break filename validation. Both the NEW and OLD (legacy) filename formats must continue to be recognized. Auto-conversion of OLD → NEW must be preserved.
  2. Publishing targets the branch specified in config.json (default: main). Do NOT hardcode the branch name anywhere in the publish workflow. The config value is the single source of truth. (See Section 19 R1.)
  3. Do NOT break the IPC security model. Renderer process never gets direct Node.js access. All privileged operations go through contextBridge in preload.js.
  4. Do NOT remove the auto-stash / restore-branch behavior. If the user is on a branch other than the configured target with uncommitted changes, publishing must stash, switch to target branch, publish, switch back, and pop the stash. This is non-negotiable safety behavior.
  5. Do NOT change the folder-rename retry logic. When renaming storm folders, Word COM may lock files. Up to 5 retries with backoff [500, 1000, 2000, 3000, 5000] ms must be preserved for EBUSY, EPERM, and EACCES errors.
  6. Preserve Content Security Policy. Electron window’s CSP must remain strict: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'. CSP additions for Phase 2 features are ALLOWED only where necessary (e.g., connect-src for NHC endpoints) and MUST be enumerated explicitly in Section 28.
  7. Preserve context isolation. contextIsolation: true and nodeIntegration: false must remain in the BrowserWindow config.
  8. Never auto-publish without a valid approval trail. Every published briefing must have either: a human manual click, an email “YES” reply, OR the user has explicitly set approvalMode: auto-publish AND the draft passed all number-sanity checks (no [NEEDS REVIEW] markers, no numeric mismatches with NHC source).
  9. Never write secrets to config.json. Email passwords, OAuth tokens, and API keys MUST be stored in the OS credential store (Windows Credential Manager via keytar). Only non-secret configuration goes in config.json. (See Section 28.)

2. Application Overview

  • Type: Electron desktop application (Windows)
  • Version: 1.0.0
  • Package name: tropical-update-publisher
  • Electron version: latest stable (v33 or higher at build time) — NOT the v1 ^28.0.0. The rebuild MUST upgrade.
  • Primary dependency: simple-git ^3.22.0
  • Build tool: electron-builder ^24.9.1 → NSIS installer for Windows
  • App ID: com.wxmanbran.tropical-update-publisher
  • Product name: Tropical Update Publisher (window title shows Tropical Update Uploader)
  • License: MIT
  • Author: WxManBran

2.1 Launch Paths

The app can be launched via:

  1. npm start — runs electron . from the tool directory
  2. launcher.bat — Windows batch file wrapper
  3. launcher.vbs — VBScript wrapper (hides console window)
  4. Tropical Update Uploader.lnk — Desktop shortcut that points to the VBS launcher
  5. NSIS installer built via npm run build:windist/ folder

2.2 Window Configuration

  • Size: 800×700 px, minimum 600×500
  • Title: “Tropical Update Uploader”
  • Icon: assets/icon.ico
  • Preload script: preload.js
  • Platform quit behavior: On all-windows-closed, quits unless on macOS (darwin)

3. Configuration (Hardcoded)

Defined in main.js as DEFAULT_CONFIG:

Setting Value Notes
repoPath path.resolve(__dirname, '..', '..') WxManBran repo root (two dirs up from tool)
incomingPostsPath incoming/posts Destination folder (relative to repo root)
branch main Always publishes to main
tropicalUpdatesPath C:\Users\keen4\iCloudDrive\Documents\UNITED_WxManBran\Tropical Updates_Briefings iCloud source folder for docs

4. Directory Structure Rules

4.1 Tropical Updates iCloud Source Folder

Tropical Updates_Briefings/
├── 2024/
│   ├── 09L_Milton/
│   │   ├── 2024-10-07-12PM-Hurricane-Milton.docx
│   │   └── ...
│   └── 14L_Oscar/
├── 2025/
│   ├── 94L/                     (Invest folder — ID only)
│   ├── 07L_TD7/                 (Tropical Depression folder)
│   ├── 08L_PTC8/                (PTC folder)
│   └── 09L_Imelda/              (Named storm folder)
└── 2026/

Folder naming rules (enforced by determineFolderName() in main.js):

  • Pattern: ^(\d+[A-Z])_(.+)$ (e.g., 09L_Imelda) OR just the ID (e.g., 94L for invests)
  • Storm ID format: digits + single uppercase letter (e.g., 09L, 14L, 94L)
  • Folder name priority (highest to lowest):
    1. Named Storm: {ID}_{StormName} (e.g., 09L_Andrew)
    2. Tropical Depression: {ID}_TD{number} (e.g., 07L_TD7)
    3. PTC (Potential Tropical Cyclone): {ID}_PTC{number} (e.g., 08L_PTC8)
    4. Invest only: {ID} (e.g., 94L)
  • Named storms are detected when filename name component matches ^[A-Za-z]{3,}$ AND does not match ^(TD|PTC)\d+$ AND does not match ^\d+[A-Z]$

4.2 Repo Destination Folder

WxManBran/
└── incoming/
    └── posts/
        ├── 2025-10-01-12PM-Hurricane-Imelda.docx
        ├── 2025-10-01-12PM-Hurricane-Imelda.meta.json
        └── ...
  • All published documents go to incoming/posts/ (flat — no year/storm subfolders at the repo destination)
  • Metadata sidecar files (.meta.json) sit alongside their .docx files

5. Filename Conventions

5.1 NEW Format (Required)

Pattern: YYYY-MM-DD-TIME-Slug.docx (TIME is optional)

Regex: ^(\d{4})-(\d{2})-(\d{2})(?:-(\d{1,2}(?::\d{2})?[ap]m))?-(.+)\.docx$ (case-insensitive)

Examples:

  • 2025-09-26-4pm-Invest-94L.docx
  • 2025-10-01-12PM-Hurricane-Imelda.docx
  • 2025-01-19-Tropical-Update.docx (no time component)
  • 2025-10-15-2:30pm-Tropical-Storm-Helene.docx (minutes allowed)

Validation rules:

  • Year: 2020 ≤ YYYY ≤ 2100
  • Month: 01 ≤ MM ≤ 12
  • Day: 01 ≤ DD ≤ 31
  • Date must be a real calendar date (Feb 30 rejected)
  • Slug must be at least 2 characters
  • Extension must be .docx (case-insensitive)
  • Time format: H[:MM]am/pm or HH[:MM]AM/PM

5.2 OLD Format (Legacy — Auto-Converted)

Pattern: Name_Time_M-D-YY.docx

Regex: ^([A-Za-z0-9]+)_(\d{1,2}[ap]m)_(\d{1,2})-(\d{1,2})-(\d{2,4})\.docx$ (case-insensitive)

Examples:

  • Imelda_12pm_9-29-25.docx → auto-converts to 2025-09-29-12pm-Imelda.docx
  • Helene_4pm_10-15-24.docx2024-10-15-4pm-Helene.docx

Conversion rules:

  • 2-digit year 00-99 becomes 20xx (padded to 4 digits)
  • Month and day padded to 2 digits with leading zeros
  • Time is lowercased
  • Original order Name_Time_Date is re-ordered to Date-Time-Name
  • Original filename is stored for reference; converted filename is used for upload

5.3 Filename Generation (Document Creator)

Pattern: YYYY-MM-DD-TIME-Type-Name.docx

Generation rules (DocCreator.generateFilename()):

  • Name is sanitized: [^a-zA-Z0-9\s-] stripped, spaces → hyphens
  • Duplicate type prefix removed (e.g., Hurricane-Hurricane-AndrewHurricane-Andrew)
  • For Invest type: name = storm ID uppercased (e.g., 94L)
  • For all other types: name = user-entered storm name

Valid types (from UI dropdown):

  • Invest
  • PTC (Potential Tropical Cyclone)
  • Tropical-Depression
  • Tropical-Storm
  • Hurricane (default)

5.4 Title Display Formatting (FileValidator.parseFilename)

  • Slug hyphens → spaces
  • Each word capitalized
  • Storm IDs preserved in uppercase form (e.g., 94l94L) via regex (\d+)[lL]\b$1L
  • Date formatted as Mon, Jan 19, 2025 (en-US locale, short)
  • Time formatted as H:MM AM/PM ET (Eastern Time label hardcoded)

6. UI Structure (index.html)

The app is a single-page interface with 7 stacked sections:

6.1 Header

  • App title with WMB_Logo.png
  • Git status indicator (colored dot + branch name, shows “(uncommitted changes)” warning if dirty)

6.2 Create New Briefing Document (DocCreator)

  • Date input (HTML5 date picker, defaults to today in local time)
  • Time dropdown (24 options: 12AM–11PM, defaults to current hour in local time)
  • Storm dropdown (populated from listStorms(year), with “➕ Create New Storm…” option + separator)
  • New storm fields (hidden by default, shown when “Create New Storm” selected OR when an existing storm is selected):
    • Storm ID input (e.g., 09L) — hidden for existing storms
    • Storm Name input — label changes based on type:
      • Invest: field hidden (uses ID only)
      • PTC: label = “PTC Number:”, placeholder = “e.g., PTC1”
      • Tropical-Depression: label = “TD Number:”, placeholder = “e.g., TD1”
      • Tropical-Storm/Hurricane: label = “Storm Name:”, placeholder = “e.g., Andrew”
  • Update Type dropdown (5 options; defaults to Hurricane)
  • Filename preview (live-updates, shows validation state)
  • Create & Open Document button (creates .docx via Word COM, opens in Word)
  • Add to Upload Queue button (enabled after document is created)

6.3 Quick Browse: Tropical Updates

  • Year dropdown (populated from listYears(), sorted descending)
  • Storm dropdown (populated from listStorms(year), sorted by numeric ID)
  • Refresh button (🔄 — reloads current storm’s files)
  • Sync Folders button (🔃 — renames all folders in year based on contents)
  • Storm files list (checkboxes, shows filename + modified timestamp, sorted newest-first)
  • Add Selected button (adds checked files to upload queue)

6.4 Drop Zone

  • Drag-and-drop target for .docx files
  • “Browse Other Location” button (opens OS file dialog defaulting to iCloud tropical updates folder)
  • Visual state classes: drop-zone--active, drop-zone--success, drop-zone--error
  • Only .docx files accepted (others rejected with warning)
  • Single global URL input (applies to all queued files in a publish batch)
  • Live validation with ✓/✗ status indicator
  • Video ID preview when valid
  • Accepted URL patterns:
    • youtube.com/watch?v=<11char-id>
    • youtu.be/<11char-id>
    • youtube.com/embed/<11char-id>
    • youtube.com/v/<11char-id>
  • Video ID regex: [A-Za-z0-9_-]{11}

6.6 Queued Files List

  • Valid files show: ✓ icon, filename, parsed title, formatted date, formatted time
  • Conversion indicator (🔄) for auto-converted legacy filenames showing target filename
  • Invalid files show: ⚠ icon, filename, bulleted list of errors
  • Remove button (×) per file
  • “Clear All” button in header

6.7 Actions Section

  • Commit message input (auto-populated with “Add tropical update(s)” based on file count)
  • Upload button (disabled until ≥1 valid file AND commit message present AND not currently publishing)
  • Button text updates: Upload N File(s) based on count

6.8 Status Log

  • Scrolling log of timestamped entries
  • Entry types: info (•), success (✓), warning (⚠), error (✗)
  • Maximum 50 entries retained (older trimmed)
  • Real-time git log streaming via IPC event
  • Shows full destination path: “Files are copied to {fullIncomingPath}”

7. IPC API Surface (preload.jsmain.js)

All renderer-to-main communication via contextBridge.exposeInMainWorld('api', ...):

7.1 Configuration

  • getConfig(){repoPath, incomingPostsPath, fullIncomingPath}

7.2 File Operations

  • copyFiles(fileInfos) — Copies .docx files to incoming/posts/, overwriting if exists. Creates {name}.meta.json sidecar when youtubeId provided. Returns per-file results with overwritten flag.
  • listExistingFiles() — Lists .docx files currently in incoming/posts/.
  • openFileDialog() — OS file picker, filtered to .docx, multiSelect, defaults to iCloud tropical updates path.

7.3 Git Operations

  • publish(files, commitMessage) — See Section 8 for full workflow.
  • getGitStatus() — Returns {success, branch, isClean, staged, modified, not_added}.

7.4 Event Listeners

  • onGitLog(callback) — Subscribes to git:log IPC events (real-time publish logs). Returns unsubscribe function.

7.5 Document Creator

  • createDocument({filename, year, stormFolder}) — Creates Word doc via PowerShell COM, opens in Word. Rejects if file exists.
  • createStormFolder(year, stormFolder) — Creates folder in iCloud tropical updates path.
  • updateStormFolderName(year, currentFolderName) — Renames folder based on contained files’ parsed storm info.
  • syncAllStormFolderNames(year) — Bulk rename for all folders in a year.

7.6 Quick Browse

  • getQuickBrowsePath() → tropical updates base path
  • listYears(){success, years} (directory names matching ^\d{4}$, sorted desc)
  • listStorms(year){success, storms[{folder, id, name, displayName}]} (sorted by numeric ID)
  • listStormFiles(year, stormFolder){success, files[{name, path, size, modified}]} (.docx only, sorted by modified desc)

7.7 Drag-Drop Helper

  • dragDrop.getFilePaths(files) — Extracts .path property from dropped File objects (must be called synchronously in drop handler).

8. Git Publish Workflow (git:publish handler)

Target branch: Always main (hardcoded; ignores DEFAULT_CONFIG.branch)

Step-by-step:

  1. Verify repo. git.checkIsRepo(). Abort if not a git repo.
  2. Log start. “Starting publish process…”
  3. Get current branch via git.branch(). Store as originalBranch.
  4. If NOT on main: a. Check status via git.status() b. If dirty: git stash push -m "tropical-update-publisher-auto-stash". Set didStash = true. c. git checkout main
  5. If already on main: just log “On branch: main”
  6. Pull latest. git pull origin main
  7. Stage files. For each filename:
    • Add incoming/posts/{filename} (forward slashes)
    • If {name}.meta.json sidecar exists, also add it
  8. Check staged. If status.staged.length === 0:
    • Log “No changes to commit” warning
    • Restore branch + pop stash if needed
    • Return {success: true, message: 'No changes to commit'}
  9. Commit. git.commit(commitMessage). Log commit hash.
  10. Push. git push origin main. Log success.
  11. Restore branch. If originalBranch !== main:
    • git checkout {originalBranch}
    • If didStash: git stash pop
  12. Return {success: true, commit: hash, logs: [...]}.

Error handling:

  • On any error: attempts to restore original branch + pop stash
  • Returns {success: false, error: message, logs: [...]}
  • Restore failures are logged but don’t mask original error

Real-time logging:

  • Each step calls addLog(message, type) which pushes to local logs[] AND emits git:log IPC event to renderer for live StatusLog updates

9. Document Creation (Word COM Automation)

Endpoint: doc:create IPC handler in main.js

Process:

  1. Determine destination folder:
    • If year + stormFolder provided: {tropicalUpdatesPath}/{year}/{stormFolder}/
    • Else if only year: {tropicalUpdatesPath}/{year}/
    • Else: {tropicalUpdatesPath}/{currentYear}/
  2. Create destination directory if missing (recursive: true)
  3. Reject if {destDir}/{filename} already exists (success: false, error: "A file with this name already exists")
  4. Write PowerShell script to {os.tmpdir()}/create-word-doc.ps1:
    • Creates Word.Application COM object (invisible)
    • Creates new empty document
    • Saves as .docx (format 16 = wdFormatDocumentDefault)
    • Closes document and quits Word
    • Releases COM object, runs GC
  5. Execute: powershell -NoProfile -ExecutionPolicy Bypass -File {script} with 30-second timeout
  6. Delete temp script
  7. Verify file was created on disk
  8. Open file via shell.openPath() (launches default .docx handler, typically Microsoft Word)

Return: {success, filePath, message} or {success: false, error}


10. Storm Folder Auto-Naming Logic

Function: determineFolderName(stormId, filenames) in main.js

Process:

  1. Parse each filename using parseStormFilename():
    • Matches ^(\d{4}-\d{2}-\d{2}-\d{1,2}[AP]M)-(.+)\.docx$
    • Extracts date-time prefix + remainder
    • Remainder is split by checking for known type prefixes (longest first):
      • Tropical-Depression
      • Tropical-Storm
      • Hurricane
      • Invest
      • PTC
    • Returns {type, name} or null
  2. Track presence of: named storm name, PTC (+ number), TD (+ number)
  3. Named storm detection: name matches ^[A-Za-z]{3,}$ AND NOT ^(TD|PTC)\d+$ AND NOT ^\d+[A-Z]$
  4. Priority decision:
    • If named storm found → {stormId}_{name}
    • Else if TD found → {stormId}_TD{number} (number from filename OR extracted from stormId)
    • Else if PTC found → {stormId}_PTC{number}
    • Else → {stormId} (plain ID, for invests/unknown)

Rename retry logic (storm:updateFolderName):

  • Up to 5 attempts
  • Backoff delays: [500, 1000, 2000, 3000, 5000] ms between retries
  • Retries only on EBUSY, EPERM, EACCES error codes (Word file locks)
  • Returns pendingRename: {from, to} in error if all retries fail, so user can retry via Sync Folders button

11. YouTube Metadata Sidecar Files

When a YouTube URL is entered in the global YouTube section and files are published:

  1. YouTube ID is extracted via regex (see Section 6.5)
  2. For EACH file being copied, a sidecar {name}.meta.json is created:
    {
      "youtube_id": "dQw4w9WgXcQ"
    }
    
  3. Sidecar is placed in incoming/posts/ alongside the .docx
  4. Sidecar is git-added alongside the .docx
  5. Same global YouTube ID is applied to all files in a single publish batch

Note: The UI shows ONE YouTube input (not per-file), so all files in a single upload share the same video ID.


12. File List State & Dedup Rules

  • Queue stored in FileList.files (Map keyed by original filename)
  • Attempt to add a file already in queue (by original filename): rejected with warning “{filename} already in queue”
  • Attempt to add a file whose converted name collides with existing converted name in queue: rejected with warning
  • Valid files have full parsed metadata: {date, dateFormatted, time, timeFormatted, slug, title, filename}
  • Invalid files retain their errors array for display
  • Per-file YouTube URL field exists in FileList data model but UI uses global YouTube input (per-file input not currently rendered)

13. Status Log Behavior

  • Max 50 entries, FIFO eviction
  • Types: info, success, warning, error
  • Icons: , , ,
  • Timestamps: en-US 12-hour with seconds (10:42:15 PM)
  • Auto-scrolls to bottom on new entry
  • set() replaces all entries (used for “Ready” states)
  • add() appends (used for ongoing operations)
  • clear() empties all entries
  • Git publish real-time updates stream in via git:log IPC subscription

14. Git Status Indicator

  • Green dot (--connected): on a branch, no uncommitted changes
  • Yellow dot (--warning): on a branch, has uncommitted changes (text shows “(uncommitted changes)”)
  • Red dot (--error): git status check failed or not in a repo
  • Checked on app startup and after each publish completion

15. Assets

  • assets/WMB_Logo.png — app header logo
  • assets/icon.ico — window icon and installer icon
  • assets/README.md — asset documentation

16. Build & Distribution

  • Target: Windows (build:win script)
  • Installer: NSIS
  • Options:
    • oneClick: false (user sees install wizard)
    • allowToChangeInstallationDirectory: true
  • Output: dist/ folder
  • Icon: assets/icon.ico

17. Platform / Environment Requirements

  • OS: Windows (relies on PowerShell + Word COM automation)
  • Node.js: 18+ (per README)
  • npm: 9+ (per README)
  • Microsoft Word: required for document creation (Word COM)
  • PowerShell: required (uses powershell -NoProfile -ExecutionPolicy Bypass)
  • Git: must be installed and configured with WxManBran repo credentials
  • iCloud Drive: Tropical Updates_Briefings folder must be accessible at the hardcoded path

18. Known Hardcoded Values (Non-Config)

  • Repo path is derived relatively (../.. from tool dir) — breaks if tool is moved
  • iCloud tropical updates path is hardcoded to keen4 user
  • Target branch is hardcoded to main (ignores DEFAULT_CONFIG.branch)
  • Timezone label is hardcoded to “ET” in time display
  • Commit message auto-text is hardcoded to “Add tropical update(s)”
  • Auto-stash message is hardcoded to “tropical-update-publisher-auto-stash”
  • PowerShell script temp path is hardcoded to {tmpdir}/create-word-doc.ps1
  • PowerShell script timeout is 30 seconds

19. REQUIRED FIXES (Must Apply in Rebuild)

All 5 items below MUST be implemented in the rebuilt tool. These address quality gaps found in the v1.0 audit.

R1 (was D1): Use DEFAULT_CONFIG.branch as the single source of truth

  • Current flaw: DEFAULT_CONFIG.branch = 'main' is defined but the git:publish handler hardcodes its own const targetBranch = 'main' (ignoring config).
  • REQUIRED: git:publish must read the target branch from DEFAULT_CONFIG.branch. Remove the local hardcoded targetBranch constant. Update log messages that reference “main” explicitly to use the config value.
  • Acceptance: Changing DEFAULT_CONFIG.branch in one place changes the publish target. No hardcoded 'main' string in the publish workflow.

R2 (was D2): Make YouTube URL per-file, expose in UI

  • Current flaw: FileList.files entries have a youtubeUrl field, but the UI only exposes ONE global input. All files in a publish batch share the same video ID. Per-file data model is unused.
  • REQUIRED: Add a per-file YouTube URL input inside each queued file row in the FileList UI. The existing global YouTube input should be removed OR repurposed as a “apply to all” quick-fill convenience (user decides; both acceptable). Each file’s per-file URL is what gets written to its .meta.json sidecar on publish.
  • Acceptance: User can queue 3 files, enter 3 different YouTube URLs, publish, and each .meta.json contains the correct video ID for its file.

R3 (was D3): Fix code-comment drift in folder priority logic

  • Current flaw: Code comment in determineFolderName() says “Priority: Named Storm > PTC > TD > Invest” but the actual code checks TD before PTC.
  • REQUIRED: Update the comment to match the code: “Named Storm > TD > PTC > Invest”. Keep the actual priority logic unchanged (TD is checked before PTC today and that is the correct behavior).
  • Acceptance: Comment and code agree.

R4 (was D4): Make timezone label configurable

  • Current flaw: Time display is hardcoded to “ET” in FileValidator.parseFilename.
  • REQUIRED: Add a timezoneLabel field to DEFAULT_CONFIG (default: "ET"). parseFilename reads this value instead of the hardcoded string. No UI setting needed in v2; config-only for now.
  • Acceptance: Changing DEFAULT_CONFIG.timezoneLabel changes the label in all displayed times.

R5 (was D5): Make paths portable (no hardcoded usernames)

  • Current flaw: tropicalUpdatesPath is hardcoded to C:\Users\keen4\iCloudDrive\.... Tool fails for any other user.
  • REQUIRED:
    1. Create a config.json file (at tool root) that stores user-specific paths (tropicalUpdatesPath, any future per-machine values).
    2. On first launch, if config.json is missing, show a first-run setup dialog prompting user for the iCloud tropical updates path (default-fill with %USERPROFILE%\iCloudDrive\Documents\UNITED_WxManBran\Tropical Updates_Briefings). Save user’s choice to config.json.
    3. On subsequent launches, load paths from config.json.
    4. Add a “Settings” menu option to change the path later.
    5. config.json must be added to .gitignore so per-user config doesn’t leak into the repo.
  • Acceptance: Tool runs on a machine with a different username without any code edits. First-run dialog handles the setup.

20. Phase 2 Features — NHC Monitoring + AI-Drafted Updates

Goal: Transform the tool from a manual publisher into a semi-automated briefing generator. The tool monitors NHC for active storm updates, drafts consistent WxManBran-formatted briefings using an AI backend, sends drafts via email for review, accepts revision instructions, and publishes on approval. All existing drag-drop / manual publish behavior remains fully intact.

20.1 NHC Monitor (Background Service)

A background service running inside the Electron app’s main process that polls NHC public products and detects changes.

Default polling cadence: every 5 minutes during an active Atlantic hurricane season, with exponential backoff if NHC endpoints are slow/down.

NHC data sources consumed (all ENABLED by default; each togglable in Settings):

Source Code Product Name Endpoint Pattern Why It Matters
TCPAT{n} Public Advisory https://www.nhc.noaa.gov/text/refresh/MIATCPAT{n}+shtml/...TCPAT{n}.shtml Main narrative advisory (every 6 hrs, intermediates every 3 hrs near landfall)
TCMAT{n} Forecast Advisory https://www.nhc.noaa.gov/text/refresh/MIATCMAT{n}+shtml/...TCMAT{n}.shtml Structured data: center, wind radii, intensities, forecast points
TCDAT{n} Forecast Discussion https://www.nhc.noaa.gov/text/refresh/MIATCDAT{n}+shtml/...TCDAT{n}.shtml Meteorologist reasoning, model analysis, confidence
TCUAT{n} Position Estimate / Intermediate https://www.nhc.noaa.gov/text/refresh/MIATCUAT{n}+shtml/...TCUAT{n}.shtml Quick position updates between main advisories
WTNT{n}/WOCA{n} Watches & Warnings https://www.nhc.noaa.gov/text/refresh/MIAWTNT{n}+shtml/...WTNT{n}.shtml Watch/warning changes, landfall updates
Graphics Cone, wind field, surge https://www.nhc.noaa.gov/storm_graphics/AT{nn}/... Attached to briefing, not parsed (image embed)
Active Storms Index JSON feed https://www.nhc.noaa.gov/CurrentStorms.json Authoritative list of active basin storms for auto-discovery
RSS Feed XML https://www.nhc.noaa.gov/index-at.xml Fallback change-detection stream for Atlantic basin

HTTP client rules:

  • User-Agent header set to WxManBran-Tropical-Publisher/2.0 ([email protected]) so NHC can contact if needed
  • Respect Cache-Control and If-Modified-Since headers (send them on re-fetch)
  • Timeout: 15 seconds per request
  • Exponential backoff on failure: 30s → 60s → 120s → 300s (cap at 5 min)
  • Rate limit: max 1 request per endpoint per minute

Change detection:

  • Each fetched product is fingerprinted (SHA-256 hash of the text body after normalization)
  • Fingerprints stored in C:/Users/keen4/WxManBran/tools/tropical-update-publisher/state/nhc-fingerprints.json
  • When a fingerprint changes OR a new advisory number appears OR a Special Advisory is detected → trigger draft pipeline
  • Inter-advisory special notices are distinguished from scheduled releases in the dashboard

Storm tracking:

  • Monitor automatically discovers all active Atlantic basin storms from NHC current products page
  • User can override: manually add invest IDs (e.g., 94L) or exclude specific storms from monitoring
  • Monitor state persisted: which storms active, last advisory number per storm, last fetch time

20.2 AI Drafter (Model-Agnostic Draft Generator)

When the Monitor detects a change, the Drafter invokes the configured AI backend to produce a WxManBran-formatted briefing document.

Supported AI backends:

Backend CLI / API Default Model Invocation (exact)
Claude (default) Claude CLI via user’s Anthropic subscription claude-opus-4-6 with high thinking budget claude -p "{prompt_file}" --model claude-opus-4-6 --thinking-budget 32000 --output-format stream-json --dangerously-skip-permissions
Cursor Cursor agent CLI composer-2 agent -p "{prompt_file}" --model composer-2 --output-format stream-json --trust
Codex Codex CLI gpt-5.4-high (highest reasoning effort) codex exec -p "{prompt_file}" --model gpt-5.4-high --output-format stream-json

Prompt passing convention: Write full prompt to temp file, pass file path via -p {path}. Avoids shell-escaping issues with multi-line prompts containing quotes, backticks, code blocks, and Unicode.

  • Backend choice stored in config.json (draftBackend: "claude" | "cursor" | "codex") with per-backend model override
  • Settings UI allows switching backend and model per session without restart

Drafting prompt inputs (assembled by the Drafter):

  1. House style guide (stored at {repoRoot}/tools/tropical-update-publisher/docs/house-style.md). REQUIRED BUILD TASK: during the rebuild, an agent MUST generate a starter house-style.md by reading all 7 .docx files in C:/Users/keen4/iCloudDrive/Documents/UNITED_WxManBran/Tropical Updates_Briefings/2025/09L_Imelda/ (converting via mammoth or docx-parser) and extracting: voice/tone patterns, section ordering, recurring phrases, terminology conventions, numerical formatting norms, and heading structure. The output is a markdown file the user can refine manually after launch. This file is committed to the repo and versioned.
  2. Briefing templates (read at C:/Users/keen4/iCloudDrive/Documents/UNITED_WxManBran/WxManBran.com_Templates/):
    • United_Tropical_Update_Template_(Blog_Posts).docx — primary blog-post format
    • United_Tropical_Update_Template_(Watches_Warnings_Impacts).docx — used when W/W are active
    • Previous_Storms_Template.docx — historical context template
  3. Few-shot examples (the 5 most recent .docx updates from the current storm’s folder, e.g. Tropical Updates_Briefings/2025/09L_Imelda/*.docx)
  4. Raw NHC sources (fetched text for all enabled product codes for the specific storm)

Drafting rules (enforced in the system prompt):

  • NEVER copy NHC text verbatim. Re-state in WxManBran voice.
  • NEVER invent a number, wind speed, pressure, lat/lon, or forecast point that is not in the NHC source material.
  • Use the template’s section structure exactly; do not introduce new sections.
  • Highlight any discrepancy between previous advisory and current (intensity change, track shift, new watch/warning).
  • If confidence is low (contradicting NHC sources, unclear language), flag it with a [NEEDS REVIEW: ...] marker inline.
  • Preserve all watches/warnings as issued by NHC; do not interpret or re-classify.
  • Date-time stamps must reference the NHC advisory time, not the draft time.

Output:

  • Draft .docx written directly to the storm’s folder in Tropical Updates_Briefings
  • Filename auto-generated per existing convention: YYYY-MM-DD-TIME-Type-Name.docx
  • Accompanying .draft-meta.json sidecar tracking: source advisory numbers consumed, AI backend/model used, confidence flags raised, generation timestamp, human review state

20.3 Review & Approval Workflow

Every draft goes through human-in-the-loop review by DEFAULT. User can change this in Settings:

Setting: approvalMode Behavior
review (DEFAULT) Draft generated → email sent → user replies yes/no → publishes only on “yes”
auto-publish Draft generated → auto-pushed to site with no review (use with caution)
dashboard-only Draft generated → appears in dashboard for manual review → no email

Email review flow (when approvalMode: review):

  1. Draft is generated and saved to storm folder
  2. Tool sends an email to user (configured reviewerEmail in config.json) with:
    • Subject: [WxManBran Draft] {storm_name} — {advisory_type} — {date-time}
    • Body: Full draft text inline (plus link to open local .docx)
    • Diff view: shows what changed vs. previous advisory for same storm
    • Reply instructions: “Reply YES to publish, NO to request changes”
  3. Tool polls the reply inbox (IMAP) every 1-2 minutes for a reply
  4. On YES reply: tool auto-publishes via existing git workflow (copy to incoming/posts/, commit, push to main). User receives confirmation email.
  5. On NO reply: tool parses user’s revision instructions from email body. Sends revision instructions + original draft + original NHC source back to the AI backend. New draft is generated. New email is sent. Loop continues until user replies YES or explicitly cancels with reply “CANCEL”.
  6. Each revision cycle is logged with: instructions received, changes made, new draft fingerprint.

Email configuration (in config.json):

  • reviewerEmail (where drafts are sent)
  • senderEmail + senderPassword (SMTP credentials OR OAuth token for Gmail/Outlook)
  • imapInbox (where to watch for replies; typically same as senderEmail)
  • maxRevisionCycles (default: 10 — safety cap on infinite loops)

Fallback if no email reply within time window (configurable, default 4 hours):

  • Draft stays in dashboard as “pending review”
  • User can approve/revise via dashboard manually at any time

Draft filename-collision rule (storm folder):

  • If AI-drafter target filename {date}-{time}-{type}-{name}.docx already exists in the storm folder (because user manually created one), the AI draft is saved with suffix -ai-draft.docx instead
  • Dashboard shows both files side-by-side; user decides which to publish
  • Never overwrite a user-created document automatically

Draft resume on app restart:

  • All in-flight drafts have state persisted to state/draft-queue.json after each phase (fetching, generating, emailing, awaiting-reply, revising)
  • On app launch, resume any drafts in non-terminal states:
    • fetching → restart from NHC fetch
    • generating → re-submit to AI backend (idempotent — uses same prompt)
    • awaiting-reply → resume IMAP polling
    • revising → re-submit revision prompt
  • Drafts in terminal states (approved, rejected, cancelled, published) are archived to state/draft-history/
  • Any draft older than 48 hours in a non-terminal state is auto-cancelled with a notification

AI backend cost controls (required for API-billed backends):

  • config.json has aiCostLimits:
    • dailyTokenCap (default: 500000) — halt drafting when exceeded
    • weeklyTokenCap (default: 3000000)
    • dailyDollarCap (default: 20) — for API backends with known per-token pricing
  • When a cap is hit: all in-flight drafts complete, no new drafts generated, dashboard shows warning banner
  • Caps reset at midnight local time (daily) and Sunday midnight (weekly)
  • User can override/disable caps per Settings, with a confirmation dialog
  • CLI-subscription backends (Claude CLI on Anthropic subscription, Cursor CLI) bypass dollar caps but still respect token caps

20.4 Rich Dashboard

New top-level view added to the Electron app alongside existing drop-zone/browse UI. Toggleable via menu; optional sections user can add/hide.

Available dashboard sections (all toggleable via Settings):

Section Content
Active Storms All storms currently being monitored; latest advisory number, intensity, position; WWR color-coded
Pending Drafts AI-generated drafts awaiting review with YES/NO/EDIT actions
Draft Pipeline Status Currently running draft jobs, queue, last completion time
Publish History (Current Season) All published updates this season with per-storm counts, links to git commits
NHC Monitor Status Last fetch time per product, errors, rate-limit state
Inter-Advisory Alerts Special advisories and off-schedule NHC releases detected since last regular advisory
Revision History Per-draft revision cycles: what was requested, what changed
AI Backend Stats Calls made, tokens used, cost estimate (if API-billed), model performance notes
Quick Actions Force-refresh NHC, pause monitor, force-draft for a storm, manual NHC-source fetch

User preferences:

  • Each section can be shown/hidden via a Settings panel with checkboxes
  • Section order drag-and-drop customizable
  • Settings persisted to config.json under dashboard.sections

20.5 New Configuration Additions (config.json)

{
  "tropicalUpdatesPath": "C:\\Users\\{user}\\iCloudDrive\\...\\Tropical Updates_Briefings",
  "timezoneLabel": "ET",
  "branch": "main",
  "nhcMonitor": {
    "enabled": true,
    "pollIntervalMinutes": 5,
    "enabledProducts": ["TCPAT", "TCMAT", "TCDAT", "TCUAT", "WTNT", "WOCA", "graphics", "special"],
    "manuallyWatchedStorms": [],
    "excludedStorms": []
  },
  "draftBackend": "claude",
  "backendModels": {
    "claude": "claude-opus-4-6",
    "cursor": "composer-2",
    "codex": "gpt-5.4"
  },
  "approvalMode": "review",
  "reviewerEmail": "keen4@...",
  "senderEmail": "drafts@...",
  "senderProvider": "gmail-oauth2",
  "imapInbox": "drafts@...",
  "maxRevisionCycles": 10,
  "reviewTimeoutHours": 4,
  "dashboard": {
    "sections": ["activeStorms", "pendingDrafts", "draftPipeline", "publishHistory", "nhcStatus", "alerts", "revisions", "aiStats", "quickActions"],
    "sectionOrder": ["..."]
  }
}

20.6 New Dependencies Required

  • nodemailer — SMTP sending for draft emails
  • imap or imapflow — inbox polling for replies
  • node-fetch — NHC product fetching (if not using built-in fetch)
  • docx or mammoth — read Word templates and few-shot examples for AI prompt assembly
  • officegen or similar — generate new .docx with template-matching format
  • node-cron or custom timers — polling scheduler

20.7 New IPC Endpoints

Channel Purpose
nhc:getMonitorStatus Dashboard query for monitor health
nhc:getActiveStorms List of currently tracked storms
nhc:forceFetch(stormId) Manual fetch trigger
nhc:pauseMonitor() / nhc:resumeMonitor() Toggle monitor
draft:listPending Dashboard query
draft:generate(stormId, advisoryNum) Manual draft trigger
draft:approve(draftId) Approve and publish
draft:revise(draftId, instructions) Request revision
draft:reject(draftId) Drop draft
draft:getHistory(stormId) Revision log
email:testConnection() Verify SMTP/IMAP setup
settings:get / settings:update Config management
ai:testBackend(backend) Verify AI backend CLI/API works

20.8 Failure Modes & Safeguards

  • NHC unreachable: Monitor keeps retrying with backoff; dashboard shows warning; no drafts attempted while down
  • AI backend unavailable: Draft job queued with “waiting for backend” status; user notified after 15 min
  • Email service down: Draft saved to dashboard-only state; user sees it next time they open the app
  • Infinite revision loop: Hard-capped by maxRevisionCycles (default 10)
  • Ambiguous/missing NHC data: Draft flagged with [NEEDS REVIEW] markers; never auto-publishes even if approvalMode: auto-publish
  • Hallucinated number detection: Post-draft sanity check — compare all numeric values in draft against extracted NHC values; flag mismatches
  • Storm ends / dissipates: Monitor detects final advisory, removes storm from active list after 72 hours, archives fingerprints

20.9 Phase 2 Acceptance Criteria

  1. During an active storm, user can leave the app running and receive an email draft within 10 minutes of each NHC advisory release.
  2. User can reply YES via email and see the briefing live on WxManBran within 2 minutes.
  3. User can reply NO with revision instructions and receive an updated draft within 3 minutes.
  4. Dashboard accurately reflects all active storms and their last-received advisory at a glance.
  5. Special/intermediate advisories trigger drafts and flagged alerts within one polling cycle.
  6. User can switch between Claude, Cursor, and Codex backends with a single Settings change and the next draft uses the new backend.
  7. No published briefing contains a wind speed, pressure, position, or forecast point that does not appear in the source NHC products it consumed.
  8. All Phase 1 manual drag-drop / browse / publish workflows continue to function unchanged.

21. File Inventory

File Purpose LoC
main.js Electron main process, IPC handlers, git ops ~897
preload.js Context bridge API exposure ~170
index.html Single-page UI markup ~229
src/app.js Renderer entry, app init, YouTube handling ~261
src/services/fileValidator.js Filename format validation + conversion ~259
src/components/DocCreator.js Document creator UI component ~572
src/components/QuickBrowse.js Year/Storm/Files browser ~338
src/components/FileList.js Queued files display ~295
src/components/DropZone.js Drag-drop handling ~159
src/components/Publisher.js Publish button + workflow coordinator ~208
src/components/StatusLog.js Status log display ~153
package.json npm + electron-builder config ~36
launcher.bat Windows batch launcher -
launcher.vbs VBScript launcher (hides console) -
create-shortcut.ps1 Desktop shortcut installer -
convert-icon.ps1 Icon conversion helper -

23. GUI / UX Polish Requirements (Professional Rebuild)

Goal: Transform the tool from a functional-but-plain utility into a polished desktop application that looks and feels professional. The current UI uses vanilla JS, flat CSS, and emoji icons. The rebuild modernizes all of that.

23.1 Tech Stack (Required)

  • React 18 + TypeScript (strict mode) for all UI
  • Vite for development build (fast HMR)
  • Tailwind CSS + shadcn/ui component library for consistent design
  • Framer Motion for animations and transitions
  • Lucide React for icons (replace ALL emoji in UI chrome)
  • Radix UI primitives underneath shadcn/ui for accessible dropdowns, dialogs, toasts, tooltips
  • TanStack Query (React Query) for IPC data fetching, caching, and revalidation
  • Zustand for cross-component state (settings, queue, dashboard prefs)
  • date-fns for all date/time formatting
  • react-hotkeys-hook for keyboard shortcuts

23.2 Design System

Color palette (dark mode primary, light mode supported):

  • Base: neutral grays (slate-950 → slate-50)
  • Primary accent: deep blue (#1e40af) — WxManBran brand-adjacent
  • Storm-red accent: (#dc2626) for warnings, hurricane severity, errors
  • Success green: (#16a34a)
  • Warning amber: (#d97706)
  • Info cyan: (#0891b2)

Severity color-coding for storms/watches/warnings:

  • Tropical Depression → blue-500
  • Tropical Storm → cyan-400
  • Hurricane Cat 1-2 → amber-500
  • Hurricane Cat 3 → orange-600
  • Hurricane Cat 4 → red-600
  • Hurricane Cat 5 → red-900
  • Watch → amber background
  • Warning → red background

Typography:

  • Sans: Inter (fallback: system)
  • Mono: JetBrains Mono (for filenames, commit hashes, paths)
  • Type scale: 12 / 14 / 16 / 18 / 20 / 24 / 30 / 36 px
  • Weights: 400, 500, 600, 700

Spacing: Tailwind default scale (4px base) Border radius: rounded-md (6px) for cards, rounded-lg (8px) for dialogs, rounded-full for pills/badges Shadows: subtle (shadow-sm on cards, shadow-xl on dialogs/popovers)

23.3 Layout & Navigation

Primary layout: Sidebar + main content area (NOT a long scrolling page like v1).

Sidebar sections:

  1. Publish (existing drop-zone + queue workflow)
  2. Drafts (pending AI drafts awaiting review)
  3. Storms (active storm dashboard)
  4. History (publish log, season archive)
  5. Monitor (NHC status, alerts)
  6. Settings (config, email, backends, dashboard layout)

Sidebar behavior:

  • Collapsible to icon-only mode (save screen real estate)
  • Badge counters on sidebar items (e.g., “Drafts (3)” when 3 pending reviews)
  • Active route highlighted with accent bar
  • Keyboard-navigable

Status bar (persistent footer):

  • Monitor state indicator (green dot = active, yellow = degraded, red = paused/error)
  • Current AI backend + model name
  • Last git push timestamp
  • Active storms count
  • Current git branch
  • Network connectivity indicator

Top bar:

  • App logo + name left-aligned
  • Global search (⌘K) center
  • Theme toggle (dark/light) right
  • Quick-publish button right (if queue has files)

23.4 Motion & Feedback

Transitions:

  • Route/view changes: 200ms ease-out fade + slide
  • Cards appearing in lists: staggered 50ms fade-in
  • Modal open/close: scale + fade (Radix default)
  • Sidebar collapse: 150ms width transition

Micro-interactions:

  • File dropped: border pulse animation + success checkmark fly-in
  • File removed from queue: fade out + collapse
  • Publish in progress: button morphs to progress bar
  • Draft received: toast slides in from top-right with spring physics
  • Storm card hover: subtle lift (translateY -2px + shadow increase)

Loading states:

  • Skeleton screens for Storm cards, Draft lists, History (NOT spinners where avoidable)
  • Inline spinners only for sub-1-second operations (button clicks)
  • Progress bars with percentage for multi-step operations (publish, draft generation)

Toasts (Radix Toast via shadcn):

  • Top-right corner
  • Auto-dismiss 4s (success), 8s (error), manual-dismiss for critical
  • Types: success, error, warning, info, loading
  • Stack gracefully (max 5 visible)
  • Click-to-dismiss and undo action support

23.5 Component Polish

Drop zone:

  • Dashed border baseline, becomes solid + pulsing when dragging valid files over it
  • Background gradient shifts subtly on hover
  • Shows file icons + names mid-drag (preview of what will be added)
  • Ripple effect on drop
  • Invalid file type: shake animation + red border flash

File queue item (each queued file):

  • Card with expansion toggle for details
  • Left: file icon (Word doc) + validation status badge
  • Center: filename (mono font), parsed title (sans), metadata badges (date, time, storm)
  • Right: per-file actions (remove, edit YouTube URL, view parsed metadata)
  • Drag handle for reordering
  • Checkbox for batch select
  • Conversion badge if legacy filename auto-converted
  • Inline YouTube URL input with live validation (✓ green border or ✗ red border)

Storm card (dashboard):

  • Card with storm ID + name header + severity-colored left border
  • Mini sparkline of intensity history (wind speed over last 5 advisories)
  • Cone graphic thumbnail (from NHC graphics)
  • Last advisory number + time since issued
  • Watch/warning badges
  • Current Cat/SS/TS badge
  • “Draft update” quick-action button
  • Click to expand into full detail panel

Draft review card:

  • Side-by-side layout: NHC source (left) / AI draft (right)
  • Syntax highlighting on numeric values (so you can visually compare at a glance)
  • Diff indicators showing what AI added/changed vs. template
  • [NEEDS REVIEW] markers highlighted yellow
  • Inline edit button per paragraph
  • Bottom action bar: Approve Revise (opens instruction input) Reject Open in Word

Dashboard cards (generic):

  • All cards share design language: header with icon + title + optional count badge, body, footer with timestamp
  • Hover state: subtle shadow lift
  • Click to expand for more detail
  • Drag handles for user to rearrange
  • Toggle-hide from Settings

23.6 Accessibility Requirements (WCAG 2.1 AA)

  • All interactive elements keyboard-reachable via Tab; skip-to-content link
  • Focus indicators visible (ring-2 ring-primary-500)
  • ARIA labels on all icon-only buttons
  • Color contrast ratio 4.5:1 minimum for text (7:1 for small text in dark mode)
  • Screen reader announcements for toasts and state changes (aria-live regions)
  • Color is never the sole indicator (watches/warnings also have icons + text)
  • Color-blind friendly palette (tested against deuteranopia and protanopia)
  • Reduced-motion mode: respects prefers-reduced-motion, disables non-essential animations
  • Font size scales with user’s OS setting

23.7 Keyboard Shortcuts (Documented in Settings > Shortcuts)

Shortcut Action
⌘K / Ctrl+K Open command palette
⌘P / Ctrl+P Go to Publish view
⌘D / Ctrl+D Go to Drafts view
⌘S / Ctrl+S Go to Storms view
⌘H / Ctrl+H Go to History
⌘, / Ctrl+, Open Settings
⌘Enter / Ctrl+Enter Publish current queue
Del Remove selected file(s) from queue
⌘A / Ctrl+A Select all files in queue
Esc Close dialog / cancel operation
⌘N / Ctrl+N New document (opens Doc Creator)
⌘R / Ctrl+R Refresh current view
⌘/ / Ctrl+/ Toggle sidebar collapse
⌘⇧D / Ctrl+Shift+D Toggle dark mode

23.8 Command Palette (⌘K)

Fuzzy-searchable command launcher modeled on VS Code / Linear:

  • Navigate to any view
  • Execute actions: “Publish queue”, “Force draft for Imelda”, “Open Settings > Email”
  • Recent commands at top
  • Keyboard-only flow (Tab, Arrow keys, Enter)

23.9 Empty States

Every view has a designed empty state:

  • Publish view (no files): illustration + “Drop files here or browse” CTA
  • Drafts (no pending): “No drafts awaiting review. Monitor is {state}.”
  • Storms (no active): “No active storms in the Atlantic basin right now.”
  • History (new season): “No publishes this season yet.”
  • Quick onboarding links in each empty state

23.10 Onboarding Wizard (First Launch)

Step-by-step walkthrough on very first launch (skippable):

  1. Welcome + tool overview
  2. Set iCloud tropical updates path (with browse button)
  3. Verify git repo connection + branch
  4. Pick AI backend (Claude default) + test connection
  5. Configure email reviewer (optional — can skip and add later)
  6. Dashboard layout: pick which sections to show
  7. Theme preference (dark/light/system)
  8. Review shortcuts
  9. “You’re ready!” → opens to Publish view

Completion state saved in config.json as onboardingComplete: true.

23.11 Error Recovery UX

  • All errors shown as toasts with: clear message + suggested action + “Copy details” button (for bug reports)
  • Retry buttons on failed operations (publish, draft, email send, NHC fetch)
  • Never show raw stack traces to user — log them to state/errors.log and summarize in UI
  • Friendly language: “NHC website is not responding” instead of “ECONNREFUSED tcp://…”
  • Critical errors persist in a dismissible banner at top of window until resolved

23.12 Context Menus (Right-Click)

  • On a queued file: Remove / Preview in Word / Copy filename / Edit YouTube URL / Move to top
  • On a storm card: Force draft / Mute notifications / Exclude from monitoring / Open folder
  • On a draft: Approve / Revise / Reject / Open source / Copy diff
  • On a published item in history: Open in Word / Copy commit URL / View on WxManBran

24. Additional Intelligent Features

Features beyond basic NHC ingestion that genuinely elevate the tool.

24.1 Storm Intelligence

  • Intensity trajectory chart — sparkline on each storm card showing wind history
  • Track map preview — embedded leaflet/mapbox map of the NHC cone for each active storm
  • Comparison mode — overlay current storm against historical analogs (find storms with similar track/intensity in same area)
  • Peak-intensity forecast highlight — dashboard pill showing “Peak: Cat 4 expected in 36h”

24.2 Publishing Intelligence

  • Auto-commit-message — tool generates git commit messages from the file’s parsed metadata: "09L Imelda — Hurricane update, 10-01 12PM" instead of generic “Add tropical update(s)”
  • Smart scheduling — schedule a publish for a specific time (e.g., publish drafted update at the top of the next hour)
  • Multi-destination publish — push same briefing to main WxManBran + optionally: Twitter/X thread, Facebook post, Discord webhook, email newsletter (each toggleable)
  • Social media card preview — generated Open Graph image per briefing showing storm name + key stats
  • Published-to-live check — after git push, tool polls WxManBran.com to confirm briefing is actually live, alerts if not within 5 min

24.3 Content Quality

  • Readability scoring — Flesch-Kincaid / grade-level of each draft, flags if wildly inconsistent with your norm
  • Terminology consistency checker — house style linter (e.g., always “landfall” not “touchdown”, always “m.p.h.” not “mph”, per your preference)
  • Number sanity-check — validates all wind speeds, pressures, lat/lon values against NHC source + plausibility ranges
  • Spelling + grammar — quiet background check using retext/write-good
  • Duplicate content detection — flags if draft is ≥70% similar to prior briefing for same storm (might indicate stale NHC data or drafter loop)

24.4 Historical & Season Features

  • Season archive browser — dedicated view for past seasons with all storms, publish counts, storm summaries
  • Storm timeline view — visual timeline of an entire storm’s life from first advisory to dissipation with all published updates pinned
  • Annual report generator — end-of-season PDF summary of all storms + briefings published that year
  • Template evolution tracking — diff each briefing against previous one for same storm to show what changed

24.5 Notifications & Alerts

  • OS-native desktop notifications — on draft ready, publish success, NHC special advisory, monitor error
  • Sound alerts (optional, togglable) — different sounds for different event severities
  • Do-not-disturb schedule — mute notifications overnight, except for CRITICAL (special advisory, hurricane warning issued)
  • Mobile push (stretch) — companion mobile notification app or Pushover/ntfy integration

24.6 Quality of Life

  • Auto-save queue state — if app crashes, queue is restored on next launch
  • Undo last publish — one-click revert the most recent git push (reset to prior commit + force-push with confirmation)
  • Word document template diff — tool warns if you’ve modified a base template in a way that would break AI drafting
  • Offline mode — queue up drafts + drop files offline, tool auto-syncs when back online
  • Backup config — one-click export of config.json (minus credentials) for moving to a new machine
  • Update checker — tool checks for newer versions on startup (once per day, non-intrusive)

24.7 Audit & Compliance

  • Full audit log — every action (published, drafted, revised, fetched) logged with timestamps in state/audit.log
  • Source attribution tracking — each published .docx has embedded metadata: which NHC products were consumed, which AI backend generated it, revision count
  • Draft fingerprinting — SHA-256 hash of every draft version stored; detects tampering
  • Revision diff preservation — keep all prior draft versions for 90 days, viewable in History

25. Out of Scope (v2 — Explicitly NOT in this rebuild)

Items the v2 rebuild does NOT deliver (so agents do not speculatively add them):

  • Mobile companion app / mobile push notifications — explicitly deferred to v3
  • macOS or Linux support — Windows only (PowerShell + Word COM dependencies)
  • Multi-user / team collaboration — single-user desktop tool
  • Multi-repo / multi-site publishing to unrelated websites — WxManBran repo only (multi-destination SOCIAL posting in 24.2 is in scope)
  • Document-contents validation (grammar of briefing prose before publish) — readability scoring IS in scope (24.3), but no hard-gate grammar check
  • Live-site hosting or preview — tool publishes via git; rendering is the site’s job
  • YouTube uploading — tool only captures a video ID for metadata; does NOT upload to YouTube
  • Model training / fine-tuning — uses off-the-shelf AI backends only
  • Telemetry / analytics / user tracking — zero telemetry by default
  • Auto-update mechanism — update CHECKER is in scope (31.4); actual binary auto-update is not
  • Merge-conflict resolution UI — tool pulls before push; on conflict, surfaces error to user for manual resolution

In-scope features that SUPERSEDE the v1 “does not do” list (informational, not a contradiction):

  • v1 did NOT auto-pull NHC advisories; v2 DOES (Section 20.1)
  • v1 did NOT post to social media; v2 DOES support multi-destination publishing (Section 24.2)
  • v1 did NOT schedule publishes; v2 DOES (Section 24.2)
  • v1 did NOT track publish history or undo; v2 DOES (Sections 24.2, 24.6)
  • v1 did NOT render/preview Word docs; v2 renders plain-text previews for review
  • v1 had NO per-file YouTube URL; v2 DOES per R2 (Section 19)

26. AI Prompt Templates (Required — Do NOT Improvise)

The Drafter MUST assemble prompts using the exact template structure below. Agents may refine wording but MUST preserve all sections and ordering.

26.1 System Prompt (sent to AI backend)

You are a meteorologist-writer for WxManBran.com, a trusted regional weather
briefing site focused on the Atlantic tropical basin. Your job is to rewrite
official NHC advisories into consistent WxManBran-voice briefings that preserve
100% of factual content.

ABSOLUTE RULES:
1. NEVER invent, estimate, or round any number (wind speed, pressure, lat/lon,
   forecast time, forecast position). Use exact values from NHC source material.
2. NEVER copy NHC text verbatim. Re-state every sentence in WxManBran voice.
3. NEVER contradict, re-interpret, or re-classify watches and warnings.
4. If a fact is ambiguous or missing from source, insert `[NEEDS REVIEW: reason]`
   inline. Do not guess.
5. Follow the WxManBran house style guide exactly. Follow the template structure
   exactly. Do not add or remove sections.
6. Reference times using the advisory time, not the current time.
7. Output is a complete briefing document matching the supplied template format.

You will receive: the house style guide, the active briefing template, 5 recent
WxManBran briefings for this storm (as style examples), and the raw NHC source
products for the current advisory cycle. Produce the briefing as your entire
response, with no preamble or commentary.

26.2 User Prompt Structure (sent per draft job)

## STORM
{storm_id} — {storm_name} — Advisory #{advisory_number}

## HOUSE STYLE GUIDE
<full contents of docs/house-style.md>

## TEMPLATE TO USE
<full text of the selected Word template, converted to plain text>

## RECENT BRIEFINGS FOR THIS STORM (STYLE EXAMPLES)
--- EXAMPLE 1: {filename} ---
<full text>
--- EXAMPLE 2: {filename} ---
<full text>
(...up to 5...)

## NHC SOURCE PRODUCTS (FOR THIS ADVISORY CYCLE)
--- TCPAT{n}: Public Advisory ---
<full text>
--- TCMAT{n}: Forecast Advisory ---
<full text>
--- TCDAT{n}: Forecast Discussion ---
<full text>
(...all enabled products...)

## PRIOR ADVISORY (FOR CONTEXT / DIFF)
Advisory #{prior_number} was issued at {prior_time}.
Key changes: {computed diff — intensity, position shift, watches added/removed}

## TASK
Produce a complete WxManBran briefing for Advisory #{advisory_number}.

26.3 Revision Prompt Structure (on “NO” email reply)

You previously drafted a WxManBran briefing. The reviewer rejected it and
provided revision instructions. Apply the instructions to produce a new draft.
Preserve all absolute rules from the system prompt.

## PREVIOUS DRAFT
<full text of prior draft>

## REVIEWER INSTRUCTIONS
<full text of user's email reply, with YES/NO/CANCEL keywords stripped>

## ORIGINAL NHC SOURCES
<same NHC source block as original draft>

## REVISION CYCLE
This is revision {n} of {maxRevisionCycles}.

## TASK
Produce a revised draft implementing all reviewer instructions. If an instruction
conflicts with an absolute rule (e.g., reviewer asks you to invent a number),
insert `[NEEDS REVIEW: reason]` and do NOT comply.

26.4 Number-Sanity-Check Prompt (post-draft validation)

Compare the draft briefing against the NHC source material. List every numeric
value in the draft that does NOT appear verbatim (or within 1 unit of rounding)
in the source. Output format: JSON array of {value, location_in_draft,
nhc_source_expected, mismatch_type}. If no mismatches, output [].

This runs automatically after every draft and before any auto-publish. Any non-empty result blocks auto-publish (even if approvalMode: auto-publish).


27. Testing Strategy

27.1 Test Stack

  • Unit tests: Vitest for both main-process and renderer-process code
  • Component tests: React Testing Library for all React components
  • E2E tests: Playwright with Electron support
  • Coverage target: 80% line coverage for main-process, 70% for renderer

27.2 Required Test Coverage

Main process (main.js and submodules):

  • FileValidator — all NEW format variations, all OLD format conversions, invalid inputs
  • Git publish workflow — mock simple-git, verify stash/checkout/pull/add/commit/push sequence
  • Git error recovery — stash+branch restoration after every failure mode
  • Storm folder auto-naming — 12+ scenarios (named, TD, PTC, Invest, collisions)
  • Folder rename retry — simulate EBUSY and confirm 5-attempt backoff
  • Word COM document creation — mock execSync, verify script content + temp cleanup
  • NHC fetchers — mock HTTP, verify fingerprinting, change detection, rate limiting
  • AI backend invocations — mock CLI execution, verify prompt file written + command line
  • Email sender — mock nodemailer, verify SMTP call
  • IMAP poller — mock inbox, verify YES/NO/CANCEL parsing

Renderer:

  • Every React component has at least one rendering test
  • DocCreator filename generation — test all type × name combinations
  • Publisher button state machine
  • Drag-drop handling
  • Dashboard section show/hide
  • Command palette fuzzy search

E2E (Playwright + Electron):

  • First-run onboarding wizard end-to-end
  • Drop-to-publish happy path
  • Create-document-then-publish flow
  • Draft review cycle (mock NHC + mock AI backend)
  • Settings persistence across app restart

27.3 Test Data Fixtures

Store in tests/fixtures/:

  • Sample NHC advisories (TCPAT, TCMAT, TCDAT) for 3 real past storms (Imelda 2025, Helene 2024, Milton 2024)
  • Sample WxManBran briefing .docx files (extracted from 2025 season)
  • Sample email YES/NO reply bodies
  • Golden-output draft briefings (expected AI output for known inputs, used for snapshot testing the prompt assembly, NOT the AI response itself)

27.4 CI

  • GitHub Actions workflow running on every push to tropical-publisher-v2 branch
  • Run: lint (ESLint + Prettier), typecheck (tsc –noEmit), unit tests, component tests
  • E2E tests run on main branch merges only (slower)
  • Build verification: electron-builder --win --publish=never must succeed

28. Security & Credentials

28.1 Credential Storage (Required)

All secrets use the OS credential store via the keytar npm package:

Secret Service Name Account Name
Email sender password (SMTP) WxManBran-TropicalPublisher smtp-{senderEmail}
Gmail OAuth2 refresh token WxManBran-TropicalPublisher oauth-{senderEmail}
IMAP password WxManBran-TropicalPublisher imap-{imapInbox}
GitHub PAT (if used for push) WxManBran-TropicalPublisher github-{username}
Anthropic API key (if API mode used) WxManBran-TropicalPublisher anthropic-api
  • config.json stores ONLY the account identifiers (email addresses, usernames), never passwords/tokens
  • First-run setup prompts for each credential and stores via keytar.setPassword()
  • Settings UI has “Update credentials” buttons that rewrite keytar entries

28.2 Content Security Policy (Phase 2)

Extended CSP needed for NHC fetching and graphics display:

default-src 'self';
script-src 'self';
style-src 'self' 'unsafe-inline';
img-src 'self' https://www.nhc.noaa.gov data:;
connect-src 'self' https://www.nhc.noaa.gov https://api.anthropic.com;
font-src 'self' data:;

No other origins permitted. Any new origin requires a spec update.

28.3 Code Signing

  • Windows NSIS installer must be signed (EV Code Signing certificate if available; self-signed acceptable for personal use)
  • electron-builder config updated with win.certificateFile + signing settings
  • If no cert available, document the SmartScreen warning users will see

28.4 Dependency Security

  • npm audit --omit=dev must show 0 high/critical vulnerabilities on every build
  • Dependabot or Renovate bot enabled for dependency updates
  • No dependencies with fewer than 1000 weekly downloads (reduces supply-chain risk)

28.5 Sandboxing

  • sandbox: true added to BrowserWindow webPreferences (stricter than just contextIsolation)
  • Any required Node APIs in renderer go through preload only
  • No remote module usage (deprecated and unsafe)

29. Error Handling & Logging

29.1 Logging Strategy

  • Library: electron-log
  • Levels: error, warn, info, debug
  • Destination: {userData}/logs/main.log and {userData}/logs/renderer.log
  • Rotation: max 5MB per file, keep 5 rotated files
  • Production default: info level
  • Dev / Settings > Advanced > Enable debug logging: debug level

29.2 Error Taxonomy

Error Type Retry? User Notification Example
Transient network Yes, exp backoff Silent toast if recovered in <30s NHC unreachable, SMTP timeout
Rate limit Yes, wait stated time Toast with countdown NHC 429, AI backend throttle
Auth failure No Modal with “Reconfigure” button Git push denied, email login failed
Validation error No (user input) Inline error on field Invalid filename, bad YouTube URL
File system Depends Toast + log Folder locked, disk full
AI backend hallucination Auto-revise once Flag as [NEEDS REVIEW] Number mismatch detected
Irrecoverable No Persistent banner Config corrupted, keytar unavailable

29.3 Crash Reporting

  • electron-log captures uncaught exceptions
  • On crash: log to disk + show recovery dialog on next launch offering to open log file
  • Optional: user can opt-in to send anonymized crash summary to a user-configured email (no telemetry by default)

29.4 Audit Log (Separate from Error Log)

Every state-changing action writes to {userData}/logs/audit.log (append-only JSON lines):

{"ts":"2026-04-05T14:23:11Z","actor":"user","action":"publish","payload":{"files":["..."],"commit":"abc123"}}
{"ts":"2026-04-05T14:26:02Z","actor":"monitor","action":"nhc-fetch","payload":{"storm":"09L","product":"TCPAT","changed":true}}
{"ts":"2026-04-05T14:26:45Z","actor":"drafter","action":"generate","payload":{"storm":"09L","backend":"claude","tokens":8420}}

Audit log retained for 1 year, never rotated or auto-deleted.


30. State Persistence & Migration

30.1 State File Locations

All persistent state in {userData}/ (Electron’s app.getPath(‘userData’)):

File Purpose Migration Concern
config.json User configuration Yes — versioned, migration required
state/nhc-fingerprints.json NHC product content hashes No — can be regenerated
state/active-storms.json Currently monitored storms No — reseeds from NHC
state/draft-queue.json Pending drafts awaiting review Yes — preserve across upgrades
state/revision-history/ Prior draft versions (90-day retention) Yes
state/publish-history.json Publish log (current season) Yes
logs/main.log / renderer.log / audit.log App logs No

30.2 Config Migration

  • config.json includes a schemaVersion field (integer, starts at 1)
  • On app launch, if schemaVersion < currentVersion, run migration chain in src/migrations/
  • Migrations named 001-to-002.ts, 002-to-003.ts, etc.
  • Each migration writes a backup config.json.v{n}.bak before modifying
  • Failed migration: show error dialog, load read-only mode, preserve old config

30.3 State Cleanup

  • state/revision-history/ entries older than 90 days deleted on startup
  • state/publish-history.json rolled over annually (archived to state/publish-history-{year}.json)
  • state/nhc-fingerprints.json entries for storms inactive >30 days purged

31. Build, Release, & Versioning

31.1 Version Scheme

  • Semantic versioning: MAJOR.MINOR.PATCH
  • v1.x.x = current manual publisher
  • v2.0.0 = this rebuild (NHC monitoring + AI drafter + dashboard)
  • v2.x.x = Phase 2 enhancements
  • v3.0.0 = future major additions (mobile companion, multi-site, etc.)

31.2 Build Pipeline

  • Dev: npm run dev → Vite dev server + Electron with HMR
  • Production build: npm run build:win → electron-builder → NSIS installer in dist/
  • Output artifact: Tropical Update Publisher Setup {version}.exe
  • Build must succeed on clean Windows machine with only Node.js + Git installed

31.3 Release Process

  1. Update package.json version
  2. Update CHANGELOG.md (Keep-A-Changelog format)
  3. Tag commit: git tag v2.0.0
  4. Build installer: npm run build:win
  5. Test installer on clean VM
  6. Publish release notes (manual, no auto-update in v2)

31.4 Update Checker (Optional, Toggleable)

  • On startup (once per 24h), check a JSON endpoint for latest version
  • If newer available, show non-intrusive banner: “v2.1.0 available — download”
  • Never auto-updates without user action
  • Endpoint URL: configurable (user-hosted, e.g., GitHub Releases API)

32. Phase 1 Acceptance Criteria (Existing Features Preserved)

Every one of these MUST pass in the rebuild:

  1. User drags 3 valid .docx files onto drop zone → all 3 appear in queue with ✓ status, parsed titles, formatted dates/times
  2. User drops a Imelda_12pm_9-29-25.docx (legacy format) → file appears in queue, shows conversion badge, uploads as 2025-09-29-12pm-Imelda.docx
  3. User drops a file with bad date (e.g., 2025-13-45-Storm.docx) → file appears with ⚠ status and validation errors listed
  4. User drops a .pdf file → warning toast, file rejected
  5. User clicks “Browse Other Location” → OS file dialog opens to configured tropical updates path
  6. User selects year + storm in Quick Browse → .docx files for that storm list, sorted newest first
  7. User clicks “Sync Folders” → all folders in year are renamed per content rules; status log shows each rename
  8. User on branch feature-x with uncommitted changes clicks Upload → tool stashes, switches to configured target branch, pulls, commits, pushes, returns to feature-x, pops stash
  9. Publishing fails mid-push → tool restores original branch + stash, error toast shown
  10. User creates a new Hurricane document with Storm ID 09L + Name “Andrew” → file created via Word COM at {tropicalUpdatesPath}/{year}/09L_Andrew/YYYY-MM-DD-TIME-Hurricane-Andrew.docx, Word opens
  11. User adds global YouTube URL + publishes 3 files → all 3 .docx files get matching .meta.json with same video ID (OR, if R2 implemented, each file gets its own per-file video ID)
  12. User completes onboarding wizard on first launch → config.json exists, no hardcoded paths, tool works for any user
  13. User changes DEFAULT_CONFIG.branch to develop → next publish targets develop branch
  14. Timezone label shows ET by default; changing config to “CT” → all displayed times now show “CT”

33. Expected Anvil Deliverables (Per Iteration)

Each iteration of this project should produce a docs/ folder containing at minimum:

  • Tropical-Update-Publisher_Master_Plan.md — full build plan organized into phases + sessions, with every feature from Sections 19, 20, 23, 24 broken down into discrete build sessions (each session is ≤4 hours of work for a later code agent)
  • ARCHITECTURE.md — tech stack decisions, folder structure, IPC architecture, data flow diagrams (as mermaid or ASCII)
  • DECISIONS.md — ADR log documenting significant choices (Electron version, React vs other, keytar vs vault, etc.)
  • CHANGELOG.md — Keep-A-Changelog format starting at [Unreleased] for v2.0.0
  • TESTING_STRATEGY.md — per Section 27
  • ENVIRONMENT_SETUP.md — dev machine requirements, Node/Electron/Python/Word, first-run setup
  • README.md — project entry point pointing to other docs
  • START_HERE.md — quickstart for someone picking up the project
  • tech-debt.md — initial tech debt registry
  • Session prompts in docs/sessions/ — one prompt file per build session, ready to hand to an execution agent
  • Risk Registry (from Window 4) inside the master plan

The final deliverable (after execution of session prompts in a separate phase) is a working Electron application that passes all Phase 1 acceptance criteria (Section 32) plus the Phase 2 acceptance criteria (Section 20.9).


END OF SPECIFICATION v2.0