147 lines
19 KiB
Markdown
147 lines
19 KiB
Markdown
# 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](evidence/rules-proof.json).
|
||
|
||
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:
|
||
|
||
1. Preserve independent pending fields through tab unmounts, without PUT.
|
||
2. Retain failed-save draft/error through tabs and explicit retry.
|
||
3. Retain blank/out-of-range input and associated validation through tabs.
|
||
4. Explicit Discard resets to the saved baseline without PUT.
|
||
5. Pending save acknowledges5 while preserving newer dirty6.
|
||
6. Old instance completion cannot affect a new Settings session.
|
||
7. Changing UI language while Rules is hidden preserves its draft/no refetch.
|
||
8. 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`, owned `UserRuleSettings`, `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](../../../../../../job-tracker-ui/src/components/SettingsView.tsx) | 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](../../../../../../job-tracker-ui/src/components/RulesSettingsCard.tsx) | 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](../../../../../../job-tracker-ui/src/i18n/translations.ts) | 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](../../../../../../job-tracker-ui/src/settings-view.test.tsx) | Eight behavioural cases added; original five retained |
|
||
| [Native browser regressions](../../../../../../job-tracker-ui/e2e/rules-draft-repair.spec.ts) | 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](evidence/rules-proof.json)):
|
||
|
||
- [Desktop EN/light draft and focused navigation](evidence/rules-draft-en-light-1440.png)
|
||
- [Tablet Bokmål/dark save failure](evidence/rules-save-error-nb-dark-768.png)
|
||
- [Phone EN/dark draft](evidence/rules-draft-en-dark-390.png)
|
||
- [Narrow phone Bokmål/light validation](evidence/rules-validation-nb-light-320.png)
|
||
- [Narrow phone explicit discard](evidence/rules-discard-nb-light-320.png)
|
||
|
||
## 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](../../PRESERVATION_ACCEPTANCE.md). 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](evidence/validation.json), [native proof](evidence/rules-proof.json) and [final repository checks](evidence/repository-validation.json) 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](evidence/repository-validation.json) | 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](../INTERMITTENT_FAILURES.md).
|
||
- 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.
|