fix(ui): preserve compose draft identity and delivery safeguards
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
# P7-06 — Compose and send
|
||||
|
||||
**Implemented scope: CONDITIONAL GO. Affected regression, final native consumers, actual47-image review and finite hygiene pass; normal development push pending.** This is the third mandatory P7-06 boundary after [embedded forms](FORMS.md) and [inbox/read review](INBOX.md). Follow the [approved handoff](../../phase-6/p6-05/PHASE7_HANDOFF.md#migration-order-and-commit-boundaries); optional P7-06F remains DEFERRED pending its mode→draft/review action and fallback decision.
|
||||
|
||||
[Compose entry](evidence/compose-entry.json) records the preceding inbox commit `a791e353b062cc91e10429cd767350ec9f0aaef2`. Its normal push was observed on **2026-10-14 at 05:14:53.828571 UTC**: clean local HEAD = origin = live `codex/ui-redesign-phase4`; local/origin/live `main` remained `27657f31131cd95d42f0e1290853ec9c9b3959a4`. That receipt establishes synchronization of the predecessor, not acceptance or push of this slice.
|
||||
|
||||
## Existing contract and scope
|
||||
|
||||
The existing global correspondence composer remains the owner of new messages, replies, saved drafts, review confirmation and send feedback. It keeps the existing provider registry, owned-job choices, draft routes, explicit send confirmation, shared confirmation/toast infrastructure and English/Bokmål catalogue. The sole API presentation correction marks existing draft DTO timestamps as UTC consistently after SQLite reload. No new document persistence, mailbox grant, provider integration, renderer, route or schema is introduced.
|
||||
|
||||
The server assigns a saved draft's request UUID and revision. PUT and DELETE require the captured revision; new-attempt preparation changes identity only for that owner's matching definitive failed attempt. Confirmed send uses the owned job, provider, request UUID and payload hash. An uncertain or terminal attempt cannot restart. The stored-draft DTO has no delivery-status field; resuming a draft does not prove provider delivery status or authorize a new identity.
|
||||
|
||||
The entry inspection identified concrete safety defects: a held Save could merge into later work, Save and Send could race while persistence changed the UUID, edits could clear recorded delivery protection, and recipient-only or subject-only changes could be discarded without confirmation. This slice repairs those paths before accepting the migrated presentation.
|
||||
|
||||
## Bounded repairs
|
||||
|
||||
A synchronous operation guard reserves Save, Send, Discard, failed-attempt preparation and draft replacement before any confirmation or asynchronous work. Each completion belongs to its captured draft/job/provider/request identity and mounted owner. Conflicting controls lock during writes; confirmation triggers remain available for Cancel focus restoration. An unmounted completion does not refresh drafts or attach its result to another screen.
|
||||
|
||||
Recipient, subject and body form the complete dirty check. Saved content has a separate persisted baseline, so an unchanged saved draft can be resumed later while unsaved edits require deliberate replacement or discard confirmation. Cancel preserves all fields and makes no write. Explicit Send captures the reviewed payload; repeated activation cannot create another confirmation or another concurrent POST.
|
||||
|
||||
Recorded failed, uncertain, pending, sending and sent results lock field editing and ordinary Save/Send. Edits cannot implicitly rotate the request UUID or erase the result. Only an authoritative failed result offers explicit new-attempt preparation. Managed drafts use the server's new UUID/revision, retaining all local unsaved fields and comparing them with the returned persisted baseline. A generic HTTP409 is treated as uncertain; plain preflight 4xx errors keep the same request key without inventing a failed attempt. Preparation remains a separate action from reviewing and sending again.
|
||||
|
||||
Owned job choices and saved drafts have independent loading/error states and GET-only Retry. A successful write updates the known draft DTO locally before a following list read. If that read fails, recovery repeats the GET without replaying the write. A post-write read starts a fresh generation even when an earlier list GET is still pending; a late old snapshot cannot replace the committed state. HTTP401/403 recovery withholds unauthorized saved data. Revision errors keep the current fields and captured revision with localized feedback on the original action.
|
||||
|
||||
The composer uses existing density tokens, H3 hierarchy, named focus groups, shared feedback and wrapping content. Three new catalogue entries describe owned-job read failure, no matching jobs and discard failure; existing saved-draft feedback is reused. The unsaved-change wording now covers all three fields. Both English and Bokmål remain catalogue-backed. The disabled editor's informational character counter uses the existing secondary-text token after a measured contrast failure. No second toast/dialog framework or dependency is introduced.
|
||||
|
||||
## Affected regression evidence
|
||||
|
||||
All times below are UTC on **2026-10-14**. Exact commands, reports, failed attempts and frozen source captures remain under `.artifacts/phase7/p7-06/compose/`.
|
||||
|
||||
| Observation | Actual result | Evidence boundary |
|
||||
|---|---|---|
|
||||
| Published entry frontend | **42 cases/5 suites PASS** | Earlier inbox GREEN, 05:08:44.577597–05:09:06.324638. All 14 frozen as-run sources equal published `a791e353` Git blobs under UTF-8/LF normalization. This is source equivalence to the earlier execution, not a new run at the clean commit |
|
||||
| Existing backend baseline | **18 PASS** | 05:17:30.164673–05:17:33.709705, `--no-build` against the existing Release DLL. Entry source and binary equality are recorded separately |
|
||||
| Compose meaningful RED | **43 pass/7 fail of 50** | `unit-red`, 05:21:42.841428–05:22:08.085477. All original 42 cases pass; the new repeated-confirmation case already passes. Seven failures expose write ownership, lost protection, dirty confirmation, GET recovery, unmount and revision feedback defects |
|
||||
| First candidate | **35 pass/15 fail of 50** | `unit-candidate-1`, 05:29:14.641920–05:29:40.212958. An unconditional asynchronous approval wait delayed the no-confirmation fast path; this implementation regression was repaired rather than waived |
|
||||
| Second candidate | **47 pass/3 fail of 50** | `unit-candidate-2`, 05:31:13.767478–05:31:53.017579. Canonical localized HTTP409 feedback and the new uncertain-state observation were corrected before the next run |
|
||||
| Initial compose GREEN | **50 cases/5 suites PASS** | `unit-candidate-3`, 05:34:01.640374–05:34:28.524596; zero failed/pending/todo cases |
|
||||
| Review-added RED | **50 pass/2 fail of 52** | `unit-review-red`, 05:37:16.529544–05:37:49.453325. Two ordinary additions reproduce unsaved-field loss during server identity rotation and a post-write list read suppressed by an older pending GET |
|
||||
| Review GREEN | **52 cases/5 suites PASS** | `unit-review-green`, 05:40:17.199755–05:40:46.294482; all 14 pre/post source captures match, zero failed/pending/todo cases |
|
||||
| Fresh backend guard regression | **24 PASS** | 05:29:23.773379–05:29:43.744878, fresh Release compilation and test execution with `--no-restore`; zero failed/not-executed cases |
|
||||
| UTC contract RED | **24 pass/1 fail of 25** | `backend-utc-red`, 05:51:46.857292–05:51:56.549754. The ordinary new Fact observes `Unspecified` after SQLite reload where the DTO contract requires UTC; all ten sources have true pre/post captures |
|
||||
| UTC contract GREEN | **25 PASS** | `backend-utc-green`, 05:53:02.535746–05:53:11.849241, fresh Release compilation and execution; all ten pre/post sources match, zero failed/not-executed cases |
|
||||
| Final affected GREEN | **52 cases/5 suites PASS** | `unit-final`, 06:00:56.823199–06:01:25.904825, after the counter-color correction. All 14 pre/post sources match; zero failed/pending/todo cases. The read-only 10:36:21.072658 resumption check confirms the same 14 working sources |
|
||||
|
||||
The final affected frontend source includes `CorrespondenceInboxPage.tsx` SHA256 `38ed35060aab502bf64c947dfad6d6e0cc9e4970d7ee16dd3518a3da1d3543f4`. The latest product export is `LFCrTZzDtX5hNBSRBj5Xx`; its 10:36 gates and final native pre/post receipts retain source/export correspondence. Later C8/C5 fixture corrections change only the browser specification; browser TypeScript and native checks rerun against the same product export, with no product rebuild inferred.
|
||||
|
||||
Six new backend cases cover pending/sending/uncertain/sent rejection and mismatched owner/request-key failures. They assert unchanged draft content, revision, request identity and attempt history across fresh contexts. The existing successful rotation case now also proves persisted identity/content/history readback. A seventh case proves UTC kinds, exact ticks and complete DTO fields through Create/Get/List/Put/NewAttempt, with stored values and failed history unchanged by reads.
|
||||
|
||||
The original native whole-DTO comparison found Create timestamps ending in `Z` while reloaded Get/List timestamps omitted it. Four `DateTime.SpecifyKind(value, DateTimeKind.Utc)` DTO mappings restore the existing UTC transport contract without converting ticks or mutating stored data. Routes, ownership, request identity, revision checks and schema remain unchanged. Assertions retain exact DTO equality; no timestamp normalization hides the failure.
|
||||
|
||||
The fresh 24-case API DLL was SHA256 `5e32c5b5b60fa0fda7bed6f392b75c3087a321476244f5aa9ff229d6a1372fa8`; compilation reflects current assembly revision metadata. Its ten-source receipt is timestamped **05:29:52.777062**, after the command ended at **05:29:43.744878**, with matching later captures at 05:31:55.584513. `backend-final-source/capture-qualification.json` corrects the original receipt and post-receipt's inaccurate “during compilation” wording while retaining their raw bytes. That evidence is not a pre-build capture. The later UTC RED/GREEN runs have genuine ten-source pre/post captures; current GREEN API DLL SHA256 is `951ca8d7b46345c4aab976215d50d42b769bad1aa4a1e1b97fd3cfaaf5615527`.
|
||||
|
||||
## Browser, responsive and accessibility evidence
|
||||
|
||||
Ten new native cases cover actual owned draft create/list/detail/update/delete/reload and stale revisions; anonymous/foreign-owner denial; job-choice and draft-list GET recovery; held Save/unmount; dirty Cancel and Send Cancel; real failed-only identity preparation; explicitly labelled delivery-state fixtures; strict actual language HTTP204; and finite bilingual/theme/width reflow. Every failed attempt below remains immutable, with complete reports, source qualifications, axe vectors and PNGs.
|
||||
|
||||
| Native observation | Actual result | Qualification and repair |
|
||||
|---|---|---|
|
||||
| Previous committed export `uhKavi8_u6PrcEm2rZ1On` | **0 pass/10 fail**, 05:42:41.382361–05:48:48.604257 | `native-red-old-uhK` is an old-export RED comparison, not current UI acceptance. Four cases fail whole-DTO UTC serialization before later assertions; remaining failures expose missing recovery/dirty feedback and write or delivery locks. Case 7 stops at the missing dirty indicator and cannot prove its later preparation assertions. The separate unit RED proves unsaved-field loss |
|
||||
| First repaired export `sk-T5pqHLrLW1H-3l7x6i` | **8 pass/2 fail**, 05:56:16.089420–05:57:12.095693 | `native-candidate-1-skT`; 19 source pre/post captures match. Both failures are the 12px informational character counter at **4.48:1**, `#777680` on white, in held-Save and uncertain states. The existing secondary-text token repairs it; the 4.5:1 assertion and all axe rules remain unchanged. Later assertions in those failed cases were not reached |
|
||||
| Counter-corrected export `LFCrTZzDtX5hNBSRBj5Xx` | **9 pass/1 fail**, 10:36:54.419717–10:37:55.849468 | `native-candidate-2-counter`; 19 pre/post captures match. Held Save/unmount and counter checks pass. C8 passes its first uncertain fixture but cannot find the independently seeded second pending draft: navigation to the identical URL/fragment retains the mounted list. C8 now reloads between independent fixtures and proves a new `performance.timeOrigin`; no product auto-refetch, lost-state reset, skip or relaxed assertion is added |
|
||||
| C8 fixture-corrected run, same `LFCrTZzDtX5hNBSRBj5Xx` export | **9 pass/1 fail**, 10:46:44.269454–10:47:53.113854 | `native-candidate-3-c8`, result SHA256 `142c0bba53863e1c518386746a8a2e6946d448f13011b242044d4743aebad4c0`; 19 pre/post sources match. All five C8 status fixtures pass. C5 waits for the destination URL before proving the lazy workspace rendered; its trace has no destination-workspace GET before Back. Releasing Save and going Back encounters a replacement dialog for saved A before B's field assertion. The fixture now requires B's actual heading/tablist and zero old draft regions before release. Product code and strict persistence/identity assertions remain unchanged |
|
||||
| Final compose, same product export | **10 PASS**, 10:51:11.697665–10:52:14.846477 | `native-final-verified`; all five C8 status branches and C5's actual-unmount/late-completion assertions execute and pass; zero skipped, unexpected or flaky cases, retries 0 |
|
||||
| Final inbox/read consumer | **8 PASS**, 10:52:14.851996–10:53:01.436862 | Same final capsule/export; prior inbox query, decision and recovery assertions remain ordinary passing cases, retries 0 |
|
||||
| Final ordinary P2-D01 | **1 PASS**, 10:53:01.442415–10:53:13.190106 | Existing `legacy workspace preserves frozen documents` case, retries 0; no skip or expected-failure marker |
|
||||
|
||||
The earlier C8-corrected specification SHA256 is `1cb1d3f458bdfc8ce856b9e1f54a1d59ffa25e535ff4835b8bc99375a130902c`; `native-final-c8-pre.json` captures that attempt. The final C5/C8 specification SHA256 is `5cb7a8f67926eb8abaf87e740eef5192921a52d39d14cbdad556476d4e818f0d`. `native-final-unmount-pre.json` and `native-final-unmount-post.json` prove all 19 inputs unchanged across final compose/inbox/P2-D01 execution. Final capsule `native-final-verified/results.json` SHA256 is `21a02c6d67dc2732e4a230787261b406be8db3100d5df3a10065f5669f482a11`.
|
||||
|
||||
The four retained attempts contain, respectively, **13/22/23/23 axe scans**, **0/2/0/0 violation nodes**, **143/228/239/235 incomplete nodes**, and **25/26/26/26 PNGs**. Incomplete categories are short badge content, overlapped contrast backgrounds and `aria-hidden-focus` modal-open checks; retain their complete contexts for the final contextual review. Zero automated violations in a failed run do not establish workflow or human accessibility acceptance.
|
||||
|
||||
Finite responsive samples cover 320, 360, 390, 768, 1024, 1280, 1440 and 1920px, including 1280×320 landscape, both languages and themes. Long drafts, keyboard setup, review, Cancel and viewport-focused captures use zero app-root overflow assertions. This is a finite sample, not every width/theme/language/state combination.
|
||||
|
||||
Final compose records **24 all-enabled axe scans, 0 violation nodes and 246 incomplete nodes**: 24 short notification badges, 219 overlapped contrast backgrounds and 3 modal hidden-root focus checks. Final inbox adds **20 scans, 0 violations and 84 incomplete nodes**: 20 badges and 64 overlap backgrounds. Ordinary P2-D01 performs no axe scan. The combined final evidence is **44 scans / 0 violations / 330 incompletes**, with every vector retained. `axe-triage-final.json` records category dispositions; badge/background contrast and full modal Tab/Shift-Tab containment and reader behavior remain conformance obligations. Incompletes are not counted as passing.
|
||||
|
||||
The triage receipt measures **23 informational character helpers**, including locked states, from recorded foreground and nearest Paper background; minimum calculated ratio is **9.3359:1**. This resolves the specific counter-color failure and does not settle blending, every other contrast target, fonts or contextual manual contrast acceptance.
|
||||
|
||||
The final capsule retains **47 PNGs**. Root has inspected 16; the remaining 31 are under independent visual review. Image existence does not establish review completion. Root's actual bilingual review identifies a residual localisation issue for **P7-11**: changing an open saved draft from English to Bokmål preserves the generic context label **“Saved email draft”** in its opening locale while surrounding controls translate. Stored source/job/subject names remain verbatim. Strict language HTTP204 and unsaved-field/identity preservation pass, but this residual prevents a blanket localisation PASS.
|
||||
|
||||
The browser boundary distinguishes actual isolated HTTP/SQLite draft persistence from capability/delivery presentation fixtures. One case permits real `/email/send` only after independently proving the fresh owner has no Gmail connection: the server must record `failed/reauthorization_required` before delivery. Its failed-only new-attempt response is real. Other send requests are blocked or fulfilled by labelled browser fixtures. Uncertain/pending/sending/sent/generic409 UI states do not prove provider persistence or successful delivery. No external recipient is sent mail, and no provider grant, Google/Outlook/IMAP execution, Gmail sync/import/create/unlink or model call is authorized by this evidence.
|
||||
|
||||
Current product app TypeScript, lint, browser TypeScript and frontend build all exit **0** between **10:36:14.414955 and 10:36:54.413694**, producing `LFCrTZzDtX5hNBSRBj5Xx`. Browser TypeScript reruns after final test-only C8/C5 corrections at **10:51:06.686512–10:51:11.691657**, exit **0**. Automated names, keyboard and axe evidence cannot close human screen-reader, contextual contrast, physical touch/software-keyboard, real zoom, fonts/photo or print qualification.
|
||||
|
||||
### Final acceptance checks pending
|
||||
|
||||
Final browser TypeScript and **19/19 native cases** pass on the unchanged export, with matching pre/post sources. Complete the remaining visual review and record its actual scope; retain the triaged manual accessibility and localisation qualifications. Documentation, secret, whitespace, route/preservation checks, commit and normal development push remain **PENDING**. This draft does not claim final slice acceptance or push.
|
||||
|
||||
## Count reconciliation and preservation
|
||||
|
||||
| Inventory | Inbox exit | Compose additions | Derived inventory |
|
||||
|---|---:|---:|---:|
|
||||
| Frontend cases/suites | 729/94 | +10 cases/+0 suites | **739/94** |
|
||||
| Browser cases | 291 | +10 | **301** |
|
||||
| Backend cases | 1,034 | +7 | **1,041** |
|
||||
|
||||
These are inventory calculations, not fresh whole-suite pass counts. Latest actual whole frontend **573/89**, backend **1,033** and sidecar **49** remain inherited until P7-12's integrated run. New cases remain ordinary assertions; no skipped or expected-failure case conceals a regression.
|
||||
|
||||
All **57 preservation groups remain 56 PARTIAL / PRES-C11 DEFERRED**, with **32 routes**. Evidence narrows W05–W10 and M01–M06 workflow risks without declaring any complete group accepted. M04 remains qualified by the actual draft/send boundary and pending provider/manual evidence. Original owner authorization, explicit confirmation, payload identity, revision conflicts, uncertain delivery, frozen package bytes, CV associations and canonical Career Profile authority remain binding.
|
||||
|
||||
M01–M12 remain NOT VERIFIED. Keep RD11, RD21, actual provider delivery, screen-reader, physical touch/software keyboard, real zoom, production fonts/photo/print, Board/Dashboard reliability and other recorded debt. P5-13 remains withheld; P7-06F remains deferred. F04 ingress, backup/restore, schema/configuration verification and tested rollback still prevent production acceptance.
|
||||
|
||||
## Remaining acceptance work
|
||||
|
||||
Complete visual review, run finite documentation/secret/whitespace/route/preservation checks, then commit this bounded repair and verify the normal development push against HEAD. Record the commit and actual receipt after the push. Carry the generic saved-draft locale residual into P7-11 and preserve all manual qualifications. Proceed to the approved P7-07 metadata contract before its Documents UI; do not restructure correspondence persistence there.
|
||||
|
||||
**Compose/send acceptance: PENDING. Overall P7-06: ongoing. Production: NO-GO; main unchanged. Phase 8 implementation: none.**
|
||||
|
||||
### Final visual observation
|
||||
|
||||
All **47 final PNGs** were actually displayed and reviewed: root16 and independent reviewer31, with exact per-image references in the portable result. No blocking visual defect was found. Full-page/sticky/dialog/transition captures and resized long images retain their stated limits. In addition to the opening-locale draft-context label, English singular count copy (`1 attachments`, `1 items`) remains a P7-11 residual. No human reader, physical-device, zoom, provider or production acceptance follows. Final runtime/source/visual gates establish bounded **CONDITIONAL GO**; finite hygiene and normal development push follow below.
|
||||
|
||||
Final compose hygiene PASS observed2026-10-14 11:01:54.856146UTC: secret/working/staged/phase whitespace gates pass,16 docs/486 local links resolve,57 groups56PARTIAL/C11DEFERRED and32original routes retained. All47finalimages actually reviewed. Implemented scope CONDITIONAL GO; normal development push pending, productionNO-GO/mainunchanged.
|
||||
Reference in New Issue
Block a user