Functional Fix Pass — Cycle 12
Functional Fix Pass — Cycle 12
Context
Cycle 12 report (refinement-functional-cycle-12.md) showed 0 FAIL/BLOCKED/PARTIAL/REGRESSION and
21 MISSING features across Phase 2 surfaces, with 18 consecutive green full-suite runs. The
report’s recommended sequencing opened with “Session D: F039 (Document Creator renderer)” — the
only Phase 1 feature whose spec (§5.3 + §6.2) predicates later Phase 2 work (draft review, storm
folder sync, NHC monitoring). Phase 2 features remain MISSING by design per the release plan and
are out-of-scope for Cycle 12 fixes; they are tracked against their master-list entries.
Every fix below cites the spec section that justifies the change. The project root is
C:\Users\keen4\WxManBran\tools\tropical-update-publisher\build_v2\v1\tools\tropical-update-publisher;
all paths below are relative to that root.
Fix 1 — F039: Document Creator Renderer Section
Feature ID
F039 (MISSING → PASS)
Root Cause
Spec §6.2 defines the “Create New Briefing Document” form (date, hour dropdown, storm dropdown with
“Create New” row, new-storm ID/name fields, update type, live filename preview, Create button, Add
to Upload Queue button). Spec §5.3 defines the filename generation rules (sanitize
[^a-zA-Z0-9\s-], collapse whitespace to hyphens, dedupe duplicate type prefix, Invest uses bare
storm ID). Spec §10 defines the new-storm folder basename priority ({id} for Invest,
{id}_TD{N}/{id}_PTC{N} for TD/PTC, {id}_{Name} for named storms).
No renderer component implemented the spec. The Ctrl+N binding
(useAppShortcuts.ts:217-225) and command palette createNewDocument callback
(CommandPalette.tsx:155-160) both routed to /publish with a toast.message('Doc Creator is
not available yet'…) stub.
Files Changed
Created:
src/renderer/features/doc-creator/docCreatorModel.ts— pure model for filename / folder / readiness calculations. Re-uses sharedFileValidator.validateDocxFilenamefor the live preview. No React / Electron /node:*imports.src/renderer/features/doc-creator/DocCreator.tsx— React component implementing §6.2. UsesuseQuery(browse:listStormskeyed on the derived year) +useMutation(doc:create,storm:createFolder) against the existingipcClient+invokeWithTimeoutplumbing. Emits status-log entries on success viausePublishQueueStore.appendLocalLogEntry. “Add to Upload Queue” forwards the created absolute path tousePublishQueueStore.enqueuePaths.src/renderer/features/doc-creator/useDocCreatorFocusStore.ts— minimal Zustand counter store (focusRequestId+requestFocus()) so Ctrl+N / palette can focus the section without DOM queries or imperative ref plumbing. IncludesresetDocCreatorFocusStoreForTestsfor test hygiene.tests/renderer/docCreatorModel.test.ts— 28 unit cases covering §5.3 filename generation, sanitization, duplicate-type dedupe, Invest path, hour tokens, storm folder §10, readiness gating, and validator behavior on malformed inputs.tests/renderer/docCreator.test.tsx— 8 RTL cases covering rendering (§6.2), storm dropdown loading, live preview update flow, Invest field swap, happy-path create → queue flow (storm-folder + doc IPC sequencing + queue enqueue), DOC_EXISTS rejection surfacing, and the Ctrl+N focus bus.
Modified:
src/renderer/routes/Placeholders.tsx— mounts<DocCreator />at the top ofPublishPlaceholder(above the DropZone + FileList). New import ofDocCreator.src/renderer/hooks/useAppShortcuts.ts— Ctrl+N nownavigate(APP_PATHS.publish)thenuseDocCreatorFocusStore.getState().requestFocus(). Stubtoast.message(...)removed.src/renderer/features/command-palette/CommandPalette.tsx—createNewDocumentcallback now mirrors the Ctrl+N flow (navigate +requestFocus()). Stubtoast.message(...)removed.tests/renderer/windowApiMock.ts—WindowApiMockOptionsnow exposesbrowseListStorms,docCreate,stormCreateFolderdefaults + overrides, mirroring the existing pattern for git / dialog APIs.refinement-state/refinement-functional-master-list.md— F039 entry updated (status MISSING → PASS, code path + evidence populated).
What Was Done
- Extracted the filename / folder / readiness logic into
docCreatorModel.tsso the component stays declarative and the rules stay unit-testable without React.generateFilenamematches theFILENAME_PATTERNgrammar inFileValidator.ts;buildStormFolderBasenameimplements the §10 priority;buildFilenameSegmentdedupes a leading type prefix (case-insensitive) soHurricane+Hurricane AndrewstaysHurricane-Andrew(§5.3). - Built
DocCreator.tsxas a form-centric React component:useStatefor the five form fields (date, time, storm selection, new-storm ID, storm name, update type).useQuerywithqueryKey: ['browse', 'storms:{year}']keyed on the derived year so changing the date refreshes the dropdown.- Sequential mutation chain in the Create handler: if the “Create New Storm” row is picked,
call
storm:createFolderfirst; on success, calldoc:create; on success, record the created path so the “Add to Upload Queue” button can hand it tousePublishQueueStore. Each IPC call is wrapped withinvokeWithTimeout+wrapRendererIpcFailurefor consistent §29 error handling. - All surface elements have
data-testidhooks for the RTL suite and accessibility labels.
- Wired the focus bus: Ctrl+N in
useAppShortcuts.tsand the command palette’screateNewDocumentaction bothnavigate('/publish')and calluseDocCreatorFocusStore.getState().requestFocus(). The component’s effect listens forfocusRequestIdbumps and moves focus to the date input after scrolling the section into view. - Added tests (see file list above). Both suites pass in isolation and in the full suite.
- Updated the master list to reflect the PASS status + evidence.
Spec Citations
- §5.3 — filename generation grammar (
YYYY-MM-DD-[TIME-]Type-Name.docx), sanitization rules, valid update types (Invest, PTC, Tropical-Depression, Tropical-Storm, Hurricane). - §6.2 — DocCreator section layout, hour dropdown token shape, Hurricane default, required-field gating.
- §7.5 —
doc:create/storm:createFolderIPC contract (year + stormFolder + filename →{success, filePath}withDocCreateFailureCodeerrors). - §9 — Word COM automation flow (main-side owned; renderer only reacts to the structured result).
- §10 — new-storm folder basename priority.
- §23.7 — Ctrl+N opens Doc Creator.
- §23.8 — command palette executes the same
createNewDocumentaction. - §24 row 013 —
DOC_EXISTS/WORD_*failure code wire format (surfaced verbatim in the DocCreator error region).
Verification
npm run typecheck— clean across main, preload, renderer tsconfigs.npm run lint— clean (0 errors, 0 warnings).npm run build— clean renderer (index-BB4SzVx5.js1.1 MB) + main build.npm test— 795/795 tests pass (including the 36 new F039 tests). A single earlier run surfaced a flaky timeout inshadcn-smoke.test.tsx > keeps keyboard focus inside the dialog while open; the test passes in isolation and on the re-run. This is unrelated to F039 (pre-existing dialog focus-trap timing sensitivity under heavy parallel load).
Risk
- Low. The fix is additive: new pure model + new component + new tests. No existing IPC
handlers, services, routes, or shared types were changed. The two renderer integration
points (Ctrl+N, command palette) previously rendered stub toasts — switching them to the
real focus bus cannot regress any working surface. The
windowApiMockextension only adds new default handlers behind the existing Proxy fallback. - Known lenient validation corner.
FILENAME_PATTERNaccepts filenames whose slug is only a trailing hyphen (e.g.,YYYY-MM-DD-TIME-Hurricane-.docxwhen the name is blank). The DocCreator guards against this path withevaluateFormReadiness, which surfaces “Enter a storm name.” before the Create button becomes enabled, so operators can never triggerdoc:createwith an empty slug in practice.
IMPLEMENTATION_COMPLETE