Platform release record
| Document ID | QMSN-CL-001 |
| Applies to | Every released version of the QMS Nordic platform |
| Part of | The software validation pack |
Releases of the QMS Nordic platform. Each release is the unit a customer validates: a validation report names a version, and their auditor asks what changed since. Every entry carries a change class and states, explicitly, whether it affects a validation you have already completed.
| Class | What it means | What you do |
|---|---|---|
| Patch | No behaviour change to a validated function | Record the new version. |
| Minor | New or changed behaviour, backwards compatible | Review this entry for functions you rely on; test only those. |
| Major | Changed behaviour of a validated workflow, or anything touching records, isolation, the audit trail or e-signature | Re-run the affected parts of your T4 and re-issue T5. |
Unreleased
The validation pack now covers the process modules, and ships an OQ and a PQ. (Patch — no behaviour change; evidence and documents only.) Nine new integration suites exercise the regulatory gates of CAPA, complaints, management review, training, suppliers, equipment and calibration, software lifecycle and submissions through the real server actions on the CI database, plus a unit suite for the vigilance clocks. A generated Module Evidence Annex (QMSN-MEA-001) states per module what they prove and what is left to the customer; a generated Operational Qualification (QMSN-OQ-001) puts our evidence beside each of the 26 requirements with a confirmation step; a Performance Qualification (QMSN-PQ-001) organised by module replaces the withheld Caelum-era draft. Pack revision G → H.
Built-in audit checklist templates can now be installed and seen. (Patch — one row-level-security policy added, no behaviour change to tenant data.) The shared templates are rows with no tenant; the table's policy admitted only a tenant's own rows, so "Install built-ins" was refused and the list read "Built-in (0)" in production. A read-only policy for shared rows is added, the install goes through the admin client, and a test run as the application role proves that every tenant reads them and none can alter them. Policy count 174 → 175; pack revision F → G.
Periodic review reminders are evidenced. (Patch — no behaviour change.) URS-DOC-05 — review dates tracked and surfaced before they lapse — was the last platform requirement without evidence; the 1.0.0 table below says the reminder "is not implemented", which was wrong: the daily job has existed since the data model, it was the tests that did not. The reminder rules now live in a pure module with unit tests, and the job is exercised end to end on the CI database. Pack revision E → F.
Type checking and linting now block the build. (Patch — no behaviour change to any validated function.) Both have run on every change since the first release and could not fail it; the 1.0.0 entry below lists that as a known limitation, and the traceability matrix carried the evidence for URS-CHG-01 as "reported but not blocking". That was accurate, and it was also how 151 type errors accumulated unheard — among them queries naming columns their tables do not have, which is why a CAPA effectiveness check never reached its owner and a document approval never assigned the training it should have. All are cleared; both checks now fail the build; the matrix says so; and a test fails the build if the workflow and the matrix ever disagree about it in either direction. Nothing about the platform's behaviour changed — only what can now reach production.
Quotations are raised and numbered in the platform console. Internal to Aitech; nothing in a customer tenant changes, and no validated workflow is touched. Recorded here because the console is described in the validation pack and its operational qualification has grown three test cases (OQ-BE-004 to OQ-BE-006).
A quotation now takes its number from the database rather than from whichever browser raised it, so two offers cannot carry the same number. Numbers are allocated when a quotation is finalised rather than when it is started, so an abandoned draft leaves no hole in the sequence.
Numbering and finality are separate events. A quotation is numbered so the PDF you email carries a real reference; it is fixed only once you say it has gone to the customer. Between those two moments it stays editable, because fixing a typo you have just spotted changes nothing anybody has seen. Draft, finalised and sent — editing closes at sent, and from there the document is a record of what somebody was actually given.
The subscription price is read from the published price list rather than typed, and re-read on the server when the quotation is saved. An operator chooses the term and the currency; they do not choose what the subscription costs.
A quotation prints in English or Danish, chosen per quotation. The language belongs to the customer rather than to the operator, so it is a property of the document and not the console, which stays in English. Both languages are held under one type, so a string added to one and not the other fails the build; a test catches the other half of that, where an English sentence is copied into the Danish copy and never translated.
The three commercial-terms lines — billing, payment terms, price at renewal — were stored as English sentences and are now stored as keys, so they print in the customer's language rather than in ours. Amounts follow the document: DKK 4.320,00 on a Danish quotation, DKK 4,320.00 on an English one.
A quotation can be emailed to the customer from the console, with the document attached as a PDF and a short covering note in the same language. Sending is what marks it sent, so the status reports something that happened rather than something somebody remembered to record; the manual Mark as sent remains for a quotation delivered some other way.
Sending is refused outright when email is not configured. Without RESEND_API_KEY every send falls through to a console stub, and a stub send that flipped the quotation to sent would leave a customer who was never written to and a record claiming otherwise. /api/health now reports emailConfigured so that is a fact you can check rather than assume.
The PDF is rendered from the stored row rather than from the screen, and it takes its wording and figures from the same modules the on-screen document does — so the layout is duplicated but no price, clause or translation is.
The free trial no longer appears on a quotation. It was in three places — the included list, the term ("from the end of the free trial"), and the first of the next steps — and all three are gone in both languages. The term now runs from the subscription start date. The trial itself is unchanged in the product and on the public pricing page; this is about what an offer says.
A quotation carries a start date, printed in the commercial terms, and the committed term runs from it. Optional: an offer is often made before a start date is agreed, and the document then says so rather than leaving the reader to work out when the term begins.
Quotation actions say what went wrong. Next.js redacts anything thrown from a server action in production, so every message written for the operator arrived as a paragraph about Server Components — a missing font module and a failed validation were indistinguishable, and telling them apart took a log dive. Expected conditions are returned rather than thrown and reach the screen intact; an unexpected fault is reported to Sentry and comes back with a reference to quote.
AI-generated documents are written in your language, not only English. Ten generators — the document drafter, the CER, PMS, PMCF, GSPR, audit-scope and management-review populators, and the hazard and root-cause suggesters — now take the language from the ones you set at Admin → Compliance, with a picker at each generation point to override it. Clause identifiers, standard names and document codes are left untranslated.
Class: Minor for a tenant that works in English — nothing changes. Review this entry if you maintain documents in another language: content that previously generated in English will now generate in yours, and AI output remains unvalidated content requiring review either way.
Class: Minor. No tenant-facing behaviour changed.
1.5.0 — 2026-09-01
Eleven features were not working. Several of them produced regulated output, and produced it wrongly rather than not at all.
These were found by turning on a type check that had been suppressed since the first commit. None was reported by a user, because almost none of them failed visibly: the queries behind them were rejected by the database, the rejections were caught, and an empty result was rendered in place of an error. An empty inbox looks like a quiet week.
Vigilance reporting classified incidents incorrectly (Major)
The EU MIR draft read three fields that do not exist on a complaint — isDeath, isSeriousInjury, isSeriousPublicHealthThreat. Every read returned nothing, so every conditional fell to its least-severe branch.
- A death was classified as a trend report.
- Its deadline was always 15 days. Article 87 allows 2 days for a serious public health threat and 10 for a death or unanticipated serious deterioration. Neither branch could be reached.
- The FDA draft classified everything as a malfunction.
Now read from the fields the record actually holds. One limit, stated rather than hidden: a complaint records seriousInjuryOrDeath as a single fact, so a death cannot be told from a serious injury. The EU deadline uses 10 days for both — the shorter of the two — because a deadline that is early is a nuisance and one that is late is a breach.
Validation impact: <strong>re-check any MIR drafted before this release.</strong> The classification and the deadline on the draft were wrong, in the direction of less urgency. No submission was made on your behalf and no record was altered — the draft is prepared for you to review and submit — but if you relied on the draft's stated deadline to plan a submission, that date was too late. Your own reportability decision and its evidence are unaffected.
The PSUR reported zero serious incidents (Major)
The same three non-existent fields were counted to produce the serious-incident figure, so the count was always zero, whatever the complaints said. The severity breakdown grouped on a severity column that a complaint does not have, so every complaint landed in one "UNSET" bucket.
Validation impact: <strong>any PSUR generated before this release understates serious incidents, and its severity breakdown carries no information.</strong> Regenerate it. Complaint records themselves were never wrong — only this report's reading of them — so a regenerated PSUR is correct without any data being re-entered.
The document bundle threw, and its approval record had no timestamps (Major)
Downloading several documents as a bundle ordered approvals by a column that does not exist, so the download failed outright. The approvals.json inside it — the file you hand an auditor to show who signed what — recorded no signing time for any signature. It now carries signedAt, the moment that constitutes the signature under 21 CFR Part 11 §11.50, and the document version released.
Validation impact: <strong>any bundle exported before this release is incomplete as evidence.</strong> Re-export it. The signatures themselves are intact and always were; the export omitted their timestamps. Nothing was signed that should not have been.
The daily digest was never sent, and the inbox was always empty (Minor)
Both list what is outstanding, and both silently listed nothing.
- No tenant has ever received a digest email. Its complaint section selected a column that does not exist — and never used it — which failed before the message could be assembled. Audit findings never appeared in a digest either.
- Every user's inbox showed no tasks, no CAPAs and no training due, because each of the three queries named the wrong assignment column.
Validation impact: none to your records — these read data, they do not write it. But <strong>do not treat the absence of past digests or inbox items as evidence that nothing was outstanding.</strong> If your process relies on the digest as a prompt, that prompt was not being delivered.
Smaller faults, same cause (Minor)
- The management-review agenda was drafted with no device class, every time.
- The iCal calendar feed rejected every subscription. It failed closed — invalid tokens were never accepted.
- Servicing and installation records could not be saved at all.
- The saved-report print page would not render.
- The data-integrity page showed a confident "0 access-review attestations" from a count of something that does not exist. The figure has been removed.
- Six design-control document types were drafted from the leaner of two definitions that had both been declared.
Validation impact: if your validation covered servicing records, installation records or report printing, those functions did not work and could not have been exercised. Everything else here affects what was displayed or drafted, not what was stored.
What has not changed
No record was created, altered, deleted or approved incorrectly by any of this. Every fault was a read that failed or returned nothing. Tenant isolation, the audit trail, the signature chain and retention are untouched, and remain covered by the tests listed in the test evidence report.
Why they were not found sooner
The type check that names all of these had been configured not to block the build, and 145 of its findings had accumulated. Eleven were real. Ten of the eleven were a single wrong column name, and most were wrapped in error handling that turned a rejected query into an empty result — which is why they looked like ordinary quiet output rather than failure.
The check now runs on every change and its findings are being cleared. It will block the build when the count reaches zero.
1.4.0 — 2026-08-28
Sign-in could fail for signed-out users; audit records that should have been written were not; and a restore of production has now been rehearsed.
Sign-in looked users up through a control that could not see them (Major)
The sign-in action looked the account up with the tenant-scoped database client. That client resolves an organisation from the session — and at sign-in there is no session. The isolation policy on the user table admits rows whose organisation is null or matches the current one, so with no organisation set the only visible row is the platform account. Every real account came back as absent and the form answered "No account found" without ever checking the password.
A visitor who still held a valid session could sign in normally, because the client then had an organisation to work with. A genuinely signed-out user could not. That is why this survived: the people exercising the product were already signed in.
The message compounded it, telling the operator to "run the seed if the database is empty" — a developer's message that made a broken query look like missing data.
The three lookups in that action now use the owner connection, which is what the authentication layer itself already did. Authentication was never broken; only the check standing in front of it.
Validation impact: <strong>no record was created, altered or approved incorrectly, and no isolation control was weakened</strong> — the failure was a refusal, not an admission. If your T4 covered sign-in while already authenticated it did not exercise this path; we suggest confirming a sign-in from a signed-out browser. If a user reported being unable to sign in between the enforcement of row-level security and this release, that report was accurate and is explained here.
Audit records for password reset were not being written (Major)
recordAudit writes through the tenant-scoped client. Completing a password reset has no session, so the write was refused by the database. On the completion step this surfaced as an error page after the password had already been changed — the reset succeeded and reported failure. On the request step the call is wrapped in a handler that discards errors, so those records were simply never written, silently.
Audit writes from paths that legitimately have no session — password reset, signup, invitation acceptance, billing webhooks — now use the owner connection. Where an organisation is established the tenant-scoped client is still used, so a caller inside one organisation still cannot write an audit record for another.
Validation impact: <strong>there is a gap in the audit trail and it cannot be backfilled.</strong> Between the enforcement of row-level security and this release, PASSWORD_RESET_REQUESTED and PASSWORD_RESET_COMPLETED records were not written. Password resets in that window did occur and did take effect; they are not evidenced in the audit trail. No existing record was altered or removed, and no other action type was affected. If your validation relies on the audit trail being complete for authentication events, treat that window as a documented gap rather than as evidence that no resets occurred.
A restore of production has been rehearsed and witnessed (Patch)
URS-RET-03 — data is backed up and restorable — could not be closed by a test and had never been demonstrated. It has now been.
A point-in-time copy of production was restored to a separate branch and verified: all 4,006 audit records rehashed to their contents, all reachable from the start of their chain, and row-level security and tenant isolation present on the copy — a scoped read returned 31 of 547 documents, so isolation was live on the restored data rather than merely configured. The application was then run against the copy and signed into, and the audit log and validation pack confirmed intact.
Measured recovery time: 45 minutes, from the decision to restore to the system being usable — against a four-hour commitment.
Evidence: the restore verification report dated 2026-08-28, filed with the validation pack. Deviation CAELUM-DEV-0001 is closed. Rehearsed annually.
Validation impact: your testing burden decreased. Requirements we hold no evidence for fall from three to two, and no platform requirement rated High is now without evidence. If your T3 records RA-17 as an accepted risk on the basis that a restore had never been demonstrated, that entry can be re-rated.
AI usage is metered on every engine, and can be refused (Minor)
Token usage was recorded by two of the fourteen engines that call the drafting model, and not at all for spoken conversation or for reading answers aloud. The allowance could therefore be passed without the product noticing.
Every engine is now metered. Spoken audio and speech draw more from the allowance than text does, in proportion to what they cost to serve — audio about eleven times a text token, and reading an answer aloud about eleven times per character. The interface says so before either begins.
Validation impact: <strong>a new failure mode exists that did not before.</strong> Where AI features previously ran regardless of the allowance, a request may now be refused when the allowance is exhausted — spoken conversation and read-aloud included. This affects availability of AI assistance, not the integrity of any record: nothing is written, altered or approved differently. AI drafting already behaved this way. If your validation covers AI features, add the exhausted- allowance case to it.
Disclosed with this release, and not yet fixed
| What it means for you | |
|---|---|
| Sign-in is not recorded in the audit trail | Only the demo sign-in path writes a LOGIN record, and that path is disabled in production, so no successful sign-in has been audited since 2026-05-28. The Access Review page reads those records to show a last-sign-in date and therefore shows none. No published requirement claims sign-in auditing, so nothing we told you was untrue — but if your own procedure expects authentication events to be auditable, this does not yet meet it. |
| 67 places where the audit chain forks | Two records claiming the same predecessor, from writes batched between 23 May and 22 June 2026, across 8 organisations. Every record hashes to its own contents and every record is reachable from the start of its chain, so nothing was altered and nothing is missing. At those points the chain alone does not prove nothing was removed; deletion of audit records by the application is separately blocked at the database. The cause is understood and is not fixed — it recurs whenever audit records are written in a batch. The existing 67 cannot be repaired without destroying the property the chain exists to prove. |
1.3.0 — 2026-08-26
Making a document effective now requires that it was approved.
A control that lived in the interface now lives in the system (Major)
makeEffective had no status check. The rule — only an approved document may become effective — was enforced by the page, which renders the button only for approved documents, and not by the action behind it. A form post could take a draft straight to effective, and that includes an AI draft nobody had reviewed.
This was found while writing the test for URS-DOC-03, which is the point of writing tests for published claims.
The action now requires the approved status, an approval record to exist, and — where the signature records which version it released — that version to be the current one. Signatures predating v1.2.0 carry no version and are accepted rather than blocked.
Validation impact: <strong>review this even though nothing you already recorded stops working.</strong> Two requirements we published — URS-DOC-03 (only approved, current versions are presented as effective) and URS-AI-02 (no AI draft becomes effective without human review) — described the interface rather than the platform on every release up to and including v1.2.0. If your validation relied on either, the control you were relying on was real in normal use and bypassable by a crafted request. It is now enforced server-side and tested. No record was altered; nothing needs re-approving. If your T4 covered these by clicking through the interface, that testing remains valid and now also holds for the underlying action.
One behaviour change to be aware of: a document whose approval is for an earlier version can no longer be made effective. If you have a document approved at version 1 and edited to version 2, it needs approving again — which was always the intent, and is now enforced.
Six requirements move from no evidence to covered (Patch)
URS-DOC-03, URS-DOC-04, URS-ACC-01, URS-AUD-04, URS-RET-01, URS-RET-02, plus URS-AI-02. Twenty-one tests, run against a database rebuilt from the migration history on every change.
Requirements we hold no evidence for fall from ten to three, and those needing something from you from fifteen to eleven.
Validation impact: your testing burden decreased. Nothing about the platform changed here beyond the fix above — only what we can show you.
The three that remain, and why
| Why it is still yours | |
|---|---|
URS-ACC-04 access removed when a user leaves | The platform removes access when told to. Knowing when to tell it is your joiner/leaver process, and no vendor evidence covers it. Most commonly found gap under this clause. |
URS-DOC-05 periodic review surfaced before it lapses | Review scheduling exists in the data model; the reminder behaviour is not implemented, so there is nothing to evidence yet. |
URS-RET-03 data is backed up and restorable | Cannot be closed by a test. Managed point-in-time recovery is configured; a restore of production has never been rehearsed, so we cannot evidence a recovery time. This is an open deviation on our side and is disclosed in the Master Plan. |
1.2.0 — 2026-08-26
A signature records the version it released.
document_approval.documentVersion (Major)
21 CFR Part 11 §11.70 requires an electronic signature to be linked to its record. That link existed and was correct — editing a signed document produces a new version and returns it to draft, so a signature has never been able to carry silently onto text it did not approve — but it lived only in the audit trail. Answering "which version did this signature approve?" meant joining the signature to its audit event, which is not where a QA lead should have to look.
Signatures now carry the version directly. URS-SIG-04 moves from partially to fully covered.
Signatures recorded before this release are backfilled from the SIGN audit event where one carried the version. Where it did not, the field is left empty rather than filled with a guess: an empty field means "not captured at signing", which is true, where a fabricated version number on a signature record would not be.
Validation impact: <strong>classified Major because it touches electronic signature and changes a table holding records</strong> — review it even though nothing you already did stops working. The column is additive and nullable; no existing signature was altered, none was deleted, and the audit trail is unchanged. If your T4 testing covered the signature-to-record link by reading the audit event, that testing remains valid. If you want the stronger assertion, re-run the relevant T4 step against the signature record itself. Historical signatures showing no version are a limitation of what was captured at the time, not a loss of data.
1.1.1 — 2026-08-26
The e-signature and audit-trail controls are now demonstrated, not asserted.
Controls exercised on every change (Patch)
The traceability matrix published five requirements with no automated evidence, all High risk, and several more whose evidence was the presence of a database trigger rather than its behaviour. A schema column is a design assertion, not verification — the pack said so itself, and told you to ask us for a demonstration.
That demonstration now runs on every change, against a database rebuilt from the migration history, so the controls are proven on the same artifact the release ships:
- a signature cannot be produced without a live challenge issued for this document to this user, and an expired or foreign challenge is refused;
- signing records the signer, the time and the meaning, and the action — not the caller — supplies the meaning;
- the author of a document cannot approve it;
- the audit trail refuses UPDATE and DELETE for the application's own database role, and an alteration made by a role that can bypass that refusal is detectable by recomputing the hash chain;
- editing a signed document produces a new version and returns it to draft, so a signature cannot carry silently onto text it did not approve.
Each test was checked by removing the control it covers and confirming the test fails — a test that passes either way is not evidence.
Validation impact: <strong>your testing burden decreased.</strong> Requirements we hold no evidence for fall from 12 to 10, and those needing something from you from 21 to 16. Nothing about the platform changed — only what we can show you. If you have already completed T4, the testing you did remains valid; you may be able to reduce scope at your next revalidation. See pack revision B.
Still uncovered, and still yours
Ten requirements. The largest are retention (URS-RET-01/02/03), obsolete- document control (URS-DOC-04) and human review of AI drafts (URS-AI-02). The traceability matrix names every one.
1.1.0 — 2026-08-26
The validation pack becomes usable, and says what it means.
Two independent review rounds — three reviewers, then five — found 21 and then 22 defects in the pack released with 1.0.0. All are fixed. The pack itself is at revision B; its document history (QMSN-VMP-001 §8) records what changed.
The pack can now be completed and filed (Minor)
Every document downloads as Word or Markdown, and the whole pack as one archive. The Word files carry real tables with usable column widths, working hyperlinks, and a document-ID and page-number footer — previously the pack was read-only HTML, so producing the five deliverables meant retyping them. Printing produces the whole document rather than one clipped page, and no longer loses bold text and links when the reader is on a dark theme.
Validation impact: none to a completed validation. If you started from a copy downloaded before today, see revision B in the Master Plan's document history — template T2's requirement identifiers changed, and that does affect a completed T2.
Requirement identifiers reconciled (Minor)
Template T2 and our requirement register assigned the same URS-* identifiers to different requirements: URS-DOC-04 and URS-ACC-03 each meant one thing in the document a customer signs and another in the evidence they were told to look it up in, and the access-control block was off by one from 03 onward. Seven requirements T2 asks a customer to confirm had no vendor position at all, including the whole retention group. T2, the URS and the traceability matrix are now generated from one register, and a build check fails if an identifier exists in one and not the others.
Validation impact: <strong>read this if you have completed T2.</strong> A confirmation recorded against URS-DOC-04 or any URS-ACC-* identifier before today maps to a different requirement than it does now. Re-check those rows against revision B. Nothing about the platform changed — only which requirement each identifier names.
Corrections to our own claims (Patch)
The traceability matrix stated that type-checking and linting block the build. They do not, and never did — the workflow marks both continue-on-error, and this release record already disclosed it. The matrix now says so, and the generator refuses to publish a "blocking" claim for a step that is not.
Document control was cited as ISO 13485 §4.2.3 — the 2003 numbering; §4.2.3 is Medical device file in the 2016 edition and control of documents is §4.2.4. 21 CFR 820.40 was cited as live law by a pack that states QMSR superseded Part 820 on 2026-02-02; it is now cited through QMSR §820.10.
The Master Plan now states what we have not done, distinct from what we do not provide: no disaster-recovery rehearsal against production, no third-party penetration test, no WCAG 2.1 AA audit, no automated retention enforcement.
Validation impact: none to the platform. If you relied on the matrix's change-control row, or on §4.2.3 appearing in your own documents, update them.
The release record reaches production (Patch)
This document was not included in the deployed application bundle, so QMSN-CL-001 returned a 404 in production while working in development — the one document that tells you whether a release affects your validation.
Validation impact: none. It was unreachable, not wrong.
Also
- Requirement counts move from 19 with 5 declared gaps to 26 with 12. The same system, counted honestly: seven requirements previously had no vendor position, and partial coverage is now distinguished from full.
- Template T3 no longer pre-rates the likelihood of controls we hold no evidence for, and now states how to derive a risk rating from impact and likelihood.
- Template T1 lists the 21 CFR Part 11 obligations that validating software does not discharge — including §11.100(c), the written certification to FDA.
- Accessibility: tick boxes are announced as choices rather than as states, tables carry row headers and captions, and the contents list works below desktop width.
- The public /legal pages no longer force wide tables to scroll sideways on phones — a regression introduced by work on the pack, which shares the renderer.
Known limitations unchanged from 1.0.0, plus: the five requirements with no automated evidence are still uncovered, and electronic signature remains the largest of them.
1.0.0 — 2026-08-25
First controlled release.
The platform has been in production for months, but every deploy until now was an unlabelled change to a validated system. Nothing named what a customer was running, so nothing could say what changed since they validated it. This release is the baseline that fixes that: from here every change is classified, recorded and traceable to a version.
Tenant isolation — enforced by the database (Major)
Row-level security is enabled and forced on all 134 tables holding customer data, with 174 policies, and the application connects as a role that cannot bypass them. Previously the only protection was the application remembering to filter every query — one omission from a cross-tenant disclosure.
Verified by connecting as the application role: with no tenant context a read returns 0 rows where the owner sees 544; scoped to one tenant it returns exactly that tenant's rows; and a scoped connection asking directly for another tenant's rows by id returns nothing.
Validation impact: a manufacturer relying on segregation of records between legal entities or sites should re-read this section. The control changed from procedural to technical.
Controlled change history (Major)
The migration history could not rebuild the database — two migrations altered tables before the migration that created them, and 10 tables were never created by any migration at all. It has been rebuilt on a verified baseline that carries the full security configuration, not just table structure.
Deploys now apply reviewed migrations (prisma migrate deploy) instead of reshaping the schema directly. A blocking CI job rebuilds the database from empty on every commit, asserts all 174 policies, 600 grants and 3 audit-tamper triggers survive, and proves tenant isolation is enforced on the result.
Validation impact: this is the §4.1.6 change-control evidence. The recorded history demonstrably reproduces the validated system.
Credential rotation (Major)
The production database owner credential shipped inside a handoff bundle and was treated as compromised. Rotated, and the old credential confirmed rejected.
AI document drafting — completeness (Major)
Drafts were being silently truncated. The model was never told a length budget and the output ceiling was too low, so 89% of SOPs and 100% of Plans stopped mid-sentence — and because the stop reason was ignored, the truncated text was streamed, saved and audited exactly as if it were complete.
Now: the budget is stated in the prompt, the ceiling is raised, and every finished document carries a completion marker. If a draft is unfinished — whether it ran out of room or stopped early of its own accord — generation continues from where it stopped, up to three times. If it still cannot finish, the user is told and the draft is flagged as incomplete in its provenance.
Validation impact: any AI-drafted document created before this release may be incomplete. Check for a document that ends mid-sentence, mid-table-row, or under a heading with nothing beneath it.
AI drafting — honest failure (Major)
When the AI service was busy, a failed request silently substituted a generic template for an AI-drafted document. It appeared, looked plausible, and was saved. A rate-limited request now returns an error and produces no document at all; the template survives only where it was always correct — a development environment with no API key configured.
Validation impact: a controlled procedure can no longer be a template that arrived by accident.
Corrections (Patch)
- Helpdesk reply threading: outbound messages carried an identifier containing spaces, which is invalid and could be rejected or rewritten in transit, breaking the reply chain.
- Eight contact addresses were unusable, including the address published on the public status page for reporting incidents.
- Both lead-capture flows reported a rejected email as delivered, and never logged the rejection.
- Document reference prefixes derived from a manufacturer name now treat an initialism as one unit ("JB-Medico" → JBM, not JM).
Engineering
Test suite 99 passing at release (from 8 failing at the start of this work); the suite has grown since — the count for any release is in that release's CI run. Continuous integration blocking on tests, build and the migration rebuild. Code-owner review configured over validation evidence, database schema and the e-signature, audit and isolation surface. The change-control procedure is documented and followed internally; ask for it if your supplier assessment requires a copy.
Known limitations
- Branch protection is not enabled — our code-review rules are followed by agreement, not enforced by the platform. The control is procedural.
- Documents longer than roughly 32,000 tokens cannot yet be generated in a single request.
- Type checking and linting run in CI but do not block, pending a backlog of pre-existing errors.
QMSN-CL-001 · v1.5.0 · pack rev. J (2026-09-07)
Published at qmsnordic.com/legal/validation/changelog. Cite the version above in your validation record — a pack is evidence, and evidence has to stay retrievable at the version you validated against.
