19 KiB
P5-15-01R-02 — Rules draft preservation
Source: c8e32238ef628f34c6ae8f64c6cb40ca5d8c68ac; development codex/ui-redesign-phase4. This checkpoint repairs P5-15-01-SETTINGS-01 only. Production main remains unchanged at 27657f31131cd95d42f0e1290853ec9c9b3959a4 and NO-GO. No Analytics repair, saved-search application change, P5-15-02, P5-13, Docker, B07 or deployment.
P5-15-01R-02: GO for the bounded Rules draft repair. Native retention, explicit save/discard/validation/retry, reload and account checks pass; all final applicable suites below have fresh results. The full ordinary browser command still exits1 for the separately retained AUTH-01 Analytics query-state defect. This is not an all-green application acceptance or production GO.
Reproduction and demonstrated root cause
A disposable authenticated Development/SQLite application seeds the actual six-field Rules record with Applied follow-up14. Edit that field to5, change Follow-ups→Notifications→Follow-ups. Before implementation, the browser observes14 on return while the database remains14 and zero Rules PUTs occur. The ordinary regression fails on the visible value: expected5, received14. It is not a mocked persistence response or missing-selector failure. Before/after native proof and fingerprints.
The former SettingsView.TabPanel returned null for inactive sections, unmounting RulesSettingsCard. Its component-local s held both fetched data and the unsaved draft. Returning mounted a new card, fetched persisted14 and replaced the destroyed draft. This is an editing-session ownership defect, not deletion/corruption of persisted rules. No evidence establishes which historical release introduced it.
The new ordinary browser regression was committed/pushed before application changes as 5b5dcd0b80aa0713545ecf1fdab079fb9dec28ee. Lower-level tests then reproduced the missing parent ownership/error/discard/race contract: a clean pre-fix Settings run had6passes/6failures. The old five tests and teardown case passed; six new desired-behaviour cases failed. The final Settings file now has13passing cases, including eight new cases over the inherited five:
- Preserve independent pending fields through tab unmounts, without PUT.
- Retain failed-save draft/error through tabs and explicit retry.
- Retain blank/out-of-range input and associated validation through tabs.
- Explicit Discard resets to the saved baseline without PUT.
- Pending save acknowledges5 while preserving newer dirty6.
- Old instance completion cannot affect a new Settings session.
- Changing UI language while Rules is hidden preserves its draft/no refetch.
- Rules editing leaves unrelated notification preference loading unchanged.
Actual workflow and persistence contract
- Route
/settings, Follow-ups tab or phone section selector. General, Follow-ups, Notifications and Backup are internal sections of the same Settings instance. - Authenticated local-account
GET/PUT /api/rules, ownedUserRuleSettings,RulesController. The current user comes from authenticated context; client record IDs/forged owner fields do not choose another user's record. - One atomic six-field configuration:
appliedFollowUpDays,appliedGhostDays,offerFollowUpDays,offerGhostDays,feedbackFollowUpDays,feedbackGhostDays. Applied/Offer/Feedback are independent scenarios within one save, not three separately persisted rules. - GET retrieves/initializes the owner's configuration. PUT returns204 and clamps integer day values to1–365. The unchanged API has no revision/ETag or multi-client write-conflict contract.
- Persisted baseline, unsaved session draft, pending save, validation/save errors, successfully acknowledged snapshot and explicitly discarded values remain distinct.
- Internal section navigation retains the draft. Leaving Settings ends its in-memory session and discards unsaved changes, deliberately stated in the visible localized scope notice. No new cross-session recovery, automatic saving or browser storage is introduced.
Smallest responsible repair
| File/component | Change and reason |
|---|---|
| SettingsView | Owns one Rules draft/baseline/load/save/error state above the existing unmounting panels; both tabs and phone selector use the same entry action. A single pending load is retained across transitions; loaded drafts are not refetched/reinitialized on tab return or locale change |
| RulesSettingsCard | Controlled six-field form; retains existing labels/layout; explicit Save, bounded Discard, associated validation and durable localized load/save feedback. No API call or second local draft |
| Translations | Three concise EN/Bokmål strings for successful save, integer day validation and the Settings-session draft boundary; existing Save/Discard/error labels reused |
| Settings regressions | Eight behavioural cases added; original five retained |
| Native browser regressions | Four ordinary cases using actual SQLite persistence/account APIs; no CSS class assertions, expected failures, skipped tests or live providers |
Only three application-code files change. Settings is the shared owner; Rules is its sole form caller; translations are required for user-visible behaviour. No unrelated application file, backend, model, migration, API contract, dependency, renderer or deployment configuration changes.
Save validates all six strings as required whole numbers1–365 before PUT. This preserves blank/out-of-range input for correction rather than silently converting it to0/default or accepting the server clamp as successful input. Per-field helper text is associated with the input. Save submits a captured snapshot and acknowledges only that snapshot; if the user edits5→6 while5 is saving, completion records persisted5 while preserving dirty6. One pending submission is enforced. Discard returns to the last acknowledged baseline and writes nothing; it is disabled while a save is pending.
Failed save leaves all pending values and dirty state available, shows a durable in-page error, and permits explicit Save retry. No stack trace/provider text is exposed and no success is announced for failure. Invalid input/errors survive section changes. Existing authenticated-session invalidation and app/provider disposal remain authoritative; async completions from an unmounted Settings instance cannot set state/toast in a new instance. No second concurrency or identity system is added.
Behavioural evidence
| Journey | Fresh observed result |
|---|---|
| Original edit→tab→return | **PASS:**14→5→return5; persisted14; zero PUTs, proving retention is not autosave |
| All six fields / repeated internal switches | **PASS:**5/40/9/20/11/25 retained through two round trips in each of four variants; no cross-scenario draft substitution |
| Validation / internal return | **PASS:**blank Applied and Offer366 retained with named associated errors; zero PUTs; correction permits normal save |
| Save failure / retry | **PASS:**declared one-shot synthetic PUT500 retains draft/error across tabs; next real SQLite PUT204 persists all six values; dirty clears only after acknowledgement |
| Successful save / reload | **PASS:**native GET and browser reload show the acknowledged values; all scenarios remain distinct |
| Explicit discard | **PASS:**new Applied6/Offer10 returns to persisted5/9; no data mutation or implicit save |
| Delayed loading | **PASS:**one held real GET across repeated transitions; loading is named; release initializes once, not over a newer draft |
| Pending save / newer edit | **PASS:**submitted5, newer draft6, returned204, persisted5, dirty6; Discard restores5 |
| Leave Settings / re-enter | **PASS:**visible session warning; unsaved8 discarded deliberately; re-entry retrieves persisted5 |
| Account switch / crafted owner / anonymous | **PASS:**A persisted14/draft9; B forged owner/ID request updates only B22; A remains14; old page disposed and B displays22 without A draft; anonymous GET/PUT401 |
| Locale switch / unrelated settings | **PASS:**lower-level hidden Rules draft survives locale change without a new Rules GET; editing Rules does not trigger unrelated notification-preference refetch |
The four focused browser cases pass4/4 with zero retries/skips/flaky/expected failures. Fault injection is limited to the explicitly declared PUT500 and held native reads/writes; ownership, persistence and readback exercise the real SQLite API. No server conflict semantics are invented.
Accessibility, localisation and responsive evidence
Actual variants: EN/light1440, Bokmål/dark768, EN/dark390, Bokmål/light320, height900. Desktop/tablet use keyboard activation of selected tabs; phones use keyboard section-selector opening/selection/focus return. Save/Discard activation, labelled numeric inputs, associated validation, non-colour dirty text and alert/status semantics are asserted. Reduced-motion is enabled for the matrix. Reflow checks test visible controls/text bounds, not specific CSS classes.
Focused evidence includes 16 screenshots and16axe scans;0reported violations;150incomplete node occurrences:142contrast and8hidden-focus. These are repeated occurrences, not150distinct defects or accepted checks. A coding-agent visual review inspected genuine EN desktop/light and phone/dark drafts, Bokmål dark tablet error, and320px Bokmål validation/discard images: fields, error text and actions remain readable, focus is visible, narrow content wraps. This does not certify human screen-reader announcements, physical touch/software keyboard, broader zoom or complete accessibility.
Representative images (all16 are in proof manifest):
- Desktop EN/light draft and focused navigation
- Tablet Bokmål/dark save failure
- Phone EN/dark draft
- Narrow phone Bokmål/light validation
- Narrow phone explicit discard
Preservation traceability
| Contract | Existing behaviour → repair → evidence | Current complete-group disposition |
|---|---|---|
| PRES-S03 | Separate Rules/preferences scopes and explicit Save → parent session draft, associated validation, durable failure/retry/discard →13Settings cases +4native journeys +original diagnostic | FAIL→PARTIAL, not PASS; notification contention/language persistence and full human/provider acceptance remain open |
| PRES-W11 | Owned follow-up/ghosting configuration → same six-field atomic API/save and no auto-write → native six-value readback, forged-owner/anonymous cases, unchanged RulesEngineProspect backend regressions | PARTIAL unchanged; scheduling/calendar/provider delivery and historical500 cause not qualified here |
| PRES-E07/E08 | Reachable Settings navigation, locale/theme and feedback → shared tab/phone session entry, localized status/errors →four keyboard/reflow/theme/locale variants | PARTIAL unchanged; broad human AT/device/zoom scope remains open |
| PRES-E09 | Account-private draft disposal and stale-session rejection → retain existing API/session owner and suppress obsolete instance completion →native A/B/forged-owner/401 +component teardown | PARTIAL unchanged; not a complete deployed/BFCache/session certification |
All57 original contracts stay unchanged. Current complete-group totals after the scoped repair are PASS0 / PARTIAL55 / FAIL1 / DEFERRED1 / BLOCKED0 / NOT VERIFIED0 =57. E02 Analytics sign-in query loss remains FAIL; C11/P5-13 remains DEFERRED/Production-withheld. Original00,01 andR-01 statuses are retained in parent matrix. A PARTIAL group still contains unverified sub-workflows.
Fresh validation and diagnostic reconciliation
Backend1,033/1,033; frontend472/472 in82suites; revision5/5 and focusedAI108/108 are backend subsets; sidecar49 and configuration3 are fresh. TypeScript, lint, frontend production build and API/test-project Release builds pass. Frontend count464→472 is exactly eight new Settings behavioural cases. Backend count is unchanged; no backend test/model/contract changes.
All rows below are fresh after the final application change, not inherited success. Portable validation, exact commands/times, fingerprints and limits, native proof and final repository checks distinguish ordinary cases, subsets and independent suites.
| Suite/check | Fresh result | Reconciliation |
|---|---|---|
| Backend full | 1,033/1,033 | Unchanged count; no backend production/test change |
| Frontend full | 472/472,82suites | 464+8new behavioural Settings cases; no failures/pending |
| Ordinary browser | 128pass /1fail /129total | R-01's125cases +4new Rules cases;117inherited,4audit,3saved-search and4Rules cases pass; AUTH-01 is the one real failure |
| Standalone preference diagnostics | 2pass /0fail /2total | Previously1pass/1fail; original Rules assertion now verifies retained5/persisted14/zero PUTs |
| Focused Rules native browser | 4/4 | Subset rerun, not four additional full-suite passes |
| Sidecar | 49/49 | Native JUnit; unchanged |
| Focused AI | 108/108 | Backend subset; deterministic, no configured-model qualification |
| Real HTTP | 2/2 | Actual API grounding/stale/account and Production withholding before provider/write; unrelated AI available |
| Revision safety | 5/5 | Backend subset; source/override/history/frozen references preserved |
| Standard fidelity | 16/16;36preview=36PDF pages | Same16fixtures, native output and unchanged predeclared tolerances |
| Settings exports | 8/8;24preview=24PDF pages | Same8exports/settings and unchanged tolerances |
| Rate-limit / ingress / PDF admission / configuration | 9/6/1/3 | Fresh separate local suites; not live F04/production evidence |
| Additional offline helpers | Supply-chain3 / evaluation5 / extension3 | Unchanged deterministic parser/config regressions; no Docker/provider execution |
| App/E2E TypeScript, lint, frontend production/API/test Release builds | PASS | Fresh static/build commands, zero exit codes; no renderer/deployment changes |
| Secrets, whitespace, docs/57-group/32-route hygiene | PASS; final repository result | Actual final checks exit0; original contracts/main unchanged and remote equals the pushed code checkpoint before the final documentation commit |
The full browser command exits1 specifically at functional-acceptance.spec.ts:154: sign-in reaches /analytics without the requested ?months=6. No expectation was weakened, skipped or relabelled. P2-D01 passes normally. The original normal/admin saved-search failures and all three R-01 native repair cases remain passing; no saved-search code is touched. Both original diagnostic assertions now pass. Counts are not combined across overlapping suites/subsets.
All32route census patterns retain expected native entry/refresh/read states;98selected native GET observations include zero500s. The login query-continuity failure remains separate from entry/refresh and authorization. This repair does not reclassify full route-workflow acceptance.
Full-suite axe evidence is 267scans /0reported violations /2,040incomplete node occurrences: contrast1,768; hidden-focus41; prohibited-attribute209; valid-value18; table-header4. Focused16scans/150incompletes overlap and must not be added to the full totals. These are incomplete repeated occurrences, not passes or distinct confirmed defects. Human AT/device/zoom requirements remain open.
The prior R-01 CV “Unsaved” assertion passes in this unchanged full frontend run; native CV workflows also pass. It remains OPEN because its cause is unestablished. No CV assertion, timeout, implementation or renderer was altered to obtain a pass.
Intermediate tooling findings are retained separately: the first post-fix prebuilt browser attempt used API5202 while the disposable API was5302; correcting the test build environment resolved that harness issue. Two subsequent selectors were corrected to scope the existing Settings navigation and observe actual session-disposal navigation. No production code or product expectations changed to accept those failures. A required-input marker briefly changed exact labels; moving the native required attribute to the input restored existing accessible names. Final runs use the original required behaviour with retries0.
Outstanding obligations and risk
- AUTH-01 Analytics sign-in query loss: ordinary known failure, untouched; remains before P5-15-02 progression is reconciled.
- CV “Unsaved” intermittent assertion: prior R-01 failure has no demonstrated cause/fix; subsequent passing runs do not close it. Intermittent register.
- Historical language-preference500, notification SQLite-lock contention and Rules-save500 root causes remain open. R-02 qualifies draft/error recovery, not a backend reliability repair.
- Production provider and multi-client conflict qualification remain separate; native evidence here uses SQLite, not deployed MariaDB. No backend API conflict/version semantics are added.
- Human screen reader, incomplete axe review, physical touch/software keyboard, production font/photo, broader pointer/reflow and Board scaling/touch debt remain open.
- Production F04 ingress, current complete backup/tested restore, configuration/schema/secrets and tested rollback remain release blockers.
- P5-05, P5-13 qualification/reviewUI and followMode compose UI remain explicitly deferred; unqualified AI Production withholding remains active, zero qualification calls/reviews performed.
Immediate commit/push ledger and stopping point
| Completed logical unit | Commit / boundary |
|---|---|
| Native RED regression, reproduction contract and bounded TODO | 5b5dcd0b80aa0713545ecf1fdab079fb9dec28ee; pushed before implementation |
| Parent-owned Rules repair, eight component regressions, four native journeys and focused proof | 30f513d0c993800ec5d5583329ed65dc7ec9a8d4; pushed after focused browser/full frontend/backend/static validation; remote equality and clean tree verified |
| Final acceptance/evidence reconciliation | This documentation/evidence checkpoint; commit/push immediately after final repository checks. Its exact resulting hash and final remote/tree equality are reported to the owner after commit (no circular self-hash in this file) |
Read-only subagent review found no actionable correctness/security issue; it inspected the sole caller, actual API/session disposal and snapshot/validation tests, and did not claim test execution or human acceptance.
P5-15-01R-02: GO for the bounded repair; Production remains NO-GO. P5-15-02 is not cleared by this checkpoint while AUTH-01 remains an unreconciled ordinary failure. Stop before Analytics repair, P5-15-02, P5-13, Docker, B07 and deployment.