Tropical Update Publisher — Project Specification v2.0
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(offmainin 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)
- 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.
- 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.) - Do NOT break the IPC security model. Renderer process never gets direct Node.js access. All privileged operations go through
contextBridgeinpreload.js. - 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.
- 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 forEBUSY,EPERM, andEACCESerrors. - 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-srcfor NHC endpoints) and MUST be enumerated explicitly in Section 28. - Preserve context isolation.
contextIsolation: trueandnodeIntegration: falsemust remain in the BrowserWindow config. - 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-publishAND the draft passed all number-sanity checks (no[NEEDS REVIEW]markers, no numeric mismatches with NHC source). - Never write secrets to
config.json. Email passwords, OAuth tokens, and API keys MUST be stored in the OS credential store (Windows Credential Manager viakeytar). Only non-secret configuration goes inconfig.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 showsTropical Update Uploader) - License: MIT
- Author: WxManBran
2.1 Launch Paths
The app can be launched via:
npm start— runselectron .from the tool directorylauncher.bat— Windows batch file wrapperlauncher.vbs— VBScript wrapper (hides console window)Tropical Update Uploader.lnk— Desktop shortcut that points to the VBS launcher- NSIS installer built via
npm run build:win→dist/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.,94Lfor invests) - Storm ID format: digits + single uppercase letter (e.g.,
09L,14L,94L) - Folder name priority (highest to lowest):
- Named Storm:
{ID}_{StormName}(e.g.,09L_Andrew) - Tropical Depression:
{ID}_TD{number}(e.g.,07L_TD7) - PTC (Potential Tropical Cyclone):
{ID}_PTC{number}(e.g.,08L_PTC8) - Invest only:
{ID}(e.g.,94L)
- Named Storm:
- 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.docxfiles
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.docx2025-10-01-12PM-Hurricane-Imelda.docx2025-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/pmorHH[: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 to2025-09-29-12pm-Imelda.docxHelene_4pm_10-15-24.docx→2024-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_Dateis re-ordered toDate-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()):
Nameis sanitized:[^a-zA-Z0-9\s-]stripped, spaces → hyphens- Duplicate type prefix removed (e.g.,
Hurricane-Hurricane-Andrew→Hurricane-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):
InvestPTC(Potential Tropical Cyclone)Tropical-DepressionTropical-StormHurricane(default)
5.4 Title Display Formatting (FileValidator.parseFilename)
- Slug hyphens → spaces
- Each word capitalized
- Storm IDs preserved in uppercase form (e.g.,
94l→94L) 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”
- Storm ID input (e.g.,
- 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
.docxfiles accepted (others rejected with warning)
6.5 YouTube Link Section
- 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
6.9 Footer
- Shows full destination path: “Files are copied to {fullIncomingPath}”
7. IPC API Surface (preload.js → main.js)
All renderer-to-main communication via contextBridge.exposeInMainWorld('api', ...):
7.1 Configuration
getConfig()→{repoPath, incomingPostsPath, fullIncomingPath}
7.2 File Operations
copyFiles(fileInfos)— Copies.docxfiles toincoming/posts/, overwriting if exists. Creates{name}.meta.jsonsidecar whenyoutubeIdprovided. Returns per-file results withoverwrittenflag.listExistingFiles()— Lists.docxfiles currently inincoming/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 togit:logIPC 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 pathlistYears()→{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.pathproperty 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:
- Verify repo.
git.checkIsRepo(). Abort if not a git repo. - Log start. “Starting publish process…”
- Get current branch via
git.branch(). Store asoriginalBranch. - If NOT on main:
a. Check status via
git.status()b. If dirty:git stash push -m "tropical-update-publisher-auto-stash". SetdidStash = true. c.git checkout main - If already on main: just log “On branch: main”
- Pull latest.
git pull origin main - Stage files. For each filename:
- Add
incoming/posts/{filename}(forward slashes) - If
{name}.meta.jsonsidecar exists, also add it
- Add
- 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'}
- Commit.
git.commit(commitMessage). Log commit hash. - Push.
git push origin main. Log success. - Restore branch. If
originalBranch !== main:git checkout {originalBranch}- If
didStash:git stash pop
- 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 locallogs[]AND emitsgit:logIPC event to renderer for live StatusLog updates
9. Document Creation (Word COM Automation)
Endpoint: doc:create IPC handler in main.js
Process:
- Determine destination folder:
- If
year+stormFolderprovided:{tropicalUpdatesPath}/{year}/{stormFolder}/ - Else if only
year:{tropicalUpdatesPath}/{year}/ - Else:
{tropicalUpdatesPath}/{currentYear}/
- If
- Create destination directory if missing (
recursive: true) - Reject if
{destDir}/{filename}already exists (success: false, error: "A file with this name already exists") - Write PowerShell script to
{os.tmpdir()}/create-word-doc.ps1:- Creates
Word.ApplicationCOM object (invisible) - Creates new empty document
- Saves as
.docx(format16= wdFormatDocumentDefault) - Closes document and quits Word
- Releases COM object, runs GC
- Creates
- Execute:
powershell -NoProfile -ExecutionPolicy Bypass -File {script}with 30-second timeout - Delete temp script
- Verify file was created on disk
- 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:
- 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-DepressionTropical-StormHurricaneInvestPTC
- Returns
{type, name}ornull
- Matches
- Track presence of: named storm name, PTC (+ number), TD (+ number)
- Named storm detection: name matches
^[A-Za-z]{3,}$AND NOT^(TD|PTC)\d+$AND NOT^\d+[A-Z]$ - 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)
- If named storm found →
Rename retry logic (storm:updateFolderName):
- Up to 5 attempts
- Backoff delays:
[500, 1000, 2000, 3000, 5000]ms between retries - Retries only on
EBUSY,EPERM,EACCESerror 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:
- YouTube ID is extracted via regex (see Section 6.5)
- For EACH file being copied, a sidecar
{name}.meta.jsonis created:{ "youtube_id": "dQw4w9WgXcQ" } - Sidecar is placed in
incoming/posts/alongside the .docx - Sidecar is git-added alongside the .docx
- 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:logIPC 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 logoassets/icon.ico— window icon and installer iconassets/README.md— asset documentation
16. Build & Distribution
- Target: Windows (
build:winscript) - 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
keen4user - 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 thegit:publishhandler hardcodes its ownconst targetBranch = 'main'(ignoring config). - REQUIRED:
git:publishmust read the target branch fromDEFAULT_CONFIG.branch. Remove the local hardcodedtargetBranchconstant. Update log messages that reference “main” explicitly to use the config value. - Acceptance: Changing
DEFAULT_CONFIG.branchin 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.filesentries have ayoutubeUrlfield, 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.jsonsidecar on publish. - Acceptance: User can queue 3 files, enter 3 different YouTube URLs, publish, and each
.meta.jsoncontains 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
timezoneLabelfield toDEFAULT_CONFIG(default:"ET").parseFilenamereads this value instead of the hardcoded string. No UI setting needed in v2; config-only for now. - Acceptance: Changing
DEFAULT_CONFIG.timezoneLabelchanges the label in all displayed times.
R5 (was D5): Make paths portable (no hardcoded usernames)
- Current flaw:
tropicalUpdatesPathis hardcoded toC:\Users\keen4\iCloudDrive\.... Tool fails for any other user. - REQUIRED:
- Create a
config.jsonfile (at tool root) that stores user-specific paths (tropicalUpdatesPath, any future per-machine values). - On first launch, if
config.jsonis 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 toconfig.json. - On subsequent launches, load paths from
config.json. - Add a “Settings” menu option to change the path later.
config.jsonmust be added to.gitignoreso per-user config doesn’t leak into the repo.
- Create a
- 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-ControlandIf-Modified-Sinceheaders (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):
- 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 starterhouse-style.mdby reading all 7.docxfiles inC:/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. - Briefing templates (read at
C:/Users/keen4/iCloudDrive/Documents/UNITED_WxManBran/WxManBran.com_Templates/):United_Tropical_Update_Template_(Blog_Posts).docx— primary blog-post formatUnited_Tropical_Update_Template_(Watches_Warnings_Impacts).docx— used when W/W are activePrevious_Storms_Template.docx— historical context template
- Few-shot examples (the 5 most recent
.docxupdates from the current storm’s folder, e.g.Tropical Updates_Briefings/2025/09L_Imelda/*.docx) - 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
.docxwritten 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.jsonsidecar 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):
- Draft is generated and saved to storm folder
- Tool sends an email to user (configured
reviewerEmailinconfig.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”
- Subject:
- Tool polls the reply inbox (IMAP) every 1-2 minutes for a reply
- On YES reply: tool auto-publishes via existing git workflow (copy to incoming/posts/, commit, push to main). User receives confirmation email.
- 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”.
- 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}.docxalready exists in the storm folder (because user manually created one), the AI draft is saved with suffix-ai-draft.docxinstead - 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.jsonafter each phase (fetching, generating, emailing, awaiting-reply, revising) - On app launch, resume any drafts in non-terminal states:
fetching→ restart from NHC fetchgenerating→ re-submit to AI backend (idempotent — uses same prompt)awaiting-reply→ resume IMAP pollingrevising→ re-submit revision prompt
- Drafts in terminal states (
approved,rejected,cancelled,published) are archived tostate/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.jsonhasaiCostLimits:dailyTokenCap(default: 500000) — halt drafting when exceededweeklyTokenCap(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.jsonunderdashboard.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 ifapprovalMode: 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
- During an active storm, user can leave the app running and receive an email draft within 10 minutes of each NHC advisory release.
- User can reply YES via email and see the briefing live on WxManBran within 2 minutes.
- User can reply NO with revision instructions and receive an updated draft within 3 minutes.
- Dashboard accurately reflects all active storms and their last-received advisory at a glance.
- Special/intermediate advisories trigger drafts and flagged alerts within one polling cycle.
- User can switch between Claude, Cursor, and Codex backends with a single Settings change and the next draft uses the new backend.
- No published briefing contains a wind speed, pressure, position, or forecast point that does not appear in the source NHC products it consumed.
- 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:
- Publish (existing drop-zone + queue workflow)
- Drafts (pending AI drafts awaiting review)
- Storms (active storm dashboard)
- History (publish log, season archive)
- Monitor (NHC status, alerts)
- 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):
- Welcome + tool overview
- Set iCloud tropical updates path (with browse button)
- Verify git repo connection + branch
- Pick AI backend (Claude default) + test connection
- Configure email reviewer (optional — can skip and add later)
- Dashboard layout: pick which sections to show
- Theme preference (dark/light/system)
- Review shortcuts
- “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.logand 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-v2branch - 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=nevermust 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.jsonstores 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-builderconfig updated withwin.certificateFile+ signing settings- If no cert available, document the SmartScreen warning users will see
28.4 Dependency Security
npm audit --omit=devmust 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: trueadded to BrowserWindow webPreferences (stricter than just contextIsolation)- Any required Node APIs in renderer go through preload only
- No
remotemodule usage (deprecated and unsafe)
29. Error Handling & Logging
29.1 Logging Strategy
- Library:
electron-log - Levels: error, warn, info, debug
- Destination:
{userData}/logs/main.logand{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-logcaptures 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.jsonincludes aschemaVersionfield (integer, starts at1)- On app launch, if
schemaVersion < currentVersion, run migration chain insrc/migrations/ - Migrations named
001-to-002.ts,002-to-003.ts, etc. - Each migration writes a backup
config.json.v{n}.bakbefore 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 startupstate/publish-history.jsonrolled over annually (archived tostate/publish-history-{year}.json)state/nhc-fingerprints.jsonentries 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 indist/ - 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
- Update
package.jsonversion - Update
CHANGELOG.md(Keep-A-Changelog format) - Tag commit:
git tag v2.0.0 - Build installer:
npm run build:win - Test installer on clean VM
- 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:
- User drags 3 valid .docx files onto drop zone → all 3 appear in queue with ✓ status, parsed titles, formatted dates/times
- User drops a
Imelda_12pm_9-29-25.docx(legacy format) → file appears in queue, shows conversion badge, uploads as2025-09-29-12pm-Imelda.docx - User drops a file with bad date (e.g.,
2025-13-45-Storm.docx) → file appears with ⚠ status and validation errors listed - User drops a .pdf file → warning toast, file rejected
- User clicks “Browse Other Location” → OS file dialog opens to configured tropical updates path
- User selects year + storm in Quick Browse → .docx files for that storm list, sorted newest first
- User clicks “Sync Folders” → all folders in year are renamed per content rules; status log shows each rename
- User on branch
feature-xwith uncommitted changes clicks Upload → tool stashes, switches to configured target branch, pulls, commits, pushes, returns tofeature-x, pops stash - Publishing fails mid-push → tool restores original branch + stash, error toast shown
- 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 - User adds global YouTube URL + publishes 3 files → all 3 .docx files get matching
.meta.jsonwith same video ID (OR, if R2 implemented, each file gets its own per-file video ID) - User completes onboarding wizard on first launch → config.json exists, no hardcoded paths, tool works for any user
- User changes
DEFAULT_CONFIG.branchtodevelop→ next publish targetsdevelopbranch - 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.0TESTING_STRATEGY.md— per Section 27ENVIRONMENT_SETUP.md— dev machine requirements, Node/Electron/Python/Word, first-run setupREADME.md— project entry point pointing to other docsSTART_HERE.md— quickstart for someone picking up the projecttech-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