144 lines
12 KiB
Markdown
144 lines
12 KiB
Markdown
# RD21 acceptance addendum — 23 October 2026
|
|
|
|
**The reproduced SQLite stale-write and partial-commit defects are repaired and locally verified.
|
|
Formal RD21 remains UNRESOLVED because the unchanged issue requires supported MariaDB runtime
|
|
evidence. Phase 9, Phase 10 readiness and production remain NO-GO.**
|
|
|
|
This supplements the original [Phase 9 acceptance](../p9-07/FINAL_ACCEPTANCE.md); it does not
|
|
rewrite its historical findings or approval boundaries. The remediation uses the requested
|
|
`fix/rd21-acknowledged-edit-loss` branch from final Phase 9
|
|
`8e7e3f6387544e24082ebe2c107c0b868897959e`. Qualified product checkpoint: `1b89a77`.
|
|
|
|
## Separate acceptance decisions
|
|
|
|
| Decision | Disposition and evidence boundary |
|
|
| --- | --- |
|
|
| A. RD21 | **UNRESOLVED (qualification).** SQLite implementation repair passes. The [original required closure evidence](../p9-04/RD21_CONCURRENCY_ISSUE.md) explicitly includes MariaDB runtime constraints, held readers/writers and partial failure. That qualification remains absent. |
|
|
| B. Phase 9 functional QA | **NO-GO.** Local remediation assessment and complete automated regression are finished, but formal RD21 and original output/provider/manual conditions remain open. |
|
|
| C. Human accessibility/device acceptance | **Outstanding.** Fresh automated route/accessibility diagnostics do not replace NVDA/VoiceOver, physical touch/drag/software keyboard, forced colours, human zoom/reflow or professional Bokmål review. Original manual rows remain. |
|
|
| D. Provider/database qualification | **SQLite verified locally; MariaDB NOT TESTED.** No inference from SQLite or SQL generation to MariaDB runtime. External providers, enabled-worker recovery and model semantics remain separate. |
|
|
| E. Phase 10 readiness | **NO-GO.** Preserve all routes, compatibility paths, canonical/CV/frozen boundaries and remaining criteria. This handoff authorizes no cleanup/deletion or Phase 10 implementation. |
|
|
| F. Production | **NO-GO.** Storage faults, backup/restore/rollback, live advisories, deployment identity, ingress and provider/manual gates remain. No production database, migration, service restart, filesystem repair, deployment or model action occurred. |
|
|
|
|
## Reproduction and exact root cause
|
|
|
|
The unchanged original characterization ran at the exact baseline using two independent
|
|
authenticated clients and synthetic SQLite data. Both clients read one record. A successfully
|
|
saves a changed summary/title; B submits its older complete snapshot with a different field
|
|
changed. Career accepts B with 200; jobs accepts B with 204. Fresh reads restore A's field to its
|
|
old value: both `lostFirstEdit=true`. Its one passing characterization is evidence of unsafe
|
|
behavior, not a safety pass. [Captured observations](original-reproduction.json).
|
|
|
|
Both domains lacked the editor's source precondition on replacement writes. Career incremented
|
|
an internal version without comparing the source revision or enforcing it as an EF concurrency
|
|
token. Jobs had no equivalent revision token. A new request could therefore load the current row
|
|
and replace it with arbitrarily old client fields. The optional awaited job summary also left a
|
|
read/write interval that a controller-only comparison could not protect.
|
|
|
|
Career had a second proven failure: the canonical aggregate transaction committed before the
|
|
controller's Identity projection update. Injecting an Identity failure left canonical content
|
|
changed despite a failure response. Parse and reviewed import acceptance shared that split
|
|
boundary. Native SQLite RED tests capture persisted loss and partial commits; see
|
|
[before-regressions.json](before-regressions.json) and [root-cause analysis](ROOT_CAUSE.md).
|
|
|
|
## Implemented correction
|
|
|
|
Career source snapshots expose a coherent revision. Manual save, history restore, parse and
|
|
reviewed import acceptance require that revision. EF compares the original version at the
|
|
eventual database write, and competing first saves use the existing unique owner constraint.
|
|
Canonical children/history, raw text, Identity projection and applied-import metadata commit in
|
|
one transaction. Failure rolls back and clears failed tracked changes. GET no longer backfills
|
|
unversioned canonical data. The obsolete identity-only CV projection bypass fails closed.
|
|
|
|
Jobs have an application-managed EF concurrency token, advanced by every tracked job mutation.
|
|
Generic replacement and directly related cover-letter, application-answer and legacy tailored-CV
|
|
snapshot writers require their source revision. Related persisted text loss was reproduced
|
|
before extending the correction. Opportunity/timeline changes remain in the same SaveChanges
|
|
transaction; rejected requests append no success history or side effects. Generic Notes editing
|
|
preserves the separately embedded application answer and validates company ownership.
|
|
|
|
Clients keep source revision and draft together. They acknowledge saves after successful server
|
|
responses, retain rejected/network-failed drafts, exclude overlapping edits and late modal/read
|
|
responses, and require deliberate review/reload after conflict. They never fetch a newer revision
|
|
to authorize an older body. Whole-branch review found two integration defects: the committed job
|
|
header was hidden by CORS, and Answers could mount from a stale overview after a sibling write.
|
|
Both received deterministic RED evidence, one fix wave and a clean scoped re-review. Actual
|
|
cross-origin JavaScript and native successive saves prove the repaired integration.
|
|
|
|
[SAVE_CONTRACT.md](SAVE_CONTRACT.md) specifies endpoint/status/revision compatibility. Older
|
|
unversioned replacement clients intentionally fail closed: missing Career preconditions return
|
|
400, stale Career returns 409, and missing/invalid/stale job revisions return 409. Existing 204
|
|
job writes return the committed `X-Job-Version`, exposed by the allowlisted credentialed CORS
|
|
policy. No automatic stale retry, silent merge, arbitrary delay or new dependency was added.
|
|
|
|
## Completed verification
|
|
|
|
| Lane | Actual final result |
|
|
| --- | --- |
|
|
| Backend Release | **1,109 passed**, zero failed/skipped, exit 0; actual SQLite persisted-state, overlap, rollback, tenant, first-save, stale restore/import and populated migration upgrade/rollback/reapply coverage. |
|
|
| Frontend Jest | **934 passed / 105 suites**, zero failed/pending/todo, exit 0 on final source. |
|
|
| Ordinary browser | **362 passed**: serial shards 98 + 87 + 88 + 89; exact unique discovery membership, four exit 0 outcomes, no run errors/retries/skips/flakiness. |
|
|
| Separate browser contracts | **23 passed**: AI withholding 2, auth recovery 2, rate limits 9, ingress 6, PDF admission 1, preferences 3; no retries/skips/flakiness. |
|
|
| Sidecar | **56 passed**, zero failed/skipped, exit 0; `SKIP_MODEL_LOAD=1`, no model semantics claim. |
|
|
| New remediation browser | **6 passed**, no retries/skips: original A/B ordering, independent immediate SQLite reads, navigation/refresh/new authenticated session, missing revisions, native draft retention/recovery, actual cross-origin header, successive Answers saves and fresh source entry after letter save; includes conforming bounded sustained workload. |
|
|
| Conforming volume companion | **3 passed**: 100 CV revisions/additive restore, 100 exact timeline messages and 100 cancellation notifications/ownership/counts. Only Career seed revision supplied; original assertions retained. |
|
|
| Unchanged historical diagnostics | **16 passed / 2 failed**, exit 1: old Career seeds in `data.spec.ts` and `sustained.spec.ts` expect 200 without a revision and receive 400. Actual failures retained; zero skipped/retried. |
|
|
| Unchanged historical volume | **2 passed / 1 failed**, exit 1: old CV-volume Career seed also expects unversioned 200 and receives 400. Actual failure retained. |
|
|
| Types/lint/build | Final full TypeScript and ESLint exit 0; API Release and UI static export builds pass. New companion diagnostics receive separate TypeScript checking. No deployment. |
|
|
| Offline security | Supply-chain 3, evaluator 5, browser-runtime 6, ingress 3, Node dependency/SMTP 15 and extension 3 pass; tracked-secret scan passes and SBOM generated. No live advisory refresh. |
|
|
| Native PDF oracle | **16/16 standard fixtures, 36 pages; 8/8 Settings fixtures, 24 pages**. Unchanged oracle/tolerances, exact membership/page counts, inputs hashed before/after verification against final shard-produced bytes. Automated fidelity evidence; no new human/print/device acceptance. |
|
|
|
|
[validation.json](validation.json) retains exact cases, process timestamps/exits and report hashes.
|
|
The final source manifest binds **878 inputs**, SHA256
|
|
`7487f343786928e90a3c8a740571c29e5c934c80fa822abb80c7a39483e1353b`, including source,
|
|
active fixtures, historical diagnostics, static export and API/test assemblies. Source checks
|
|
before ordinary shards and after final diagnostics found no drift. The later conforming volume
|
|
copy has separate original/copy hashes in its [fixture provenance](volume/fixture-provenance.json).
|
|
|
|
The unchanged original reproduction was rerun and intentionally retained its actual post-fix
|
|
failure at the now-invalid unversioned seed. The conforming companion preserves the same A/B
|
|
read/write ordering with each source revision and stronger assertions: A's commit is read directly
|
|
from SQLite immediately after acknowledgement; B receives 409; A's values/history/projection
|
|
remain after rejection, navigation, reload and a new authenticated session. This is an explicit
|
|
contract adaptation, not a changed historical test or an assertion that the old fixture passed.
|
|
|
|
Counts are separate lanes, never summed into a blanket all-green total. Focused tests overlap full
|
|
suites; initial remediation 4 and focused integration 2 overlap final 6. Pre-review Jest 930 is
|
|
superseded by final 934. One earlier ordinary run was interrupted after 22 passes for confirmed
|
|
review findings and is not a completed suite. Baseline unsafe characterization and native RED
|
|
records remain. The initial job RED runner overwrote its first raw TRX; that provenance limitation
|
|
is disclosed in before-regressions.json rather than replaced with fabricated raw evidence.
|
|
The finite sustained run shared its host with frontend/static qualification work; it is a bounded
|
|
correctness workload and cannot establish an uncontended performance baseline or SLA.
|
|
|
|
## Provider, preservation and remaining risks
|
|
|
|
Docker `default`, `desktop-linux` and `desktop-windows` read-only probes failed because engine
|
|
named pipes were absent. No native MariaDB/MySQL executable or common Program Files installation
|
|
was found. No service installation, restart, download or production access was attempted.
|
|
MariaDB concurrency/first-save constraints/held-reader-writer/partial rollback must be exercised on
|
|
an isolated disposable supported instance before formal closure. Do not infer deployment safety
|
|
from a generated SQL script or the local SQLite migration test.
|
|
|
|
All **57 groups and 33 routes** remain; **56 PARTIAL / C11 DEFERRED**, zero upgrades to PASS.
|
|
Original remaining criteria and historical observations are retained in the
|
|
[reconciliation](PRESERVATION.md). Canonical versus CV-specific null-as-inherit overrides,
|
|
append-only revisions and submitted/frozen archives retain their passing native contracts.
|
|
The [related review](RELATED_REVIEW.md) distinguishes already-guarded Dashboard/CV/email drafts,
|
|
narrow job lifecycle commands and independent settings/company/contact records. This is not
|
|
complete multi-session acceptance of every independent editor.
|
|
|
|
Historical overwritten edits are not proven recoverable. Existing history may help a specific
|
|
record, but no live records were inspected and no recovery is claimed. Creation-intent idempotency
|
|
remains separate and unimplemented. Extraction discard-versus-accept lifecycle concurrency was
|
|
considered but not reproduced; it is not certified by these replacement-write tests. Unknown
|
|
network outcomes still require deliberate current-data review before retry. Mixed old/new clients
|
|
and servers, real MariaDB and production rollback remain unqualified; schema downgrade does not
|
|
preserve revision identity. Original duplicate reminder GET and raw `Created` terminology findings
|
|
remain separate low-priority observations.
|
|
|
|
Next qualification: run the same persisted-state, transaction/tenant and migration contracts on
|
|
disposable MariaDB; complete the original manual/provider/output/release gates; then reassess
|
|
RD21 and Phase 9. Phase 10 receives this blocked handoff only. No main merge, production action,
|
|
model activation, new integration or Phase 10 implementation is authorized by this addendum.
|