Functional Testing — Cycle 6

Date: 2026-04-16 Working directory: C:\Users\keen4\WxManBran\tools\tropical-update-publisher\build_v2\v1\tools\tropical-update-publisher Source baseline: unchanged from refinement-state/refinement-functional-verify-5.md. Between verify-5 and cycle 6, no src/ code was authored. The only filesystem additions since verify-5 are this cycle’s artifact directories (refinement-state/functional-cycle-6-artifacts/, refinement-state/test-fixtures/cycle-6/, refinement-state/screenshots/cycle-6/) and the per-cycle smoke scripts that reference the shipped dist/ build artifacts without touching source.

Summary

Metric Value
Total features 111
Tested this cycle 79
PASS 79
FAIL 0
BLOCKED 0
PARTIAL 0
REGRESSION 0
MISSING 32
UNTESTED 0
Health Score 100.0%
Delta vs Cycle 5 (pre-fix) +1 PASS / -1 MISSING (F008 MISSING → PASS — landed in verify-5; cycle 6 reconfirms)
Delta vs Verify-5 Identical (79/0/0/0/32/100%). No new code since verify-5; cycle 6 is a pure stability re-verification.

Health Score formula: PASS / (PASS + FAIL + BLOCKED + PARTIAL) = 79 / 79 = 100.0%.

Totals sum audit (via grep on master list): ^### F\d{3} → 111, ^- Status: PASS → 79, ^- Status: MISSING → 32, ^- Status: (FAIL|BLOCKED|PARTIAL|UNTESTED) → 0. Totals: 79 + 32 + 0 = 111 ✓.

Phase 0: Mechanical Completeness Sweep

All three sweep commands PASS with 0 errors and 0 warnings (aside from the pre-existing Tailwind informational warning about duration-[var(--motion-duration-medium)] — not an error and unchanged since cycle 2).

Command Result Log
npm run typecheck exit 0 refinement-state/functional-cycle-6-artifacts/typecheck.log
npm run build exit 0 (Vite → dist/renderer/, tsc → dist/main/, tsc → dist/preload/) refinement-state/functional-cycle-6-artifacts/build.log
npm run lint exit 0 refinement-state/functional-cycle-6-artifacts/lint.log

No compile errors to fix before feature testing. The toolchain is healthy for cycle 6.

Full-Suite Test Runs (× 3 consecutive)

Run Test Files Tests Duration Log
1 54/54 PASS 567/567 PASS 59.23s refinement-state/functional-cycle-6-artifacts/test-run-1.log
2 54/54 PASS 567/567 PASS ~60s refinement-state/functional-cycle-6-artifacts/test-run-2.log
3 53/54 + 1 flake 566/567 + 1 flake 66.39s refinement-state/functional-cycle-6-artifacts/test-run-3.log

Runs 1 and 2 were fully green. Run 3 reproduced the known parallel-load flake on tests/renderer/shadcn-smoke.test.tsx > App + Dialog smoke > closes the dialog when Done is activated — the same test that flaked in verify-5 run 2. Isolated rerun of shadcn-smoke.test.tsx is 16/16 PASS (refinement-state/functional-cycle-6-artifacts/shadcn-smoke-isolated.log, 2289ms). This flake is a known parallel-load race on the Dialog close control under pool: 'threads', not a regression; F084 remains PASS under the existing “isolated rerun clean” exit criterion established in verify-4.

Isolated Reruns (regression check on formerly-flaky specs)

Test File Result Duration Log
tests/renderer/shadcn-smoke.test.tsx 16/16 PASS 9.09s (tests 2.29s) refinement-state/functional-cycle-6-artifacts/shadcn-smoke-isolated.log
tests/renderer/useGitPublish.test.tsx 2/2 PASS 4.74s (tests 168ms) refinement-state/functional-cycle-6-artifacts/usegitpublish-isolated.log

Both isolated reruns confirm the cycle 4 fixes continue to hold: the openDialogReliably(user, triggerName) helper in tests/renderer/shadcn-smoke.test.tsx:18-34 and the vi.waitFor(() => expect(...).toBe(true)) wrapper at tests/renderer/useGitPublish.test.tsx:37-39 still prevent the previously observed races when the specs run without parallel-CPU contention.

Direct Service Smoke Evidence (Cycle 6 — fresh artifacts against dist/ build)

Five service-level smokes regenerated this cycle against the latest build artifacts in dist/main/:

FileValidator — tests/shared/fileValidator.test.ts backing + direct smoke

Fixture: refinement-state/test-fixtures/cycle-6/file-validator-smoke.json (generator: smoke-file-validator.js)

Exercises 22 cases and 8 timezone labels (cycle 5 had 7 labels; cycle 6 adds HST).

  • Happy paths (11 ok): NEW-format with time / without time / with minutes, invest 94L (both L and lowercase l), legacy Imelda_12pm_9-29-25.docxparseFilename returns null correctly for legacy inputs that have not been converted, boundary years 2020 and 2100, boundary months Jan and Dec, year-inside-range base case. Title formatting preserves Invest storm-ID casing (94l94L).
  • Rejection paths (11 ok): short slug → FILENAME_SLUG_INVALID; month 13 / month 0 → FILENAME_DATE_INVALID “Month must be between 01 and 12.”; extension .txtFILENAME_EXTENSION_INVALID; empty string → FILENAME_INVALID “Filename must be non-empty.”; Feb 30 → FILENAME_DATE_INVALID “Invalid date: 2/30/2025 does not exist.”; path separator → FILENAME_INVALID “Filename must not contain path separators.”; year 2019 / year 2101 → FILENAME_DATE_INVALID “Year must be between 2020 and 2100 (inclusive).”; day 00 / day 32 → FILENAME_DATE_INVALID “Day must be between 01 and 31.”
  • Timezone label breadth (8 ok): ET, CT, UTC, PT, GMT, MT, AKT, HST all emit correctly as the suffix on "12:00 PM <TZ>". R4 (timezoneLabel config) confirmed end-to-end across the full continental + Alaska + Hawaii dropdown scope.

determineFolderName — src/main/services/storms/determineFolderName.js

Fixture: refinement-state/test-fixtures/cycle-6/folder-name-smoke.json (generator: smoke-folder-name.js)

Exercises 13 cases (cycle 5’s 12 + 1 new named-beats-td-only).

  • Positive priority matrix (11 ok): named-stormRULE_NAMED_ONLY09L_Imelda; tropical-depressionRULE_TD_ONLY07L_TD7; ptcRULE_PTC_ONLY08L_PTC8; invest-onlyRULE_INVEST_ONLY94L; named-beats-ptcRULE_NAMED_ONLY10L_Andrew; td-beats-ptcRULE_TD_BEATS_PTC11L_TD11; ptc-beats-investRULE_PTC_BEATS_INVEST12L_PTC12; td-beats-investRULE_TD_ONLY13L_TD13; named-beats-td-and-ptcRULE_NAMED_BEATS_TD14L_Bonnie; named-beats-invest-onlyRULE_NAMED_ONLY15L_Chris; new named-beats-td-onlyRULE_NAMED_BEATS_TD16L_Dorian.
  • Error paths (2 ok): empty-filesSTORM_FOLDER_NO_CANDIDATES; mismatched-stormNumberDETERMINE_FOLDER_STORM_NUMBER_MISMATCH.

R3 priority (Named > TD > PTC > Invest) reconfirmed with every ordered pair (Named>TD, Named>PTC, Named>Invest, TD>PTC, TD>Invest, PTC>Invest) now represented by at least one positive case. The new named-beats-td-only case tightens the Named>TD binding that was previously only exercised through the 3-way named-beats-td-and-ptc triple.

FileCopyService per-file YouTube metadata (R2 acceptance)

Fixture: refinement-state/test-fixtures/cycle-6/file-copy-perfile-yt-result.json (generator: smoke-file-copy-perfile-yt.js)

Three files copied; per-file YouTube IDs applied individually:

  • 2025-10-01-...: youtube_id = "dQw4w9WgXcQ" (sidecar written)
  • 2025-10-02-...: youtube_id = "aBc12345678" (sidecar written)
  • 2025-10-03-...: no youtubeId supplied → no sidecar (expected)
  • metaWritten flags: [true, true, false] — matches the per-file input.
  • distinct_youtube_ids: true — the two sidecars carry different IDs (R2 acceptance criterion “User can queue 3 files, enter 3 different YouTube URLs, publish, and each .meta.json contains the correct video ID for its file”).
  • all_copied: 3 — every source was copied regardless of whether it supplied a YouTube ID.

IncomingFilesService (F035) — service + structured error

Fixture: refinement-state/test-fixtures/cycle-6/incoming-files-smoke.json (generator: smoke-incoming-files.mjs)

  • Happy path: directory with 2025-10-03-Update.docx, 2025-10-01-Update.docx, 2025-10-02-Update.DOCX, 2025-10-01-Update.meta.json, notes.txt, .docx (5-char), z.docx (6-char) → returns ["2025-10-01-Update.docx","2025-10-02-Update.DOCX","2025-10-03-Update.docx","z.docx"].docx (length 5) rejected, z.docx (length 6) accepted, case-insensitive extension matched, meta.json and .txt excluded, alphabetical sort preserved.
  • ENOENT: missing dir → { names: [] }.
  • Empty dir: existing empty dir → { names: [] }.
  • EACCES: simulated permission denial → rejected with { code: "FILES_LIST_INCOMING_IO_ERROR", hasMessage: true } matching the service’s FILES_LIST_INCOMING_IO_ERROR constant.

Service contract (Section 7.2 listExistingFiles()) verified end-to-end at the shipped build artifact level, on top of the 7 vitest unit tests that cover the same surface with mocks.

F008 launcher scripts — direct structural smoke

Fixture: refinement-state/test-fixtures/cycle-6/launchers-smoke.json (generator: smoke-launchers.js)

  • launcher.bat (424 bytes): exists, calls npm start, cd /d "%~dp0", preserves %ERRORLEVEL% exit code.
  • launcher.vbs (697 bytes): exists, references launcher.bat, shell.Run ..., 0, ... (hidden window style), uses Scripting.FileSystemObject, non-blocking (bWaitOnReturn = False).
  • create-shortcut.ps1 (1374 bytes): exists, targets launcher.vbs, writes Tropical Update Uploader.lnk under GetFolderPath('Desktop'), uses WScript.Shell, graceful absent-icon handling (Test-Path $iconPath), $ErrorActionPreference = 'Stop'.
  • allStructuralChecksPass: true.

Complements the 3 vitest assertions in tests/shared/launchers.test.ts that ran green in all three full-suite runs this cycle.

MISSING Feature Inventory (unchanged from Verify-5)

Filesystem sweep confirms 32 MISSING features remain as file-absence gaps rather than test-surface gaps:

Path / feature Status
assets/ (F009 WMB_Logo.png, icon.ico, asset docs) absent
src/main/services/nhc/, src/main/services/ai/, src/main/services/email/ (F093–F102, F104) absent
src/renderer/features/publish/ (F030–F034) contains only DropZone.tsx + dropZoneModel.ts — no FileQueue, CommitMessage, YouTubeUrlInput, StatusLog, Footer components
src/renderer/features/doc-creator/ (F039) absent
src/renderer/features/quick-browse/ (F045) absent
Header logo + git-status indicator widget (F050) absent
Command palette / shortcut wiring / context menus / designed empty states (F087–F090) absent
Rich dashboard sections (F103), publishing intelligence (F105), multi-destination (F106), content quality (F107), historical/season (F108), notifications (F109), QoL workflow (F110), audit enhancements (F111) absent

F008 was on this list in cycle 5; verify-5 landed launcher.bat, launcher.vbs, create-shortcut.ps1, and their backing test, moving it to PASS.

None of these gaps block the tested features — they are acknowledged multi-session deliverables per the master list and spec Sections 19, 20, 23, 24.

Detailed FAIL List

No FAIL entries in this cycle.

Partial Features

No PARTIAL entries in this cycle.

Blocked Features

None.

Regressions

None. Every feature marked PASS in verify-5 remained PASS in cycle 6. The single run-3 flake on shadcn-smoke > closes the dialog when Done is activated is the same race documented in verify-5 and cycle-4 (isolated rerun 16/16 green both times), not a regression.

New Discoveries

  • Timezone label breadth now covers HST. Cycle 5 confirmed 7 labels (ET, CT, UTC, PT, GMT, MT, AKT); cycle 6 adds HST. Total label coverage: 8 distinct strings all flowing through FileValidator.parseFilename at src/shared/validation/FileValidator.ts:474 without canonical-list enforcement. R4 acceptance continues to hold for any non-empty timezone string.
  • determineFolderName Named>TD-only binding directly covered. Cycle 5 exercised Named>TD via the 3-way named-beats-td-and-ptc case (where PTC is also present). Cycle 6 adds named-beats-td-only (16L_Dorian over TD16) to isolate the pairwise Named>TD invariant. The smoke fixture now contains a positive case for every ordered priority pair.
  • Full suite stability is ~2/3 green, 1/3 shows known flake. Cycle 5: 3/3 green runs. Verify-5: 3/4 green runs, 1 flake. Cycle 6: 2/3 green runs, 1 flake — consistent with the verify-5 pattern that the cycle-4 fix reduced but did not fully eliminate the parallel-load race on the Dialog close control. Isolated rerun is clean, so F084 stays PASS. No action needed; the flake rate is within the existing exit criterion (≤33% with isolated-clean).
  • Source tree fully stable since verify-5. find src tests -newer refinement-state/functional-cycle-5-artifacts/typecheck.log -type f -name '*.ts' -o -name '*.tsx' -o -name '*.js' returns only tests/shared/launchers.test.ts — the F008 test that landed during verify-5. No unrecorded source edits between verify-5 and cycle 6.
  • Test-count growth reconciled. Cycle 5: 53 files / 564 tests. Cycle 6: 54 files / 567 tests (+1 file, +3 tests). The delta is entirely attributable to tests/shared/launchers.test.ts (3 assertions: .bat existence + npm-start call, .vbs existence + hidden-window run, .ps1 existence + Desktop shortcut) authored during verify-5.
  • F008 physical backing audit complete. All three launcher files exist at the tool root with the structural invariants the spec demands: launcher.bat → 424 bytes, calls npm start; launcher.vbs → 697 bytes, hidden window (style 0), non-blocking; create-shortcut.ps1 → 1374 bytes, writes Tropical Update Uploader.lnk to Desktop, graceful missing-icon fallback. Direct structural smoke this cycle (launchers-smoke.json) confirms allStructuralChecksPass: true for all 14 boolean checks across the three files.

Remaining Missing Features (unchanged from Verify-5)

Implementation gaps — each carries the same priority as it held in verify-5:

  • F009 asset directory + WMB_Logo.png / icon.ico — MEDIUM (installer icon path referenced in package.json build.win.icon; app runs but title-bar icon falls back to Electron default)
  • F030–F034 publish queue rows / commit-message input / YouTube UI / status log / footer destination path — HIGH (core publish-view UX)
  • F039 document-creator renderer section — HIGH (spec §6.2)
  • F045 quick-browse renderer section — HIGH (spec §6.3)
  • F050 header logo + git-status indicator widget — MEDIUM (spec §6.1)
  • F087–F090 command palette / keyboard shortcut wiring / context menus / designed empty states — MEDIUM
  • F093–F111 Phase 2 (NHC monitor, AI backends, email review, draft queue, rich dashboard, historical views, notifications, QoL, audit/compliance) — deferred multi-session scope (spec §20, §23, §24)

Testing Priority (Applied)

  1. ✓ App compiles: typecheck + build + lint all PASS.
  2. ✓ Core navigation works: AppShell + routing tests (F010–F020) still green under full suite.
  3. ✓ Primary workflow end-to-end: F028 file copy + R2 per-file YouTube + F029 git publish workflow all PASS via publishService.test.ts (12 tests), fileCopyService.test.ts (18 tests), direct smoke fixture this cycle.
  4. ✓ Secondary features: FileValidator (55 tests), determineFolderName (16 tests), stormFolderService (20 tests), quickBrowseService (22 tests), docHandlers (24 tests), wordDocumentService (17 tests), first-run (F006/F054/F080), settings (F055–F060), credential vault (F061), CSP (F062), audit log (F065), publish history (F066), launchers (F008, 3 tests), all green.
  5. ✓ Error handling: F071–F081 all PASS via existing tests; F084 dialog accessibility PASS under isolated run and 2/3 full-suite runs, known-flaky run 3 reproduces verify-5’s pattern.
  6. ✓ Polish features tested where applicable; F091/F092 (theme/sidebar persistence) PASS.

Evidence Highlights

  • Compile sweep: refinement-state/functional-cycle-6-artifacts/typecheck.log, build.log, lint.log
  • Full suite runs: refinement-state/functional-cycle-6-artifacts/test-run-1.log, test-run-2.log, test-run-3.log — 567/567 + 567/567 + 566/567
  • Isolated reruns: shadcn-smoke-isolated.log (16/16), usegitpublish-isolated.log (2/2)
  • FileValidator smoke: refinement-state/test-fixtures/cycle-6/file-validator-smoke.json (generator smoke-file-validator.js)
  • determineFolderName smoke: refinement-state/test-fixtures/cycle-6/folder-name-smoke.json (generator smoke-folder-name.js)
  • FileCopyService per-file YT smoke: refinement-state/test-fixtures/cycle-6/file-copy-perfile-yt-result.json (generator smoke-file-copy-perfile-yt.js)
  • IncomingFilesService smoke: refinement-state/test-fixtures/cycle-6/incoming-files-smoke.json (generator smoke-incoming-files.mjs)
  • F008 launcher smoke: refinement-state/test-fixtures/cycle-6/launchers-smoke.json (generator smoke-launchers.js)

Git Safety

No live git-publish E2E was re-executed in this cycle — F029 coverage is derivative on the stable static test surface (publishService.test.ts 12 tests, gitService.mocks.test.ts 11 tests, publishHistoryService.test.ts 2 tests, gitGetStatusHandlers.test.ts 7 tests, all PASS in npm test). The cycle-2 disposable-branch E2E artifact (refinement-state/test-fixtures/cycle-2/publish-e2e-result.json) remains the authoritative live evidence.

Repo inspected this cycle: git branch --show-currenttropical-publisher-v2 (unchanged); git status --short → only _site/* static output modifications outside this tool’s scope, unrelated to tropical-update-publisher code. No disposable test branches were created this cycle, no remote refs were touched, no test branches exist locally or on origin. No git tests touched main.

GUI / Screenshots

No GUI launch was attempted this cycle because (a) the prior cycles’ screenshots (refinement-state/screenshots/cycle-2/interactive-first-run-before.png and interactive-after-continue.png) are still representative, (b) the source tree is unchanged since verify-4/verify-5 apart from the F008 text files at the tool root (which have no renderer surface), and (c) a live Electron launch could show a blocking first-run modal under the current %APPDATA% config state, per the GUI App Launch Safety guardrail. The cycle-6 screenshot directory refinement-state/screenshots/cycle-6/ was created for future GUI-launch runs but intentionally left empty in this cycle.

Verification That State Is Accurately Recorded

Master list totals audited directly via grep against refinement-state/refinement-functional-master-list.md:

  • Total feature sections (^### F): 111 (matches legend)
  • ^- Status: PASS: 79
  • ^- Status: MISSING: 32
  • ^- Status: PARTIAL / FAIL / UNTESTED / BLOCKED: 0 each

Totals sum: 79 + 32 + 0 + 0 + 0 + 0 = 111 ✓

Health Score: 79 / (79 + 0 + 0 + 0) = 100.0% ✓


End of cycle 6 report. No regressions, no new PARTIAL/FAIL/BLOCKED, no state transitions required, no master-list edits necessary (verify-5 already landed F008 MISSING → PASS and the master list reflects it). The codebase is in the same 100% health state as verify-5 with additional, fresh cycle-6 evidence reconfirming the four primary service-layer contracts (FileValidator, determineFolderName, FileCopyService, IncomingFilesService) plus the F008 launcher-script contract. The single run-3 flake on the Dialog close control is the known parallel-load race; isolated rerun is clean.