Release register
What changed in each release, which modules it touches, and our assessment of whether a controlled function was affected — so you can risk-assess a release against the state you qualified.
The classification on each change is our assessment of what we changed. It is an input to your decision, never a substitute for it. We do not know what you qualified, what your intended use is, or how you scoped your performance qualification — so nothing here is a statement that your system remains validated. That conclusion is your quality function's to reach.
- Re-qualification recommendedA control changed — an electronic signature, a lifecycle rule, an access restriction, record retention, or how records are handled. Re-executing the affected part of your performance qualification is the conservative course.
- Review against your intended useThe behaviour of a controlled function changed. Check whether the change falls inside the intended use you validated, and whether your performance qualification still covers it.
- No controlled function changedThis change did not alter a function that operates under your quality system — presentation, internal structure, performance, or content. It is not a statement that you need take no action; that remains your assessment.
The Complementary User Entity Controls register in the Validation Library is corrected: it said a customer's own export was the only way to recover uploaded files, which stopped being true when Indelio's own off-site copies began. Nothing in the Service changed.
CUEC-05 (RECORD EXPORT CADENCE) IS CORRECTED. The register, downloadable from the Validation Library, said that for uploaded files your own export was "the only recovery path that exists", because the hosting supplier's backups do not contain files. That has not been true since Indelio began keeping its own nightly, encrypted off-site copies, stored with Amazon Web Services in the United States: of uploaded files since 2 October 2026 and of the database since 11 October 2026. Each has been restored in a test, files on 2 October 2026 and the database on 11 October 2026. The entry now says so. It also says what still holds: up to 24 hours of work can be lost, after a full restore of the database every user sets a new password and enrols two-factor again, and those copies are Indelio's, not yours. The control itself is unchanged: an export you hold is your own independent copy, under your control and on your retention schedule, and we still recommend taking one regularly. The register's date moves to 2026-10-11.
Why this classification: Classified “none”: no function of the Service changed. A published validation document was corrected, so it is listed here: if your CUEC assessment or your supplier assessment of Indelio quoted CUEC-05's earlier wording, or relied on "the only recovery path" as a reason for your export schedule, review that record against the corrected text. Your own control, taking regular exports, is unchanged.
validation-library-sync.test.tsThe off-site copy of the database, added switched off in 2026.09.18.126, is switched on: an encrypted copy of the database is made each night and held by Amazon Web Services, as the Privacy Policy describes. No record, rule or signature changes, and no restore from a copy has yet been performed.
THE NIGHTLY DATABASE COPY IS SWITCHED ON. The two scheduled jobs added in 2026.09.18.126 now run. Each night at 03:45 UTC one consistent, read-only snapshot of the database is made: your records, electronic signatures, the audit trail and user accounts (names, work email addresses and roles). It is encrypted with a key Amazon Web Services never holds and stored in Amazon S3 in US East (Ohio). Each copy is held for 30 days under a lock that prevents its deletion before then, including by Indelio. Passwords, two-factor settings and sign-in sessions are not copied, and a copy found to contain them is refused before it is stored. At 04:30 UTC a second job, with read-only access, reads the newest copy back and checks that it decrypts and that every table matches what was recorded when it was made. Each run is recorded, and any run that does not succeed alerts Indelio. The daily integrity check now alerts if no complete copy has been made in the last 26 hours. Three things this does not do: it does not shrink the window of work that could be lost (anything written after the last nightly copy is not in it), the read-back is not a restore, and no restore from a copy has been performed yet. The copy is held for Indelio to recover the Service. It is not a feature your users see, and it is not something you can download.
Why this classification: Classified “requalify”: how records are handled changed. A copy of every organization's records, signatures and audit trail now leaves the primary database provider each night and is held by a second sub-processor for 30 days under a deletion lock. Nothing you use changed, and no record, rule or signature moves. The part to re-assess is your supplier and data-flow assessment of Indelio: Amazon Web Services now receives the database, as the Privacy Policy and sub-processor list in force since 2026-10-07 state, and the copies are retained for 30 days. ⚠️ Treat the copy as a protection against losing the Supabase account or project, not as a proven recovery: no restore from it has been performed. A later release will record the first restore drill. The jobs run under the same scheduler credential as every other scheduled job, already in your inventory from 2026.09.18.126.
db-backup.test.tsrelease-gate.test.tsaudit-gaps.test.tsApproving an amended quality policy now binds the signature to the amended version. Until now, approving straight after an amendment, without saving the draft, bound the signature to the previous version. An approval of an earlier version of the policy no longer reads "broken".
AN AMENDED QUALITY POLICY IS APPROVED AS ITS OWN VERSION. The quality policy's signature binds its statement and its version number. Amending an Effective policy raised the version number but kept the previous version's content hash, so approving it straight after the amendment, without saving the draft, signed the previous version's hash. Amending now records the new version's hash. Approval now works the hash out again from the saved statement and version, and refuses with "Nothing was signed" if the saved hash does not match, before anything is signed or written. A policy amended before this release, and not saved since, is refused until its statement is saved: pressing Save draft records the hash for the version, and approval then works. In the E-signatures list, the approval of the version in force is checked against its wording and reads "✓ intact" or "✗ broken" as before. An approval of an earlier version, or any approval while the policy is being amended, now reads "earlier version — not checked against this one". Until now, every earlier approval read "✗ broken" once a new version was saved, although nothing had been changed.
Why this classification: Classified “requalify”: a lifecycle rule changed and a signature's binding changed. Approving a quality policy is now refused where it used to proceed, when the saved content hash does not match the statement and version, and the signature now binds the hash worked out from them rather than the saved one. Found in an internal review of the Quality Policy records (#99). ⭐ No stored record changes: no signature or value already recorded moves. ⚠️ An approval already made straight after an amendment, before this release, stays bound to the previous version's hash, and now reads "✗ broken" while that version is in force. Look for it in the E-signatures list: amending the policy and approving it again records a correctly bound signature on a new version. ⚠️ If your own validation amends and approves the policy, that step now binds the new version, and a draft left over from an earlier amendment must be saved before it is approved.
quality-policy.test.tsA project deliverable's "Linked document" must name a controlled document that exists in your organization. It used to accept any text; a code that names no document is now refused, and nothing is written.
A DELIVERABLE'S LINKED DOCUMENT IS CHECKED, NOT TAKEN ON TRUST. When a deliverable is added to a project, or its "Linked document" is changed, the code must name a controlled document in your organization. Otherwise the change is refused before anything is written, with a message naming the code. A reference that cannot be checked is refused. If your plan does not include Documents, the reference is recorded as external and not checked. A document held outside Indelio goes in the deliverable's Notes. A linked document already stored is not re-checked when you edit something else on the deliverable, and is never changed. On the project page, a stored linked document that names no document is shown as plain text, "names no record in this organization".
Why this classification: Classified “requalify”: a lifecycle rule changed — adding or editing a project deliverable is now refused where it used to proceed, when a NEW or CHANGED linked document names no controlled document in the organization or cannot be checked. Same rule as the typed references checked in 2026.09.18.116 (#76) and 2026.09.18.128, extended to the last reference held back from it for a scope decision (decided by Alexis 2026-10-09: check it). ⭐ No stored record changes: no hash, signature or existing value moves, and a value already stored is not re-checked, so a project that already holds a linked document naming nothing can still be edited. ⚠️ If your own validation of the Projects workflow links a deliverable to a document code that does not exist in Indelio, that step now refuses — link an existing document, or put an outside document in Notes.
record-refs.test.tsA summative usability test can no longer be approved while a task result determined "Validated" records use errors or close calls and no root cause. Until now the root cause was asked for only when the determination was not "Validated".
OBSERVED USE ERRORS NEED A ROOT CAUSE BEFORE A SUMMATIVE TEST IS APPROVED. Approving a summative usability test now requires a root cause on every task result that recorded one or more use errors or close calls, whatever its determination. Until now it was required only for "Validated with residual risk" and "Not validated", so a result determined "Validated" with use errors and an empty root cause could be approved. The approval is refused before anything is signed, and the message names the result and what it recorded. The existing refusal for a result that is not "Validated" keeps its wording and now also says that nothing was signed. On the result form, the root cause is marked "required" by the same rule, once a result's saved use errors or close calls are above zero. A clean pass (no use errors and no close calls) still needs no root cause. Nothing already approved changes.
Why this classification: Classified “requalify”: a lifecycle rule changed — approving a summative test is now refused where it used to proceed, when a result determined "Validated" records use errors or close calls and has no root cause. Found in an internal review of the Human Factors records (#96). ⭐ No stored record changes: no hash, signature or value moves, and an approved test stays approved. ⚠️ If your own validation of the Human Factors workflow approves a test with such a result and no root cause, that step now refuses — record the root cause and approve again.
hf-summative-root-cause.test.tsThe reference boxes on Human Factors records must name a record that exists. A Use Specification's design project, a URRA's Use Specification and risk assessment, a Critical Tasks Register's Use Specification, a summative test's Use Specification and Critical Tasks Register, and an HFE/UE report's four references used to accept any text; a reference to a record that is not in your organization, or to a cancelled one, is now refused, and nothing is written.
HUMAN FACTORS REFERENCES ARE CHECKED, NOT TAKEN ON TRUST. Where a person types another record's code on a Human Factors record — a Use Specification's design project, a URRA's Use Specification and risk assessment, a Critical Tasks Register's Use Specification, a summative test's Use Specification and Critical Tasks Register, and an HFE/UE report's Use Specification, URRA, Critical Tasks Register and summative test — the code must name a record of that kind in your organization. Otherwise the change is refused before anything is written, with a message naming the code. A reference to a cancelled record is refused too; one to a superseded record is accepted, because it records what the record was written against. A reference that cannot be checked is refused. If your plan does not include the module a reference names, the reference is recorded as external and not checked. A reference already stored is not re-checked when you edit something else on the record, and is never changed. Assembling an HFE/UE report now refuses a stored reference that names no record, saying which one, and nothing is changed; until now it was written into the report as "not found", and approving the report signed that. Importing critical tasks now refuses a stored Use Specification reference that names no record; until now it imported nothing and reported success. Where these references are shown, on each record and in each list, they link only to a record that exists; one that names nothing is shown as plain text, "names no record in this organization".
Why this classification: Classified “requalify”: a lifecycle rule changed — saving a Human Factors record is now refused where it used to proceed, when a NEW or CHANGED reference names no record of the right kind in the organization, names a cancelled one, or cannot be checked; and HFE/UE report assembly and the critical-task import now refuse a stored reference that names nothing. Same rule as the typed references checked in 2026.09.18.116 (#76), extended to the Human Factors references held back from it for a scope decision (decided by Alexis 2026-10-08: check them). ⭐ No stored record changes: no hash, signature or existing value moves, and a value already stored is not re-checked, so a record that already holds a reference to nothing can still be edited. An HFE/UE report already assembled keeps the snapshot it was approved with, including any reference shown in it as not found. ⚠️ If your own validation of the Human Factors workflow assembles a report from a reference that names nothing, or imports critical tasks through one, that step now refuses — re-run it with a reference to an existing record.
record-refs.test.tsThe Privacy Policy and the Sub-processors page are updated in two places: the list of AI features, and an off-site backup copy of the database held by Amazon Web Services. The Privacy Policy and the Terms of Service take a new effective date, so every user is asked to accept them again. No database migration, and no change to what the software does.
THE PRIVACY POLICY AND SUB-PROCESSORS PAGE ARE UPDATED BEFORE ANYTHING NEW IS SENT. (1) The AI features named for Anthropic now match the Service: drafting training quiz questions and the in-app help assistant are added. An internal compliance advisor that Indelio uses on its own security program, and never on customer data, is no longer listed. (2) Amazon Web Services is now stated to receive, besides the uploaded-file backup, an encrypted copy of the database: records, electronic signatures, the audit trail and user accounts (names, work email addresses and roles), but not passwords, two-factor settings or sign-in sessions. Each copy is kept for 30 days under a lock that prevents its deletion before then, including by Indelio, and the Data retention section says so. This entry describes what Amazon Web Services would receive. It does not say that a database copy is running: the copy is built but locked. The effective date of both documents moves to October 7, 2026. Nothing else in the Terms of Service changed: its text differs only in the date.
Why this classification: Classified “review”: an accepted legal document changed and every user is asked to accept it again, and an existing sub-processor is disclosed as able to receive a copy of the database. No quality record, signature, audit entry, access rule or stored data changed, and no code sends anything new in this release. ⚠️ If your supplier list, data-flow diagram or data-protection impact assessment describes what Amazon Web Services or Anthropic receive from Indelio, update it to match the Sub-processors page.
subprocessors.test.tslegal-acceptance.test.tscopy-claims.test.tsA database migration adds one empty table, and two scheduled jobs are added for an off-site copy of the database that is switched off. Nothing is copied anywhere in this release, no setting is turned on, and no existing record, rule or signature changes.
DATABASE: ONE TABLE FOR A JOB THAT IS SWITCHED OFF. Run `supabase/db_backup.sql`. It adds one append-only table (no row can ever be changed or removed, and no user can read it) that will record each run of an off-site copy of the database. The copy is switched off in this release, so the table stays empty. Installation qualification step IQ-06 now counts 119 migration files.
Why this classification: Classified “review”: no function you can use changed, but the database you qualified did — one table is added, and IQ-06's migration count moves from 118 to 119. Apply the migration under your own change control and re-run IQ-06; nothing else in your intended use is affected.
db-backup.test.tssql-run-order.test.tsprotocol-code-sync.test.tsSCHEDULED JOBS: two new nightly jobs, `/api/cron/db-backup` (03:45 UTC) and `/api/cron/db-backup-read-back` (04:30 UTC), are registered for an off-site copy of the database. Both are switched off: each answers that it is locked and reads nothing, writes nothing and sends nothing anywhere. They require the same scheduler credential as every other scheduled job. The daily integrity check's answer gains one field saying the copy is locked; its checks and alerts are unchanged.
Why this classification: Classified “review”: the set of scheduled jobs on your instance changed, and a job is an unattended endpoint that runs across every organization. Both new jobs are locked in code and cannot copy anything until a later release; record them against your scheduled-job inventory (IQ §7) so the later release that switches them on is reviewed against a known starting point.
db-backup.test.tsrelease-gate.test.tsaudit-gaps.test.tsSuppliers: a supplier's audit can now be linked to a Supplier audit recorded in Indelio, and Indelio checks that it exists. The existing free-text box is kept, renamed "External audit reference", for audits done outside Indelio.
SUPPLIERS: on the Evaluation card, "Supplier audit in Indelio (code)" links a Supplier audit; a code that names no audit in your organization, or an audit that is not a Supplier audit or was cancelled, is refused. "Audit reference" is renamed "External audit reference (not an Indelio audit)" and works as before. Re-run `supabase/supplier.sql` (one column added). The link becomes part of what a supplier approval signs once it is set; suppliers approved before this keep their signatures unchanged.
Why this classification: Classified “review”: no existing record, rule or signature changes; a new optional field is added to the supplier evaluation, checked when used. The migration file count (IQ-06) is unchanged.
supplier-audit-link.test.tssupplier.test.tsA database migration adds one setting for a capability that is not available. Nobody can turn it on, no page shows it, and no existing record, rule or signature changes.
DATABASE: ONE SETTING FOR A CAPABILITY THAT IS NOT AVAILABLE. Re-run `supabase/ai_audit.sql`. It adds one column to your organization's settings, empty for every organization. Nobody — an administrator included — can set it in this release, and no page or setting in Indelio shows it. The migration file count checked by installation qualification step IQ-06 does not change.
Why this classification: Classified “review”: no function you can use changed, but the database you qualified did — one column is added to a migration you have already applied, so re-apply it under your own change control. Your configuration surface is unchanged: the column cannot be set, so the control switches your CSA plan declares are still the same three. IQ-06's count stays at 118.
ai-audit-unlock.test.tsai-audit.test.tsA database migration adds one table and one database function for a capability that is not available, and an audit finding's removal entry now also records which internal draft it came from. Nothing new can be used, no setting is turned on, and no existing record, rule or signature changes.
DATABASE: ONE TABLE AND ONE FUNCTION FOR A CAPABILITY THAT IS NOT AVAILABLE. Re-run `supabase/ai_audit.sql`. It adds one append-only signature table (readable only within your organization, changed by nobody, guarded by a database check) and one database function that only Indelio's server can call. Both stay empty: no organization can use the capability, and the pages for it answer "not found". The migration file count checked by installation qualification step IQ-06 does not change.
Why this classification: Classified “review”: no function you can use changed, but the database you qualified did — one table and one function are added to a migration you have already applied, so re-apply it under your own change control. IQ-06's count stays at 118.
ai-audit-disposition.test.tsai-audit.test.tsAUDITS: when a finding is removed from an audit, its audit-trail entry now also records an internal reference field alongside what the finding said. The field is empty for every finding you can create today. Adding, editing and removing findings, the audit's content hash and every signature are unchanged.
Why this classification: Classified “none”: the removal entry gains one field that is empty for every existing and every new finding; no behaviour, rule, hash or signature changes.
audit-finding-trail.test.tsA database migration adds one database function for a capability that is not available. Nothing new can be used, no setting is turned on, and no existing record, rule or signature changes.
DATABASE: ONE FUNCTION FOR A CAPABILITY THAT IS NOT AVAILABLE. Re-run `supabase/ai_audit.sql`. It adds one database function that only Indelio's server can call, and that writes only to the tables added in release 2026.09.18.121, which stay empty. No page, action or setting in Indelio calls it in this release. The migration file count checked by installation qualification step IQ-06 does not change.
Why this classification: Classified “review”: no function you can use changed, but the database you qualified did — one function is added to a migration you have already applied, so re-apply it under your own change control. Nothing else in your intended use is affected, and IQ-06's count stays at 118.
ai-audit-run.test.tsai-audit.test.tsA database migration adds empty tables and organization settings for a capability that is not available. Nothing new can be used, no setting is turned on, and no existing record, rule or signature changes.
DATABASE: TABLES AND SETTINGS FOR A CAPABILITY THAT IS NOT AVAILABLE. Run `supabase/ai_audit.sql`. It adds four tables, which start empty and are append-only (a row can never be changed or removed), two organization settings that are left unset, and one column on audit findings that stays empty. No page, action or setting in Indelio uses any of them in this release. Installation qualification step IQ-06 now counts 118 migration files.
Why this classification: Classified “review”: no function you can use changed, but the database you qualified did — four tables, two settings and one column are added, and IQ-06's migration count moves from 117 to 118. Apply the migration under your own change control and re-run IQ-06; nothing else in your intended use is affected.
ai-audit.test.tsprotocol-code-sync.test.tsThe automated test suite that supplies Indelio's OQ evidence now runs on Vitest 5 (was Vitest 4). Nothing that ships to the application changed. No database migration.
TEST FRAMEWORK UPGRADE. The test runner and its coverage tool (vitest and @vitest/coverage-v8 4.1.11 → 5.0.3) were upgraded deliberately, because this suite is the OQ evidence. Every test was compared one by one before and after: the same suites and the same tests were collected, and every test kept its result. One test file needed a change that Vitest 5 requires (two module-mock declarations moved from inside a test group to the top of the file, where Vitest had always run them). Vitest 5 also prints text values in generated test names without quotation marks, so some test names in the run output read slightly differently. Two dependencies moved with it: the Node.js type definitions (@types/node 20 → 22, matching the Node.js 22 the suite runs on, and required by Vitest 5), and four build-time helper libraries used when the application is compiled (picomatch, nanoid, source-map-js, @jridgewell/sourcemap-codec, patch and minor versions).
Why this classification: Classified “none”: no rule, state transition, permission, signature, content hash, audit entry, stored record or screen changed. The test framework does not ship to production, type definitions do not run, and the four helper libraries are used only while the application is compiled; the production build was checked after the upgrade. It is recorded because the suite is Indelio's OQ evidence, and a customer relying on that evidence should know the tool that produces it changed. The run of record is stated in the OQ evidence summary §1, and the reporter that checks it ran under the new version.
release-gate.test.tsoq-run-of-record.test.tsvalidation-library-sync.test.tsThe CAPA and Change Control SOP templates now tell your staff to link a CAPA to the change controls that carry out its actions, using the link added in release 2026.09.18.117.
SOP TEMPLATES: CAPA ↔ CHANGE CONTROL LINK. The CAPA SOP template (SOP-QMS-002, revision 1.2) gains a step in Implement (§5.5): where an action is carried out through a change control, raise the change and link it to the CAPA under "Change controls"; Linkage (§5.8) and Records (§6) say so too. The Change Control SOP template (SOP-QMS-003, revision 1.4) replaces "it references the originating CAPA" in Linkage (§5.7) with linking the change to that CAPA under "CAPAs", and adds the links to Records (§6). Both say that a number naming no record is refused, that the link shows on both records, and that a link removed in error stays in the audit trail with its removal.
Why this classification: Classified “none”: template text only — no function, record, rule or signature changed; the link itself shipped in release 2026.09.18.117. If you adopted either template, decide whether your procedure should require the link, and revise it under your own document control.
copy-claims.test.tsRoutine update of eleven third-party libraries (minor and patch versions). The Word-document viewer now shows two kinds of content it used to leave out. No change to any rule, signature or stored record. No database migration.
LIBRARY UPDATES, ONE OF WHICH CHANGES WHAT THE WORD-DOCUMENT VIEWER SHOWS. The library that converts a Word (.docx) file for viewing in the browser (mammoth 1.12.1 → 1.13.0) now reads text placed inside custom XML elements, and handles paragraphs that were MOVED while tracked changes were on. Earlier versions left such text out of the in-browser view without saying so. A controlled .docx with either may therefore show text in the viewer that it did not show before. The file itself, its recorded hash and every signature are unchanged, because the viewer only reads the stored file. The same release fixes two weaknesses in how a crafted document is read (prototype pollution in 1.12.2, excessive backtracking in 1.12.3). The other updates: the Anthropic SDK (0.121.0 → 0.131.0; retry and timeout handling, new endpoints Indelio does not call); the database and sign-in libraries (@supabase/supabase-js 2.112.4 → 2.117.2, @supabase/ssr 0.12.5 → 0.12.7; a fix for a session refresh that lost a race to another open tab, and passkey and storage features Indelio does not use); React and React DOM 19.2.8 → 19.3.0 (new APIs; transitions render independently); the email library (resend 6.24.0 → 6.31.0; new APIs only); the ZIP library used by the data export (jszip 3.10.1 → 3.10.2; binary-type detection and Node Blob fixes); and the browser-test runner (@playwright/test 1.62.1 → 1.63.0) and two React type packages, which do not ship to production.
Why this classification: Classified “review”: no rule, state transition, permission, signature, content hash, audit entry or stored record changed, but one thing a person reads did. The in-browser view of a Word document can now include custom-XML content and moved tracked-change text that it omitted before. Review it if your procedures rely on what the in-browser viewer shows of a .docx. The downloaded file, the record of the document and what a signature binds are unaffected. Each library's release notes were read for changes that reach users or a control: the sign-in library's passkey API (now enabled by default in the library) and its multi-factor clean-up fix touch only passkey enrolment, which Indelio does not call; the session-refresh fix is in the browser library, while the idle timeout, multi-factor step-up and sign-out are enforced on the server and do not change. Indelio's AI requests are built by its own code with pinned model names, so the SDK update changes no request Indelio sends.
release-gate.test.tsdocx-render.test.tsexport.test.tsai-model.test.tsA CAPA can now be linked to the change controls that carry out its actions, and each change to the CAPAs it serves. Both codes are checked against your organization's records before anything is recorded, and a link that is removed stays on record as removed. A database migration is required: supabase/capa_change_link.sql.
CAPA ↔ CHANGE CONTROL LINK. Until now nothing linked a CAPA to a change control: the connection could only be written into a text field ("implemented under CC-004"), which nothing checked. The CAPA page now has a "Change controls" section and the change page a "CAPAs" section; a link made on either shows on both. The code typed must name a record of that kind in your organization, otherwise nothing is recorded, and a lookup that cannot be completed is refused and says so. Links are append-only: removing one records a second entry, so who linked what, when, and who removed it are all kept. Each link and removal is one audit-trail entry shown on both records. A link is not part of either record's signed content, so it can be made in any state and never affects a signature; a record page that carries a signature asks for the reason as for any other change to it. The section shows each linked record's current state, and says so if the links could not be read rather than showing none.
Why this classification: Classified “review”: a new function in CAPA and Change Control. No existing record, hash, signature, lifecycle rule or role changes, and nothing that was allowed before is refused now: a CAPA or change record looks and behaves exactly as before until someone links it. Review it if your procedures describe how a CAPA's implementing changes are traced, or if your qualification covers the CAPA and change pages. Requires the new migration supabase/capa_change_link.sql (an append-only table, select-only to signed-in users); IQ-06 now counts 117 migrations. Decided by Alexis Sockwell, 2026-10-05.
capa-change-link.test.tsentitlements.test.tsedit-reason-coverage.test.tsaudit-gaps.test.tsA typed reference to another record must name a record that exists. Reference boxes across audits, complaints, nonconformances, CAPA, risk review, calibration, corrections, determinations and assessments used to accept any text; a reference to a record that is not in your organization is now refused, and nothing is written.
TYPED RECORD REFERENCES ARE CHECKED, NOT TAKEN ON TRUST. Where a person types another record's code — an audit finding's CAPA reference (follow-up), a complaint's or nonconformance's "CAPA reference (if escalated)", a CAPA's source reference, an Initial Impact Determination's source reference, a manual risk-review flag's source, a calibration event's "NCR / CAPA raised", a correction or removal's complaint, CAPA and risk-file references, and an assessment's parent record — the code must name a record of that kind in your organization. Otherwise the change is refused before anything is written, with a message saying which code was not found. A source reference is read against its source: a CAPA whose source is "Complaint" must name a complaint, and one whose source is "Other" names no record and is not checked. A reference that cannot be checked is refused too. If your plan does not include the module a reference names, the reference is recorded as external and not checked. A reference already stored is not re-checked when you edit something else on the record, and is never changed. Where a stored reference is shown — an audit finding's CAPA, a CAPA's source, an assessment's parent, a calibration event's NCR / CAPA, a risk-review flag's source, and the references on a complaint, nonconformance, correction or determination — it links only to a record that exists; one that names nothing is shown as plain text, "names no record in this organization", and one that could not be checked says so. Until now the audit finding, the CAPA source and the assessment parent rendered the stored text as a link to a page that might not exist.
Why this classification: Classified “requalify”: a lifecycle rule changed — saving any of these records is now refused where it used to proceed, when a NEW or CHANGED reference names no record of the right kind in the organization, or cannot be checked. Same rule as the Initial Impact Determination (#17) and trend dispositions (#24), now applied to the rest of the class (#76, decided by Alexis 2026-10-05). ⭐ No stored record changes: no hash, signature or existing value moves, and a value already stored is not re-checked, so a record that already holds a reference to nothing can still be edited. Found by following the typed audit-finding CAPA reference into the code; the others were found by deriving every column that can hold a record code from the schema and every write to it, not from a list. The reference boxes on Human Factors records, a supplier's audit reference and a project task's document reference are the same defect but may name external documents on purpose; they are NOT checked by this release and await a scope decision. A risk item's control reference is deliberately not checked: it may name an implementing record outside Indelio.
record-refs.test.tscomplaint-source-email.test.tsregistry-columns.test.ts⛔ SECURITY. Three database functions that run with the database owner's rights could be called by anyone holding the public application key; they no longer can. Seven database functions, including the one that computes the audit trail's hash chain, now have a fixed search path. A database migration is required: supabase/security_definer_grants.sql.
⛔ SECURITY. THREE INSTALLATION-CHECK FUNCTIONS COULD BE CALLED FROM A BROWSER WITHOUT SIGNING IN. iq_columns(), iq_policies() and iq_probe_append_only() are used by the automated installation check, through the privileged server connection. They run with the database owner's rights (SECURITY DEFINER), and their migrations removed the default PUBLIC permission to call them — but the hosting platform also grants that permission to its signed-out and signed-in browser roles by name, and those grants were left in place. So anyone holding the public application key, which every browser receives, could read back the full list of tables and columns and every database access rule, and could make the append-only probe run. No record could be read or changed through them: they return the database's structure, never your rows, and every write the probe attempts is refused and rolled back by the append-only protection. supabase/security_definer_grants.sql removes both browser roles' permission; the installation check is unaffected, because only the privileged server connection calls them. The one function a signed-in user still needs is current_org(): every access rule calls it to keep each organization to its own rows, so it is kept for signed-in users and removed for signed-out ones, as before.
Why this classification: Classified “review”: an access control around the database was tightened. No record, signature, role, lifecycle rule or hash changed, and nothing the application does was refused before and allowed now, or the other way round: the only callers of these three functions are the installation check and Indelio's own tools, through the privileged connection, which keeps its permission. Review it if your qualification relies on the database's access configuration, or if your own tools call these functions with a browser key, which will now be refused. Found on 2026-10-03 in the hosting platform's security advisor; the two older functions of the same kind (iq_introspect and billing_founding_claim) were already correct. A new automated check replays every permission statement in installation order, so a function created later in the same way cannot pass, and a live test on a fresh installation calls each such function as a signed-out and as a signed-in user and requires refusal.
security-definer-grants.test.tsiq-check.test.tsSEVEN DATABASE FUNCTIONS NOW HAVE A FIXED SEARCH PATH, INCLUDING THE AUDIT TRAIL'S HASH CHAIN. A function whose search path is not fixed looks up the tables and functions it names wherever the calling session says, so an object with the same name placed earlier on that path could stand in for the real one. The function that computes the audit trail's hash chain (chain_audit) had its path fixed in August, but a later migration that re-created it to serialize appends per organization silently undid that, because re-creating a function resets its settings. It now carries the setting in its own definition, and the same is done for six trigger functions that protect records (accepted calibration events, imported batches and rows, import provenance, the compliance register's history, and CAPA action completers). The hash chain's code is unchanged: only its setting is. Changing a function's setting cannot change its code, and the code of all seven functions is fingerprinted before and after the migration is applied, and must be byte-for-byte the same.
Why this classification: Classified “review”: hardening of functions that enforce record protection and compute the audit trail's tamper evidence. Their behaviour does not change: the same rows are refused, and every audit entry is chained with exactly the same formula. On a development instance the full chain was recomputed before and after the change, and a new entry was added after it; every hash and every link matched (69 entries, 0 mismatches). Existing audit entries are not touched. The automated check fails if any function's path is not fixed at the END of an installation, which is where this one was lost.
security-definer-grants.test.tsThe first electronic signature after you sign in now asks for your user ID — the email you sign in with — as well as your password; later signatures in the same sign-in ask for your password, as before. ⚠️ Behaviour change for every signer, in every module. A database migration is required: supabase/signing_session.sql.
THE FIRST SIGNATURE IN A SIGN-IN TAKES BOTH PARTS: YOUR USER ID AND YOUR PASSWORD. 21 CFR 11.200(a)(1)(i) requires the first signing in a continuous period of controlled access to use every component of the signature, and later signings at least one. Until this release no signature asked for the user ID: the identity came from the sign-in alone, so the first signature was given with one typed component. A sign-in now counts as one continuous period: it ends when you sign out, after 30 minutes without activity, or when the login expires. Every signing form shows a User ID box above the password box until that sign-in has recorded a signature; the box takes the email you sign in with, typed (browser and password-manager filling is suppressed and pasting is refused, as on the password box). The server checks both parts at the moment of signing, whatever the screen showed. A wrong user ID is refused exactly as a wrong password is — one message that does not say which part was wrong — and is recorded and reported to your administrators the same way. A signature refused for another reason (a missing role, an author approving their own record) does not count, so the next attempt still asks for both. If the system cannot tell whether your sign-in has already signed, it asks for the user ID again. Settings → Billing's plan change and resubscribe are not signatures and still ask for your password only.
Why this classification: Classified “requalify”: an electronic-signature control changed for every signer, in every module (21 CFR 11.200(a)(1)(i)–(ii)). Something that was accepted before — the first signature of a sign-in, given with the password alone — is now refused. Signatures already applied are unchanged: no signature value, record hash, manifestation or PDF moved. The evidence kept for each signing password check now also records which sign-in it belonged to and whether the user ID was typed, and your data export carries both. The Part 11 package's 11.200 rows were corrected at the same time: one said every component was supplied at every signature, which was not true, and three were labelled with the wrong part of the clause; the package now states the regulation's five parts.
first-signing-user-id.test.tsunauthorized-use.test.tssigning-auth-age.test.tspassword-clearing.test.tsvalidation-doc.test.tsA management review now records the figures management saw: the author captures counts taken from your records, they are kept unchanged with the review, and the approval signature covers them. ⚠️ Behaviour change: a management review can no longer be approved until its facts have been captured, and this applies to reviews already in Draft. A database migration is required: supabase/mr_facts.sql.
MANAGEMENT REVIEW RECORDS THE FIGURES MANAGEMENT SAW. Until this release every review input was text typed by a person, so the signed record could not show which numbers were in front of management. On a Draft review the author now presses Capture facts and gives the period the review covers. Indelio counts, from your own records and as they stand at that moment: CAPAs (open by state, by age, overdue, opened and closed in the period, closed with the effectiveness result overridden); complaints (received in the period, open, in triage, determined reportable, reportability still undecided, with neither a linked CAPA nor a no-CAPA reason); nonconformances (open by state, detected in the period, open against suppliers, and the suppliers with the most); trend signals (detected, at alert level, without a disposition, and dispositions recorded in the period); open audit findings (by classification, and those with no linked CAPA); actions from other approved reviews (not done, and overdue); overdue training; and suppliers past re-evaluation or on hold. Each figure links to the list it came from, and they are shown beside the inputs they inform. Each capture is kept and cannot be changed or removed; capturing again while the review is in Draft makes the new capture the one the review uses, and the audit trail records which capture it replaced. The record page and the PDF show the capture, who made it and when, the period and every figure. A figure that could not be read is shown as "could not be read", never as 0. The free-text inputs and the conclusions are unchanged: the figures are counts, and what they mean is still written by people. ⚠️ BEHAVIOUR CHANGE: approval is now refused when no facts have been captured, when the capture does not match its own hash, or when any figure in it could not be read. This applies to every approval from this release on, including reviews already in Draft.
Why this classification: Classified “requalify”: an electronic signature now binds more than it did, and a lifecycle step has a new precondition. The review's content hash includes the hash of its latest capture, so the approval signature ("Management review approved by") covers the figures; approval is refused, in the data layer, without a valid and fully readable capture. Reviews already approved are unchanged: a review with no capture hashes exactly as it did before this release, so every existing approval signature still verifies (pinned in the tests against the formula as it stood). Capturing facts on a review that already carries a signature (a reopened one) asks for a reason for change, as any other change to it does. A new append-only table (management_review_facts, protected against update and delete even for the service role, readable only within your organization) and a new column on the review record are created by supabase/mr_facts.sql, and the table is included in the organization data export. Test evidence: every figure against fixtures; for each of the nine sources, a failed read is "could not be read" and no other figure changes, including a read that returns an error with an empty list; free-text CAPA references on audit findings are not counted as linked; the facts hash survives the database round trip and changes on any figure; the approval refusals are each ahead of the signature; 6 mutants, all caught.
mr-facts.test.tsmanagement-review.test.tsA nightly job that copies every uploaded file to a second location, in another provider and region, has been added together with a restore test and an alarm. It has not yet completed in production, and until it has and a restore test has passed there, uploaded files are still held in one place only. A database migration is required: supabase/file_backup.sql.
A SECOND COPY OF UPLOADED FILES — BUILT, NOT YET PROVEN. Uploaded files (controlled documents, evidence attachments, migration sources and imported spreadsheets) are kept in Supabase Storage, and Supabase's database backups do not include them. A nightly job now walks every storage bucket and copies each new or changed file to Amazon S3, in a separate account and a different US region (Ohio). Each copy is stored under its SHA-256, the write is refused if a copy already exists under that name, and S3 checks the bytes it receives against that hash. The job records every run, complete or not. A walk that does not finish, a file that cannot be copied, or work left when the run's time limit is reached is recorded as incomplete and raises an alert to Indelio's operator; it is never recorded as complete. Every day a restore test reads copies back, as an account that can only read, and checks each one against the run's manifest and against the hash recorded when the file was uploaded: on Sundays every copy, on other days the copies the last complete backup newly made. Every day, a separate check raises an alert when no complete copy has been made in 26 hours. ⚠️ None of this has run in production yet. Until it has, and a restore test has passed there, nothing should be read as saying your files are backed up: today they are held in one place.
Why this classification: Classified “review”: a new control over the availability of your records, run by Indelio. Nothing in your records, signatures, audit trail or access changes, and no file is altered or removed: the job only reads from storage and adds to a separate bucket. Two new append-only tables record what was copied and every run, and a database migration (supabase/file_backup.sql) creates them. Amazon Web Services, which receives the copies, was named as a sub-processor in the Privacy Policy and on the Sub-processors page in release 2026.09.18.111; this release does not change either. Test evidence: the run's verdict is a pure function, and every way of falling short is tested and counted; the job walks every bucket from its root; the restore test is proven on a flipped byte, a missing copy, an altered manifest, a file that disagrees with the hash recorded at upload, and a file the database holds that the backup does not; the daily check is proven to catch a flipped byte in last night's new copy; the alarm runs from a different scheduled job from the backup; 34 mutants, all caught. That the AWS side behaves as configured is proven only by the first production run and restore test.
file-backup.test.tsThe Privacy Policy and the Sub-processors page name a new sub-processor, Amazon Web Services, for off-site backup copies of uploaded files. The Privacy Policy and the Terms of Service take a new effective date, so every user is asked to accept them again. No database migration, and no change to what the software does.
AMAZON WEB SERVICES IS NAMED AS A SUB-PROCESSOR BEFORE IT RECEIVES ANYTHING. Our Privacy Policy says its sub-processor list is complete, and that adding one changes the policy and its effective date so that every user is asked to accept it again. We are preparing an off-site backup of the files uploaded to Indelio, held in Amazon S3 in US East (Ohio). Before any file is copied there, Amazon Web Services is added to the list in the Privacy Policy and on the Sub-processors page, with what it would receive: a copy of each uploaded file, and a list of those files giving each one's storage path (which includes its file name), size and content hash, used only to back up and restore them. It does not receive the database: records, electronic signatures, the audit trail and user accounts are not sent to it. This entry names a supplier and its purpose; it does not say that a backup is running. The effective date of both documents moves to October 1, 2026. Nothing else in the Terms of Service changed: its text differs only in the date.
Why this classification: Classified “review”: an accepted legal document changed and every user is asked to accept it again, and a new party is disclosed as able to receive copies of uploaded files. No quality record, signature, audit entry, access rule or stored data changed, and no code sends anything to Amazon Web Services in this release. ⚠️ If your supplier list, data-flow diagram or data-protection impact assessment names Indelio's sub-processors, add Amazon Web Services (Amazon S3, US East (Ohio)) for uploaded-file backups.
subprocessors.test.tslegal-acceptance.test.tscopy-claims.test.tsA records export now lists every uploaded file with the SHA-256 recorded when it was stored, including every file whose contents are not in the archive, and checks the contents it does include against that value. No database migration.
EVERY FILE IN AN EXPORT CARRIES ITS RECORDED HASH. Our Terms of Service say that when you export your records, files are included where size allows and otherwise listed in the manifest with their content hashes. The export did not do that: files-index.csv named each file but gave no hash, and manifest.json listed no files. Now every row of files-index.csv carries the SHA-256 of the file as it was recorded when it was stored (new column sha256Recorded), whether or not its contents are in the archive. This covers evidence attached to records, uploaded controlled-document versions, bulk-migration source files and imported spreadsheets. A file stored before Indelio recorded a hash has none: its cell says "NOT RECORDED" instead of being left blank, and the archive's notes give the count. manifest.json now lists every file whose contents are not in the archive, with its hash (files.notIncluded). For each file whose contents ARE included, a second new column (sha256Check) says whether they still hash to the recorded value. A file that does not match is reported as MISMATCH, is called out in the notes, and marks the export incomplete, the same as a signed document that does not match its signature. The two columns are added at the end, so every existing column keeps its position. The Settings page also said "File content is not included". That has been untrue since files were added to the export, and it now says what the export contains. The export's own README and manifest said files are stored in three places; there are four, and they now say so.
Why this classification: Classified “review”: what a records export contains changed. files-index.csv has two more columns, manifest.json has more fields, and a new condition (included bytes that do not match their recorded hash) marks an export incomplete. No quality record, signature, audit entry or access rule changed, and nothing about how files are stored changed. ⚠️ If a procedure or a qualification exercised the export, or a script reads files-index.csv, check it against the new columns. The existing columns keep their positions. ⚠️ A file attached before Indelio recorded hashes has no recorded value; the export says so on its row and never implies every file has one. Test evidence: every file source must name a hash column that exists in the schema, and every source is exercised with decoy values in the other hash-shaped columns, so a hash read from the wrong column fails. The new tests were run against the export code from before this change and 12 of 12 failed. 15 new mutants are caught, including the hash arriving from a lookalike column through `??` and through a spread (44/44 export mutants).
export-files.test.tsexport.test.tsA reason for change is now chosen from a list of standard reasons, with a comment. When a reason is needed is unchanged. No database migration.
A STANDARD REASON, THEN A COMMENT. Once a record carries an e-signature, any change to it — including filling in an empty field or adding a new item — needs a reason, as before. The "Reason for change" box now asks you to choose one of five standard reasons — Typographical error, Scope change, Regulatory request, Corrective action update, or Other — and to add a comment if you wish. For Other, the comment is required. Nothing is chosen for you. The audit entry records it as "<reason> — <comment>", or the reason alone, in the same place the typed reason was recorded before. The server refuses, before anything is saved, a reason that is not one of the five (however it was sent), Other without a comment, and a comment that is only an email address. Reasons recorded before this release are unchanged. Two actions used to take their own typed reason as the reason for change when none was given — removing a design↔risk link (its removal reason) and approving a due-date extension (the request's reason). On a signed record they now need the reason for change chosen in the box as well; their own reasons are recorded where they always were. The box is also no longer shown to someone who cannot save on that page (for example a view-only user): each page asks the same rule its saves are refused with.
Why this classification: Classified “review”: the content of a controlled record's audit entry changes shape — every new reason for change now begins with one of five standard reasons — and a change that was accepted before with a free-text reason is now refused until a standard reason is chosen. A procedure, work instruction or training that tells people to type a reason, or a review that reads reasons, should be checked. No signature, signature meaning, content hash, permission or lifecycle rule changes, and when a reason is required — any change to a signed record, including filling in an empty field or adding a new item — is unchanged. Test evidence: the five reasons are held to the decided list; each is accepted alone and with a comment; free text, near misses ("Typo", "Other stuff") and unknown reasons are refused for signed and unsigned records alike; Other needs a comment that is more than hidden characters; an email-only comment is refused; a real CAPA edit with an unknown reason leaves the record and its trail untouched; the page offers exactly the list, in order, with none chosen, and composes the reason with the shared function; every reason-demanding write takes its reason only from the box, and each page that shows the box asks the rule its saves use; twelve mutants, all caught.
edit-reason.test.tsreason-box-placement.test.tscopy-claims.test.tsThe complementary user entity controls (CUEC) register is now published under Compliance & Validation, with five controls added, and every CUEC reference in the validation documents links to it. The Part 11 validation package's sections 5 to 7 are now in plain language. No database migration.
THE CUEC REGISTER IS PUBLISHED, AND EVERY REFERENCE TO IT LINKS TO IT. The Part 11 validation package and the CSA implementation-plan template refer to the controls your company must put in place by number (CUEC-02, CUEC-04 and so on), but the register that lists them was not among the documents under Compliance & Validation, so it could not be opened. It is now published there as the sixth document, written for your company: for each control, what Indelio does, what it cannot do, what your company must do, how you can show you did it, and what is exposed if you do not. Every CUEC number in the documents links to its entry. Five controls were added for obligations the Part 11 package already gave you with no entry in the register: training users for their tasks (CUEC-10), a written policy holding each person accountable for their electronic signature (CUEC-11), controlling your own procedures for using Indelio (CUEC-12), verifying each person's identity before they can sign (CUEC-13), and the certification letter to FDA (CUEC-14). The CSA template's list of CUECs now includes them.
Why this classification: Classified “none” because no control, record, signature, hash, role or lifecycle rule changes: this publishes a vendor document and links to it. ⚠️ If your CSA implementation plan was completed from the template's earlier list of nine CUECs, it does not record CUEC-10 to CUEC-14; each is an obligation Part 11 already placed on your company, so record how you meet it. Test evidence: the register is served from its source; every CUEC number cited in any published document resolves to an entry; every obligation the Part 11 package gives your company cites one; two mutants caught.
cuec-register.test.tscsa-template-config-surface.test.tsvalidation-library-sync.test.tsTHE PART 11 VALIDATION PACKAGE'S SECTIONS 5 TO 7 ARE IN PLAIN LANGUAGE. The package's outline of IQ, OQ and PQ, its list of open gaps and its ongoing controls named program files, tests and internal identifiers. They now say the same things in plain words, and no statement became stronger. Gap 9 (two customer obligations missing from the CUEC register) is closed by the register above.
Why this classification: Classified “none”: wording only; no control changes, and the meaning of each statement is kept. Test evidence: every section of the published package, not only the requirements table, is now checked for program names and file paths, proven on the lines that were removed; one mutant caught.
validation-doc.test.tsscan-cadence-claims.test.tsThe reason-for-change box on a signed record is now styled as an ordinary form field; on a document it sits beside Save and is shown only to the draft's author. The rule for when a reason is needed is unchanged. No database migration.
THE REASON BOX LOOKS LIKE WHAT IT IS, AND SITS WHERE IT IS USED. On a signed record, the box where you type a reason for a change was an amber panel at the top of the page. On a document that had been sent back for changes, it sat directly beside the amber "Returned for changes" note and looked the same, so it was not clear which one to fill in. It is now a plain labelled field ("Reason for change") on every record page. On a document it has moved into the editor, directly above Save changes (above Replace file for an uploaded file), and it is shown only to someone who can save there: the author of a Draft version. Reviewers, approvers and anyone viewing an In Review, Approved or Effective version no longer see it, because nothing they could save there needs it. The server's rule is unchanged: once a record carries an e-signature, any change to it — including filling in an empty field or adding a new item — needs a reason, and a change without one is refused, whether or not the box is on screen. On the other record pages the box is still shown on every signed record, at the top of the page.
Why this classification: Classified “none”: presentation only. The rule that demands a reason, the refusal, what is written to the audit trail, and who may change a document draft are all unchanged. The three document writes that demand a reason (draft text, draft file, proposed effective date) now take their Draft-and-author check from one shared function that the page also uses to decide whether to show the box, and every refusal message is word-for-word the same. A work instruction or training material that shows the box's old look or position on a document, or tells a reviewer to use it, should be updated. Test evidence: a ledger over every requireEditReason call in lib/ (91) proves each document write decides with the shared function and has no author or state check of its own, and fails on any call whose table it cannot resolve; a real-browser rendering proves the box lies between the content and Save and is not styled like the send-back note; the box renders nothing for someone who cannot save; 13 mutants, all caught.
reason-box-placement.test.tsedit-reason.test.tscopy-claims.test.tsEvery night Indelio now checks that each record's creation has its audit entry, and alerts if one does not. The Part 11 package states the limit this check exists for. No database migration.
A NIGHTLY CHECK FINDS RECORDS WHOSE CREATION HAS NO AUDIT ENTRY. A change to a record and its audit entry are written in two steps; if the second step fails, the person sees an error, but the record already exists without its entry. Every night Indelio now checks every CAPA, change, complaint, nonconformance, document version, risk file, supplier, audit, management review, project, design project, equipment record and event, field action, determination, assessment, human-factors record, PMS plan, quality policy and objective, training assignment, requirement, group and quiz, spreadsheet import, migration batch and trend disposition for its creation entry, records the result as a run of the daily integrity checks — found or not — and alerts Indelio's operator by email when any is missing. A table it could not read is reported as not checked, never as clean. It changes nothing in a record and adds no entry to it. Limits: it checks creation only, not later changes to a record, and not items inside a record (an action, a risk item); making the two steps one is a later change. Each run's result and each alert email state these limits in the same words, so neither can be read as covering more.
Why this classification: Classified “review”: a new detective control over the audit trail (21 CFR 11.10(e)). It does not change how any record is created, changed, signed or retained, and it writes nothing to a customer's records — but it changes what the audit trail is checked for, and the Part 11 package now states the two-step limit it detects, which a customer's own assessment of 11.10(e) may rely on.
audit-gaps.test.tsvalidation-doc.test.tsAn organization's administrator can now give someone the implementer role from the Admin page, and both role tables warn when one person is given the implementer role together with a quality role. No database migration.
THE ADMIN PAGE OFFERS THE IMPLEMENTER ROLE. The implementer role is for a contractor who brings records in by spreadsheet import or document migration, for QA to verify. The server already accepted it from an organization's administrator, but the Admin page offered only author, reviewer, approver, QA and admin, so only Indelio could make someone an implementer. The Admin page now lists implementer when you add a person and in the roles table, with a description matching what the role can do: import and migrate records; view records, but not create, change or sign them. Where one person is given the implementer role together with author, reviewer, approver or QA — on the Admin page, when adding a person, or in Indelio's own console — a warning now says that this person will be unable to create or edit quality records while they hold the implementer role. It is a warning, not a block, so a deliberate choice stays possible.
Why this classification: Classified “none”: who may grant a role did not change — an organization administrator could already grant implementer on the server, and the rules for what an implementer can do are unchanged (release 2026.09.18.103). Only the page now offers the choice. ⚠️ If your procedures say only Indelio assigns the implementer role, your administrator can now do it from the Admin page; every role change is still written to the audit trail and emailed to the person. The warning changes nothing that is enforced; it states the rule of release 2026.09.18.103 at the moment a role is granted. Test evidence: a check that the Admin page offers every role the server accepts, proven failing on the page before this change; both role tables rendered with a person holding QA and implementer (warned), implementer alone, QA alone, and admin with implementer (not warned); three injected mutations caught.
quality-writer-every-path.test.tsSecurity update: Next.js, the framework Indelio runs on, is updated to 16.3.8 to close a critical vulnerability published today. No change to any function. No database migration.
NEXT.JS SECURITY UPDATE. A critical vulnerability was published on 2026-09-30 for Next.js 16.2.0 to 16.3.5 (GHSA-vcvr-r3jv-pc5j: remote code execution in its image-generation feature, next/og ImageResponse). Indelio ran 16.3.3 and now runs 16.3.8, a patch release in the same line. Indelio does not use that image-generation feature, so the vulnerable code was not reachable from Indelio's own pages; it is updated anyway. A build-time helper used only when the application is compiled (brace-expansion, through the error-monitoring build plugin) is also updated, from 5.0.9 to 5.0.12, closing a high-severity denial-of-service advisory. After the update the dependency audit reports no known vulnerabilities.
Why this classification: Classified “none”: a patch update of the web framework and of a build-time helper. No control, signature, record, content hash, audit entry, role rule or lifecycle rule changed, and no page behaves differently. ⚠️ It changes third-party software your instance runs on, so if your supplier controls track framework versions, record Next.js 16.3.3 → 16.3.8. Test evidence: the full automated suite and a production build, both run on the updated versions; the dependency audit that blocks a release on any critical vulnerability passes.
release-gate.test.tsCreating or changing a quality record now needs the author, reviewer, approver or QA role: an implementer, or an administrator without one of those roles, can view records but not change them. Pages hide or disable what a person cannot do, and Help has a new "Who can do what" section. Trial and IQ functions on the platform console now check the platform administrator on the server. No database migration.
CREATING OR CHANGING A QUALITY RECORD NEEDS A QUALITY ROLE. Until this release most record edits checked no role: any signed-in member of your organization, a user holding only the implementer role included, could create and edit a CAPA, a complaint, a nonconformance, a change control, a supplier, an equipment record, an audit, a correction or removal, an impact determination, an assessment, a risk assessment, a design project, the human-factors records, a management review, a project or a post-market surveillance plan, add and remove their items, link them, move them to a working state, and attach evidence. Now each of those needs the author, reviewer, approver or QA role. A user holding the implementer role (even alongside another role), or an administrator without one of those roles, is refused before anything is read or written. Any quality role may edit: a record has no single owner who alone can change it. Controlled documents keep their stricter rule, and now also refuse an implementer: only the document's author edits a draft. Marking a document citation "verified" by a subject-matter expert now needs the reviewer, approver or QA role. Approvals, closures and the other signatures are unchanged. Record pages hide the "New" forms and disable the editing controls for a person who cannot use them, with a note saying why, and Help has a new "Who can do what" section generated from the same rule.
Why this classification: Classified “requalify”: an authority check on who may input or alter a record was added across every quality-record module (21 CFR 11.10(g)). Decided by the system owner on 2026-09-30: a quality role — author, reviewer, approver or QA — never implementer, never an administrator alone, no owner-only editing. No content hash, signature, record, audit entry or lifecycle rule changes, and records already written are unaffected. ⚠️ If anyone in your organization creates or edits records while holding only the implementer or admin role, they now need a quality role; the implementer role keeps spreadsheet import and document migration. ⚠️ An administrator who is not also author, reviewer, approver or QA can no longer create or edit post-market surveillance plans, human-factors records, assessments, risk links, management review follow-up, or start a supplier re-evaluation, or remove evidence, all of which admin alone could do before. ⚠️ If your performance qualification creates or edits a record as an implementer or as admin alone, that step now fails by design. ⚠️ Not changed by this release: the quality policy and training administration stay administrator or QA, a training quiz may still be written by an administrator, and an administrator can still reassign a document's reviewer. Test evidence: a ledger derives every write path in the server code and fails on one the rule does not cover unless it is named with its reason; every covered path is called for real as an implementer, as admin alone and as QA-with-implementer, and must refuse before touching the database, and as each quality role, and must get past the check; proven failing on the pre-fix code, where an implementer edited a CAPA; 13 injected mutations, all caught.
quality-writer-every-path.test.tscopy-claims.test.tssignature-role-every-path.test.tsTHE PLATFORM CONSOLE'S TRIAL AND IQ-SIGNATURE FUNCTIONS CHECK THE PLATFORM ADMINISTRATOR ON THE SERVER. Starting, extending and converting a trial, marking a founding partner, deactivating or reactivating an organization, and signing an IQ run were checked only by the console screen that calls them. The functions themselves now check that the signed-in person is a platform administrator, so a future caller cannot skip the check.
Why this classification: Classified “none” for your organization: these are the vendor's platform-console functions, which no tenant user could reach before or can reach now; the same people can do the same things. The check moved into the server functions themselves, so it cannot be left out by a new screen. Test evidence: each function refuses a tenant user who is not a platform administrator before anything is read or written, and lets a platform administrator through; two injected mutations caught.
quality-writer-every-path.test.tssignature-role-every-path.test.tsTHE PART 11 VALIDATION PACKAGE MARKS 11.10(g) MET, AND SAYS HOW. Earlier today the package was corrected to say that most record edits were not role-checked (gap 10). With the control above built, the 11.10(g) row now states the rule for input and alteration and is marked met, gap 10 is closed with its remaining limits stated, and the requirement statements and the Security page say which role does what.
Why this classification: Classified “none”: this describes the control recorded above; the control itself is classified with its own change. ⚠️ The row covers the system as released; whether your organization gives roles only to the people you authorize remains yours (CUEC-02), and changes made before this release by people without a quality role are attributed to them in the audit trail if you want to review them.
copy-claims.test.tsvalidation-doc.test.tsrtm-sync.test.tsThe complaint investigation sign-off now checks the signer's role: an implementer can no longer apply it. The Part 11 validation package now says plainly that most record edits are not yet role-checked. No database migration.
ONLY AN INVESTIGATING ROLE CAN SIGN OFF A COMPLAINT INVESTIGATION. Until this release the electronic signature "Investigated by" checked the signer's password but not their role, so any signed-in member of your organization could apply it, including a user holding only the implementer role. Every other signature already checked a role. Now it needs the author, reviewer, approver or QA role. A user who also holds the implementer role, or who holds only admin, is refused, and nothing is signed or changed. The same rule already decides who may record a complaint closure addendum.
Why this classification: Classified “requalify”: an authority check on an electronic signature changed (21 CFR 11.10(g), 11.50). A user holding only implementer or only admin, or implementer alongside another role, can no longer sign off a complaint investigation. Authors, reviewers, approvers and QA sign as before. No content hash, record, audit entry or lifecycle rule changes, and signatures already applied are unaffected. ⚠️ If a person in your organization has been signing investigations while holding only the implementer or admin role, they now need an investigating role, and you may want to review the investigations they signed. ⚠️ If your performance qualification signs an investigation as an implementer, that step now fails by design. Test evidence: the real sign-off run against an in-memory database, refusing an implementer, a user with no role and admin alone with nothing written, and letting each investigating role sign; proven failing on the pre-fix code, where an implementer's signature went through. A ledger over every signature in the product requires each to check a role, and was proven to catch removed gates.
signature-role-every-path.test.tscomplaint-closure-gate.test.tsTHE PART 11 VALIDATION PACKAGE SAYS THAT MOST RECORD EDITS ARE NOT ROLE-CHECKED. For 21 CFR 11.10(g), which requires authority checks on who may electronically sign and on who may input or alter a record, the package described the signature checks and marked the clause met. It did not say that creating and editing most records, such as a CAPA, a complaint, a nonconformance or a supplier, checks no role, so any signed-in member of your organization, an implementer included, can do it. The clause is now marked as a gap and a new entry (gap 10) is in the open-gaps list. The requirement statements that said roles are enforced on every action now say they are not yet met for record edits. The Security page no longer says role-based access is enforced "on every action".
Why this classification: Classified “none”: no control changed. The documents now describe what the system already does, and until now they described more than it enforces. ⚠️ If you relied on the Part 11 package's 11.10(g) row to conclude that only authorized roles can alter your records, that conclusion was not supported. Until the gap is closed, give accounts and the implementer role only to people you authorize to edit quality records, and review the audit trail for their changes. The package's Comments column says so.
copy-claims.test.tsvalidation-doc.test.ts"Sign out of all devices" has moved from the Account page to the header, beside Sign out. No database migration.
SIGN OUT OF ALL DEVICES IS BESIDE SIGN OUT. Release 2026.09.18.99 put "Sign out of all devices" at the bottom of the Account page, where nobody looking for it would find it. It is now a button in the header on every page, next to Sign out. Clicking it asks "Sign out here and on every other device?"; choose "Yes, everywhere" to go ahead, or Cancel. What it does is unchanged: it ends every session you have and records SIGN_OUT_ALL_DEVICES in the audit trail. The Account page no longer has the card.
Why this classification: Classified “none”: only where the button is changed. What it does, the confirmation it asks for, and the audit entry it writes are unchanged and still enforced on the server (lib/sign-out.ts). ⚠️ If your procedures or training material say to find it on the Account page, update them. Test evidence: the header is asserted to render the button beside Sign out and the Account page not to; the scope, confirmation and audit-order tests from release 2026.09.18.99 still pass.
sign-out-scope.test.tsFixed: signing in could send you straight back to the sign-in page with "You were signed out automatically". No database migration.
SIGNING IN NO LONGER TRIPS THE INACTIVITY TIMEOUT. The inactivity timeout keeps the time of your last activity in your browser. Signing out did not clear it and signing in did not reset it, so if you had signed out more than 30 minutes earlier, your very next sign-in read that old time: the new session was ended on its first page and you were returned to the sign-in page with "You were signed out automatically after a period without activity" (for an account with two-factor authentication, at the code step). It could look like sign-in hanging on "Signing in…". Now signing in, and completing the two-factor step, start the activity clock afresh, and Sign out and Sign out of all devices clear it. As a second safeguard, a session that signed in less than 30 minutes ago is never treated as inactive, whatever the browser has kept from before: the time it signed in is read from the session itself. The timeout itself is unchanged: a session left without activity for 30 minutes still ends. The fault predates release 2026.09.18.98; that release made the timeout also end the session at the sign-in service, which it still does.
Why this classification: Classified “requalify”: the behaviour of an access control changed (the inactivity timeout, 21 CFR 11.10(d), 11.300(d)) — it no longer fires on a session that has just begun. What counts as inactivity, the 30-minute period and what happens when it fires are unchanged. No signature, content hash, record, role or lifecycle rule changes. ⚠️ If your performance qualification signs out, waits more than 30 minutes and signs in again, that sign-in was refused before this release and succeeds now; a session genuinely left idle for 30 minutes is still ended. Test evidence: the real sign-in, two-factor, Sign out and Sign out of all devices actions executed, each shown to write or clear the activity stamp; the real proxy shown to end a one-second-old session carrying the old stamp (the production fault, also present before release 2026.09.18.98) and to let the same session through with the stamp sign-in now writes, while a genuinely idle session still times out; the second safeguard shown, against the real sign-in service on a disposable database, to keep a session signed in one second ago signed in despite an hour-old leftover, while a session older than the window is still timed out and anything it cannot read gets no exemption; proven failing on the pre-fix code; nine mutants, all caught.
sign-in-idle-stamp.test.tsidle-timeout.test.tsidle-revoke.test.tsSign out now ends your session on this device only. A new "Sign out of all devices" on the Account page ends every session you have, after a confirmation, and is recorded in the audit trail. No database migration.
SIGN OUT ENDS THIS DEVICE ONLY; SIGNING OUT EVERYWHERE IS ITS OWN ACTION. Until this release, Sign out in the header, and the automatic sign-out after a period without activity, ended every session the person had, on every device and browser. So a tab left idle on one computer, or a Sign out clicked there, also signed the same person out of their phone and every other browser. Now both end only the session on the device where they happen. The Account page has a new "Sign out of all devices" button for when you do want every session ended, for example after using a shared computer or if a device is lost. It asks you to confirm, signs you out here too, and writes a SIGN_OUT_ALL_DEVICES entry to the audit trail naming you. The entry is written only after the sign-in service confirms the sessions were ended. If it cannot end them, you are told that your other devices may still be signed in, and nothing is recorded.
Why this classification: Classified “requalify”: an access control changed — which sessions a sign-out ends (21 CFR 11.10(d)). Sign out and the inactivity sign-out now end one session rather than all of them, and there is a new way to end all of them, which is audited. No signature, content hash, record, role or lifecycle rule changes. ⚠️ If your procedures say that signing out ends a user's sessions everywhere, that is no longer true of Sign out; it is true of "Sign out of all devices". If your performance qualification checks that signing out on one device signs the user out on another, that check now fails by design. Test evidence: a ledger over every sign-out call in the product requires an explicit scope and allows "all devices" in exactly one place, proven failing on the pre-fix code; the sign-out functions executed for scope, the confirmation, and the audit entry coming only after a confirmed sign-out; and, on a disposable database, signing out one device leaves another signed in while "Sign out of all devices" ends all three sessions tested; five mutants, all caught.
sign-out-scope.test.tsidle-revoke.test.tsThe 30-minute inactivity timeout now ends the session at the login service, not only in the browser. A copy of a session taken before the timeout no longer works after it. No database migration.
THE INACTIVITY TIMEOUT ENDS THE SESSION, NOT ONLY THE BROWSER'S COPY OF IT. When a signed-in user was inactive for 30 minutes, Indelio sent them to the sign-in page and deleted the sign-in cookies from their browser, but the session itself stayed valid at the login service. Anyone holding a copy of those cookies from before the timeout could go on using the session: another tab sharing the same browser, a synced browser profile, or a stolen value. Now the timeout ends that session at the login service first, then clears the browser's cookies. Only that one session ends; the same person signed in on another device stays signed in. If the login service cannot be reached within a few seconds, the browser is still signed out and the failure is logged. The Part 11 validation package's gap register (row 2, idle session timeout) now describes this.
Why this classification: Classified “requalify”: an access control changed. What the inactivity timeout does to a session (21 CFR 11.10(d), 11.300(d)) is stronger. A session ended by the timeout is now refused by the login service, where before it was only removed from the browser. Nothing a user sees changes: the timeout still fires after the same period of inactivity, shows the same notice, and leaves other devices signed in. No signature, content hash, record or lifecycle rule changes. ⚠️ If your performance qualification tests the idle timeout by checking the browser is returned to sign-in, that still passes; it did before, too, while a copied session kept working. A test that re-uses a session copied before the timeout now shows it refused. Test evidence: the real proxy executed against a fake login client (order, this-session-only scope, and a refused, throwing or unanswered revoke each still signing the browser out and logging why), and on a disposable database a real session's captured access and refresh tokens are refused after the timeout while the same person's other session keeps working; proven failing on the pre-fix code, where both captured tokens still worked; five mutants, all caught.
idle-revoke.test.tsidle-timeout.test.tsThe controlled PDF of a document now marks a review signature made on an earlier text of the version, as the document's page already does. No database migration.
THE CONTROLLED PDF SAYS WHICH REVIEWS COUNT. Since release 2026.09.18.95 a review signature counts toward approval only if it was made on the version's current text, and the document's page marks one that was not. The controlled PDF — the watermarked copy with the electronic-signature list — printed that review exactly like a current one. It now prints, on its own line directly under such a review signature: "(made on an earlier text of this version - does not count toward approval)". The decision uses the same rule as the page, so the two cannot disagree. It appears in both forms of the controlled PDF: a typed document's, and the signature page added to an uploaded PDF. ⚠️ Expect this line on controlled PDFs from now on: a document approved before release 2026.09.18.95 on a review of an earlier text will show it too. The signatures themselves are printed exactly as before and are not changed, and a PDF already downloaded is unaffected.
Why this classification: Classified “review”: the content of a controlled record's printed form changed — a line is added to the electronic-signature list — and a procedure or a check that compares printed copies may rely on its format. No signature, signature meaning, content hash, permission or lifecycle rule changes, and which reviews count toward approval is unchanged from release 2026.09.18.95. Test evidence: the route is executed and the text read back from the PDF bytes, on both the typed-document and the uploaded-PDF branch — the note appears for an earlier-text review and not for a current one or for an approval, it fails closed on a missing hash like the page, the signature line is unchanged, and the note's characters trigger no omitted-character disclosure; proven failing on the code before this change; a ledger requires the route to import the page's rule and words rather than hold a copy; three mutants, all caught.
pdf-report.test.tssend-back-review-round.test.tsWhen you are not signed in, an action now says "You are not signed in — please sign in again." instead of "Your session has expired", which was often not true. No database migration.
THE NOT-SIGNED-IN MESSAGE NO LONGER CLAIMS A SESSION EXPIRED. When an action ran without a signed-in user, it answered "Your session has expired — please sign in again." Indelio cannot tell why nobody is signed in: you may have signed out, possibly in another tab of the same browser, the inactivity timeout may have ended the session, or your organization's access may be suspended. The message named one cause, and on 2026-09-29 it was the wrong one: a production check failed with it 91 seconds after sign-in, and the cause was a Sign out clicked in another tab sharing the same login. The message now says only what is known: "You are not signed in — please sign in again." When it appears, the action did not run. The inactivity timeout's own notice on the sign-in page is unchanged.
Why this classification: Classified “none”: only the wording of a refusal changed. Who can act, when, and what is recorded are unchanged; the same check refuses the same requests. No signature, content hash, lifecycle rule, access restriction or retention rule changes. ⚠️ If your procedures or training material quote the old sentence ("Your session has expired"), update them to the new one. Test evidence: a ledger over every string literal in app/, lib/ and proxy.ts forbids any text saying a session expired, with the detector proven on the real pre-fix lines and proven not to catch the true reset-link message or Stripe's event name; three mutants, all caught.
not-signed-in-wording.test.tsbilling.test.tsSending a document back for rework now ends its review round, so the next submission can name the same reviewer again and only the reviewers it names gate approval. A review signature now counts toward approval only if it was made on the version's current text. No database migration.
A SEND-BACK ENDS THE REVIEW ROUND. Returning a document for rework moved it to Draft but left every reviewer assignment active. Resubmitting it to the same reviewer was then refused with a raw database error ("duplicate key value violates unique constraint…") and nothing was submitted; resubmitting to someone else went through, but the first round's reviewer stayed assigned and approval went on requiring their review signature. Now a send-back ends every active assignment — kept on the record with who ended it and "Returned for rework — <the note>" — and writes an END_REVIEW_ROUND entry naming them. Submitting for review also ends any assignment still active from an earlier round ("Superseded by a new review round"), so a document sent back before this release can be resubmitted. If an assignment still collides, the author sees a plain message and nothing moves.
Why this classification: Classified “requalify”: which reviewers must sign before a document can be approved changes after a send-back — a reviewer from an earlier round is no longer required unless the author names them again. No signature, signature meaning or content hash changes, and assignments are ended, never deleted. Which review signatures count is the second change in this release. Test evidence: the real functions driven against an in-memory database enforcing the same unique index, proven failing on the pre-fix code with the production error; a ledger over every write to a document version's state requires the only path back to Draft to end the round, after the state moves so a failure cannot leave a document In Review with nobody assigned; six mutants, all caught.
send-back-review-round.test.tsdocument-reviewers.test.tsA REVIEW COUNTS ONLY FOR THE TEXT IT SIGNED. A review signature binds the content of the version at the moment of signing, but approval counted any review signature by an assigned reviewer on the version, whenever it was made. So a document reviewed, sent back, rewritten and resubmitted to the same reviewer could be approved at once — on a review of text it no longer contained. Now a review signature counts toward approval, the reviewer's own to-do list, the approvers' to-do list and reassignment only when it was made on the version's current text (its bound content hash equals the version's current content hash). The earlier signature is not removed or altered: it stays in the record, and the document page shows it as "made on an earlier text of this version — does not count toward approval". If the text did not change, an earlier review still counts. If the current text's hash cannot be read, approval is refused rather than counting every signature. The SOP-QMS-001 Document Control template gains a step describing a send-back and this rule, and the Part 11 validation document's 11.10(f) entry now says a document cannot be approved until every named reviewer has signed the text being approved — if your procedure was built from the earlier template, compare it.
Why this classification: Classified “requalify”: an electronic-signature control changed — which review signatures satisfy the approval gate. A reviewer whose review was of an earlier text must sign the current text before the document can be approved. No signature is created, removed or altered, no signature meaning or content hash changes, and nothing about how a signature is applied changes. ⚠️ Documents ALREADY approved are not re-opened or re-evaluated: the gate runs only at the moment of approval. If one of them was approved on a review of an earlier text, its page now says so beside that review signature — which is a true statement about the record, and yours to assess. ⚠️ The controlled PDF's signature list is unchanged by this release: it still lists every signature with the content hash it is bound to, and does not carry the earlier-text note. Test evidence: proven failing on the pre-fix code (round 2 approvable on a round-1 review of different text); every read of review signatures in the product, found from source, must compare the signature's hash with the current content; a signature with no hash, or a version whose hash cannot be read, fails closed; an unchanged text keeps its review; five mutants, all caught.
send-back-review-round.test.tsdocument-reviewers.test.tsA database migration is required in this release: run `supabase/compliance_history.sql`. It affects only Indelio's own internal compliance console, which is not part of your quality system — nothing in your organization's records or workflows changed. ⚠️ It does add one file to the installation qualification: IQ-06 now lists 112 migration files where it listed 111.
INDELIO'S OWN SOC 2 REGISTER NOW KEEPS A PERMANENT HISTORY OF EVERY CHANGE. This is the vendor's internal compliance tracker — the register Indelio keeps about its own SOC 2 programme — and it is not part of your quality system and holds none of your data. Until this release, changing a control's status, evidence or notes, or marking a recurring task done, overwrote what was there: the row kept only who changed it last. Now every change is kept, permanently and newest first — the old and new value of each field that changed, who changed it and when. The history is written by the database in the same step as the change, so a change that cannot be recorded does not happen, a change that does not say who made it is refused, and the history itself can never be edited or deleted. ⚠️ The history starts with the first change after this release; earlier changes were overwritten and are not reconstructed.
Why this classification: ⭐ Classified “review” for the same reason as release 2026.09.18.38, and not because of the console. The console change touches nothing of yours: the tables are platform-global, hold no organization's data, are unreachable by any tenant, and are not quality records — no signature, lifecycle rule, access restriction or retention rule of yours is affected. On its own that would be “none”. ⚠️ IT IS “REVIEW” BECAUSE THE MIGRATION CHANGES AN INSTALLATION QUALIFICATION STEP YOU EXECUTE: IQ-06 now lists 112 migration files in `supabase/run-order.json` where it listed 111. If you hold IQ evidence against 111 files, a fresh install or a re-execution will differ from that record, and the new file must be applied. Test evidence: compliance-history.test.ts enumerates every write to the two register tables across lib/, app/, scripts/ and the migrations — which found a third write path beyond the two first named — reports any it cannot read, proves on the migration set before this release that neither table had a history trigger, and holds the trigger's refusal, fields and append-only protection; its live half proves on a disposable database that an update is recorded with old and new values, that an unattributed update is refused and changes nothing, and that the history cannot be edited or deleted; 9 mutants.
compliance-history.test.tscompliance.test.tsedit-trail-coverage.test.tsCorrected: a document's audit trail now has a Reason column. Release 2026.09.18.91 said the reasons for cancelling a training assignment, discarding a revision and rolling back a migrated document were shown on the document's page; they were recorded, but that page did not display any entry's reason until this release. No database migration.
CORRECTED: A DOCUMENT'S AUDIT TRAIL SHOWS WHY. The audit trail on a document's page listed each entry's number, time, actor, action, record and fingerprint — and no reason. Every other record page shows the reason. So on a document, why a training assignment was cancelled, why a revision was discarded, why a migrated document was rolled back — and the reason on every other entry — was recorded in the audit trail but not shown on the page. It now has a Reason column. Release 2026.09.18.91 described those three reasons as shown on the document's page; that was not true until this release (the rollback reason was, and is, shown on the migration batch's page). Found in the production check of 2026-09-29.
Why this classification: Classified “review”: nothing is recorded differently — every reason was already stored with its entry — but what a reader of a document's audit trail sees has changed, and a statement in release 2026.09.18.91 is corrected. No signature, content hash, permission or lifecycle rule changes. ⚠️ If you relied on 2026.09.18.91's description to review document-level reasons on screen, those reasons were not shown on the document's page until this release. Test evidence: a ledger that parses every audit table rendered in the application — derived from each table showing an entry's action, not from a list — and requires the column headed Reason to be the cell that renders the entry's reason, directly or through a component whose own source does; it was proven failing on the document page's table as it stood before this fix.
audit-table-reason.test.tshidden-reasons-visible.test.tsAny change to a record that already carries an e-signature, including filling in an empty field or adding a new item, now needs a reason for change, which is recorded in the audit trail with the old and new values. A record no one has signed can still be changed freely. No database migration.
A SIGNED RECORD CHANGES ONLY WITH A REASON. Since release 2026.09.14.2 every change has recorded who made it, when, and the previous and new value of each field. It did not record why. Now, once a record carries an e-signature — any signature, ever, including one on a record that has since been reopened or amended — any change to it or to one of its items (CAPA actions, risk items, design inputs and outputs, audit findings, review actions, human-factors items, project deliverables, quiz questions) — including filling in an empty field or adding a new item — is refused unless the person gives a reason. The record page shows a "Reason for change" box once the record is signed; what is typed there is saved in the same audit entry as the change, in the entry's reason and as edit_reason in its details. It applies to CAPA, change control, complaints, nonconformances, suppliers, equipment (a signature on the equipment itself — an accepted calibration signs that calibration record, not the equipment's details), the quality policy, document versions, design projects, risk assessments, audits, management reviews, validation projects, post-market surveillance plans, Initial Impact Determinations, assessments, field actions, the five human-factors records and training quizzes. A record no one has signed yet is a draft and needs no reason. Not affected: a step that only moves a record's state; signing itself; confirming a complaint out of triage and releasing a held email to the AI (a triage item carries no signature until it is discarded, and the discard signature ends triage); marking an approved CAPA action done; releasing an approved document version; and the regulatory-change review sweep. Approving a CAPA action's due-date extension records the request's own reason unless the approver gives one. If Indelio cannot read a record's signatures, the change is refused rather than let through. SOP templates updated with one line each: document control, CAPA, change control, complaint handling, nonconformance, design control, training, supplier qualification, risk management, internal audit, management review, validation of quality system software (a test step), quality manual, calibration and maintenance, post-market surveillance, corrections and removals, human factors.
Why this classification: Classified “requalify”: a lifecycle rule changed on every record that can be signed — a change that used to be saved is now refused without a reason, and the audit entry for a change to a signed record now carries that reason. ⭐ No record, signature or content hash changes: the reason is not a material field and never enters a hash; existing signatures and their bindings are untouched; changes made before this release carry no reason. ⚠️ Anyone who edits a signed record (for example a CAPA after its request was approved, or a risk assessment that was reopened) must now type a reason first. Test evidence: edit-reason.test.ts runs the rule on real lib/ code — a signed CAPA's risk cannot move High → Medium without a reason and nothing is written; with one, the reason is in the same entry as the old and new values; hidden-character and email-only reasons are refused; an unreadable signature table refuses; drafts are free; the reason never moves the content hash. On the pre-fix code the first case failed. edit-reason-coverage.test.ts derives its universe from every write in lib/ and app/, fails on any table it cannot classify, checks the signature registry against the migrations both ways, and holds every change path, server action, panel and record page to the rule; on the pre-fix code every change path was an offender. 18 mutants, all caught. copy-claims.test.ts holds every help topic, SOP template, validation document and this entry to saying the rule includes filling in an empty field or adding a new item, and fails on the wording of a narrower rule that was built and reverted the same day; 4 mutants, all caught.
edit-reason.test.tsedit-reason-coverage.test.tscopy-claims.test.tsThe reason a person types when returning a CAPA request, discarding a document revision, removing a design↔risk link, cancelling a training assignment or rolling back a migrated document is now shown in the record's audit trail. ⚠️ Corrected 2026-09-29 in release 2026.09.18.93: on a document's page these reasons were recorded but not displayed — that page's audit trail had no Reason column until 2026.09.18.93. Skipping a file in a document migration now requires a reason, and each migration batch page shows its audit trail. No database migration.
A TYPED REASON IS SHOWN, NOT HIDDEN. Five actions recorded the reason a person typed only in the hidden details of the audit entry, under a fixed sentence, so the record's audit trail never said why. Each entry now reads "<what happened> — <the reason typed>": "CAPA request returned to owner for updates — …" (on the CAPA), "Revision discarded without taking effect — …" (on the document), "Design↔risk traceability link removed — …" (on both the risk file and the design project), "Training assignment cancelled — …" (on the document), and "Migrated document rolled back (withdrawn — set Obsolete) — …" (on the document). Hidden characters are removed from the text shown. Entries written before this release are unchanged: the audit trail is append-only. Skipping a file in a document migration now REQUIRES a reason, which is refused when empty and shown as "Migration item skipped — …". And each migration batch page now has its own audit trail, listing the batch's creation, commits, skips and QA verification and the rollback of any document it created — until this release those entries were in the audit trail but on no page.
Why this classification: Classified “review”: the visible text of six audit entries now carries the reason the user typed, which your auditors and procedures may rely on, and skipping a migration file is refused without a reason — a data-migration procedure that skips files should say a reason is recorded. No signature, signature meaning, content hash, role requirement or lifecycle rule changes, nothing that was allowed is refused, and each entry's details are exactly as before. ⚠️ Only future entries change — an entry written before this release still shows only the fixed sentence, with the reason in its details. Test evidence: for each action, the audit call read from the source must carry the typed text in its visible reason and be written under a record id the record's page reads (checked with the page's own filter); the suite was proven failing on the pre-fix code, and against 13 mutants. The migration batch's audit trail is read with the same filter every record page uses, and a failed read is shown as unreadable, never as an empty trail. The two inventories of hidden reasons shrank by exactly these entries.
hidden-reasons-visible.test.tsevery-comment-saved.test.tssigned-reason-visible.test.tsNo database migration in this release. The 21 CFR Part 11 validation package's traceability table is rewritten in plain language: code and evidence columns are gone, a Comments column says what your company must do, and every row carries one of four statuses. One row moves from met to built-not-yet-tested.
THE PART 11 TABLE NOW SPEAKS TO YOU, NOT TO OUR DEVELOPERS. On an independent validation consultant's advice, the requirements traceability table in §4 of the Part 11 package no longer shows code or configuration names or internal evidence paths — those belong in our design records, and they are kept there. "How Indelio meets it" is reworded in plain language with the same meaning; no claim is stronger than before. A new Comments column says what your company must do for a requirement to be met in full, and names the complementary user entity control (CUEC) where there is one. The status legend is now four plain statuses: ✅ Met — built and covered by automated tests; 🟡 Built — formal test not yet written; 👤 Your responsibility; ❌ Gap. STATUS CHANGES: §11.300(a) (unique identification code and password combinations) moves from ✅ to 🟡 — it is enforced by our authentication provider and the evidence is theirs, with no automated test of ours, so under the new legend it cannot read as Met. §11.10(j) (written policies holding people accountable for their electronic signatures) moves from 🟡 to 👤 — those policies are your SOPs. §11.100(b) and §11.100(c) read "Customer" and now read 👤, with no change of substance. No row moved to ✅. The Validation Plan's description of the traceability matrix is corrected to match.
Why this classification: Classified “none”: no controlled function changed — not one line of application behaviour was altered; what changed is how the validation package describes behaviour that was already there. ⚠️ THERE IS AN ACTION HERE IF YOUR OWN PART 11 ASSESSMENT CITED OUR PACKAGE FOR §11.300(a) as met: it now reads 🟡, because the only evidence is the authentication provider's, not an automated test of ours. Nothing about how identities are kept unique changed. ⚠️ SECOND: §11.10(j) now reads 👤 — if your plan relied on our package for it, record the SOP that holds your signers accountable; like §11.100(b) and (c), it is not yet in the CUEC register. Test evidence: validation-doc.test.ts holds the customer table and the internal copy to the same clauses, requirement text and pinned statuses, and fails on code words, a missing comment on a 👤 row, or an unreadable row; proven on the real rows from before the rewrite and by 6 mutants.
validation-doc.test.tsvalidation-library-sync.test.tsedit-trail-coverage.test.tsDiscarding a complaint triage item as not a complaint is now a QA decision, signed with a password and a reason. No database migration.
DECIDING AN ITEM IS NOT A COMPLAINT IS SIGNED BY QA. Every email that reaches complaint intake is kept as a Triage item. Discarding one — deciding it is not a complaint — can now be done only by a person with the QA role, who gives a reason and re-enters their password: the e-signature "Discarded as not a complaint by", bound to the item's content. The reason is shown in the item's audit trail ("Triage item discarded as not a complaint — <reason>"). A reason that is only an email address is refused. The item stays in the Discarded state and is never deleted. Until this release any user with access to complaints could discard a triage item, unsigned, and the reason was recorded only in the audit entry's details. Users without the QA role no longer see the Discard control; they can still confirm an item as a complaint.
Why this classification: Classified “requalify”: a role requirement and an electronic signature were added to a quality decision. Discarding a triage item now requires the `qa` role (refused in `lib/` for every other role, approvers and administrators included) and a password e-signature with a new signature meaning, "Discarded as not a complaint by". Your SOP for complaint handling should say who may discard and that it is signed; the SOP-QMS-004 template is updated (v1.3, §5.2). ⚠️ If only non-QA users triaged in your organization, they can no longer discard: assign the QA role to whoever makes that decision. ⭐ No content hash changes and no existing record moves: items discarded before this release are unchanged, and the new signature binds the item's existing content hash. Test evidence: the discard run for real against an in-memory database — each non-QA role, an empty reason and an email-only reason refused with nothing written (no signature, no state change, no audit entry), and the order signature → state move → decision entry asserted, so a failed move records no decision; the password verified in the server action before `lib/` is reached; the suite proven failing on the pre-fix code; and 8 mutants. It leaves the shrink-only list of unsigned actions that hide their reason.
triage-discard-signed.test.tssigned-reason-visible.test.tssigning-text.test.tsCorrected: the Privacy Policy now says what happens to a complaint email when it reaches Indelio. Its effective date moves to September 29, 2026, so every user is asked to accept it again at their next sign-in. The Terms of Service wording is unchanged. No database migration.
CORRECTED: THE PRIVACY POLICY SAYS WHAT HAPPENS TO AN ARRIVING COMPLAINT EMAIL. It said that once a complaint email arrives, the AI reads it. That has not been the default since release 2026.09.18.69: the organization's Complaint email intake setting decides. By default the email is stored unread and no part of it reaches the AI until someone with the QA or administrator role releases it; the setting can instead send each email to the AI as it arrives, or never send one. The policy now says exactly that. Because the policy's text changed, its effective date changed to September 29, 2026, and every user is asked to read and accept the Terms of Service and Privacy Policy again at their next sign-in — the Terms carry the same effective date, but their wording did not change.
Why this classification: Classified “review”: no record, signature, permission or AI behaviour changes — the policy is corrected to match what the product already does. It is not “none” because a controlled document your organization relies on changed, and every user meets a re-acceptance step at their next sign-in; that acceptance is recorded, hash-bound to the new text. Test evidence: legal-acceptance.test.ts pins the new effective date and both documents' content hashes together, so the text cannot change without the date; copy-claims.test.ts bans the old sentence wherever a reader can meet it, proven on the sentence that shipped, with one mutant restoring it.
legal-acceptance.test.tscopy-claims.test.tsCorrected: the help, the Complaints product page, the Sub-processors page, Settings and the Complaint Handling SOP template now describe complaint email intake as it works. Indelio does not give you an email address — your own forwarder posts each email to an intake endpoint — and by default the AI reads no email until QA or an administrator releases it. No change to how the product behaves. No database migration.
CORRECTED: COMPLAINT EMAIL INTAKE IS DESCRIBED AS IT WORKS. Several places said complaints could be emailed to an address Indelio provides, or that the AI reads each inbound complaint email. Neither is true. Indelio does not provide an email address. An administrator generates an intake token under Settings → Email intake, and your organization sets up a forwarder — for example an n8n, Zapier, Make or Power Automate flow, or your email service's inbound webhook — that posts each complaint email, with the token, to the endpoint shown there. Setting it up is yours to do, or we set it up with you during onboarding. Every email is stored as a Triage item; since release 2026.09.18.69 the default Complaint email intake setting holds each email, so the AI reads nothing until someone in QA or an administrator releases it. Settings now also shows the message format the endpoint expects. Where earlier release notes speak of an address or a mailbox for intake, they mean this forwarder. The Complaint Handling SOP template is revised to v1.2 (§5.1): if you adopted it, update your copy's intake step.
Why this classification: Classified “review”: no behaviour, record, signature or permission changes — this corrects text that overstated what exists. It is not “none” because the Complaint Handling SOP template, which customers adopt into their own quality systems, changed in §5.1: a procedure that told staff complaints are emailed to an address Indelio provides, or that the AI reads each email, describes a process that does not run. Test evidence: copy-claims.test.ts now bans the claims wherever a reader can meet them, proven on the four sentences that shipped, with the six occurrences in dated release history pinned by count; 4 mutants, each restoring one of those sentences.
copy-claims.test.tsApproving the quality policy now signs only the statement the approver is looking at. If the text on screen is not the saved version — an unsaved edit, or a change someone else saved — nothing is signed and the approver is told why. No database migration.
THE QUALITY POLICY IS SIGNED AS THE APPROVER SAW IT. The policy statement is edited and approved on the same form: one text box, a Save draft button and an Approve button. The approval signature binds the SAVED statement. Until this release, an approver who changed the text and pressed Approve instead of Save draft signed the previously saved words while believing they had signed their edit — and if a colleague saved a change after the approver opened the page, the approver signed words they had never seen. Approval is now refused unless the words submitted are exactly the saved words (line endings and hidden characters aside, which the save already normalises). The refusal says "Nothing was signed", and tells the approver to save the draft first or reload the page to read the saved version.
Why this classification: Classified “requalify”: a signing rule changed — approval of the quality policy is now refused where it used to proceed (the submitted statement differs from the saved one). ⭐ No signed record moves: the signature still binds the saved statement's content hash, exactly as before; what changed is that it can no longer bind a statement other than the one the signer was shown. Existing signatures are untouched. ⚠️ An approver who edits and then approves in one step will now be refused and must press Save draft first. Test evidence: quality-policy.test.ts holds the comparison to known answers (unedited text passes; a browser's CRLF newlines and stripped hidden characters are the same statement; an unsaved edit, a changed space, an empty or missing post are refused) and pins that the refusal comes before the signature is made and before any write; 4 mutants, the first restoring the defect exactly.
quality-policy.test.tsWhen a project, or a Human Factors critical-task register, URRA, summative test or HFE report, is cancelled or superseded — or a project is reopened — the reason the signer typed now appears in the record's audit trail. No database migration.
A SIGNED DECISION'S REASON IS SHOWN, NOT HIDDEN. Cancelling a project, reopening one, and cancelling or superseding a critical-task register, URRA, summative test or HFE report each record an electronic signature and a reason the signer types. That reason was saved only inside the audit entry's hidden details; the entry itself read a fixed text such as "Project cancelled (approved)", so the record's page never showed why. Each of these now also writes the signature's audit entry, reading for example "Project cancellation approved — <the reason typed>" or "URRA superseded by URRA-004 — <the reason typed>", as every other module's cancellation, reopening and supersession already did. The existing entries are unchanged, so the "Superseded by" link and every other place that reads them work exactly as before. ⚠️ Only decisions made from this release on show the reason: audit entries are append-only and entries already recorded are never changed, so a record cancelled before this release still shows the fixed text, with its reason in the entry's details.
Why this classification: Classified “review”: each of these actions now writes one additional audit entry. No signature, signature meaning, content hash, lifecycle rule or role requirement changed, and no existing audit entry was altered. It is not “none”, because the audit trail is where a reviewer reads why a signed decision was taken (21 CFR 11.10(e); 11.50 — the signature's meaning and context), and the reason for these seven decisions was not readable there. Test evidence: a ledger over every action that takes a typed reason, failing on any signed action whose reason is not visible and naming, one by one on a shrink-only list, the six unsigned actions that still keep it in details; its classifier proven on the real text of the project cancellation before this fix; proven failing on the pre-fix code; 12 mutants.
signed-reason-visible.test.tscancel-rule.test.tshf-superseded.test.tsA trend disposition's CAPA or C&R determination reference is now checked against the organization's records. A reference that names no such record is refused, where it used to be recorded, and an existing one that does not resolve reads "Not found" instead of a link. No database migration.
TREND DISPOSITION REFERENCES ARE CHECKED, NOT TAKEN ON TRUST. On Post-Market Trends, a CAPA disposition's CAPA reference must name a CAPA, and a C&R determination disposition's reference an Initial Impact Determination (DET-###), that exists in the recorder's organization; otherwise the disposition is refused and nothing is written. A reference that cannot be checked is refused too. If the organization's plan does not include that module, the reference is recorded as external and the audit entry says it was not checked. Dispositions already recorded are unchanged; the page re-checks their reference when it is read, links it only if it resolves, and shows "Not found" in plain text otherwise. The IID and trend dispositions now share one lookup.
Why this classification: Classified “requalify”: a lifecycle rule changed — recording a trend disposition is now refused where it used to proceed (a CAPA or determination reference that names no record of that module in the organization, or that cannot be checked). SOP-QMS-015 §5.3 step 4 said Indelio does NOT check these references; it now says it does, so a customer's adopted procedure changes (v1.1). ⭐ No recorded disposition moves: capa_ref and det_ref were already, and remain, in the disposition's content hash exactly as typed; the Not found / Linked status is computed when the page is read and is outside the hash. Production held no trend dispositions on 2026-09-27 (read-only count), so no existing record reads differently there. The IID's lookup moved unchanged into lib/module-refs.ts; its behaviour and its 15 mutants are unchanged.
trend-refs-checked.test.tsiid-refs-checked.test.tstrends-unreadable.test.tsAn instrument's page now shows the audit trail of its calibration and maintenance records, which it had been leaving out. No database migration.
AN INSTRUMENT'S AUDIT PANEL SHOWS ITS CALIBRATION AND MAINTENANCE RECORDS. Each calibration or maintenance event carries its own code (for example EQP-002-E01), and its audit entries — recorded, assessed, accepted, signed, voided — are written under that code. The instrument's page matched only entries written under the instrument's own code, so those entries did not appear on it: an instrument whose calibration had been recorded and then voided still read "This equipment's 1 event". The entries always existed, in order and with their hash chain intact, and the system-wide audit trail showed them; only the instrument's own panel left them out. It now includes every entry for each of the instrument's events, matched by the event's exact code, so an entry belonging to another instrument can never appear.
Why this classification: Classified “review”: what the instrument page shows of the audit trail changed — it now shows more. No audit entry, signature, content hash, lifecycle rule or role requirement changed, and nothing was written. It is not “none”, because the audit panel is where a reviewer reads a record's history (21 CFR 11.10(e)), and a record reviewed on this page before this release showed an incomplete history. ⚠️ The count beside the panel rises on any instrument that has events. Test evidence: a ledger over every module whose page shows an audit panel (21), classifying every audit write each one makes as the page's own, a child record's, or another page's on purpose, failing on any it cannot classify; proven on the code before the fix and against 6 mutants.
audit-panel-scope.test.tsaudit-scope.test.tsThe header now names the organization you are signed in to, beside your name and roles. Display only. No database migration.
THE HEADER SAYS WHICH ORGANIZATION YOU ARE IN. Until this release no page named the organization, so a user who belongs to more than one — or moves between a demonstration and a test organization — had nothing on screen telling them where a record would be created or a signature applied. The organization's name now appears under your name in the header on every signed-in page. It is read from your own organization's record, through the lookup that already runs on every page, so it cannot show another organization's name. If the name cannot be read, nothing is shown rather than a guess. Hidden characters are removed before display, so a name cannot be made to look like a different one. Public (signed-out) pages are unchanged.
Why this classification: Classified “none”: display only. No record, signature, permission, lifecycle rule or content hash changes; nothing that was allowed is refused and nothing that was refused is allowed. The session lookup in lib/auth.ts reads one more column (the organization's name) from the row it already reads — the same query, keyed on the signed-in user's own organization. Test evidence: header-org-name.test.ts pins the fail-safe display rule, that there is still exactly one organization lookup per session and that it is keyed on the user's own organization, and that the name renders only in the signed-in header.
header-org-name.test.tsChanging plan or resubscribing from Settings → Billing is now accepted with your account's password, and the text names you from your account instead of a name you type. The page also says what happened after a change. No database migration.
A PLAN CHANGE OR RESUBSCRIPTION IS ACCEPTED BY THE SIGNED-IN ACCOUNT, CONFIRMED BY ITS PASSWORD. On Settings → Billing, the plan-change and resubscription texts now name the administrator who is signed in, from their account, and they accept by ticking the box and entering their account password. Until this release they typed a full name, so the recorded text could name one person while another person's login made the change. A wrong or empty password is refused before anything is charged, written or sent to Stripe, and a wrong one is recorded and reported exactly as a refused e-signature is. The page also now says what happened: the confirmation of a plan change stays on the page after the form closes; the plan picker opens on the plan you are on, marked "(current)", and choosing it says so rather than showing an error; the accept button is greyed out, as well as disabled, until the box is ticked and the password entered; after a resubscription the page no longer shows "nothing to resubscribe", and the ended order reads "Ended — replaced by" the new order's number. Placing a first order on the public order page is unchanged: there is no account yet, so the name is still typed.
Why this classification: Classified “review”: who can accept a billing change has narrowed — the acceptance now requires the account's password as well as a signed-in administrator session, and the name recorded in the accepted text is the account's rather than typed. ⭐ Orders and plan changes already accepted keep the words they were accepted against — each is bound to its text by its hash — so nothing recorded moves. It is not “requalify”: no quality record, its content hash, signature meaning, lifecycle rule or role requirement changed; billing acceptances are not quality records. ⚠️ A refused billing password is counted with refused e-signatures by the unauthorized-use report, because the same check is used. ⚠️ The password is verified in the server action, before lib/billing.ts is called, as every signing password in this codebase is; lib/billing.ts keeps the administrator gate, the hash comparison and the rate rules. Test evidence: the plan-change and resubscribe flows run through the server actions with the form's own field names against an in-memory database and a fake Stripe (wrong, empty or missing-session refusals reach neither Stripe nor the order tables); a ledger that the fields each form sends are exactly those its action reads; the signing-text ledger classifies every field submitted beside the password; and 11 mutants.
billing.test.tssigning-text.test.tsAn Initial Impact Determination's CAPA, Risk File and C&R references are now checked against the organization's records. A reference that names a record which does not exist reads "Not found" and blocks approval, where it used to read "Linked". No database migration.
IID REFERENCES ARE CHECKED, NOT TAKEN ON TRUST. On an Initial Impact Determination, the CAPA number, the Risk file / RA code and the C&R request number must each name a record that exists in the signer's organization — a CAPA, a risk assessment, or a field action (FA-###). One that does not is shown as "Not found" in the Dependency summary and, when that action is Required, approval is refused and the refusal names it. Until this release any text at all in one of those boxes read "Linked" and cleared the approval gate, so a determination could be approved pointing at a CAPA that did not exist. A Product Hold request number is now shown as "Recorded (external — not checked)", never "Linked": Indelio has no product hold module, so it still satisfies the gate but is not checked. If an organization's plan does not include the module a reference points at, that reference is recorded as external in the same way. If the records could not be read at the moment of approval, approval is refused and the reference reads "Could not be checked" — a failed read is never treated as found. A determination already approved is not changed: the check runs when the page is read, so one whose reference does not resolve now shows "Not found" with a note that it was approved before references were checked and that the record is unchanged.
Why this classification: Classified “requalify”: a lifecycle rule changed — approval of a determination is now refused where it used to proceed (a Required CAPA, Risk File update or C&R evaluation whose reference names no record of that module in the organization). ⭐ No signed record moves: the references themselves were already, and remain, part of the determination's content hash; the dependency status is computed at view time and is not in the hash, so every approval signature keeps its binding. ⚠️ A draft determination that names a record which does not exist will now be refused approval until the reference is corrected. ⚠️ An approved determination may now display "Not found" against a reference — nothing about it changed; the display is new. ⚠️ Scope: this checks that the record EXISTS in the organization; it does not check the record's state (a cancelled CAPA still resolves). SOP-QMS-016 §5.3 states the rule. Test evidence: a ledger whose universe is derived from every reference box the dependency summary reads, requiring each to be classified as a module or an external reference and failing on one that is not; the lookup exercised against an in-memory database holding two tenants (another tenant's records never satisfy it; a failed read is never read as found or as not found); and 15 mutants, the first of which restores the production defect exactly.
iid-refs-checked.test.tscnr-determination.test.tsA database migration is required in this release: run `supabase/orgs_name_unique.sql`. It makes an organization's name unique, which this instance already enforces through an index added by hand. A newly installed instance did not, and could not be provisioned at all.
AN ORGANIZATION'S NAME IS UNIQUE BECAUSE A MIGRATION SAYS SO. Comparing a database built from these migrations alone against the live one, object by object, matched on every count — 113 tables, 85 triggers, 98 policies — with exactly ONE exception: a unique index on organization names that exists live and in no migration. It had been created by hand. Two things followed on a freshly installed instance. Provisioning could not run at all: every provisioning and seeding path adds the organization with an "on conflict (name)" instruction, which the database refuses outright when nothing makes that column unique. And more quietly, because an organization is created by looking the name up and then inserting it, two organizations could end up holding the same name, which no screen that identifies a tenant by name could tell apart. The migration creates the index if it is absent, so on this instance it does nothing.
Why this classification: Classified “review”. ⚠️ IT CHANGES AN INSTALLATION QUALIFICATION STEP YOU EXECUTE: IQ-06 now lists 111 migration files where it listed 110, and the new file must be applied. ⚠️ IT CAN LEGITIMATELY FAIL, and that is correct behaviour rather than a fault: on an instance that already holds two organizations with the same name the index cannot be built, and the database names the duplicate. Rename one and apply it again — there is nothing to force, because the duplicate is a real data problem with quality records hanging off both rows. It is not “requalify”: no signature, signature meaning, content hash, lifecycle rule or role requirement changed, and no table, column, policy or grant is added. ⭐ It is not “none” either: your installation record's migration count and file list change, and the position of a rebuilt or restored instance genuinely changes — it can now be provisioned, and it can no longer hold two organizations with one name. Test evidence: a ledger that derives its scope from the code's own conflict targets across the application AND the scripts (the scripts are the files that broke, and they are the ones a default source scan misses), asks the migrations to justify each target, counts a primary key as uniqueness, and fails closed on an upsert it cannot read; seven proofs on known-bad input, including the exact state before this fix and a non-unique index of the same name proving nothing; 4/4 mutants.
upsert-conflict-targets.test.tssql-run-order.test.tsA database migration is required in this release: run `supabase/documents_bucket.sql`. It creates the private storage bucket that uploaded files are kept in, which until now existed only because it had been made by hand. An instance that already has the bucket is left exactly as it is.
THE FILE STORE IS CREATED BY A MIGRATION, NOT BY HAND. Every controlled document, revision, spreadsheet import and piece of record evidence is stored in one private bucket named `documents`. Until this release nothing in version control created it — it had been made in the supplier's dashboard — so installing this system from its migrations alone produced a database whose code uploads into a bucket that does not exist, and the first attachment failed at runtime. The gap was also invisible in the installation record: `storage_mime_types.sql`, the file that sets which file types the bucket accepts, is an UPDATE, so with no bucket to update it changed zero rows and reported success. The new migration inserts the bucket as PRIVATE, with the same 50 MB cap on a single stored file that this instance already has, and then refuses to complete if it is still absent, so the same defect cannot return as a silent no-op. That cap had also been set by hand, so a freshly installed bucket had none at all; the product's own 25 MB limit is unchanged and remains the rule a person sees enforced. On an instance that already has the bucket the migration does nothing at all: the bucket, its name and its file-type allowlist are untouched.
Why this classification: Classified “review”. ⚠️ IT CHANGES AN INSTALLATION QUALIFICATION STEP YOU EXECUTE: IQ-06 now lists 110 migration files in `supabase/run-order.json` where it listed 109, and the new file must be applied. It is not “requalify”: no electronic signature, signature meaning, content hash, lifecycle rule or role requirement changed, and it adds no table, column, policy or grant. ⭐ Nor is it “none”, for two reasons. Your installation record's migration count and file list both change, so a record made against 109 no longer matches a fresh execution. And the fresh-install and recovery position genuinely changed: a database rebuilt or restored from the migrations now has the file store it writes into, where before it had none and uploads failed. ⚠️ What this does NOT change: uploaded files are still outside the database supplier's backups, so restoring a database still does not bring its files back — off-site copies of uploaded files remain in progress, and the customer-held export stays the only recovery path for them. Test evidence: a ledger whose scope is derived from every place the application names a storage bucket — not from the migrations, which on the day of the defect named no bucket at all — asserting that each bucket the code uses is created by a migration before any migration updates it, and failing closed on a bucket name it cannot resolve; six detector proofs on known-bad input, including the real state before this fix; and, in the build pipeline, a live check that the freshly installed database has the bucket and that it is not public.
storage-bucket.test.tssql-run-order.test.tsA database migration is required in this release: run `supabase/billing_plan_change.sql`. An administrator can change the plan of an active subscription from Settings → Billing and keeps the rate they pay, and an organization whose subscription ended can resubscribe there, keeping a founding rate if it does so within 30 days. The Terms of Service change with it, so every user is asked to accept them again.
CHANGING PLAN, AND COMING BACK. An administrator can now change the plan of an active subscription from Settings → Billing — up or down, between packages and sets of modules — by reading a plan-change text, ticking the box and typing their full name; the text and its SHA-256 are recorded, as an order's are. The new plan is priced from the one published price list at the rate the subscription's first payment was charged at: a founding customer stays on the founding rate whichever plan they move to, and a plan change takes no new founding place. The difference for the rest of the billing period is charged at once, or, when the new plan costs less, credited against later bills and not paid out. Moving to a plan with a higher onboarding fee charges the difference from the onboarding already paid; nothing is refunded on a move down. Modules change as soon as the payment succeeds, through the same function and audit entry as every other module change, and the plan change itself is recorded in the organization's audit trail. An organization whose paid subscription ENDED can resubscribe from the same page and pay on Stripe's page: within 30 days of the day it ended, a founding rate is kept and no new place is taken; after that it is the list rate for good, even while a founding place is still open, and the place the first subscription used is not reopened. Before this release neither was possible: a customer wanting another plan, or returning, placed a new order, priced at the list rate because their own founding place was already taken. The published pages, the order text and the Terms of Service now state this rule, and no longer describe the rate as kept only while a subscription never lapses.
Why this classification: Classified “review”, for three reasons. (1) What a customer is charged can now change after the order: a plan change charges or credits a difference and changes which modules the organization has. It writes through the identical grant path, so the entitlement control itself (what off means, what it refuses, what it never touches) is unchanged, but your quality function should know an administrator of your own organization, not only a payment or Indelio, can now change your modules. (2) ⚠️ THE MIGRATION CHANGES AN INSTALLATION QUALIFICATION STEP YOU EXECUTE: IQ-06 now lists 109 migration files in `supabase/run-order.json` where it listed 108. A fresh install or a re-execution will differ from a record made against 108, and the new file must be applied. It adds two kinds of billing event and nothing else: no table, column, policy or grant, and both billing tables stay append-only. (3) ⚠️ The Terms of Service change (the founding-rate paragraph, and a new paragraph on changing plan), so every user is asked to accept them again at their next sign-in — the acceptance gate working, not a fault. It is not “requalify”: no electronic signature, signature meaning, content hash, lifecycle rule or role requirement of a quality record changed. ⭐ Orders already accepted keep the words they were accepted against — each order is bound to its text by its hash — so this changes what new orders say, not what old ones said. Test evidence: behaviour against an in-memory database and a fake Stripe (a plan change up and down keeps the rate with every founding place taken, asks no founding claim and writes no founding row; day 29 keeps the rate, day 31 does not; a carried rate takes no place when paid and releases none when it lapses), a ledger that every kind of billing event the code writes is one the migration accepts, and 20 mutants.
billing.test.tscopy-claims.test.tslegal-acceptance.test.ts⚠️ THIS RELEASE HAS A DATABASE MIGRATION (supabase/qmsr_connected_records.sql), and IQ-06 now lists 108 migration files. Four quality records now connect where they did not: a change is approved only with five impacts answered and closes only when retraining on the documents it revised is done; a complaint records its risk-file decision and closes only with a CAPA link or a no-CAPA reason; management review actions are followed up after approval without editing the signed review; and each design signature records the design content it attested to.
CHANGE CONTROL: FIVE IMPACTS BEFORE APPROVAL, AND CLOSURE WAITS FOR TRAINING. Approval now requires validation impact and training impact (previously optional) and three new decisions — labeling, regulatory and supplier impact — each Yes or No with a rationale; a No needs one too. A change can now be linked to the controlled documents it revises, and it cannot be closed while one of those documents has a revision not yet Effective or while anyone still has open retraining on its Effective version. Only QA may close it anyway, and only by recording a reason, which is kept on the change and in the audit trail. A reason that is only an email address is refused, as for every reason signed with a password. The Change Control product page already said changes link to the documents and training they affect; until this release that was only free text, and it is now true.
Why this classification: Classified “requalify”: a lifecycle rule changed twice (approval and closure refuse where they used to proceed) and the approval signature's material content grew. ⭐ No existing record moves: the three new decisions are folded into the change's content hash only when they are filled in, so every change signed before this release keeps its exact hash and its signatures stay intact — the automated evidence pins that against the hash's form before this change. ⚠️ A change already in Assessment is refused approval until the new impacts are answered, and the refusal names what is missing. A change already Approved with no documents linked closes as before; the training gate applies once a document is linked. The training record evidences that training was completed, not that anyone is competent. SOP-QMS-003 v1.2 describes both rules.
change-impacts.test.tschange-control.test.tsremoval-trail.test.tsentitlements.test.tsCOMPLAINTS: A RISK-FILE DECISION AT INVESTIGATION, AND NO CLOSURE WITHOUT THE CAPA DECISION. Investigation sign-off now requires "Risk file update required? Yes or No" with a rationale. Closure now requires the complaint to be linked to a CAPA — with the CAPA link, not only a typed reference — or to record why no CAPA is required. Until this release a complaint could be closed with neither. Once the investigation is signed, a missing risk-file decision or no-CAPA reason is recorded as a closure addendum — a permanent, attributed entry of its own — and no longer on the complaint, so the investigator's signature stays intact; closure's audit entries name each addendum it relied on.
Why this classification: Classified “requalify”: two lifecycle rules now refuse where they used to proceed, and the investigation signature's material content grew. The risk-file decision is required at investigation sign-off rather than at closure so that the investigator's signature covers it; recorded afterwards it would show that signature as broken on every complaint. ⭐ No existing record moves: both fields are folded into the complaint's content hash only when set, pinned against the hash's form before this change. ⚠️ A complaint already Pending Closure is refused closure until the risk decision and the CAPA decision are recorded. Those are recorded as a closure addendum (a new append-only table in the same migration), never on the complaint, so its investigation signature does not move — the automated evidence closes such a complaint and checks the signature still binds. ⚠️ Behaviour change: in Pending Closure, changing the risk-file decision or the no-CAPA reason on the complaint itself is now refused and points to the addendum (the no-CAPA reason could be edited there before this release, which broke the investigator's signature). A failed read of the addenda refuses closure rather than deciding without them. SOP-QMS-004 v1.1 describes all three rules.
complaint-closure-gate.test.tscomplaint-closure-addendum.test.tscomplaints.test.tscomplaint-source-email.test.tsMANAGEMENT REVIEW: ACTIONS ARE FOLLOWED UP AFTER APPROVAL. A new action needs an owner — a person in your organization — and a due date. Once a review is approved, progress on each action is recorded as its own entry, attributed and never an edit of the signed review; until this release an approved review's actions could not be updated at all without reopening it. Anyone except an implementer can record progress; only QA or an approver marks an action Done, with the evidence and their password (the e-signature "Action completion verified by"); evidence that is only an email address is refused. A new Open actions page lists every action from approved reviews with overdue actions flagged, and a draft review shows the earlier actions it should report on.
Why this classification: Classified “requalify”: a new e-signature meaning exists, a new access restriction applies (only QA or an approver completes an action), and adding an action now refuses without an owner or a due date. The signed review's content hash is unchanged — follow-up is stored separately and cannot alter it. Actions recorded before this release keep their free-text owner. A draft review's action can no longer be set to Done by editing it; completion is a signed step after approval. SOP-QMS-011 v1.2 replaces §5.7, which told you to track actions outside the approved record.
mr-action-followup.test.tsmanagement-review.test.tsitem-numbering-fails-closed.test.tsthread-item.test.tsDESIGN CONTROLS: EACH SIGNATURE RECORDS WHAT IT ATTESTED TO. Until this release every design signature was bound to the project's five header fields only, so design inputs, outputs, the trace matrix, verifications and validations could change after sign-off while every signature still read intact. Each new signature now records a fingerprint of the design content its stage covers — a design review the header, inputs, outputs and trace matrix; verification adds the verifications; validation adds the validations; transfer, cancellation and reopening the whole DHF including its risk links — and the E-signatures table shows whether that content still matches or has changed since.
Why this classification: Classified “requalify”: what a design e-signature is compared against changed, and signing now refuses if the content it covers cannot be read. The existing binding to the header fields is unchanged, so no signature changes state on that column. ⚠️ Signatures made before this release show "not comparable" in the new column — what the design held when they were made is not recoverable, and Indelio does not invent it. After this release, a later change to an input shows every earlier signature whose stage covers it as changed; that is the purpose of the comparison, not a fault. SOP-QMS-006 v1.2 describes it; v1.1 had said design signatures were bound to the record content when only the header was.
dhf-signature-scope.test.tsdesign-controls.test.tsdesign-trace-gate.test.tsNo database migration in this release. Our published description of the founding rate now matches what the system does: it is kept while a customer stays continuously subscribed, and is no longer described as lasting for ever or as following a change of plan.
WHAT WE SAY ABOUT THE FOUNDING RATE NOW MATCHES WHAT WE DO. The Founding Partner page, the pricing page, two published articles and the internal trials console described that rate as lasting for ever, and said it followed a customer from one plan tier to another as they grew. Neither is true of the system as built: changing plan is not something Indelio does today, so a customer who wanted a different plan would place a new order, which is priced at the list rate because their own founding place is already taken; and no rate lasts for ever — it is kept while the subscription continues. Every one of those statements now says what the Terms of Service have always said: the founding rate is paid on conversion and kept for as long as the customer stays continuously subscribed, and a payment recovered on a retry does not break that. The terms recorded in the audit trail when a founding place is granted say the same, so what is written from now on is what will be honoured; entries already written keep their own words, as every audit entry does.
Why this classification: Classified “none”: wording, and nothing else. No record, signature, content hash, lifecycle rule, access restriction, price or charge changed — what a customer is billed today is exactly what they were billed yesterday, and the Terms of Service are untouched because the sentence in them was already the true one. ⭐ The direction matters: this release does not take a benefit away, it removes a description of a benefit the system never provided. ⚠️ Indelio has decided to BUILD the plan change that keeps the founding rate, and a 30-day window to return at that rate after a subscription ends; when they exist they will be described again, and a ledger case records the exact sentences that may not come back before then. If your supplier file quotes our founding-rate terms, quote the Terms of Service rather than the marketing pages.
copy-claims.test.tsNo database migration in this release. A signature is refused when the reason, comment or other text typed beside the password is only an email address — what a browser's password manager fills into that box.
CORRECTED: A SIGNED DECISION COULD RECORD THE SIGNER'S EMAIL ADDRESS AS ITS REASON. On a signing form the text box sits just above the password box, and a browser's password manager can read the pair as a login and fill your sign-in email into the text box. Indelio checked only that a reason was not empty, so an email went through and was signed: a change cancelled in Indelio's own test organization on 24 September 2026 recorded its reason as the signer's email address. The form already asked browsers not to fill these boxes; Chrome's own password manager ignores that request. Indelio now refuses, before anything is written or signed, any signing where one of those text values is ONLY an email address, and says so: "Nothing was signed: the reason is only an email address…". It applies to every action signed with a password that takes text: every cancel, reopen, supersede, reject, void, retire, hold, release, disqualify and amend reason; the review context on a document review and periodic review; a design review's stage, summary, outcome, function, participants and sole-participant reason; a risk file's post-market review summary; and the quality policy statement. Text that mentions an address among other words is accepted as before.
Why this classification: Classified “requalify”: an electronic-signature control changes (21 CFR 11.50, 11.70 — what is bound into a signed record). Every signing that takes text now refuses one input it accepted before: a value that is only an email address. Nothing already recorded changes — records and signatures made before this release keep their text, including any that already carry an email as their reason (append-only; they are not corrected). If your operational qualification signs any of these actions with a reason, the step behaves as before unless the reason is only an email address; you may add a negative case (enter an email address as the reason and confirm nothing is signed). Test evidence: a ledger derives every action that verifies a signing password, reads every form field each one takes, and requires every free-text field to reach this refusal in Indelio's server code before the record is written; a new signing action that skips it fails the suite.
signing-text.test.tsNo database migration in this release. The page shown after paying for an order no longer says Stripe emails a receipt; it describes the confirmation Indelio itself sends.
THE PAGE AFTER A PAID ORDER DESCRIBES THE EMAIL INDELIO SENDS, NOT ONE IT DOES NOT CONTROL. It said “Stripe emails your receipt”. Stripe sends that only while a setting in Stripe's own dashboard is switched on — a setting outside Indelio's code, which anyone with dashboard access can change at any time, and which nothing in Indelio would notice. The page now says what this release of Indelio actually does: a confirmation of the order is emailed to the address on it, carrying the order reference and the exact words that were accepted, with the environment and credentials following separately. It also says what to do if that confirmation does not arrive — email info@voxelioai.com with the order reference — because the confirmation is deliberately best-effort and can never fail or repeat a payment. Indelio may still switch Stripe's own receipts on; the page does not promise them either way.
Why this classification: Classified “none”: wording on a public page. No record, signature, content hash, lifecycle rule, access restriction or notification changed — the confirmation email described here already existed (release 2026.09.18.70), and nothing new is sent, withheld or altered by this change. ⭐ What changed is that the sentence is now true of this system rather than of a third party's configuration, and a test pins it: the old claim cannot return, the description must keep naming the accepted words, the route to take when the email does not arrive must remain, and the page's promise is tied to the code that sends it — removing the send fails the page's own case. If your supplier file records which notifications Indelio sends, the list is unchanged; only this page's description of them is.
billing.test.tsNo database migration in this release. A cancelled human-factors record can no longer be used as the source of another: Indelio now refuses it where it previously relied on your procedure.
CORRECTED: A SUMMATIVE TEST COULD IMPORT FROM A CANCELLED CRITICAL TASKS REGISTER, AND AN HFE/UE REPORT COULD BE ASSEMBLED OVER A CANCELLED RECORD. Since release 2026.09.18.71 each of these refused a superseded source, but a cancelled one went through, and SOP-QMS-017 left it to your procedure not to reference one. Indelio now refuses both: a summative test imports only from a Draft or Approved register, and a report is not assembled while its Use Specification, URRA, register or summative test is cancelled or superseded. A critical tasks register holding a task imported from a URRA that has since been cancelled can't be approved until that task is removed, as already applied to a superseded one. The register's own import already skipped cancelled URRAs. SOP-QMS-017 v1.3 states this as enforced by Indelio.
Why this classification: Classified “requalify”: three places now refuse what they accepted before — a lifecycle rule on which records may feed others. Nothing already recorded changes: records imported or assembled before this release keep their content and signatures. If your performance qualification imports summative tasks from a register, or assembles a report, the step is refused where the source is cancelled; if your procedure relied on SOP-QMS-017 §5.7.3 to keep cancelled records out, that is now also Indelio's control and the procedure can stay as it is.
hf-superseded.test.tsNo database migration in this release. An approved human-factors record that a newer approved one replaces can now be marked Superseded, with an e-signature, and a superseded record no longer feeds the records built after it.
NEW: SUPERSEDING AN APPROVED HUMAN-FACTORS RECORD. Since release 2026.09.18.65 an approved Use Specification, URRA, critical tasks register, summative usability test or HFE/UE report can't be cancelled, and none of them can be reopened, so when a newer record replaced an approved one, the old one stayed Approved beside it. A person with the approver or QA role can now open the approved record, choose Supersede, name the approved record that replaces it and the reason, and sign with their password — the e-signature "Supersession approved by". The record is not edited: it keeps its content and its approval signature, which still verifies against it, is marked Superseded, and names its replacement. Only an approved record can be superseded, and only by an approved record of the same type. ⭐ WHAT NO LONGER FEEDS ON A SUPERSEDED RECORD: the critical tasks register imports from Draft and Approved URRAs only, where before it imported from every URRA that was not cancelled; a register holding a task imported from a URRA that has since been superseded can't be approved until that task is removed; a summative test can't import from a superseded register; an HFE/UE report can't be assembled while it references a superseded record; and a superseded summative test's attachments stay locked. SOP-QMS-017 v1.2 replaces the step that asked you to remove tasks from a superseded URRA by hand with these enforced ones.
Why this classification: Classified “requalify”: a new lifecycle transition, carrying an electronic signature and restricted to the approver and QA roles, and four places where a record is now refused that was accepted before. ⭐ NOTHING ALREADY RECORDED CHANGES. No existing record is superseded by this release, and until someone supersedes one, every record reads and behaves exactly as it did: an approved record still shows Approved, the register import still takes the same URRAs (until now none could be superseded), and summative imports and report assembly are refused only for a superseded source. The replacement is recorded in the supersession's audit entry and is not part of any record's content hash, so no signature binding changes. If your performance qualification covers revising an approved HF record, the step after approving the new record is now to supersede the old one; if it covers building a register after a change, the manual removal of tasks from the superseded URRA is now enforced.
hf-superseded.test.tsNo database migration in this release. Whoever places an order online is now emailed their own copy of the exact order they accepted, as soon as Stripe confirms it, and cancelling a trial is confirmed to the customer in writing.
THE CUSTOMER GETS THEIR OWN COPY OF THE ORDER THEY ACCEPTED. When a card is saved for a trial order, or a straight order's first payment succeeds, the person who placed the order is emailed the order text recorded at acceptance — verbatim, from the stored record, with the SHA-256 fingerprint the acceptance is bound to — together with the order reference and what happens next. Until now nothing reached them until their environment was set up by hand, which could be the next business day: the automatic charge at the end of a trial, how to cancel it and the refund rule were accepted on the website and then not restated anywhere the customer held. An order placed against a Stripe test key says so in its subject line, so a rehearsal cannot be mistaken for a real order. The notice to Indelio's own inbox now records whether that confirmation was sent, and to which address, so one that could not be sent is seen rather than missed.
Why this classification: Classified “review”, not “none”: no control changed — no signature, signature meaning, content hash, lifecycle rule, access restriction or record retention — and the order text itself is unchanged, read from the same stored field the acceptance was hashed over rather than composed again for the email. What changed is that a communication now leaves the system automatically at a point where none did, addressed to the person named on the order, before any account exists. If your supplier file or your procedure records which notifications Indelio sends and to whom, it has one more. It is not “requalify”: nothing a user does inside Indelio behaves differently, and the email is a copy of a record, never a place a record is made. ⛔ A confirmation that cannot be sent does not fail or repeat the payment: the order and the payment are recorded first, and a failed send is reported to Indelio for a person to handle by hand.
billing.test.tsCANCELLING A TRIAL IS CONFIRMED TO THE CUSTOMER. When an administrator cancels a trial before its first charge, every administrator of that organization is now emailed confirmation: who cancelled it, the order reference, that the saved card will not be charged, the date access runs to, and that everything recorded stays readable and exportable. Until now cancelling told Indelio's own inbox and nobody else, so the only evidence the customer held was a sentence on the page they were looking at. The date is read from the organization's recorded trial end date rather than counted from the length of a trial, and when it cannot be read the email says “the trial's last day” instead of naming a date.
Why this classification: Classified “review”: cancelling behaves exactly as it did — the same append-only event, the same audit entry, the same refusals, and the same effect on the charge — and the email is a copy of what was recorded, never the cancellation itself. A send that fails does not undo or repeat the cancellation. What changed is that a communication now leaves the system where none did, to the administrators of the cancelling organization, which belongs in your record of the notifications Indelio sends.
billing.test.tsTHE PAGE AFTER A TRIAL ORDER NOW SAYS THE CHARGE IS COMING. After saving a card for a trial, the page said “Your card is saved — nothing was charged”, without saying who holds the card or that Indelio charges it automatically when the trial ends. A few clicks earlier, Stripe's own checkout offers “save my information for faster checkout” — that is Link, Stripe's wallet across all merchants, and declining it changes nothing about the trial charge. Read together, the two could leave a customer believing they had opted out of being charged. The page now says the card is held by Stripe, that Indelio charges it automatically on the last day of the 30-day trial unless it is cancelled first, that Stripe's “faster checkout” wallet is separate from the order, and that an administrator can cancel at any time from Settings → Billing. It still says plainly that nothing was charged today.
Why this classification: Classified “none”: wording on a public page, changing no function. Nothing about the trial, the charge, the card or cancelling behaves differently — the automatic charge worked exactly this way before, and was already stated in the order text the customer accepted and in the reminder emails. What changed is that the page which follows Stripe's checkout now states it too, where the ambiguity arises.
billing.test.tsA database migration is required in this release: run `supabase/complaint_intake_mode.sql`. You now decide whether the AI reads a complaint email that arrives at your intake address, and the answer for every existing organization is set to hold it unread. No complaint is lost in any setting.
YOU DECIDE WHETHER THE AI READS AN INBOUND COMPLAINT EMAIL. Complaint email intake is the one AI feature that ran with nobody in between: a message sent to your intake address was read by the AI as it arrived, and what it contained was whatever the sender chose to write. The only control was the organization-wide AI switch, which turns off every AI feature at once. Under Settings → Complaint email intake you now choose one of three: read each email as it arrives; hold each email until someone releases it; or never send an email to the AI. ⭐⭐ WHAT THIS CHANGES FOR YOU, STATED PLAINLY: the setting is HOLD for every existing organization as well as every new one, so unless you change it, an email that used to be read by the AI on arrival is now stored unread until a person acts. ⭐ EVERY SETTING STORES THE MESSAGE. It arrives as a Triage item with the original email kept, and a held item carries fixed placeholder text instead of any part of the email — so nothing a sender wrote enters the complaint's content hash before a person has read it. Someone with the QA or administrator role — the roles that can read the original email — opens the item and selects "Release this email to the AI", which is recorded in your audit trail under their name, or fills the complaint in from the stored email themselves. A Triage item whose title and description are still the placeholder text can no longer be confirmed as a complaint. ⛔ AND A COMPLAINT IS NO LONGER LOST WHEN THE AI IS OFF. With AI features switched off, the intake endpoint used to REJECT the message: the email bounced back to your mailbox and nothing recorded that a complaint had arrived. It is now stored unread instead, with the reason on the audit entry. Changing the setting is recorded in your audit trail with your name and the time.
Why this classification: Classified “requalify”, and deliberately at the stricter end. (1) HOW A RECORD IS HANDLED CHANGED, and the change is to a DEFAULT that applies to organizations that already exist: an inbound complaint email that was read by the AI on arrival is now stored unread until a person releases it, so the fields of a Triage complaint are filled in later, or by a different means, than they were before. If your performance qualification exercised email intake, it exercised the behaviour this changes. (2) A LIFECYCLE RULE TIGHTENED: a Triage item whose title or description is still the intake's placeholder text can no longer be confirmed into an official complaint. That refuses something that previously succeeded, and both fields are in the complaint's content hash. (3) A NEW ROLE REQUIREMENT: releasing a held email to the AI is restricted to the QA and administrator roles. (4) ⚠️ THE MIGRATION CHANGES AN INSTALLATION QUALIFICATION STEP YOU EXECUTE: IQ-06 now lists 107 migration files in `supabase/run-order.json` where it listed 106. A fresh install or a re-execution will differ from a record made against 106, and the new file must be applied. ⭐ What did NOT change: no electronic signature, signature meaning or content-hash definition was altered, every existing complaint's hash is unchanged (the new provenance column is deliberately outside the hash), and no record was rewritten — complaints that arrived before this release are untouched and are not described as having been held. The sub-processor list is corrected with it: it said complaint emails reach the AI automatically, which was true when it was written and is no longer the default.
complaint-intake-mode.test.tscomplaints.test.tsai-usage.test.tsedit-trail-coverage.test.tssubprocessors.test.tsNo database migration in this release. The person who entered a calibration or maintenance record can no longer void it, whatever role they hold: voiding needs a second person with the approver or QA role.
CORRECTED: A QA OR APPROVER COULD VOID A CALIBRATION OR MAINTENANCE RECORD THEY HAD ENTERED THEMSELVES. Voiding already needed the approver or QA role, a reason and an e-signature, and an accepted record could never be voided — but nothing compared the person voiding with the person who entered the record, so someone holding the role could enter a result, for example an out-of-tolerance calibration, and then withdraw it on their own. Indelio now refuses that void and says a second person must do it; any other approver or QA can. The same person was already unable to accept a record they entered. The Void option is no longer shown to the person who entered the record. SOP-QMS-014 v1.3 now states this as enforced by Indelio; v1.2 had described it as a control of your own procedure, because Indelio did not enforce it.
Why this classification: Classified “requalify”: an access restriction changed — who may void a record — and it is a segregation-of-duties control. A person holding the approver or QA role who voided records they entered can no longer do so; if your team has only one person with either role, a record that person entered can no longer be voided until a second person holds one. Records voided before this release are unchanged, and keep their signature and audit entry. If your procedures relied on SOP-QMS-014 v1.2's statement that this separation was your own control, it is now also Indelio's; the procedure can stay as it is.
equipment.test.tscancel-rule.test.tsfour-eyes.test.tsNo database migration in this release. Indelio now alerts itself, urgently and daily, whenever a new organization exists while the database's point-in-time recovery is not confirmed on.
AN ALARM FOR POINT-IN-TIME RECOVERY. Indelio's production database does not yet have point-in-time recovery, so recovery is from the daily backup and up to 24 hours of records could be lost. Indelio will switch it on before a first customer's records arrive, and this release makes that impossible to overlook: the moment an organization is provisioned while point-in-time recovery is not confirmed on, an urgent email goes to Indelio's operations inbox, the Indelio platform console shows a banner, and the email repeats every day until it is on. The status is read live from the database supplier's management interface each time, and any answer that is not a clear yes, including no answer, is treated as off. Provisioning itself is unchanged: it is never held or refused because of this alert. An organization created other than through provisioning is reported at the next daily check.
Why this classification: Classified “none”: nothing a customer's users do, see or sign changed, and no control over records, signatures, access or lifecycle was altered. The change is an internal operations alarm about Indelio's own backup configuration: the alert goes to Indelio, never to the customer, and provisioning is never held, refused or failed by it (the alarm cannot throw into provisioning). The only difference a provisioning run can show is up to eight seconds' wait while the status is read. It is recorded here because it touches the provisioning path and because it concerns your data's recoverability: until Indelio confirms point-in-time recovery is on, the supplier assessment's statement of no point-in-time recovery, with up to 24 hours of writes unrecoverable, still stands.
pitr-alert.test.tsNo database migration in this release. A complaint that arrived by email can no longer be confirmed while its title or description is still the placeholder text Indelio writes when its AI could not extract them.
AN EMAILED COMPLAINT CANNOT BE CONFIRMED WHILE ITS TITLE OR DESCRIPTION IS STILL PLACEHOLDER TEXT. A complaint that arrives by email waits in Triage for a person to review Indelio's AI-proposed record and confirm it, and confirming has always required a title and a description. When the AI cannot extract one, Indelio fills it with placeholder text: the title “Complaint received by email — summary not extracted”, or a description saying it “could not be extracted automatically”. That text is not empty, so it was accepted as a title or description, and a complaint could be confirmed with it. Confirmation is now refused until the reviewer replaces the placeholder with what the complaint says. The same applies to the title “Email complaint”, which Indelio gave an email with no subject line until release 2026.09.18.51. ⭐ Nothing is lost: both fields can be edited while the complaint is in Triage, and the refusal says what to do. A title or description the reviewer has edited, even one that still contains the placeholder wording, is accepted as written.
Why this classification: Classified “review”: the behaviour of a controlled function changed, because confirming a triage item into an official complaint is now refused in a case where it was allowed. ⭐ It is not classified “requalify” because no new rule was introduced. The rule that a title and description are required before confirming has stood since email intake was added, and the placeholders are text Indelio writes in place of a missing value, which that rule was always meant to refuse. This change makes the existing rule hold for them. The title and description are part of the complaint's signed record (its content hash), so before this change a placeholder could become the text people signed. ⚠️ IF YOUR VALIDATION CONFIRMS AN EMAILED COMPLAINT WHOSE TITLE OR DESCRIPTION IS STILL A PLACEHOLDER, that step is now refused; edit the fields first. ⚠️ Complaints already confirmed are unchanged by this release. No signature, access restriction, content hash or retention rule changed.
complaints.test.tsNo database migration in this release. Cancelling a record now follows one rule in every module: only a person with the approver or QA role, with a reason and an e-signature, and never once the record is finished. Voiding a calibration or maintenance record is now recorded as an e-signature.
CORRECTED: ANY USER COULD CANCEL A CHANGE, AN AUDIT, A RISK ASSESSMENT, A DESIGN PROJECT, A PROJECT OR A MANAGEMENT REVIEW, WITH NO SIGNATURE. Each needed only a reason. Cancelling one of these now needs the approver or QA role, a reason, and the e-signature "Cancellation approved by", applied before the record moves, so a refused signature leaves it as it was. The stages are unchanged: until the record is closed or completed (an Approved change and an issued audit report included), and for a risk assessment until it is approved, and for a management review while it is a Draft. The page now asks for the password and shows the form only to approver or QA users; for a change it now also offers cancelling at Approved, and for an audit at Report Issued, which were always allowed but not offered.
Why this classification: Classified “requalify”: the register's own definition names an electronic signature, a lifecycle rule and an access restriction, and this adds a signature and a role requirement to an act that had neither. Anyone without the approver or QA role who used to cancel these records can no longer do so, and every cancellation from this release on carries a signature record. Records cancelled before this release keep the audit entry they were cancelled with and no signature; nothing is rewritten. (The same correction to nonconformances in release 2026.09.18.17 was classified “review”; this one follows the definition.)
cancel-rule.test.tshelp.test.tsreopen-signature.test.tsCORRECTED: A URRA, A CRITICAL TASKS REGISTER, A SUMMATIVE USABILITY TEST OR AN HFE/UE REPORT COULD BE CANCELLED AFTER IT WAS APPROVED, AND NO CANCELLATION OF ONE WAS SIGNED. Cancelling needed the approver or QA role and a justification, and the page asked for a password, but no signature was recorded, and an Approved record could be cancelled. Now cancelling is the e-signature "Cancellation approved by", applied before the record moves, and only a Draft can be cancelled: an approved record is signed and locked, and a change means a new record. If the move fails after signing, the signature stays on the record (it is append-only) and the message says so.
Why this classification: Classified “requalify”: a signature is added and a lifecycle rule changes — an Approved record can no longer be cancelled. Because none of these records can be reopened, an approved record that a change supersedes now stays Approved beside its replacement. The register still imports critical tasks from every URRA for its Use Specification that is not cancelled, a superseded approved one included, so SOP-QMS-017 v1.1 has tasks imported from a superseded URRA removed before the new register is approved.
cancel-rule.test.tswrite-results-checked.test.tshelp.test.tsCORRECTED: A SIGNED USE SPECIFICATION, A COMPLETED ASSESSMENT OR AN APPROVED INITIAL IMPACT DETERMINATION COULD STILL BE CANCELLED. Their cancellation was already an approver or QA e-signature; it now also refuses once the record is finished. Only a Draft can be cancelled. The signature meanings are unchanged ("Cancelled by", "Assessment cancelled by", "Determination cancelled by"), so signatures already on record read the same.
Why this classification: Classified “requalify”: a lifecycle rule changed — a finished record can no longer be cancelled. The role and the signature were already in place and are unchanged.
cancel-rule.test.tshelp.test.tsCORRECTED: VOIDING A CALIBRATION OR MAINTENANCE RECORD RE-CHECKED THE PASSWORD AND THEN KEPT NOTHING. The void is now recorded as an e-signature — "Calibration record voided by" or "Maintenance record voided by" — bound to the record, before the record stops counting. Nothing a user does changed: the form already asked for the password. Who may void (approver or QA) and when (not once accepted) are unchanged.
Why this classification: Classified “requalify”: an electronic signature is now recorded where there was none. SOP-QMS-014 v1.2 also now says plainly that Indelio does not stop a person holding the approver or QA role from voiding a record they entered themselves — the previous text said they could not, which Indelio never enforced.
cancel-rule.test.tsNo database migration in this release. A complaint's patient-information field now shows its de-identification guidance permanently instead of only as placeholder text, warns as you type if the text looks like it identifies a patient, and asks you to confirm before saving such text. The confirmation is recorded.
⚠️ THE PATIENT-INFORMATION FIELD ON A COMPLAINT NOW STOPS AND ASKS BEFORE SAVING TEXT THAT LOOKS IDENTIFYING. Our Privacy Policy states that Indelio must not be used for patient health information. Until this release, the complaint's "Patient information" field carried its instruction to de-identify only as placeholder text inside the box — which disappears the moment you start typing — and nothing stopped a patient's name being saved. Because that field is part of the complaint's signed content and complaint records can never be edited away or deleted, a name saved there was permanent. Three things change. FIRST, the guidance (use a patient reference; never a name, medical record number, date of birth or contact details) is now shown above the field at all times, on both the new-complaint form and the complaint's own page. SECOND, as you type, the form warns you if the text appears to contain a medical record or patient ID number, a date of birth, a social-security-style number, an email address, a phone number, a long ID-style number, a name with a title (Mr, Mrs, Ms, Dr), or a reference to a patient's name. THIRD, if you save such text, the save is refused until you either replace it with a patient reference or tick a statement confirming it contains no patient identity — and that confirmation is recorded in the complaint's audit trail with what was flagged. ⭐ Nothing is ever blocked outright: you can always record the complaint. ⭐ Editing an existing complaint for an unrelated reason does not ask you to re-confirm patient text you did not change. This completes the change begun in 2026.09.18.49, which stopped the AI email intake from extracting patient details at all; this release covers details a PERSON types.
Why this classification: Classified “review”: the behaviour of a function you use changed — saving a complaint whose patient information looks identifying now requires a confirmation that previously was not asked for. No signature, lifecycle rule, access restriction or retention rule changed, and no stored record was altered. ⚠️ IF YOUR PERFORMANCE QUALIFICATION RECORDS A COMPLAINT WITH PATIENT DETAIL, re-run that step: text that looks identifying will now stop at a confirmation, so a script that expected an immediate save will not match. ⚠️ TWO LIMITS YOU SHOULD KNOW, stated rather than left for you to discover. (1) The check recognises identity by its SHAPE, so a bare name with nothing around it ("Jane Smith fell") is NOT detected — the confirmation statement, and your own procedure, are what cover that case. (2) Complaints ALREADY saved are unchanged: the field is part of the signed, append-only record, so any existing value that should not be there can only be superseded with a recorded reason, never deleted. ⭐ If your records may already hold patient identity in this field, that is worth a review under your own procedures; this release does not do it for you and cannot.
patient-details-guard.test.tsNo database migration in this release. An order placed against a Stripe test key — a rehearsal — no longer creates an organization marked as a real customer.
A REHEARSAL IS NOT A CUSTOMER. When Indelio provisions an organization from an online order, the kind of tenant it creates is now derived from the order's own Stripe mode: an order placed with a test key, which can never charge anything, creates a Test organization, and a real order creates a Customer organization as before. Until now every organization provisioned from the onboarding queue was declared a Customer outright, so a test-mode rehearsal produced an organization carrying the customer settings — mandatory administrator multi-factor authentication, and sign-ins withheld from Indelio — and had to be corrected by hand afterwards. A request that arrived through the enquiry form, with no order behind it, is still provisioned as a Customer: no order is not evidence of a test. The onboarding queue now says on the row, and in the confirmation before the click, which kind of organization it is about to create, and the result names the kind it created.
Why this classification: Classified “review”: the tenant type decides two things — whether administrator MFA is mandatory, and whether sign-ins are reported to Indelio — so this changes which policy a newly provisioned organization receives. ⭐ It changes it in only one direction and only for rehearsals: an organization whose order was real, and one provisioned from a request with no order at all, are declared exactly as before, and no existing organization's type is altered by this release (the type is written once, at creation, and changing it stays a deliberate, audited act in the platform console). Nothing about signatures, records, lifecycle rules or module access changed. If your supplier file records how Indelio separates its own test organizations from customers, it now does so from the payment processor's own mode flag rather than from an operator's declaration.
tenant-type.test.tsNo database migration in this release. Provisioning a new organization now refuses when the administrator's email already has an Indelio account, instead of taking that account over.
PROVISIONING WILL NOT TAKE OVER AN ACCOUNT THAT ALREADY EXISTS. When Indelio provisions an organization — from the platform console or from an online order — and the administrator's email address already has an Indelio account, it now refuses and names both the address and the organization that account belongs to, instead of adopting it. Adoption was silent and did two things at once: the temporary password generated for the new administrator was never set on the existing account, while the credentials email was sent anyway, so the customer received a password that could not work; and the account's membership was rewritten to the new organization, moving that person out of the organization whose records they work in, with no audit entry and nobody told. The refusal happens before anything is created: no organization row, no account, no email, and nothing moved. Re-provisioning the SAME organization still completes, because an interrupted provision must be completable. The evaluation-tenant colleague accounts Indelio seeds are held to the same rule, since their addresses are derived from the administrator's own and could collide between tenants.
Why this classification: Classified “requalify”, at the stricter reading of the register's own definition: the control that changed is who belongs to an organization. Before this release a provisioning action could move an existing user's membership from one organization to another — including an administrator — so an organization could lose its only administrator, while records already attributed to that person stayed behind and the person did not. Nothing about signatures, content hashes or lifecycle rules changed, and no existing membership is altered by this release: it removes a way for one to be changed in future. ⚠️ If your organization was provisioned with an address that already had an Indelio account, that move has already happened and this release does not reverse it — check your administrator list. If your qualification covers account provisioning or user administration, the step that changes is provisioning onto a known address: it now fails, with a message naming the account and the organization holding it, where it previously appeared to succeed.
provisioning-adoption.test.tstenant-type.test.tsauth-directory.test.tsNo database migration in this release. A new complaint, and a new Initial Impact Determination, could be created with a content hash that did not match the record as stored, so the record failed its own integrity check until it was first edited. Both are now hashed as stored.
⚠️ A NEW COMPLAINT OR DETERMINATION NOW MATCHES ITS OWN CONTENT HASH FROM THE MOMENT IT IS CREATED. Each record's content hash is computed from its material fields. For a complaint created without a reportability decision — which is every complaint logged through the "Log a complaint" form, every complaint proposed from an email, and every imported complaint whose reportability cell was blank — the hash was computed with reportability empty, and the database then stored the record with reportability "Undecided". The stored record therefore did not match its own hash. The same happened to every Initial Impact Determination created from its form, whose four required-assessment answers and outcome are stored as "Undetermined" and "Undecided". Nothing was altered: the first edit of such a record recomputed its hash from the stored values, which is why the mismatch was visible only on records never edited since they were created — on the record's own page and, for complaints, in the nightly integrity sweep and Inspection Readiness, which reported one such complaint every night from 19 September. A complaint now records "Undecided" before it is hashed; a determination is re-hashed from the row the database returns, the method Corrections & Removals has used since 23 August for the same defect. An existing record still showing the mismatch is cleared by editing it — for a complaint, by recording its reportability decision.
Why this classification: Classified “requalify”, as the identical Corrections & Removals correction was on 2026-08-23: how a record's content hash is computed at creation is part of how records are handled, and that hash is what an electronic signature binds to. No record's content, state or signatures changed, and records created or edited before this release keep their stored hashes. If your performance qualification creates a complaint or a determination and checks its integrity status before editing it, that step now passes where it failed; if your procedures treated an integrity warning on a brand-new complaint or determination as expected, they no longer need to.
hash-before-default.test.tsNo database migration in this release. Nothing the software does changed. The Terms of Service and the Privacy Policy now describe what the system actually enforces, and both take a new effective date, so every user is asked to accept them once more.
THE TERMS AND THE PRIVACY POLICY SAY WHAT THE SOFTWARE ACTUALLY DOES. Five corrections, none of which changes a function. ⭐ RECORD INTEGRITY, STATED WITH ITS LIMITS: both documents said quality records, signatures and the audit trail could not be altered or deleted by anyone, including us. That is true of the records themselves — the database refuses every update and delete the application attempts, including from the privileged account Indelio runs on, and the hash chain is re-checked nightly — but it was never true of FILES ATTACHED to a record: while a record is open, the person who uploaded a file, an administrator or QA can detach it, the stored file is deleted, and the audit trail keeps its name, size, type, content hash and who uploaded and removed it. Once a record is locked the removal is refused and the refused attempt is recorded. Both documents now say all of that, and both now say plainly that these controls are enforced by a database we administer, rather than implying that nobody at Voxelio could ever change them. ⭐ THE EXPORT IS DESCRIBED AS IT BEHAVES: one file per record type including versions, signatures and the audit trail, a manifest naming anything the export could not include, a check of the audit trail's hash chain so an archive can be verified without us, and the fact that file content above a size ceiling is listed rather than included. ⭐ WHO RECEIVES A COMPLAINT EMAIL FIRST: a new paragraph explains that your own mail system or automation sends it to Indelio, so the mail service that handled it beforehand is yours, not a sub-processor of ours. ⭐ A link to the Sub-processors page printed as raw text and now reads as an address.
Why this classification: Classified “review”, not “none”, although no code path changed. These are the documents your organization accepted, and two statements in them were stronger than the software supports — a customer who read “nothing can be deleted by anyone” and relied on it for attached evidence was relying on something untrue. ⚠️ IF YOUR VALIDATION OR YOUR PROCEDURES QUOTE EITHER DOCUMENT, re-read the retention and termination paragraphs: the limits on attachments are new information about existing behaviour, not a change to it. ⭐ Nothing became less protected by this release. No signature, lifecycle rule, access restriction, retention rule or content hash changed, and the record controls behave exactly as they did yesterday. The effective date moves, so every user is asked to accept the documents once more; the acceptance record keeps the version accepted, the hash of its text, the person and the time. ⚠️ The gaps these documents still have — no data processing agreement, no confidentiality or incident-notification terms, and no defined retention period after cancellation — are with counsel and are not addressed here.
copy-claims.test.tslegal-acceptance.test.tsONE DATABASE MIGRATION in this release (supabase/complaints_source_raw_restricted.sql). The original email behind an emailed complaint can be read by QA and administrators only.
THE ORIGINAL EMAIL BEHIND A COMPLAINT IS FOR QA AND ADMINISTRATORS ONLY. When a complaint arrives by email, Indelio keeps the email it arrived as, so the complaint can be checked against its source. Anyone who could open the complaint could read it, and it can carry a patient's details. It is now shown only to people with the QA or administrator role. Everyone else sees a line saying the original email is kept and who can read it. ⭐ This is enforced by the database, not only on the page: a signed-in user who is not QA or an administrator cannot read the email even by querying the database directly with their own login. The email itself is unchanged — nothing is removed — and it remains in the tenant data export, which only administrators can run.
Why this classification: Classified “requalify”: an access restriction changed, which is one of the controls that classification names. Who can read part of a quality record is narrower than before. The reason is release 2026.09.18.46, under which the Service must not hold patient health information; the original email is the one place a complaint keeps an inbound message word for word. ⭐ Why the database: row-level security limits which ROWS a user can read, not which columns, so every member of an organization could previously read this column directly through the database interface with their own login. The migration removes the table-wide read and grants every other column back, checks itself, and rolls back if any check fails; row-level security is unchanged. ⚠️ IF YOUR PERFORMANCE QUALIFICATION HAS A REVIEWER, AUTHOR OR APPROVER VIEW THE ORIGINAL EMAIL ON A COMPLAINT, that step no longer applies: they now see a line saying who can read it. ⚠️ A column added to complaints by a later release is readable only once that release grants it; the register will say so when it happens. No signature, lifecycle rule or retention rule changed, and no record content changed.
complaint-source-email.test.tsattack.test.tsNo database migration in this release. A complaint no longer records the name of the person who was using the device.
A COMPLAINT NO LONGER RECORDS THE NAME OF THE PERSON USING THE DEVICE. The complaint form asked for the name of the person who was using the device when the event happened. That is a person's identity by design, and a complaint record is permanent. The field is gone from the new-complaint form and from the complaint details, and Indelio refuses a value for it on every route that creates or edits a complaint, rather than dropping it silently: a form left open from before this release shows an error asking you to reload. ⭐ Everything else on the complaint is unchanged, including whether a patient was involved, the patient outcome, the facility and whether the device was used routinely. ⚠️ A complaint that already holds a name keeps it, shown read-only with a note saying why, because the complaint's signatures cover it. Release 2026.09.18.49 said this field stayed on the form; from this release it does not.
Why this classification: Classified “review”: what a user can record on a complaint changed, visibly — one field is no longer offered and a value for it is refused. The reason is release 2026.09.18.46, under which the Service must not hold patient health information; this field asked for a person's name by design, on an append-only record. ⭐ The complaint's content hash is UNCHANGED, and that is deliberate: every complaint's hash has always included this field, as blank or as a name, so removing it from the hash would have broken every complaint signature. New complaints record it as blank; existing complaints and their signatures verify exactly as before. ⚠️ IF YOUR PERFORMANCE QUALIFICATION ENTERS A DEVICE USER'S NAME ON A COMPLAINT, that step no longer applies. ⚠️ Existing values are not changed or removed — records are append-only. No signature, lifecycle rule, access restriction or retention rule changed.
complaint-device-user-name.test.tsNo database migration in this release. The read-only API answers exactly as before. Its reference now shows how to get a key and a worked example, and the API access Help topic names the confirm button exactly.
THE API REFERENCE SHOWS HOW TO GET A KEY, AND A WORKED EXAMPLE. The reference — on indelio.io/resources/api and on Settings → API access — now sets out how an administrator issues a key (including that the key is shown once and Indelio keeps no copy) and how to stop one, and gives an example: a request that reads the key from an environment variable rather than writing it into the command, the answer for a fictional risk assessment, and the refusal when no key is sent. The example is not typed by hand: Indelio's automated tests call the real endpoint with the same fictional assessment and fail if its answer differs from the published example by one character. The API access Help topic uses the same steps, and its revoke step now names the button you select to confirm, Revoke permanently.
Why this classification: Classified “none”: documentation only. The API answers exactly as it did — the same endpoints, fields, statuses, error codes, key rules and rate limit — and issuing and revoking a key work exactly as before. No record, signature, lifecycle rule, access restriction or content hash changed.
api-reference.test.tsA database migration is required in this release: run `supabase/billing.sql`. Indelio can now be ordered and paid for online, and a subscription that is not paid makes its modules read-only rather than hiding anything. The Terms of Service and Privacy Policy change with it, so every user is asked to accept them again.
ORDERING AND PAYING ONLINE. A new customer can accept an order on our website — a 30-day trial of the full suite with a card saved up front, or a straight order of a package or of individual module sets — and pay on Stripe's hosted page. The exact text of the order is recorded with its SHA-256 when it is accepted, and an administrator can read it back under Settings → Billing. A trial is charged on its last day unless an administrator cancels it first; if that charge is declined it is tried again on each of the next two days with access kept, and after that the trial ends as any trial does, with the records kept. ⭐ WHAT A PAYMENT MAY CHANGE, AND HOW. A payment can decide which modules an organization has — granting what was ordered, or, when a subscription cannot be paid after Stripe's retries or it ends, making every module READ-ONLY. It does this through the same function, the same append-only module record and the same audit entry as a change made by an Indelio administrator, with the Stripe reference recorded as the decider (“Stripe (…)”) and no person named as the actor. ⛔ Read-only means what it has always meant here: no new records and no changes, while everything already recorded stays readable and exportable and the audit trail is untouched (21 CFR 11.10(b), (c)). Nothing is hidden or deleted. A payment decision is accepted only from a notification whose Stripe signature verifies against this deployment's own endpoint secret, or from a charge Indelio itself made and saw succeed; a notification delivered twice changes nothing the second time. Card details are entered on Stripe's page and never reach Indelio.
Why this classification: Classified “review”, for three reasons. (1) A new way exists for an organization's modules to change — a payment — alongside the platform console. It writes through the identical path, so the entitlement control itself (what off means, what it refuses, what it never touches) is unchanged, but your quality function should know that a missed payment, not only a person, can now make modules read-only, and may want that in your supplier file. (2) ⚠️ THE MIGRATION CHANGES AN INSTALLATION QUALIFICATION STEP YOU EXECUTE: IQ-06 now lists 105 migration files in `supabase/run-order.json` where it listed 104. A fresh install or a re-execution will differ from a record made against 104, and the new file must be applied. (3) The daily trial sweep now charges a card-on-file trial before it considers expiry, and does not suspend one whose first charge is still being retried. Trials set up without a card behave exactly as before. It is not “requalify”: no signature, signature meaning, content hash, lifecycle rule or role requirement changed, and no module refuses anything it did not refuse yesterday. ⚠️ The Terms of Service and Privacy Policy change in this release (orders, the trial's automatic charge, cancelling and refunds, and Stripe as a new sub-processor), so every user is asked to accept them again at their next sign-in — that is the acceptance gate working, not a fault. ⚠️ THE SUB-PROCESSOR LIST IS ALSO CORRECTED, and one correction matters for your supplier file: it used to say that text reaches the AI only when a person uses an AI feature. That was not true — complaint emails sent to your organization's intake address are read by the AI automatically, so they can be proposed as complaints for a person to confirm. The list now says so, names every AI feature, no longer names a marked-up comparison feature that does not exist, and states where Vercel runs the application (its iad1 region, Washington, D.C.), verified against the live deployment.
billing.test.tsentitlements.test.tslegal-acceptance.test.tssubprocessors.test.tsedit-trail-coverage.test.tsNo database migration in this release. The read-only API answers exactly as before. Its reference is now also published on Indelio's public website, from the same source as the reference inside Indelio, and it now lists the error code that comes with each refusal.
THE READ-ONLY API REFERENCE IS PUBLIC. The reference for version 1 of the read-only API — what it serves, its endpoints, every field of every response, every status and error code, and how to look after a key — is now published at indelio.io/resources/api as well as on Settings → API access, so whoever builds a calling system can read it without an Indelio account. Both pages are rendered from one source: the same lists the API builds its responses from. So the two cannot disagree, and neither can describe a field the API does not serve. The reference now also names the error code sent with each refusal (for example invalid_api_key), which the API has always sent. The public page is linked from the website's footer and its Resources page, and the API access Help topic points to it. Keys are still issued only inside Indelio, by an administrator of your organization.
Why this classification: Classified “none”: the API answers exactly as it did — the same two endpoints, the same fields, the same statuses and error codes, the same key rules and the same rate limit. No record, signature, lifecycle rule, access restriction or content hash changed. What changed is where the API's documentation is published and what it lists. ⭐ The boundary between what the website shows without signing in and what needs a sign-in did not move: the page sits in the Resources section, which was already public. If your implementation plan quotes the API reference, three lines read differently: each refusal now names its error code, the scope of version 1 is stated at the top, and “this organization” now reads “the key's organization”.
api-reference.test.tsapi-risk.test.tsNo database migration in this release. Our website and in-app help overstated what Indelio does in several places; each statement now says what is true. If you copied any of them into your supplier file, a procedure or a risk assessment, the first change below lists them.
⚠️ CORRECTED: STATEMENTS ON OUR WEBSITE AND IN HELP THAT SAID MORE THAN INDELIO DOES. Nothing in the software changed; what we said about it did. (1) Complaints: Indelio records the MDR reportability decision and its rationale, and it does not calculate, track or remind you of the due date of an MDR report — the 30-day and 5-day clocks run under your own procedure. (2) Patient and complainant information on a complaint: anyone in your organization who can open complaints can see it, it is not encrypted separately from the rest of the complaint, and opening it is not recorded in the audit trail. Help had said otherwise. Record only what an investigation needs, and never patient health information. (3) Segregation of duties: the author of a document may review it and may not approve it, and in most modules whoever signs one step of a record cannot sign the next; in those modules the person who created a record may sign one of its steps. The Security & Compliance page, the dashboard and its welcome panel had described a stricter, universal rule. (4) The database controls: our own application cannot update or delete a signature or an audit-trail entry, and the audit trail's hash chain is re-checked every night. We had described those controls as holding against anyone with access to the database; we operate the database, so that was more than we can promise. (5) Documents: you upload your documents into your own tenant and never send us files — but they are stored in the database and file storage we operate, which we had described differently. (6) Installation Qualification against your instance is executed by us, as its own hourly line; an independent consultant can help with your URS or PQ. One FAQ answer and the Security page had implied that someone independent of us carries it out. (7) The AI features draft and revise procedures and list the regulations a procedure cites for a person to verify; nothing produces a marked-up comparison of a procedure against a regulation, though our pages had said so.
Why this classification: Classified “review” although no controlled function changed, for the reason the 2026.09.18.46 correction gave: these are statements you may have relied on. Check whether any of them reached your supplier assessment of Indelio, a procedure, a risk control or a CUEC — especially (1) if your procedure relies on Indelio for the due date of an MDR report, (2) if your privacy or PHI handling counts on views of patient information being logged, and (3) if your procedure counts on Indelio to refuse a record's creator at every step. Where one did, the control belongs in your procedure, not in Indelio. Nothing you have signed, approved or recorded is affected.
copy-claims.test.tsThe welcome panel on the dashboard now tells an evaluation or demonstration workspace that it is one, and no longer tells a customer's workspace that it is evaluating the platform. The website's footer now calls Indelio a production quality system and states its qualification status in full: Installation Qualification executed and signed by Indelio, covering the automated checks only; Operational Qualification evidence compiled and not yet signed; Performance Qualification not executed — and qualifying Indelio for your intended use stays with you. The Founding Partner page says the same before inviting you to make it your system of record.
Why this classification: Classified “none”: presentation only. Which panel text a workspace sees follows its declared tenant type, which already decides other things and is unchanged; no lifecycle rule, access restriction, signature or record changed.
copy-claims.test.tsPricing and marketing copy corrected. The Growth plan now lists what it adds over Starter — up to 30 full users instead of 10, and 20 included onboarding hours instead of 10 — in place of two items that were not defined; Scale lists unlimited users and 35 onboarding hours, and is no longer described as covering several sites, which Indelio has no way to represent. The badge marking one plan as the usual choice is gone, as is a heading implying customers had moved to us: there are none yet. Set-up is described as taking days, not being ready for regulated use in days. The module count on the Founding Partner page is now read from the product, and the audience no longer names contract manufacturers, whose batch-record, OOS and APQR work Indelio does not support.
Why this classification: Classified “none”: commercial and descriptive text only. Prices, plans, entitlements and every function of the product are unchanged.
copy-claims.test.tsNo database migration in this release. Nothing about an existing organization changes. When Indelio creates a new organization, the modules it starts with are now chosen and recorded at that moment, instead of every module being on because nothing had been written down.
A NEW ORGANIZATION'S MODULES ARE RECORDED WHEN IT IS CREATED. Indelio's own console, where our administrators create a customer's organization, now asks which modules it has: the full suite, or one or more named sets. Every module gets an explicit on or off in the organization's module record, with the reason and the Indelio administrator who decided it, and the decision is written to that organization's audit trail — the same record, through the same function, as a change made later. ⭐ The sets were also brought into line with how the modules depend on each other: the four modules that exist to answer a complaint or a nonconformance (trending, health-hazard and regulatory assessments, correction-and-removal determinations, corrections) belong with Quality Events; Management Review and Inspection Readiness come with every set, because both summarize only what the organization already records; Projects and Quality Policy & Objectives come with the full suite only. ⛔ A module that is off is READ-ONLY, never hidden: records already in it stay readable and exportable, and the audit trail is untouched (21 CFR 11.10(b), (c)).
Why this classification: Classified “none”: nothing changed for an organization that already exists. No module was switched on or off for anyone, the refusal a switched-off module gives is unchanged, and no signature, lifecycle rule, access restriction or content hash changed. What changed is the vendor console that creates an organization, which is Indelio's and not part of your quality system. ⭐ Two safeguards worth knowing about, because they are what make this safe to use on a real customer: the choice applies only to an organization that the same action has just created — re-entering an existing organization's name adopts it and leaves its modules alone — and if the module record cannot be written, the operator is told that every module is still on, rather than the failure passing silently. If you are an existing customer, the modules you have are exactly those you had yesterday.
entitlements.test.tstenant-type.test.tsNo database migration in this release. The audit trail entry for an emailed complaint no longer records the email's subject line.
THE AUDIT TRAIL NO LONGER RECORDS AN EMAILED COMPLAINT'S SUBJECT LINE. Each complaint that arrives by email writes an audit trail entry saying it was proposed from an inbound email. That entry used to record the email's subject line word for word. Because the audit trail cannot be altered or deleted by anyone — that is what makes it trustworthy — a subject line such as “Complaint re Mrs Smith, date of birth …” became permanent the moment the email arrived. The entry now records who sent the email, whether the AI flagged it as possibly MDR-reportable, and which AI model and algorithm version produced the proposal; it no longer records the subject. ⚠️ With this, the verbatim subject line is no longer kept anywhere in Indelio: the complaint's title stopped using it in 2026.09.18.51, and the retained original email holds the message body, not its subject. The AI still reads the subject when it writes the complaint's title and description, so its useful content survives as a summary.
Why this classification: Classified “review”, and the reasoning for not classifying it “requalify” is set out because the audit trail is the 21 CFR 11.10(e) control. ⭐ The audit trail's integrity is UNCHANGED: every emailed complaint still writes its entry, entries are still chained to one another and still cannot be altered or deleted by anyone, and each is still attributable — to the system actor that performed the intake and to the address that sent the email. What changed is that one descriptive field is no longer captured in one type of entry. That narrows what the record contains without changing how records are made, protected or attributed, which is why it is “review” rather than “requalify”. The reason is release 2026.09.18.46, under which the Service must not hold patient health information; the audit trail was the last and most permanent place an inbound email's text was written. ⚠️ IF YOUR VALIDATION CHECKS THE CONTENT OF EMAIL-INTAKE AUDIT ENTRIES, an expected result that includes the subject line no longer holds. ⚠️ Existing audit entries are unchanged and cannot be changed — entries written before this release keep the subject they recorded. That is the audit trail working as intended, not an omission. No signature, lifecycle rule, access restriction or retention rule changed.
complaints.test.tsNo database migration in this release. When the AI cannot produce a title for an emailed complaint, the complaint no longer takes the email's subject line as its title.
AN EMAILED COMPLAINT NO LONGER TAKES THE EMAIL'S SUBJECT LINE AS ITS TITLE. When Indelio's AI could not produce a one-line summary of an emailed complaint, the complaint's title was taken from the email's subject line. The title is part of the complaint's signed record, so a subject such as “Complaint re Mrs Smith, date of birth …” became part of the record once signed. It now reads “Complaint received by email — summary not extracted”, and the reviewer writes the real title. When the AI does produce a summary, nothing changes. Complaints proposed from this release are stamped with intake algorithm intake-extract-4.
Why this classification: Classified “review”: the behaviour of a controlled function changed, visibly — an emailed complaint the AI could not summarise now arrives with a fixed title where it previously carried the email subject. The reason is release 2026.09.18.46, under which the Service must not hold patient health information; this was the last route by which inbound email text entered the signed complaint record with no model and no person in between (the raw email itself was closed in 2026.09.18.50). ⚠️ The email subject is still recorded in the audit trail entry for each emailed complaint — this release does not change what the audit trail records. ⚠️ IF YOUR PERFORMANCE QUALIFICATION EXERCISED EMAIL INTAKE WITH A MESSAGE THE AI COULD NOT SUMMARISE, an expected title matching the email subject no longer holds. Complaints already created are not changed; records are append-only. No signature, lifecycle rule, access restriction or retention rule changed.
ai-intake.test.tsNo database migration in this release. When the AI cannot read an emailed complaint, the complaint description no longer receives a copy of the raw email — it points to the original, which is kept separately.
THE RAW EMAIL NO LONGER GOES INTO THE COMPLAINT DESCRIPTION. When Indelio's AI could not read an emailed complaint — or read it but produced no description — it filled the description with the first 4,000 characters of the email exactly as received. The description is part of the complaint's signed record, so anything in that email, including a patient's name or details, became part of the record once the complaint was signed. It now says instead that the description could not be extracted and asks the reviewer to read the original email. ⭐ Nothing is lost: the full original email was already kept separately on every emailed complaint and is shown on the complaint page; the description was only ever a duplicate of it. Complaints proposed from this release are stamped with intake algorithm intake-extract-3.
Why this classification: Classified “review”: the behaviour of a controlled function changed, visibly — an emailed complaint the AI could not read now arrives with a short pointer in its description where it previously arrived with the email text. The reason is release 2026.09.18.46, under which the Service must not hold patient health information: this was a path by which an inbound email's contents entered the signed record with no model and no person in between, so the instruction added in 2026.09.18.49 not to record identity could not reach it. ⚠️ IF YOUR PERFORMANCE QUALIFICATION EXERCISED EMAIL INTAKE WITH AN UNREADABLE MESSAGE, an expected result showing the email text in the description no longer holds. ⚠️ Complaints already created are not changed — records are append-only; where a description already holds email text that should not be there, it is corrected by a recorded supersession. The original email is still retained on each complaint and is not part of the signed record. No signature, lifecycle rule, access restriction or retention rule changed.
ai-intake.test.tsNo database migration in this release. The AI that reads an emailed complaint no longer records a patient's details or the name of the person who was using the device — a person adds those, if the investigation needs them.
AI EMAIL INTAKE NO LONGER EXTRACTS PEOPLE. When a complaint arrives by email, Indelio's AI proposes a complaint record for a person to review. It used to fill two fields about people from the email: “Patient information / outcome” and the name of the person using the device. It no longer fills either. Both fields stay on the complaint form, and the person reviewing the proposal adds anything the investigation genuinely needs. ⭐ What the AI still records: whether a patient was involved (Yes/No), whether the event may be reportable (possible MDR), the complainant's details, and the product, lot and event details. ⚠️ The AI is also now instructed not to put a patient's identity or a device user's name into the free-text description or its reviewer notes. Complaints are stamped with the intake algorithm that produced them; proposals from this release carry intake-extract-2.
Why this classification: Classified “review”: the behaviour of a controlled function changed, and it is visible in the records it produces — an emailed complaint now arrives with those two fields blank where it may previously have arrived filled. The reason is release 2026.09.18.46, which made the Privacy Policy say plainly that no data processing agreement or business associate agreement is offered and that the Service must not be used for patient health information. The AI intake was the only route by which such data could reach a complaint with no person in between, and a complaint's fields are part of its signed, append-only record. ⚠️ IF YOUR PERFORMANCE QUALIFICATION EXERCISED EMAIL INTAKE, an expected result that includes those two fields being filled no longer holds. ⚠️ Complaints already created are not changed — records are append-only, so a value already written stays on its record; if one should not be there, it is corrected by a recorded supersession, not removed. No signature, lifecycle rule, access restriction or retention rule changed.
ai-intake.test.tsNo database migration in this release. When someone is locked out after repeated failed sign-ins, or an electronic signature is refused because the password did not verify, your administrators are now told and the event is recorded in your audit trail. Until now both were stopped and nobody was informed.
⚠️ ATTEMPTED UNAUTHORIZED USE IS NOW REPORTED TO YOU, NOT ONLY BLOCKED. Two things were already prevented and detected: five failed sign-ins within fifteen minutes lock that account for fifteen minutes, and an electronic signature whose password does not verify is refused. Neither was REPORTED. A lockout notified nobody, and its counter is deleted the next time that person signs in successfully, so there was no history to review afterwards; a refused signature was recorded in the signing-authentication evidence table, which is in your export, but appeared in neither your audit trail nor anyone's inbox. From this release: the failed sign-in that engages a lock writes an ACCOUNT LOCKED entry to your audit trail and emails every active administrator in your organization; every refused signature writes a SIGNATURE REFUSED entry, attributed to the person whose session made the attempt, and the third refusal within fifteen minutes emails your administrators. ⚠️ THE SIGNING ALERT IS THE ONE TO READ. A refused sign-in is someone trying to become a user; a refused signature is someone trying to approve, release or close a quality record as somebody else, from a session that is already signed in. ⚠️ THREE LIMITS, STATED SO THEY ARE NOT FOUND LATER. The signature alert is sent on the third refusal in fifteen minutes, not the first — one mistyped password is not an attempted misuse, and an email for every typo would train your administrators to ignore the one that matters; every refusal is in the audit trail from the first. A lockout of an email address that has no Indelio account is still applied, but it is reported to no organization, because there is none to report it to. And an email counts as sent when our mail provider accepts it, which is not proof of delivery — the audit entry records how many administrators were emailed, including none.
Why this classification: Classified “review”: the behaviour of a controlled function changed, additively. Nothing about what is refused, when it is refused, or what a refusal says to the person changed — a lockout still locks after the same five attempts for the same fifteen minutes, and a signature still needs the same two components. What changed is that both events now leave a permanent entry in your audit trail and reach your administrators. ⚠️ THERE IS AN ACTION HERE IF YOU DOCUMENTED THE OLD GAP. Our Part 11 package stated until today that this clause's reporting half was open and that a customer relying on 21 CFR 11.300(d) had to cover it by other means. If your validation file, your CSA plan or a complementary-control record names a compensating control for that — a periodic manual review, a procedural check — revisit it: it may now be redundant, or it may still be your preferred control, but the basis for it has changed. ⚠️ Administrators will begin receiving security emails they did not receive before. That is the control working, not a fault, and it is worth telling them before the first one arrives.
unauthorized-use.test.tslockout.test.tssigning-auth-age.test.tsNo database migration in this release. A password manager could fill the free-text box beside the password on a signing form — the box that holds your reason for a decision. Those boxes now ask managers not to fill them.
⚠️ A PASSWORD MANAGER COULD TYPE INTO THE REASON BOX ON A SIGNING FORM. Many actions that need your password — rejecting a request, reopening a record, cancelling, voiding a calibration event, disposition decisions — put a free-text box immediately above it. A browser or password manager reads “free-text box, password box, button” as a sign-in form, and fills the first box with a username or email address. That is not hypothetical: on 2026-09-07 it happened to us, on the first Installation Qualification signature we ever applied, and what it filled was the field recording the MEANING of that signature. Because quality records are append-only, a value that lands this way cannot be edited out — it can only be superseded by a later, correct entry. Forty such boxes across twenty-five screens — including the post-market surveillance plan, which has no dashboard card of its own and so cannot be listed above — now carry the standard “do not autofill” markup, and a new test holds every form that asks for your password to that rule, so a new screen cannot arrive without it.
Why this classification: Classified “none”: no controlled function changed. Signatures are applied, verified and bound exactly as before; no lifecycle rule, access restriction, retention rule or stored record was altered, and nothing you have approved is affected. What changed is that a field can no longer be filled on your behalf as easily. ⚠️⚠️ THERE IS AN ACTION HERE AND IT IS WHY THIS ENTRY EXISTS. If a reason, justification or summary in one of your records reads like an email address, a user name or something nobody would have written there, it may have been autofilled rather than typed. Those entries are append-only and cannot be corrected in place — record a superseding entry stating the correct reason, and keep both, which is the audit trail working rather than a blemish. ⚠️ BE PRECISE ABOUT WHAT THIS IS. It is suppression, not prevention. The markup is a hint plus the published opt-out attributes of the major password managers; each is honoured by that vendor’s choice, and none of it switches a password manager off. It reduces a known way for a wrong value to reach a record; it does not make it impossible, and it should not be written into your validation file as though it did. ⭐ Where a field carries a fixed meaning rather than free text, we constrain it to a list instead — that makes the wrong value unrepresentable, and it is what we did for the Installation Qualification field that actually failed.
autofill-neighbours.test.tspassword-clearing.test.tsiq-signature-meaning.test.tsNo database migration in this release. Our Privacy Policy offered to arrange a data processing agreement or a business associate agreement. We have neither, so the policy now says so plainly and states that the Service must not be used for patient health information. The policy's effective date moves, so everyone will be asked to accept it again at next sign-in.
⚠️⚠️ WE OFFERED AN AGREEMENT WE DO NOT HAVE, AND THE REPLACEMENT STATES A LIMIT ON HOW THE SERVICE MAY BE USED. The Privacy Policy's "International and regulated data" section told you not to enter data you are not authorized to process — for example patient health information — unless a corresponding agreement is in place, and then said: "Contact us to arrange one." That offered a data processing agreement and a business associate agreement on request. Neither exists: there is no DPA template and no legal review of one. ⭐ WE CONSIDERED SIMPLY REMOVING THE INVITATION AND DECIDED THAT WAS NOT ENOUGH, because it stops the sentence being untrue without making it true — the surrounding wording still implies such an agreement can be put in place, so a reader would keep inferring it is obtainable. The section now says instead: we do not currently offer a data processing agreement or a business associate agreement, so the Service must not be used for patient health information, or for any other data whose processing requires one; and if your use would need such an agreement, say so before you subscribe and we will tell you plainly whether we can meet it. ⚠️ THAT SENTENCE IS A LIMIT ON PERMITTED USE, NOT ONLY A CORRECTION OF WORDING, and it is published deliberately as one. Because the wording of a document you accepted has changed, the effective date moves to September 20, 2026 and every user will be asked to accept the current text at next sign-in — the intended consequence of editing a document people have agreed to, not a defect.
Why this classification: Classified “none”: no controlled function, record, signature, access rule or retention behaviour changed. Nothing the software does is different — what changed is what we say we will provide, and what we say the Service may be used for. ⚠️⚠️ THERE ARE TWO ACTIONS HERE AND THEY ARE THE REASON THIS ENTRY EXISTS. FIRST: if your supplier assessment, privacy assessment or vendor file records that a data processing agreement or business associate agreement is available from us on request — a reasonable thing to have written down, because our Privacy Policy said so — that entry is wrong today and should be corrected to: neither is currently offered. SECOND, and more urgent: ⛔ IF YOU ARE ALREADY PUTTING PATIENT HEALTH INFORMATION INTO INDELIO, STOP AND ASSESS IT. The policy now states that the Service must not be used for such data, and that is not a new restriction we have imposed on you — it is us stating accurately a limit that already existed, because no business associate agreement was ever in place to make that use lawful. ⚠️ THIS IS MOST LIKELY TO REACH YOU THROUGH COMPLAINT RECORDS. The complaint form asks for a de-identified patient reference rather than a name or medical record number — but that instruction is placeholder text in the field, it disappears as soon as anyone types, and nothing in the software enforces it. So a complaint record CAN contain patient health information if someone entered it, and only a person reading those records can tell you whether one does. ⭐ If your intended use genuinely needs such an agreement, tell us before you subscribe or renew: it is a legitimate thing to ask for, we would rather hold a real request than publish a promise, and we will tell you plainly whether we can meet it. ⚠️ We would rather you heard this limit from us than discovered it when you asked for the document.
legal-acceptance.test.tscopy-control-claims.test.tsNo database migration in this release. Global search could not find eight kinds of record that have their own record numbers — including management reviews. It can now, and the page no longer claims to search every module.
⚠️ SEARCH COULD NOT FIND RECORDS THAT HAVE THEIR OWN NUMBERS. Global search covered thirteen kinds of record. Typing a management review number such as MR-001 returned "no matches" — as did an assessment number, an initial impact determination, and any of the five Human Factors records — while the search page said it searched "across every module". Eight are now searchable by number or title: Management Review, Assessments (HHE / Regulatory / PPMRM), Initial Impact Determination, Use Specification, Use-Related Risk Analysis, Critical Tasks Register, Summative Usability Test, and the HFE/UE Report. The page's wording was corrected too, and deliberately NOT replaced with a new completeness claim — because two kinds of record still are not searchable: quality objectives and trend dispositions have their own numbers but no individual page for a result to open, so a search hit would have nowhere to go. Both are recorded, with that reason, in the test that now holds this scope. Nothing about how a record is created, signed, stored or permissioned changed.
Why this classification: Classified “review”: the behaviour of a function you use changed — the same query now returns records it previously did not. No signature, lifecycle rule, access restriction, retention rule or stored record was altered, and nothing you have approved is affected. ⭐ TENANT ISOLATION WAS VERIFIED RATHER THAN ASSUMED before these were added, because a search registry is exactly where it could be got wrong: search runs through the row-level-security client, so results can only ever be your own organization's, and each of the eight tables was checked to have row-level security enabled with policies scoped to the signed-in organization. ⚠️ IF YOUR PERFORMANCE QUALIFICATION INCLUDES A SEARCH STEP, re-run it: a search that previously returned nothing for one of these record types now returns the record, so a PQ script that recorded "no results" as the expected outcome will no longer match. ⚠️ And if you had concluded from the previous wording that every record was searchable, correct that: it was not true before this release and it is not being claimed now — two record types remain unsearchable for the stated reason.
registry-columns.test.tssearch.test.tsNo database migration in this release. A correction: our About page said validation is performed by an independent consultant. It is reviewed by one — the qualification runs on record were executed and signed by us.
⚠️ CORRECTION TO A CLAIM ABOUT OUR OWN VALIDATION. The About page stated, twice and once in bold, that "validation is performed by an independent consultant who did not build the system". That overstated what happens. What is true: an independent validation consultant REVIEWS the validation package and reviewed the Installation Qualification checklist. The qualification runs themselves were executed and signed by Voxelio AI — the party that built the system — not by an independent executor. The Operational Qualification evidence is compiled and unsigned. Performance Qualification is yours by definition and has not been executed by us at all. Both pages now say "reviewed by", state those three limits beside the claim rather than a click away, and say plainly that Indelio is not "validated". The Security & Compliance page's validation section — which the About page links to as "the full validation posture" — now states the same three limits; previously it described the package you receive and never stated our own position. Nothing about the product, its controls or its records changed.
Why this classification: Classified “none”: no controlled function, record, signature or access rule changed — this is a correction to what we SAY about our own qualification status. ⚠️⚠️ THERE IS AN ACTION HERE AND IT IS THE REASON THIS ENTRY EXISTS. If your supplier assessment, vendor qualification record or validation plan records that Indelio's validation was PERFORMED by an independent party — a reasonable thing to have written down, because our About page said so — that entry is wrong and should be corrected to: the validation package and the IQ checklist are reviewed independently; the IQ runs of record were executed and signed by the vendor; OQ evidence is compiled and unsigned; PQ is not executed by the vendor. ⭐ If independent execution (as opposed to independent review) matters to your risk assessment, that is a legitimate finding to raise with us rather than something you should have to discover. ⚠️ To be accurate about what you can check without us: the IQ console that holds the run records is restricted to Voxelio AI administrators, so you cannot inspect it yourself. Ask us for the run records — verdict, commit, manifest version and the named signer of each signed run — and we will provide them; that is a vendor assertion supported by documents, not something you can independently confirm from inside the product, and you should weigh it as such. ⚠️ We would rather you heard the limit from us than found it in the record.
copy-control-claims.test.tsNo database migration in this release. The open gap recorded against 21 CFR 11.300(d) was described too narrowly: it covers rejected electronic signatures as well as failed sign-ins, and the signature case is the more serious of the two.
CORRECTION, WIDENING AN OPEN GAP WE RECORDED EARLIER TODAY. Release 2026.09.18.39 recorded §11.300(d) as partly met, on the grounds that a failed-login lockout is not reported to anyone. That was true and incomplete. A rejected ELECTRONIC SIGNATURE is not reported either — and because a signature whose password does not verify is refused before anything is written, it leaves no trace at all: no audit entry, no counter, no notification. The two are not equally serious. A refused sign-in is an attempt to become a user; a refused signature is an attempt to approve, release or close a quality record as somebody else, and it is the one an auditor asks about. The clause row and §6 gap 8 of the Part 11 package now say so, and the remediation covers both paths. ⭐ This was observed rather than reasoned: a deliberate wrong-password signature was attempted in our demonstration tenant, and afterwards the record was unchanged, carried no signature, and the newest audit entry in the whole tenant still predated the attempt.
Why this classification: Classified “none”: no controlled function changed, and nothing about the refusal itself changed — a signature whose password does not verify was refused before this release and is refused after it. What changed is the accuracy of what we publish about the gap. ⚠️ THERE IS AN ACTION HERE IF YOU READ RELEASE 2026.09.18.39 AND SCOPED §11.300(d) TO SIGN-IN ONLY. The reporting obligation you need to cover by other means extends to rejected electronic signatures, which in Indelio are entirely unrecorded. If your procedures rely on being able to review attempted unauthorised approvals after the fact, note that there is nothing in the system to review, and that this remains the case until gap 8 is closed. ⚠️ The prevention side is unaffected and was never in doubt: identity plus password are re-verified at the moment of every signature, and a failure refuses the act.
validation-library-sync.test.tsvalidation-doc.test.tssigning-form-password.test.tsNo database migration in this release. Spreadsheet import can now fill the ISO 14971 §7.1 control option and the reason the stronger options were not practicable — the last two parts of a risk item it could not carry across — and it applies the same §7.1 rule the form does, on the review screen.
THE §7.1 CONTROL OPTION ANALYSIS CAN NOW BE IMPORTED, AND AN INCOMPLETE ONE IS SHOWN TO YOU BEFORE YOU IMPORT. Two more columns can be matched when you bring a risk file in: “Control option (§7.1)” — inherently safe design, a protective measure, or information for safety — and the reason the stronger options were not practicable. ⭐ The rule travels with them. Indelio already refuses to save a risk item whose control option is below inherently safe design without that reason; the import now applies the SAME rule while you are still reviewing the file, so such a row is listed as a problem on the review screen and names which stronger option was skipped. Previously those columns could not be matched at all, so the analysis was simply dropped and the imported items were silently incomplete. An option Indelio does not recognise is refused by name rather than stored. ⭐ WITH THIS, EVERY PART OF A RISK ITEM THAT A REVIEWER'S SIGNATURE COVERS CAN BE IMPORTED — the hazard chain landed earlier today in 2026.09.18.40 and this is the last of it. ⚠️ An item with no control option is not an error, as before: every risk item written before Indelio had the field has none, and a rule that did not exist when a record was written does not retroactively invalidate it.
Why this classification: Classified “review”: the behaviour of a controlled function changed, and in one respect it is now STRICTER. A spreadsheet whose rows name a protective measure or information for safety without saying why the stronger options were not practicable will now be listed as a problem on the review screen, and those rows cannot be committed until the file is corrected, the column is matched, or the row is skipped with a reason. That is the same rule the form has always applied — the import was the one route that bypassed it — but if your performance qualification exercised a risk import with such a file, the expected result has changed and the run needs re-reading. ⚠️ AND IF YOU IMPORTED A RISK FILE BEFORE TODAY whose spreadsheet carried these columns, the values were dropped at the time and are NOT back-filled: those items have no recorded option analysis, and Indelio will not have objected, because an item with no option recorded is legitimately not an error. Add the analysis to each item, or import the file again into a new assessment. No signature, lifecycle rule, access restriction or retention rule changed, and nothing already approved is invalidated.
import.test.tsrisk-pathway.test.tsNo database migration in this release. An exported PDF that could not print one of your characters was deleting it silently; it now says so on the document, and names what was dropped.
⚠️ AN EXPORTED PDF NO LONGER DROPS CHARACTERS IN SILENCE. Indelio's PDFs use a standard PDF font, which can represent Western European characters and no others. Any character outside that set — Cyrillic, Greek, Chinese, Japanese, Korean, Hebrew, Arabic, and the accented letters used in Polish, Czech, Turkish and Romanian — was removed from the PDF with nothing said. A signer named Владимир Петров appeared as a blank on the electronic-signature page. "Małgorzata Wiśniewska" appeared as "Magorzata Winiewska" — a different name that still reads like a name, which is worse, because nothing told the reader a character had gone. Any export that cannot print a character now carries a block headed CHARACTERS OMITTED FROM THIS PRINTED COPY, listing each dropped character by its Unicode code point (the font cannot print the character itself, in the warning either) and stating that the omission affects the printed copy only and that the record held in Indelio is complete and unchanged. This covers the controlled-document PDF — including its electronic-signature page — and the CAPA, complaint, risk, design, design-weave, human-factors, management-review and installation-qualification reports. ⚠️ Two smaller corrections with it: the controlled-document PDF used a shorter character conversion than every other report, so "§4.2.4" printed there as "4.2.4" while printing as "sec.4.2.4" elsewhere — it now matches, and "×", "→", "≤" and "≥" convert there too. Nothing about how a record is stored, signed or displayed on screen changed.
Why this classification: Classified “review”: the content of a controlled output changed — an exported PDF may now carry a block that was not there before. No signature, lifecycle rule, access restriction or stored record was altered, nothing already approved is invalidated, and no re-approval is required. ⚠️⚠️ THERE IS AN ACTION HERE, AND IT CONCERNS COPIES YOU HAVE ALREADY FILED. If you have exported a PDF and filed it as evidence — in a design history file, a technical file, an audit response, or a training record — and the record contained any character outside Western European (a signer's name, a supplier, a lot number, a device name, a quoted document title), that filed copy is missing those characters and says nothing about it. Export it again and compare before relying on the filed copy. The new block tells you whether a given export is affected; a PDF without it dropped nothing. ⚠️ If your performance qualification includes a check on an exported PDF, re-run that check: an affected document has a block of text it did not have before. ⛔ THE UNDERLYING LIMIT IS NOT REMOVED AND SHOULD BE RECORDED IN YOUR OWN ASSESSMENT: Indelio's PDF exports can represent Western European characters only. Records are stored in full and in any language — the database and the audit trail keep exactly what was entered, and the record export carries the complete text — but the PDF rendering of them cannot. If your quality records are written in a language this does not cover, the PDF is not a complete copy of them and the electronic record export is the one to rely on. Embedding a font with wider coverage is being considered and is not in this release.
pdf-report.test.tsNo database migration in this release. Spreadsheet import can now fill the two hazard-chain fields, which release 2026.09.18.5 said it could not — and the Hazard field is renamed, so a column headed "Hazardous situation" now goes where it says.
SPREADSHEET IMPORT NOW FILLS SEQUENCE OF EVENTS AND HAZARDOUS SITUATION, AND THE HAZARD FIELD IS RENAMED. ⚠️ THIS SUPERSEDES A STATEMENT IN RELEASE 2026.09.18.5, which said “Spreadsheet import does not fill the two new fields yet — a column headed "hazardous situation" still imports into Hazard, as before.” That is no longer true, and if you read it then and planned around it, read this. Both hazard-chain fields can now be matched to columns when you import a risk file, in the order ISO 14971 works through a risk: Hazard, Sequence of events, Hazardous situation, Harm. An FMEA export matches without help — failure mode, cause and effect land in Hazard, Sequence of events and Harm respectively. ⚠️ TWO CHANGES TO HOW COLUMNS ARE MATCHED, both deliberate. The first field is now labelled “Hazard” rather than “Hazard / hazardous situation”, because it no longer stands for both. And a column headed “Hazardous situation” is now matched to the Hazardous situation field instead of to Hazard. If your spreadsheet has BOTH columns they are now matched correctly whichever order they appear in — previously whichever came first won, and a Hazardous situation column could take the Hazard field and leave the real Hazard column unmatched. If your spreadsheet has ONLY a “Hazardous situation” column, Indelio no longer fills the required Hazard field for you: you are asked to choose a column, because the two are now different fields holding different things. ⭐ Nothing about a risk item already in Indelio changed, and no existing record moved — this is the import screen only.
Why this classification: Classified “review”: the behaviour of a controlled function changed, in a way you can see in the records it creates. Importing the SAME spreadsheet now produces risk items that differ from the ones it produced before — more complete, because the hazard chain is carried across instead of dropped, and potentially matched to different fields, because a “Hazardous situation” column now goes to the Hazardous situation field. ⚠️ IF YOUR PERFORMANCE QUALIFICATION EXERCISED A RISK IMPORT, the column-matching step and its expected result both need re-reading: a script that expects “Hazardous situation” to appear in Hazard, or that expects the first field to be labelled “Hazard / hazardous situation”, no longer matches what the screen does. ⚠️ AND IF YOU ALREADY IMPORTED A RISK FILE whose spreadsheet had sequence-of-events or hazardous-situation columns, those values were dropped at the time and are NOT back-filled by this release — the imported items are unchanged, and the missing detail has to be added to each item or the file imported again into a new assessment. No signature, lifecycle rule, access restriction or retention rule changed, and the two new fields remain optional, so nothing you have already approved is invalidated. Note also that the §7.1 control option and its rationale still cannot be imported; that is a separate, known gap.
import.test.tsrisk-interview.test.tsNo database migration in this release. The 21 CFR Part 11 validation package now states each clause in full rather than in shorthand, lists five clauses it had not listed at all, and corrects one clause it marked as fully met when half of it is not.
THE PART 11 PACKAGE NOW STATES EACH CLAUSE IN FULL, AND ONE CLAUSE IS CORRECTED FROM MET TO PARTLY MET. An independent validation consultant reviewed the requirements traceability matrix in §4 against the text of 21 CFR Part 11 and found the requirement summaries abbreviated — accurate as far as they went, but not the whole of what each clause asks. Every summary is now the full requirement. Five clauses that were not listed at all are now listed: §11.100(b) (verify an individual's identity before sanctioning their electronic signature) and §11.100(c) (certify to FDA on paper that electronic signatures are legally binding equivalents), both of which are your organization's obligations and neither of which software can discharge; §11.200(a)(3) (components required across a series of signings), which Indelio exceeds — the password is re-verified at every signature, not only the first; and §11.300(c) and §11.300(e) (deauthorizing and testing identification tokens and cards), which are addressed for the one device that applies, an authenticator app. The two duplicate §11.10(e) rows are now one complete clause. §11.10(h) (device checks) moves from “N/A — no instrument inputs” to met: the clause covers the validity of the source of data or operational instruction, which is broader than instruments, and the case that applies is AI-assisted intake — advisory only, confirmed by a person before anything is written, with AI-borne text sanitized and the model recorded.
Why this classification: Classified “none”: no controlled function changed. Not one line of application behaviour was altered — what changed is what the validation package says about behaviour that was already there. ⚠️⚠️ THERE IS AN ACTION HERE IF YOUR OWN PART 11 ASSESSMENT CITED OUR PACKAGE FOR §11.300(d). That row read ✅ and now reads 🟡. The clause requires unauthorized use to be prevented, detected AND REPORTED IMMEDIATELY to your security unit and, where appropriate, management. Indelio does the first two — a failed-login lockout after 5 failures in 15 minutes, and a 30-minute idle session timeout — and does NOT do the third: a lockout raises no alert, notifies no administrator, and the failed-attempt counter is deleted on the next successful sign-in, so there is no history for anyone to review. If you recorded §11.300(d) as fully met on the strength of our package, correct that entry and cover the reporting half by other means until we close it (§6 gap 8). ⚠️ SECOND ACTION: §11.100(b) and §11.100(c) are yours, and they are not in the Complementary User Entity Controls register — so a plan built from that register alone does not record them. Until they are added (§6 gap 9), record how you verify a signer's identity before granting them an account, and whether your organization has filed the §11.100(c) certification letter with FDA.
validation-library-sync.test.tsvalidation-doc.test.tsedit-trail-coverage.test.tscopy-claims.test.tsA database migration is required in this release: run `supabase/compliance_actor.sql`. It affects only Indelio's own internal compliance console, which is not part of your quality system — nothing in your organization's records or workflows changed. ⚠️ It does add one file to the installation qualification: IQ-06 now lists 104 migration files where it listed 103.
INDELIO'S OWN SOC 2 REGISTER NOW RECORDS WHICH ADMINISTRATOR MOVED A ROW, AND REFUSES ANYONE ELSE IN THE DATA LAYER. This is the vendor's internal compliance tracker — the register Indelio keeps about its own SOC 2 programme — and it is not part of your quality system and holds none of your data. Until this release it recorded that a control had been marked "Implemented" and never who had marked it, and the restriction to platform administrators existed only in the screen's own code rather than in the layer that writes to the table. Both are fixed: every change to a control or a recurring task now carries the administrator's identity onto the row, and the data layer re-checks that they are a platform administrator before it reads or writes anything. ⚠️ Rows changed before this release show "not recorded" rather than a name — Indelio does not back-fill a name it cannot evidence.
Why this classification: ⭐ Classified “review” for a reason that has nothing to do with the compliance console itself. The console change touches nothing of yours: those two tables are platform-global, hold no organization's data, are unreachable by any tenant, and are not quality records — no signature, lifecycle rule, access restriction or retention rule of yours is affected, and no record in your organization moved. On its own that would be “none”. ⚠️ IT IS “REVIEW” BECAUSE THE MIGRATION CHANGES AN INSTALLATION QUALIFICATION STEP YOU EXECUTE: IQ-06 now lists 104 migration files in `supabase/run-order.json` where it listed 103. If you have executed IQ against 103 files and hold that as qualification evidence, a fresh install or a re-execution will differ from the record you hold, and the new file must be applied. That is the same reason release 2026.08.24 was classified review when the run order was first declared — an IQ step a customer's executor performs is part of your qualified state even when the thing it installs is not. ⚠️ Separately, and not a reason for the classification: if you have asked us for our position on our own SOC 2 controls as part of a supplier assessment, that register can now say who asserted each control and when, and rows asserted before 2026-09-20 will honestly show no name against them.
compliance.test.tsedit-trail-coverage.test.tsNo database migration in this release. Indelio now states exactly which of your records can be brought in from an eQMS you are leaving and which cannot, and a new complementary user entity control (CUEC-09) covers the ones that stay behind.
NEW CONTROL CUEC-09, AND A PLAIN ANSWER TO "WHAT HAPPENS TO OUR RECORDS IF WE SWITCH TO INDELIO?" Two routes bring legacy data in: bulk migration for controlled documents, and spreadsheet import for five registers — a risk analysis, a supplier list, an equipment register, a complaint log and a nonconformance log. Nothing else has an importer. CAPAs, training records, internal and supplier audits, change controls, the design history file, management reviews and post-market surveillance plans stay in the system you are leaving. That was true before this release and is now written down: the FAQ answers it, the module coverage map has a migration section listing every record type and its route, and CUEC-09 in the Complementary User Entity Controls register requires a recorded decision per record type — imported, exported and retained outside Indelio, or re-created — taken before your previous subscription lapses. §7 of the CSA Implementation Plan Template now has a row for it. ⚠️ Separately, the dashboard's Spreadsheet Import card had been naming three of the five importable registers since the complaint and nonconformance importers were added; it now names all five. Nothing about how an import behaves changed.
Why this classification: Classified “none”: no controlled function changed. No import behaviour, signature, lifecycle rule or access restriction was altered — what changed is that the scope of the import capability, which was already what it is, is now stated where you can read it. ⚠️ THERE IS AN ACTION HERE IF YOU HAVE ALREADY COMPLETED A CSA IMPLEMENTATION PLAN from the template dated 2026-09-19 or earlier: it has no row for CUEC-09, so your plan does not record a decision on records left in a previous system. If you migrated into Indelio from another eQMS, read CUEC-09 and record that decision now — the exposure it describes is that closed records you are still required to retain sit in a product you have stopped paying for, and neither system will raise it. ⚠️ If your own supplier assessment or validation scope recorded that Indelio imports your CAPA or training history, correct it: it does not, and never did.
copy-control-claims.test.tscsa-template-config-surface.test.tsvalidation-library-sync.test.tswithdrawn-qsr-citations.test.tsCORRECTION: TWO-FACTOR AUTHENTICATION WAS DESCRIBED AS "OPTIONAL" WHERE IT IS MANDATORY. Two-factor authentication (TOTP) has been required on administrator accounts since 2026-08-19 and cannot be switched off on one. The Security & Compliance page and the FAQ both still called it optional. Both now say it is mandatory on administrator accounts and available to every user. The enforcement did not change — only the description of it.
Why this classification: Classified “none”: nothing in the application changed. The access control that exists is unchanged and was already stronger than the public description of it. ⚠️ IF YOUR SUPPLIER ASSESSMENT OF INDELIO RECORDED THAT TWO-FACTOR AUTHENTICATION IS OPTIONAL — taken from our Security page or FAQ — that entry understated the control and can be updated: it is mandatory for any account holding the admin role in a customer tenant, and an administrator cannot disable their own. ⚠️ One limit to record with it: enforcement applies to customer tenants only. Evaluation sandboxes and internal test tenants are deliberately exempt, and CUEC-06 states that narrower scope for a validation reader.
copy-control-claims.test.tsmfa-policy.test.tsNo database migration in this release. The Risk Management File PDF now prints five parts of a risk item it was leaving out — including the ISO 14971 §7.1 control option analysis.
THE EXPORTED RISK MANAGEMENT FILE NOW CARRIES THE WHOLE RISK ITEM. The PDF you export from a risk assessment — the document headed "Risk Management File — Record (ISO 14971)" — printed eleven of a risk item's sixteen recorded parts and silently left out five: the ISO 14971 §7.1 control option; the reason the higher-priority options were not practicable; the reference to the record that implements the control; the external reference to a record in another system; and the design domain. All five are now printed, each above or beside the control it belongs to, and the two kinds of reference are labelled apart so a record kept in your other system is never presented as an Indelio record. ⚠️ A field that was never filled in still prints nothing — an assessment written before one of these fields existed exports exactly as it did before, with no empty labels. Nothing about how you record a risk item changed, and no risk record changed.
Why this classification: Classified “review”: the content of a controlled output changed. Indelio required, hashed and bound the §7.1 option analysis into the reviewer's signature, and then omitted it from the one document that leaves the system — so the exported file did not show the whole of what was signed. ⚠️ IF YOU EXPORTED A RISK MANAGEMENT FILE BEFORE THIS RELEASE AND FILED IT AS EVIDENCE — in a design history file, a technical file, or an audit response — that copy is missing the five parts above. Export it again and compare before relying on the filed copy. If your performance qualification includes a check on the exported risk file, re-run that check: the document has more rows in it than it did. No signature, lifecycle rule, access restriction or stored record was altered, so nothing you have already approved is invalidated and no re-approval is required.
risk-report.test.tsrisk-pathway.test.tsrisk-external-ref.test.tspdf-report.test.tsNo database migration in this release. Indelio now has a written change and defect management procedure for its own development, and the validation documents say so accurately.
NEW, AND A CORRECTION WITH IT: HOW INDELIO HANDLES ITS OWN CHANGES AND DEFECTS IS NOW WRITTEN DOWN. An independent validation consultant advised that a software supplier needs a defect management process, because customers review it when they audit a supplier. Indelio now has one: how a change reaches production (branch, tests, the merge gate, the release-register decision, deployment, migrations), how a defect is recorded and assessed for customer impact, how it is verified, where every record lives, and the limits of all of it — including that there is no separate defect tracker, that one person develops and merges, and that an administrator can bypass the merge gate. It is a vendor document, available to you on request; ask us for it as part of your supplier assessment. ⚠️ The correction: the Validation Plan said a change-control procedure governed these changes while the Part 11 Validation Package said one still had to be written. The procedure now exists and both documents describe it.
Why this classification: Classified “none”: nothing in the application changed — this is a vendor procedure and the two validation documents that describe it. ⚠️ If your supplier assessment of Indelio recorded that we had no documented change or defect management process, it can be updated; ask us for the procedure. If it recorded that we had one because the Validation Plan said so, read the procedure itself: it states what is and is not controlled, and two of those limits (no second-person review of code, an overridable merge gate) may matter to your assessment.
validation-library-sync.test.tsNo database migration in this release. The Terms of Service now say what a "validated" system actually requires, and everyone is asked to accept the Terms again.
CORRECTED: THE TERMS SAID A SYSTEM IS "VALIDATED" ONCE IQ, OQ AND PQ ARE EXECUTED AND SIGNED. That is not the whole of it, as an independent validation consultant pointed out in his review of our validation documents: IQ, OQ and PQ are part of a validation package, alongside the validation plan, the user and functional requirements, the risk assessment, the traceability matrix and the validation report. The compliance disclaimer in the Terms of Service now says that. ⚠️ The effective date of the Terms and the Privacy Policy moves to 19 September 2026, so everyone in your organization is asked to accept them again at their next sign-in; the Privacy Policy's own wording is unchanged and moves only because it carries that date. The same correction was made in the Part 11 Validation Package and on the pricing page in release 2026.09.18.31.
Why this classification: Classified “review”, and not because it is a Part 11 control — the acceptance gate is not one, and should not be recorded as one. It is “review” because every user meets a gate before they can use Indelio again, and because the corrected sentence states what your own validation must cover: read it against the package you are executing. Nothing that operates under your quality system changed — no signature, record, role, lifecycle rule or content hash — and no recorded acceptance was altered: each keeps the wording and date it was given.
legal-acceptance.test.tsNo database migration in this release. A training quiz is now approved by someone who did not write it — Indelio refuses the approval from whoever started the quiz or changed its questions or pass mark.
CORRECTED: THE PERSON WHO WROTE A TRAINING QUIZ COULD ALSO APPROVE IT. A quiz decides whether a trainee's training counts as effective, but approving one checked only the approver's role and that they had no open training on the document — not who wrote it. Now Indelio refuses the "Quiz approved by" signature from whoever started the quiz, and from anyone who added, changed or removed a question, asked AI to propose questions, or changed the pass mark; someone else with the QA or approver role approves it. On the quiz page, a writer sees why instead of the Approve button. If Indelio cannot confirm who wrote the quiz, it refuses the approval rather than assuming someone else did. Quizzes approved before this release stay approved.
Why this classification: Classified “review”: a segregation-of-duties rule was added to quiz approval — the same author-cannot-approve rule documents, risk assessments and post-market surveillance plans already have. No signature meaning, content hash or existing record changed, and an approval already signed is not undone. Checked in Indelio's production data before the change: the one approved quiz, in the Demo organization, was written and approved by the same person; it stays approved. In a one- or two-person quality team, the other person now approves quizzes; if your training procedure described quiz approval, check it matches.
training-quiz.test.tshelp.test.tsNo database migration in this release. The validation documents on the Compliance page are revised following an independent validation consultant's review. Nothing in the application changed.
REVISED: THE VALIDATION DOCUMENTS, ON AN INDEPENDENT REVIEW. An independent validation consultant reviewed the five validation documents on the Compliance page. Following that review: the Part 11 Validation Package now says that a claim of "validated" needs the complete validation package executed and approved — validation plan, URS/FRS, risk assessment, IQ, OQ and PQ, traceability matrix and validation report — not IQ, OQ and PQ alone; the CSA Implementation Plan Template now says that, although you cannot change what a role is permitted to do, you decide who holds each role, and its justified position on PQ now includes verifying your role assignments (CUEC-02); two sentences that read as instruction on validation practice were removed from the template's opening section; and notes recording what earlier versions of the documents said were removed from all of them, on the reviewer's advice that they are development history rather than validation content. The user requirement UR-AT-06 now states the requirement only; the behaviour it described is unchanged and is recorded as design.
Why this classification: Classified “none”: no function of the application changed — only the text of the validation documents. ⚠️ If you adopted the CSA Implementation Plan Template, re-read its §3.1: role assignment is now named as your configuration, and the justified position you may have adopted gained a step (c), verifying that your users hold the roles your authority matrix assigns them. The removed notes recorded corrections already described in earlier entries of this register.
validation-library-sync.test.tsrtm-sync.test.tsA database migration is required in this release: run `supabase/risk_external_ref.sql`. A risk item can now record where its control is implemented in another system, in a field Indelio never turns into a link; and a link to a record that was created after the reference naming it was written is marked, so it is not taken for the record the reference meant.
NEW: AN EXTERNAL REFERENCE ON A RISK ITEM. Beside "Implemented by (ref)" — which names records in Indelio and becomes links to them — a risk item now has "External reference", for records kept in another system. It is shown as written and never linked, so a number in it can never open an unrelated Indelio record that happens to share the number. It is on the risk item form, in the risk-item interview and its demo on the website, in the spreadsheet import as its own column, and in the audit trail when it changes. Use it when your documents or changes are kept in another eQMS.
Why this classification: Classified “review”: a new field is part of the risk file a reviewer signs — when one is recorded, it is included in the assessment's content hash, like the rest of the item. An item without one hashes exactly as it did, so no existing signature binding moves. No rule, role or lifecycle changed.
risk-external-ref.test.tsrisk-interview.test.tspublic-risk-interview.test.tsA LINK TO A RECORD CREATED AFTER THE REFERENCE TO IT IS MARKED. Indelio turns the record numbers in a risk item's "Implemented by (ref)" into links. When the record a number links to was created after the reference was written, it cannot be assumed to be the record the author meant — for example, a reference written about another system's SOP-012 before Indelio had a document with that number. Such a link is now marked ⚠ on the risk assessment, in the risk thread and the connected-records thread, on the linked record's own page, and in the design ↔ risk suggestions. While the assessment can be edited, its author can confirm the link with "This is the record I meant"; the confirmation is recorded in the audit trail — who, which reference, and the date it replaced — and does not change the content hash. The migration dates every existing reference from the audit trail.
Why this classification: Classified “review”: what a page shows beside a link changed, and a new audited action lets a person confirm one. No rule, role, signature or content hash changed, and a confirmation is allowed only while the assessment's items can be edited. ⚠️ A mark is a caution, not a finding: a link without one is only not provably later.
risk-external-ref.test.tsNo database migration in this release. When Settings cannot check whether a complaint-intake token is active, it now says so, instead of saying there is none.
CORRECTED: SETTINGS COULD SAY THERE WAS NO INTAKE TOKEN WHEN IT HAD NOT BEEN ABLE TO CHECK. If the read behind the Email intake panel failed, Settings said "No intake token yet — generate one to enable email intake", even when your intake mailbox was using a token. Generating one on that advice would have replaced the token in use and stopped email intake. Now the panel says it could not check whether a token is active, asks you to reload, and does not offer to generate one until it can check. When the read works, the panel is unchanged.
Why this classification: Classified “none”: only what the Settings page shows when a read fails changed — no rule, signature, content hash, record or audit entry changed, and generating a token works exactly as before whenever the page can read the current state.
org-credentials-audit.test.tsNo database migration in this release. Only an administrator can now change the organization's own AI key, and each change is recorded; generating or replacing the complaint-intake token is now recorded too.
CORRECTED: ISSUING OR REPLACING THE COMPLAINT-INTAKE TOKEN WAS NOT RECORDED. The complaint-intake token lets your intake mailbox create complaints in Indelio. When an administrator generated one in Settings — or generated a new one, which stops the previous token working — nothing was written to the audit trail, so there was no record of who did it or when. Each is now recorded: the first token as "intake token issued", a replacement as "intake token replaced", with the administrator's name and a short fingerprint of the new token and of the one it replaced (never the token itself). If the entry cannot be written, the token is not changed: the previous token is put back, nothing is shown, and the message says whether the previous token still works. Generating a token otherwise works exactly as before.
Why this classification: Classified “review”: what the audit trail records changed — a credential change that was unrecorded now has an entry — and generating a token now refuses when that entry cannot be written. No signature, content hash, role requirement or existing record changed; only an administrator could generate a token before, and only an administrator can now. Tokens issued or replaced before this release have no entry, and cannot be given one.
org-credentials-audit.test.tsCORRECTED: ANY SIGNED-IN USER COULD CHANGE OR REMOVE THE ORGANIZATION'S OWN AI KEY. In Settings, the form for your organization's own AI (Anthropic) key was shown to every user, and saving or removing a key was accepted from anyone signed in — an author, a reviewer or a delivery contractor, not only an administrator — with nothing written to the audit trail. That key decides which AI account your document and complaint text is sent to. Now only an administrator sees the form, and Indelio refuses the change from anyone else however it is attempted. Each change is recorded — "AI key saved", "replaced" or "removed" — with the administrator's name and the last four characters of the key (never the key itself). If the entry cannot be written, the key is not changed.
Why this classification: Classified “requalify”: an access restriction changed — users without the administrator role can no longer change the organization's AI key — and what the audit trail records changed. No signature, content hash, lifecycle rule or record changed, and AI features work exactly as before on whichever key is set. ⚠️ Because nothing was recorded before this release, Indelio cannot tell you who set your organization's current AI key. If your organization uses its own key, an administrator should confirm on the Settings page that the key shown (by its last four characters) is the one you issued, and save it again if not.
org-credentials-audit.test.tsadmin-only-in-lib.test.tsA database migration is required in this release: run `supabase/training_quiz_key_history.sql`. The audit trail no longer records a training quiz's answers — a change to a question's correct answer or rationale, or a removed question, is recorded without them, and the values are kept where only the quiz page shows them.
CORRECTED: THE AUDIT TRAIL COULD HOLD A TRAINING QUIZ'S ANSWERS. Everyone in an organization can read its audit trail, including directly from the database with their own sign-in. When a question's correct answer was changed, the audit entry recorded the old and the new answer; when a question was removed, the entry recorded the whole question, answer included — so a trainee could have read an answer before sitting the quiz. Now those entries record that the correct answer or the rationale changed, or which question was removed, but not the answer or the rationale. The values before and after are still kept — 21 CFR 11.10(e) requires them — in a separate record that no one can read directly and Indelio shows only on the quiz page, under "Answer-key changes", to someone allowed to see the quiz's answers: the QA, approver or admin role, with no open training on the document. Each audit entry holds a fingerprint of those values, and the quiz page checks every one against it, showing "matches", "does not match" or "no audit entry". If the previous value cannot be recorded, the change is refused and nothing is changed. Adding a question and changing its wording or answers are recorded as before.
Why this classification: Classified “review”: what the audit trail records for three quiz actions changed — the answer and rationale moved out of the entry into a separate append-only record the entry fingerprints — and quiz editing now refuses a change whose previous value cannot be recorded. No signature meaning, content hash, role requirement or existing audit entry changed; audit entries are never rewritten. Checked in Indelio's production data before the change: no audit entry held a quiz answer (the only quiz entries were three questions added). If your procedures describe what the audit trail shows for training quizzes, note that a quiz's answer-key history is on the quiz page, not in the audit trail. A full export of your data includes the new record.
training-quiz.test.tsremoval-trail.test.tsiq-check.test.tsrls-exempt-callsites.test.tsNo database migration in this release. When the list of documents, CAPAs, nonconformances, changes, complaints, audits, training assignments, suppliers or projects cannot be read, the page now says so, instead of saying there are none; and an NCR's named supplier can no longer be removed by accident when the supplier list cannot be read.
CORRECTED: A LIST THAT COULD NOT BE READ NO LONGER SAYS THERE ARE NONE. When the database read behind the Controlled Documents, CAPA, Nonconformances, Change Control, Customer Complaints, Audits, Suppliers or Projects list — or the training matrix on the Training page — failed, the page said there were none ("No CAPAs yet", an empty Approved Supplier List); and when an audit's findings, a document's versions or a project's tasks could not be read, the row showed 0 open findings, a Draft document or no deliverables. Each page now says the list could not be read, and that this is not a statement that there are none, with no count. In the same situation the management-review metrics now report that list as unreadable instead of counting it as zero; and where a complaint, NCR or audit finding offers to link an existing CAPA, it says the CAPAs could not be read instead of silently offering none.
Why this classification: Classified “none”: only what is shown when a read fails changed. No rule, signature, content hash or record changed, and a list that reads normally is shown exactly as before. ⚠️ A metrics snapshot or management-review record taken before this release on a day one of these reads failed would have recorded zero for that list; nothing in it distinguishes that from a true zero.
list-read-fails-closed.test.tsCORRECTED: SAVING AN NCR COULD REMOVE ITS SUPPLIER WHEN THE SUPPLIER LIST COULD NOT BE READ. On a nonconformance that names a supplier, if the list of suppliers could not be read, the "Supplier implicated" field showed "— none —"; saving the NCR's details then removed the supplier from the NCR, and with it the supplier action already chosen. Now, when the suppliers cannot be read, the field is not offered: the page says the supplier cannot be shown or changed right now, and saving the other details leaves the supplier exactly as it was. The warning to reload before approving the disposition is unchanged.
Why this classification: Classified “review”: how an NCR's details are saved changed in one situation — when the supplier list could not be read — so that the named supplier is kept rather than removed. No signature, content hash, role requirement or lifecycle rule changed, and when the supplier list reads normally the field behaves exactly as before. A removal made this way was recorded in the NCR's audit trail as "Supplier removed from this NCR" by the person who saved, and nothing in that entry distinguishes it from a deliberate removal — an NCR whose trail shows a supplier removed with no reason given is worth a second look.
list-read-fails-closed.test.tsNo database migration in this release. The risk-item interview can now be tried on Indelio's website without signing in. Nothing inside the product changed.
NEW ON THE WEBSITE: TRY THE RISK-ITEM INTERVIEW WITHOUT SIGNING IN. A public page at /product/tour/risk-interview shows the Risk Management module's risk-item interview — the same questions, labels, 1–5 scales, score bands, §7.1 control options and review screen — with a fictional glucose-meter example or a blank start. Nothing typed on it is saved or sent. It is linked from the Risk Management product page and from the home page. An automated test renders the website's interview and the product's side by side, on every screen, and fails the build if their wording or inputs differ.
Why this classification: Classified “none”: a page was added to the public website, and two public pages link to it. Nothing inside the product changed — no screen you use, record, rule, signature, content hash, role or audit entry. The page reads and writes nothing, and it sits under the /product/ addresses that were already public, so the boundary between public and signed-in pages did not change.
public-risk-interview.test.tsNo database migration in this release. A training quiz is now hidden from anyone with open training on its document — they can no longer read its answers before sitting it.
CORRECTED: SOMEONE ABOUT TO SIT A TRAINING QUIZ COULD READ ITS ANSWERS FIRST. Quizzes are written by people with the QA, approver or admin role, and the quiz editor shows which answer is correct. In a small company those people are trainees too: an approver assigned training on a document could open that document's quiz in "✎ Training quizzes", read the answers, and then sit it — so the quiz measured nothing. Now, while you have open training on a document (on any of its versions), Indelio hides that document's quiz from you: the editor says why and shows no questions, and you cannot start, change, draft with AI or approve the quiz. Once your training is complete — or for anyone who is not in training on it — nothing changes. If the person who will write a quiz is also assigned the training, they complete their own training first. If Indelio cannot confirm whether you have open training, it shows nothing and changes nothing, rather than assuming you have none.
Why this classification: Classified “review”: an access rule was added to the quiz editor and to writing and approving quizzes. No signature meaning, content hash, role requirement or existing record changed, and trainees sit quizzes exactly as before. Checked in Indelio's production data before the change: nobody had open training on a document that has a quiz. It cannot undo a quiz someone has already seen: a person who wrote a quiz and is assigned training on that document afterwards still sits a quiz whose answers they know. If that can happen in your organization, your training procedure should say how you handle it — Help says the same.
training-quiz.test.tsNo database migration in this release. When the Risk Management list cannot be read, it now says so, instead of saying there are no risk assessments.
CORRECTED: A RISK LIST THAT COULD NOT BE READ NO LONGER SAYS THERE ARE NONE. When the database read behind the Risk Management list failed, the page said "No risk assessments yet"; and when an assessment's items could not be read, it was listed with 0 risk items and 0 high risks. The page now says the risk assessments could not be read, and that this is not a statement that there are none. In the same situation the management-review metrics now report Risk as unreadable instead of counting it as zero, and the risk-file picker on Trends says it could not list the risk files instead of "No risk files exist yet".
Why this classification: Classified “none”: only what is shown when a read fails changed. No rule, signature, content hash or record changed, and a list that reads normally is shown exactly as before. ⚠️ A metrics snapshot taken before this release on a day the risk read failed would have recorded zero risk assessments; nothing in the snapshot distinguishes that from a true zero.
list-read-fails-closed.test.tsNo database migration in this release. The read-only API now refuses more than 60 requests a minute from one address, and the API reference and Help say so. Keeping API keys safe is added to the controls your organization is responsible for (CUEC-08).
RATE LIMIT ON THE READ-ONLY API, AND THE REFERENCE THAT DESCRIBES IT. Since 2026-09-18 a request to /api/v1/ from an address that has already made 60 requests in the current minute is refused with 429 Too Many Requests, by Indelio's hosting firewall, before it reaches Indelio. The API reference on Settings → API access now lists 429 — and 500 — alongside the other responses, and the API access Help topic says what 429 means and what the calling system should do. The rate limit is a setting of Indelio's hosting provider rather than part of the application; it is recorded, with how to verify it, in Indelio's assessment of that supplier.
Why this classification: Classified “review”: for an organization that uses the API, a calling system that makes more than 60 requests a minute from one address is now refused for the rest of that minute — check that your integration's call rate sits under the limit. No record, rule, signature or content hash changed, and nothing changes for an organization that has not issued a key.
api-risk.test.tsNEW CUSTOMER CONTROL: API KEY CUSTODY (CUEC-08). The register of controls that transfer to your organization now includes the custody of Indelio API keys — who may issue them, keeping each key only in the calling system's secret store, one key per system, and revoking a key when it may have been exposed or when the system or the person responsible for it leaves. The implementation-plan template's section on customer controls lists it for you to record.
Why this classification: Classified “none”: a validation document and a template changed; no function did. If you use the API, add CUEC-08 to your implementation plan.
csa-template-config-surface.test.tsA database migration is required in this release: run `supabase/pms_plan.sql`. Post-Market Surveillance now holds each device's post-market surveillance plan as its own record, PMS-001 and on, written one question at a time and approved with an e-signature by someone other than its author.
NEW: POST-MARKET SURVEILLANCE PLANS. “📋 Post-market surveillance plans” at the top of Post-Market Trends, and step 6 of a risk assessment's ISO 14971 pathway, open a new record: the plan for one device that says, before release, what will be collected once it is in use and what would show its risk file is wrong (ISO 14971 §10, ISO 13485 §8.2.1). “🧭 Write a plan, one question at a time” asks for the device and its versions; the risk file the plan keeps true; where the device is sold; where the shipment numbers come from, without which a trend is a count, not a rate; which of the six kinds of information ISO 14971 §10.2 names will be collected, and for a kind left out, why nothing from it can inform this device; for each source outside Indelio, who reviews it and how often; which risk items to watch and, for each, the limit that would mean it happens more often than the risk file estimates; what happens when a limit is crossed — flagging the risk file for review and updating it cannot be removed; how often trends, the risk file and the summary are reviewed; and who runs the plan and who keeps the risk file current. A plan is saved as a draft and its page lists anything still missing. ⭐ It is approved with the e-signature “Post-market surveillance plan approved by”, by someone with the approver or QA role who did not write it, and only once nothing is missing. An approved plan is changed by amending it — the signature “Reopening approved by”, with the reason, opens a new version in Draft, and the approved version and its signatures stay on record. A draft that will not be used is cancelled with the signature “Cancelled by” and the reason. The plan is its own record: approving or amending it never changes the risk assessment or its signatures. Step 6 of the pathway now reports “partly” when the assessment has an approved plan, and done once a post-market review is recorded; until now it could only report “not started” before a review. The SOP template SOP-QMS-015 (Post-Market Surveillance) is revised to 1.1 and now says where the plan is held.
Why this classification: Classified “review”: a new e-signed record type exists, with a new signature meaning, “Post-market surveillance plan approved by”, and step 6 of the ISO 14971 pathway now reads the plan when it derives its status. The pathway is a guide, not a gate — no approval, signature or state transition of a risk assessment depends on it, and no existing rule, role requirement, signature meaning or content hash changed. Nothing changes for an organization that writes no plan. If you adopt it, decide whether your post-market surveillance procedure names this record as the plan, and include it in your performance qualification. It is not an EU MDR post-market surveillance plan, periodic safety update report or PMCF plan, and not a Section 522 study. If you use the Indelio SOP template SOP-QMS-015, review revision 1.1 against your adopted copy.
pms-plan.test.tsrisk-pathway.test.tsentitlements.test.tscopy-claims.test.tsNo database migration in this release. Typing a word or two into the help assistant — "interview", "quiz" — now lists the Help topics that mention it, with a link to each, instead of asking what you meant.
THE HELP ASSISTANT ANSWERS A WORD OR TWO WITH THE TOPICS THAT MENTION IT. Asked "interview", the assistant had asked what was meant — although Help describes the risk-item interview. Now, when the first thing you type is a word or two (up to three words, and not a question), the reply lists the Help topics that mention it, the sentence each mentions it in, and a link that opens each topic. It is the same match the search box uses, so the two give the same topics. This reply is built from Help itself — no AI model writes it — so it also answers when your organization has switched AI off. A question, anything after the first message, and a word no Help topic mentions go to the assistant exactly as before, and the AI switch applies to those as it did.
Why this classification: Classified “none”: it changes how the help assistant answers a short first message, and nothing that operates under your quality system — no record, rule, signature or role requirement. The one behaviour to note: with AI switched off, a word or two typed into the assistant now gets the matching Help topics, because that reply uses no AI; everything else the assistant does still refuses when AI is off.
help-quick.test.tsNo database migration in this release. Cancelling a nonconformance is now an approval: it needs the approver or QA role and an e-signature, and it can't be done once the disposition is approved.
CORRECTED: ANY USER COULD CANCEL A NONCONFORMANCE, WITHOUT A SIGNATURE — INCLUDING AFTER ITS DISPOSITION WAS APPROVED. Cancelling an NCR needed only a reason. It took no role and no e-signature, although every other decision on an NCR — approving the disposition, closing it, reopening it — needs the approver or QA role and a signature, and cancelling a CAPA already did. And while the page offered Cancel only while an NCR was Open or in Disposition, the action itself allowed it at Pending Closure too, where the disposition is already approved and the only way out is closure — which is signed by a person with the approver or QA role who did not approve the disposition. A request made without the page could therefore close an NCR's life around both controls. Now: cancelling is "Cancel NCR (approval — signed)", offered to and accepted from a person with the approver or QA role, with a reason and their password — the e-signature "Cancellation approved by", applied before the NCR moves to Cancelled. It is allowed only while the NCR is Open or in Disposition; once the disposition is approved, the NCR is closed instead. Other users see that cancelling needs the approver or QA role. The Help topic and the nonconformance SOP template (version 1.2) say the same. Found while building the NCR spreadsheet import (release 2026.09.18.14).
Why this classification: Classified “review”: a role requirement and a signature were added to an act that had neither, and a stage at which it was possible is now refused. Anyone without the approver or QA role who used to cancel NCRs can no longer do so, and your procedure may say who cancels. The new signature meaning is "Cancellation approved by". No NCR already cancelled is changed, no content hash changed, and no other gate or signature moved. It is not “requalify”: the closure, disposition and reopen controls are unchanged; this adds the control the cancellation was missing.
ncr-cancel.test.tshelp.test.tsNo database migration in this release. An administrator can now issue and revoke read-only API keys in Settings → API access, and another system can read the organization's risk assessments with one.
READ-ONLY API FOR RISK ASSESSMENTS. Settings → API access (read-only) lets an administrator issue a key — named, expiring within a year, shown once — list the organization's keys, and revoke one permanently with a reason; issuing and revoking are recorded in the audit trail. With a key, another system can call GET /api/v1/risk/assessments (optionally ?state=Approved) and GET /api/v1/risk/assessments/{code}, which return each assessment with its items, its signatures, whether each signature is bound to the current content, and whether the content is intact. A key reads only its own organization; the same code in another organization is "not found". A request whose key or data could not be checked is refused as unavailable (503), never as an invalid key and never with partial data. Nothing can be created, changed or signed through the API. The API reference is on the same page, rendered from the same field lists the API serves.
Why this classification: Classified “review”: a new way to READ risk records, which is inactive until an administrator issues a key — for an organization that issues none, nothing changes. No existing rule, signature, role requirement or content hash changed. ⚠️ proxy.ts, which enforces the session boundary, now lets requests to /api/v1/ through without a signed-in session, because each of those routes authenticates with an API key instead; every route there is required by an automated check to verify the key before reading anything. If you intend to use the API, treat it as part of your intended use: decide who may issue keys, where the calling system keeps them, and include the integration in your performance qualification.
api-risk.test.tsapi-keys.test.tsNo database migration in this release. The in-app Help now explains the ways of working added this week: adding a risk item one question at a time, the ISO 14971 pathway panel, the §7.1 control option and the hazard-chain fields, training quizzes, and how a record's thread names the item each entry is about.
HELP NOW EXPLAINS THIS WEEK'S NEW WAYS OF WORKING. Every module has always had a Help topic, but a new way of working inside a module was not required to reach it — so several shipped this week with nothing in Help, and asking the Help assistant about them got "not in the help docs". Help now covers: "Walk me through it, one question at a time" and "Walk through this item" on a risk assessment; the sequence of events and hazardous situation a risk item can record; the ISO 14971 §7.1 control option and its justification; the six-step ISO 14971 pathway panel; the risk thread; training quizzes — who writes and approves them, the default pass mark, and the read, then quiz, then sign order; and how a record's thread names the item each entry is about and opens the record at it. Each is findable through the search box and the Help assistant as well as on the Help page.
Why this classification: Classified “none”: help text only. No function, rule, signature or record changed; the Help describes behaviour already released in 2026.09.17.10, 2026.09.17.14, 2026.09.18.3, 2026.09.18.5, 2026.09.18.11 and 2026.09.18.12. From this release on, every change in this register names the Help topic that explains it, or says why none is needed, and Indelio's automated checks refuse a release entry that does neither.
help-coverage.test.tsA database migration is required in this release: run `supabase/import_records.sql`. Spreadsheet import can now bring in a complaint log or a nonconformance log, one record per row. A complaint or NCR closed in your previous system comes across as closed there — never as an Indelio signature — and one that was not finished comes in as Open, so every remaining step is signed in Indelio.
NEW: IMPORT A COMPLAINT LOG OR AN NCR LOG, IN LEGACY MODE. Spreadsheet import can now bring a complaint log into Customer Complaints and a nonconformance log into Nonconformances — each row becomes its own record through the module's own write path, and QA verifies the whole transfer with one e-signature, as for every import. ⭐ ONLY A FINISHED RECORD IS CARRIED AS HISTORY: a complaint closed in your previous system arrives Closed, and an NCR closed or cancelled there arrives Closed or Cancelled, each saying which system it came from, its number there, who closed it and when, and that the closure was not signed in Indelio — on the record, on the complaint's PDF report, and in the audit entry that created it. No e-signature is created, and those origin fields cannot be changed afterwards. ⭐ ANYTHING UNFINISHED COMES IN AS OPEN: a complaint still under investigation, or an NCR awaiting disposition or closure, must be written Open in the file, and its investigation, disposition and closure are then signed in Indelio. That keeps the closure checks working — the person who investigated a complaint cannot close it, and the person who approved an NCR's disposition cannot close it — because those checks read the Indelio signature of the step before, which a record landed part-way through would not have. A status such as “Under investigation” is refused on its row with that explanation, never quietly changed. ⛔ A CLOSED COMPLAINT MUST CARRY ITS REPORTABILITY DECISION (None, MDR-30-day or MDR-5-day). A blank is never read as not reportable, and “Yes” is refused because it does not say which report; without the decision, the complaint comes in as Open and the decision is made in Indelio. A closed NCR must carry its disposition and the date it was closed; a cancelled NCR, its reason. An NCR's supplier is matched by exact name to one supplier in Suppliers, or the row says it was not found or was ambiguous. A CAPA number from your previous system is kept as that system's and is NOT linked to a CAPA in Indelio, because a matching number would otherwise connect the record to a different CAPA. A complaint or NCR whose number from the same previous system is already in Indelio, or appears twice in the file, is refused. Imported complaints are marked “imported” in the complaint list.
Why this classification: Classified “review”: a new way to create complaint and nonconformance records now exists, and it creates them in states — Closed, Cancelled — that an Indelio record otherwise reaches only by e-signatures and, for closure, a segregation-of-duties check. Your quality function should decide whether and how you use it, and your procedure for onboarding legacy records may need to say that a closure made in your previous system is accepted as that system's record. It is not “requalify”: no existing gate, role requirement, signature meaning or content hash changed — the new origin fields sit outside both modules' content hashes, so no existing signature binding can move — and nothing changes unless you run an import. One rule was tightened on the way: an NCR can no longer be CREATED naming a supplier from outside your organization. The supplier was already checked when a supplier was linked later and when a disposition was approved; creation now checks it too. The complaint and NCR list pages no longer say that every record's lifecycle was e-signed here, since an imported closure was not. The run order (IQ-06) now lists `supabase/import_records.sql` before `supabase/api_keys.sql` from release 2026.09.18.13: it was applied to Indelio's production first, and the two do not depend on each other, so the declared order is the order in which they were applied.
import.test.tsentitlements.test.tsA database migration is required in this release: run `supabase/api_keys.sql`. It adds the table that will hold read-only API keys. Nothing in Indelio issues or accepts a key yet — no screen, no endpoint and no existing behaviour changes.
FOUNDATION FOR READ-ONLY API KEYS — NOT YET USABLE. Adds the table and the server-side logic that will let an organization administrator issue a key so another system (for example, an existing eQMS) can READ that organization's risk data. No screen issues a key and no endpoint accepts one in this release, so no one can use a key yet. What the database enforces from today: a key can only ever carry the read-only risk scope; it expires within one year of the moment the database records it; only its sha256 is stored, never the key itself; revoking a key is permanent, requires a reason and cannot be undone; no key row can be edited otherwise or deleted; and neither a signed-in user nor an anonymous caller can read the table at all. Issuing and revoking a key will be administrator-only and will each write an audit-trail entry that names the key by its public prefix — never the key or its hash.
Why this classification: Classified “none”: no function that operates under your quality system changed — the new table is unused until a later release adds the screen and endpoint, and no existing table, rule, signature, role requirement or content hash changed. ⚠️ Two facts for anyone executing the installation qualification: the migration count grows by one (IQ-06), and IQ check C2 now lists `api_keys` among the tables deliberately left without a row-level-security policy, with its reason stated — every access goes through the server, and the migration refuses to finish if a policy or a client-role grant ever appears on it.
api-keys.test.tsiq-check.test.tsrls-exempt-callsites.test.tsNo database migration in this release. A record's thread no longer opens a different item than the one an entry is about, and a statement in two earlier releases that item numbers are never reused is corrected.
CORRECTED: A THREAD LINK COULD OPEN A DIFFERENT ITEM THAN THE ONE ITS ENTRY IS ABOUT. Release 2026.09.18.11 made a thread entry about one item open its record at that item, found by the item's number. But a number can pass from one item to another: items are numbered from the highest number that exists, so when the last item of a record is removed, the next item added takes its number. On such a record, an older entry about the removed item opened the page at the item that had taken its number. Found on Indelio's own production data the day it shipped — a design project where input #1 was removed and a different input #1 added. A thread now opens a record at an item only where it can show that it is that item: by the item's identity recorded in the entry, if the item still exists; by number only where that record has never removed an item of that kind. Otherwise it opens the top of the record, as before. The words of every entry are unchanged — the number an entry gives an item was that item's number at the time. Entries about a training-quiz question, a URRA item, and an audit finding's link to a CAPA now also record which item they are about, so they can be linked the same way. If the thread cannot read what it needs to decide, it links to the top of the record and says the thread is incomplete.
Why this classification: Classified “review”, as release 2026.09.18.11 was: how the thread presents audit history changed, and what three kinds of audit entry record changed — each now also records the item's identity. No stored entry, rule, signature or content hash changed. If you checked the thread's links against your intended use after release 2026.09.18.11, check them again: a link that opened a different item now opens the top of the record.
thread-item.test.tsCORRECTION: ITEM NUMBERS CAN BE REUSED. Release 2026.09.18.8 said item numbers "are never reused after a removal", and release 2026.09.18.11 said an item's number "is never reused". Both overstated it. When the removed item was the highest-numbered one, the next item added takes its number: items 1, 2 and 3; remove 3; the next one added is 3. What release 2026.09.18.8 fixed stands — two risk items, audit findings, project deliverables or training-quiz questions can no longer hold the same number at the same time. The earlier entries in this register are left as they were published; this entry corrects them.
Why this classification: Classified “none”: nothing Indelio does changed; this corrects what the register said about it. ⚠️ If you relied on the earlier statement — for example by treating an item's number as a permanent identifier across the history of a record — note that a number identifies an item only while that item exists. The audit entries for adding and removing an item record what it said, which tells two items that held the same number apart.
item-numbering-fails-closed.test.tsthread-item.test.tsNo database migration in this release. Every audit-trail entry about one item of a record — a risk item, a CAPA action, a design input, an audit finding, a deliverable, a review action, a quiz question, a human-factors item — now names that item by its number, and the record's thread opens the record at it.
AUDIT ENTRIES NAME THE ITEM THEY ARE ABOUT. An entry about one item of a record now says which item and what happened to it, in one form across the modules: "Risk item #5 added: Free-flow on door open", "Risk item #2 updated: severity 4 → 5", "CAPA action #3 (corrective) marked Done", "Design input #4 removed: <what it required>", "Audit finding #2 (Major) follow-up: status Open → Closed", "Quiz question #3 added: <the question>". Until now most of these said only "Risk item added", "Action marked Done" or "Deliverable updated", and an edit to a design input, output, verification or validation, or to a management-review action, recorded no reason at all. Each entry's details now also record the item's number, and for an addition what the item says. Short values are shown before and after (severity 4 → 5); long text is named as changed, and its full before-and-after stays in the entry's details as before. ⛔ A training-quiz entry never shows an answer or which answer is correct — only that it changed — because a document's thread is readable by the people the quiz measures. Deciding a CAPA due-date extension now reads the action it is for, so the entry can name it; if that read fails the decision is refused and nothing is saved, rather than recorded without saying which action it concerned.
Why this classification: Classified “review”: the wording and details of audit-trail entries changed for these actions — what a new entry records, not whether it is recorded. The audit trail's controls are unchanged: entries are still append-only, hash-chained, attributed and time-stamped, and no signature, signature meaning, gate, role requirement or content hash changed. Existing entries are not changed. ⚠️ If your performance qualification checks the exact wording of an audit entry for any of these actions (for example that adding a risk item records "Risk item added"), it will now read differently; check it against your intended use. The CAPA extension refusal is a new fail-closed condition on a read that normally succeeds.
thread-item.test.tsremoval-trail.test.tsaudit-finding-trail.test.tssignature-gates-unreadable.test.tsTHE THREAD NAMES THE ITEM, AND OPENS THE RECORD AT IT. On a record's thread (and a risk assessment's risk thread), each lifecycle-timeline entry about one item now names it, and clicking its record opens the record at that item, marked, instead of at the top. Older entries are named from what they already recorded — the item's number where the entry recorded it, its id resolved to its number (an item's number never changes and is never reused), or a number in its original wording. An older entry that recorded none of those is named without a number and opens the record at the top: the thread does not guess. ⛔ No existing audit entry is changed. Where the thread's description differs from the reason as it was recorded, the recorded reason is shown beneath it — or, where none was recorded, the thread says so — so the words that are the record stay on the screen. If the thread cannot read an item's number it says the thread is incomplete. A removed item links to the top of its record, because it is no longer on the page. The CAPA page now shows each action's number, and the training-quiz page can be opened at a document's quiz from its thread.
Why this classification: Classified “review”: how the audit history is presented on the thread changed — the line shown for an item entry may now be a description derived from the entry rather than its stored reason, with the stored reason beneath it. The audit records themselves are unchanged and no rule, signature or content hash changed. If you use the thread to review audit history, check the new presentation against your intended use.
thread-item.test.tsunread-not-empty.test.tsNo database migration in this release. A supplier imported with an approval from a previous system is no longer told, when placed on hold, that its approval signature is kept — it has none.
CORRECTED: WHAT A HOLD KEEPS, ON AN IMPORTED SUPPLIER. The "Place on hold" action said the supplier's qualification "and its approval signature" are kept. For a supplier brought in by spreadsheet import with an approval from your previous system (release 2026.09.18.6), there is no Indelio approval signature — so the sentence claimed one that does not exist. It now follows the record: a supplier approved in Indelio is told its approval signature is kept; an imported supplier is told its approval was carried over from the previous system and was not signed in Indelio; and where no signature could be found on a supplier that was not imported, the action says only that the qualification is kept. The help topic says the same. Found on production while checking the import.
Why this classification: Classified “none”: wording only. What a hold does is unchanged — the same e-signature, the same reason, the same effect on the Approved Supplier List — and no gate, role requirement, signature meaning or content hash changed. The correction removes a statement that was untrue for imported suppliers.
import.test.tsNo database migration in this release. After an item is removed, the next risk item, audit finding, project deliverable or training-quiz question now takes a new number instead of reusing an existing one, and those pages show each item's own number.
ITEM NUMBERS ARE NEVER REUSED AFTER A REMOVAL. Risk items, audit findings, project deliverables and training-quiz questions were numbered by counting the items that exist and adding one. Each of them can be removed while its record is still being prepared, and a removed item is gone — so after removing any item except the last, the next one added took the number of an item that still existed (items 1, 2 and 3; remove 2; the next one added became a second 3). Each is now numbered from the highest existing number, so the next item after 1 and 3 is 4. The risk assessment, audit and project pages, and the risk-item interview, now show each item's own number — the number the audit trail records — rather than its position down the page; the two differ only where an item was removed. Where two items with the same number could previously have been created, two items now show the same number, so the duplicate is visible.
Why this classification: Classified “review”: a numbering defect is corrected and no rule, signature or content hash changed. Two items sharing a number matter because a risk assessment, an audit and a project order their items by number when their content hash is calculated, and because a training-quiz question is edited and removed by its number. ⚠️ If anyone in your organization removed a risk item, audit finding, project deliverable or quiz question and then added another to the same record before this release, check that record for two items with the same number, and resolve it before the record is signed. Indelio's own production data holds none.
item-numbering-fails-closed.test.tsNo database migration in this release. Corrects the risk-item interview: its severity and likelihood screens numbered each level one too high and saved that higher number.
THE RISK-ITEM INTERVIEW SAVES THE SEVERITY AND LIKELIHOOD YOU CHOSE. In releases 2026.09.18.5 and 2026.09.18.6, the interview's severity, probability and exploitability screens showed each level with a number one too high — Negligible as 2, through Catastrophic as 6 — and saved that number. Choosing "Critical" (level 4) saved severity 5, and every level moved up by one, with the top level (6) held at 5. Opening an item in the interview also showed the wrong level as selected. The screens now number the levels 1 to 5 beside the names you already know, and save the level you chose. The form on the assessment page was never affected, and neither were the residual-risk screens. The interview's heading now says, in larger type, which item you are adding or revising and on which assessment.
Why this classification: Classified “review”: a data-entry defect in a new way of entering risk items is corrected, and no rule, signature or content hash changed. ⚠️ If anyone in your organization added or revised a risk item through the interview ("Walk me through it" or "Walk through this item") while 2026.09.18.5 or 2026.09.18.6 was current, check that item's severity and likelihood against what was intended, and correct it through the form or the interview before the assessment is reviewed. Items entered through the form are not affected.
risk-interview.test.tsA database migration is required in this release: run `supabase/import_registers.sql`. Spreadsheet import can now bring in a supplier list or an equipment register, one record per row, with any approval or calibration from your previous system carried across as that system's — never as an Indelio signature.
NEW: IMPORT A SUPPLIER LIST OR AN EQUIPMENT REGISTER, IN LEGACY MODE. Spreadsheet import could only bring a risk analysis in as a new Draft assessment. It can now also bring a supplier list into Suppliers, or an instrument register into Calibration & Maintenance — each row becomes its own record, through the module's own write path, and QA verifies the whole transfer with one e-signature as before. ⭐ A STATUS FROM YOUR PREVIOUS SYSTEM COMES ACROSS AS THAT SYSTEM'S: an Approved supplier arrives Approved, and an instrument arrives with its last calibration date, but each record says which system it came from, who gave the approval or did the calibration and when, and that it was NOT signed in Indelio. No e-signature and no calibration record is created on anyone's behalf, and these origin details cannot be changed afterwards. The next re-evaluation or calibration is done and signed in Indelio as normal; the re-evaluation is due 24 months after the approval (the interval an Indelio approval sets) unless the file gives a date, and the next calibration is due one interval after the last. The Approved Supplier List marks a legacy approval as such. ⛔ Nothing is guessed: a blank status comes in as Pending (suppliers) or Active (equipment), a blank "calibration required" as Yes, an Approved supplier without an approval date is refused, a supplier still under evaluation comes in as Pending, a date that could be read two ways (03/04/2025) is refused, and a supplier or instrument already in the register — or twice in the file — is refused until the row is skipped with a reason. The import is offered from inside Suppliers and Calibration & Maintenance, and only for modules that are part of your plan.
Why this classification: Classified “review”: a new way to create supplier and equipment records now exists, and it creates them in states — Approved, calibrated — that an Indelio record otherwise reaches only by an e-signature. Your quality function should decide whether and how you use it, and your procedure for onboarding legacy records may need to say that a legacy approval is accepted until its next re-evaluation. It is not “requalify”: no existing gate, role requirement, signature meaning or content hash changed — the new origin fields sit outside both modules' content hashes, so no existing signature binding can move — and nothing changes unless you run an import. The supplier list now marks legacy approvals, which is display only.
import.test.tsentitlements.test.tsA database migration is required in this release: run `supabase/risk_hazard_chain.sql`. A risk item can now record the sequence of events and the hazardous situation separately from the hazard, and a risk item can be added or revised through a step-by-step interview as well as the form. No existing risk item or signed assessment changes.
THE HAZARD CHAIN IS RECORDED IN ITS PARTS. ISO 14971 describes how harm arises as a chain — a hazard, a sequence of events, the hazardous situation in which someone is exposed, and the harm — and §5.4 asks for the hazardous situations as well as the hazards. Until this release a risk item held the first three in one field, labelled "Hazard / hazardous situation", so a file could name a hazard and never say how it would reach anyone. A risk item now has two further fields, Sequence of events and Hazardous situation, and the first field is labelled Hazard. Both new fields are optional. ⭐ Existing items are not changed: whatever an item's Hazard field already says stays exactly as written, and nothing is split or re-worded. The printed risk assessment shows both fields when they are recorded. ⚠️ Spreadsheet import does not fill the two new fields yet — a column headed "hazardous situation" still imports into Hazard, as before.
Why this classification: Classified “requalify”, as the §7.1 option analysis was in 2026.09.17.10, because a risk item's material content changed: the sequence of events and the hazardous situation are part of the risk file a reviewer signs, so each is covered by the assessment's content hash when it is recorded. ⭐ No existing record moves — both columns are nullable and folded into the hash only when set, so every assessment signed before this release keeps its exact content hash, and the automated evidence proves that against the hash's form before this change. The only new refusal is a length limit of 2,000 characters on each field.
risk-interview.test.tsA RISK ITEM CAN BE ADDED OR REVISED ONE QUESTION AT A TIME. Beside the form on a risk assessment, "Walk me through it" opens an interview that asks one question per screen, in the order ISO 14971 works through a risk: which part of the device the risk comes from; the hazard, the sequence of events, the hazardous situation and the harm; how severe and how likely (or, for a Cyber risk, how exploitable) before any control; which §7.1 control option, and why a stronger one was not practicable; the control and what implements it; the risk that remains; whether that is acceptable; and how the control will be verified. Each screen explains why it is asked, and the last shows the whole item under the form's own labels before it is saved. "Walk through this item" opens an existing item the same way. The form is unchanged as a way in, and an item written in either can be opened in the other.
Why this classification: Classified “review”: it is a second way to enter content the form already records, and it saves through the same server actions, so every rule the form is held to — the assessment's state, Cyber scoring, the §7.1 justification — applies unchanged, and it adds no rule of its own. The automated evidence proves that opening an existing item in the interview and saving it without changes alters nothing, and that the interview sends exactly the fields the form sends. If your procedure names how risk items are entered, it may want to mention this route.
risk-interview.test.tsNo database migration in this release. Training's writes now check that Training is part of the organization's plan, so every module that can be sold now checks before it writes. ⭐ Nothing changes for any existing customer: with no entitlement record, every module is on.
TRAINING NOW CHECKS THE MODULE FIRST, LIKE EVERY OTHER MODULE. Release 2026.09.17.15 made every module's writes check that the module is part of the organization's plan, and held Training back while its quiz feature was changing. That change has shipped (2026.09.18.3), and Training now follows the same rule: assigning training, cancelling it, managing requirements and groups, every quiz change, submitting a quiz attempt, and the read-and-understood e-signature all check first. ⛔ WHERE TRAINING IS OFF IT IS READ-ONLY: an open assignment cannot be completed until Training is turned back on. ⭐ Two things are deliberately NOT checked, because they are reads and a module that is off never hides a record (21 CFR 11.10(b)): opening the document you were assigned — which still records that it was delivered to you — and seeing a document's quizzes. ⭐ WITH NO ENTITLEMENT RECORD, EVERY MODULE IS ON — the state of every organization today — so nothing you can do in Training today is refused. ⚠️ Where the entitlement record cannot be read, the write refuses and says it could not confirm — never that Training is outside your plan.
Why this classification: Classified “review”, as releases 2026.09.17.15 and 2026.09.18.2 were: a new precondition sits in front of Training's writes, including the read-and-understood e-signature, and your quality function should know it exists — and that an off module leaves open assignments uncompletable — before it is ever used on your tenant. It is not “requalify” because, for every organization as it stands, nothing that succeeds today is refused: no entitlement record exists, so Training answers “on”. The quiz gate, the view-before-quiz rule from 2026.09.18.3, the signature meaning and the content-hash binding are all unchanged. The one new way a Training write can fail is an entitlement record that cannot be read.
entitlements.test.tsNo database migration in this release. Where a training quiz applies, the trainee now reads the document, then takes the quiz, then signs — and a quiz attempt is refused until the document has been served to them. The quiz editor now saves the correct answer that was marked, whichever answer boxes are left blank.
TRAINING QUIZZES: READ, THEN QUIZ, THEN SIGN. In release 2026.09.17.14, when a document version had an approved quiz, the trainee's training row replaced the whole read-and-sign step with a link to the quiz — including the button that serves the document — and the quiz page did not offer the document either. A trainee was therefore asked to pass the quiz before Indelio had shown them the document, and their training record could show the effectiveness quiz passed before the training it checks was delivered. Now the row offers the document first; once it has been served, the row offers the quiz; and only once the quiz is passed does it offer the read-and-understood signature. ⛔ Indelio now refuses a quiz attempt until the trainee has been served the version they are being trained on — the same version-specific record the signature already requires, so having read an earlier version does not count. Re-reading stays available at every step, so a trainee who does not pass can read the document again before their next attempt. ⭐ Where a version has no approved quiz, nothing changes: read, then sign, as before.
Why this classification: Classified “requalify” because the behaviour of a gated training step changed: a quiz attempt that release 2026.09.17.14 accepted — one made before the document was served — is now refused, and the order a trainee is shown changed from quiz-then-read to read-then-quiz. If you qualified training quizzes against 2026.09.17.14, re-run that part of your qualification. No signature meaning, role requirement or content hash changed, and the read-and-understood signature still requires both the document to have been served and, where a quiz applies, a pass. Any quiz attempt already recorded is unchanged.
training-quiz.test.tsTRAINING QUIZZES: THE CORRECT ANSWER IS THE ONE YOU MARKED. When writing or editing a quiz question, the answer you mark as correct is chosen from four answer boxes, and blank boxes are dropped when the question is saved. In release 2026.09.17.14 the marked box's position was stored as it was, so if a box above it was left blank, a different answer — the next one down — was saved as correct. And if no answer was marked at all, the first answer was saved as correct without anyone choosing it. Both were visible before approval, because the editor shows the saved correct answer with a ✓, but neither was refused. Now the answer you marked is the one saved, whichever boxes are blank; and a question with no answer marked, or with the mark on a blank box, is refused with “Mark which answer is correct.”
Why this classification: Classified “review”: it corrects how the editor records the answer key, and a question that was previously saved with a key nobody chose is now refused instead. How an approved quiz is marked, the approval signature and the gate on the read-and-understood signature are unchanged. ⚠️ If you approved a quiz under 2026.09.17.14, check each question's ✓ against the answer you intended before relying on it — the approval signature binds to the key as saved.
training-quiz.test.tsNo database migration in this release. Adding or removing evidence on a record now checks that record's module, closing the one gap release 2026.09.17.15 named. ⭐ Nothing changes for any existing customer: with no entitlement record, every module is on.
EVIDENCE NOW FOLLOWS THE MODULE OF THE RECORD IT BELONGS TO. Release 2026.09.17.15 made every module's writes check the module first, and named one exception: evidence attachments, which serve every module, could still be added to or removed from a record in a module that is off. Evidence is part of its record, so attaching a file to a CAPA is now a write in CAPA — where a module is off for your organization, its records' evidence is read-only too. Adding checks before the file is examined or stored; removing checks once the attachment has been read (the only way to know whose record it is) and before anything is changed. ⭐ WITH NO ENTITLEMENT RECORD, EVERY MODULE IS ON — the state of every organization today — so no attachment you can add or remove today is refused. Downloading evidence is a read and is never checked (21 CFR 11.10(b) and (c)). Evidence retention is unchanged: evidence a signature was granted against still cannot be removed, whatever the module's state. ⚠️ Where the entitlement record cannot be read, the change refuses and says it could not confirm — never that the module is outside your plan. Training remains the one module whose writes are not yet checked.
Why this classification: Classified “review”, as release 2026.09.17.15 was: a new precondition now sits in front of adding and removing evidence in the listed modules, and your quality function should know it exists before it is ever used on your tenant. It is not “requalify” because, for every organization as it stands, nothing that succeeds today is refused — no entitlement record exists, so every module answers “on” — and the evidence retention rule, the must-open-evidence signing gate, and every signature meaning and content hash are unchanged. The one new way an attachment change can fail is an entitlement record that cannot be read.
entitlements.test.tsNo database migration in this release. Every module's write paths now check that the module is part of the organization's plan before writing anything. ⭐ Nothing changes for any existing customer: with no entitlement record, every module is on.
EVERY WRITE IN A MODULE NOW CHECKS THE MODULE FIRST. Release 2026.09.17.13 recorded which modules an organization has; this release makes every module's data layer consult that record before it creates or changes anything — creating a record, editing it, signing it, closing, cancelling or reopening it, and the links between records. The check is the first thing each write does, so a refused write has read and written nothing. ⭐ WITH NO ENTITLEMENT RECORD, EVERY MODULE IS ON — the state of every organization today — so no write you can make today is refused by this release. ⛔ A MODULE THAT IS OFF IS READ-ONLY: no new records and no changes to existing ones, so a record still open in it cannot be progressed or closed until the module is turned back on. Reading, exporting and the audit trail are never checked, because your records must stay retrievable and readable for their whole retention period (21 CFR 11.10(b) and (c)). The refusal now says records can be neither created nor changed. ⚠️ Where the entitlement record cannot be read, the write refuses and says it could not confirm — it never says the module is outside your plan. ⚠️ Not yet covered: Training (its gate follows a concurrent change to that module) and evidence attachments, which attach to a record of any module and can still be added to a record in a module that is off. Document Migration and SOP Templates stay available to every organization, but committing a migrated document or creating one from a template now checks Documents, as a spreadsheet import already checks the module it imports into.
Why this classification: Classified “review”: a new precondition now sits in front of every write in the listed modules, and your quality function should know it exists — and what “off” does to records still open — before it is ever used on your tenant. It is not “requalify” because, for every organization as it stands, no write that succeeds today is refused: no entitlement record exists, so every module answers “on”, and no existing gate, role requirement, lifecycle rule, signature meaning or content hash changed. The one new way a write can fail is an entitlement record that cannot be read, where the write refuses rather than proceeds unchecked. ⚠️ If an entitlement is ever recorded as off for your organization, that module becomes read-only — treat that change, not this release, as the trigger to re-assess.
entitlements.test.tsA database migration is required in this release: run `supabase/training_quiz.sql`. Training can now require a trainee to pass a quiz before confirming they have read and understood a document. Nothing changes for any document until someone writes and approves a quiz for it.
TRAINING EFFECTIVENESS: A QUIZ A TRAINEE MUST PASS. ISO 13485 §6.2 asks you to evaluate whether the training you provide was effective. Until this release Indelio could show that a trainee was served a specific version of a document and then signed that they had read and understood it — but not that they understood anything. A QA person, approver or administrator can now write a multiple-choice quiz for the current version of a controlled document, optionally asking AI to propose questions from the document's text, edit them, set the pass mark, and approve the quiz with an electronic signature. Once a quiz is approved for a version, a trainee must pass it before their "Read and understood" signature is accepted. Every attempt is recorded, pass or fail. ⭐ A document with no approved quiz trains exactly as it did before, so this can be adopted one procedure at a time. ⚠️ A passed quiz is evidence that training was EFFECTIVE; it is not evidence of COMPETENCY, which means somebody observed the person doing the work, and nothing in Indelio records competency.
Why this classification: Classified “requalify” because a signature gate changed: the read-and-understood e-signature is now refused for a version that has an approved quiz until the trainee has passed it. Where no quiz is approved, the signature behaves exactly as before. ⭐ Deliberate choices you may need to reflect in your procedure: quizzes are written only by the QA, approver or admin roles, because writing one means seeing its answers and most staff hold the author role; a quiz belongs to one version of a document, so a revision starts without a quiz rather than inheriting questions written for the text it replaced; and if the quiz or the trainee's result cannot be read, the signature is refused rather than allowed. ⚠️ Known limit: a QA person who writes a quiz and is also assigned training on that document sits a quiz they wrote — nothing refuses that yet. Your training procedure should say how you handle it.
training-quiz.test.tsAI CAN PROPOSE TRAINING QUIZ QUESTIONS FROM A DOCUMENT. On a draft quiz, AI reads the version's text and proposes multiple-choice questions about what the procedure requires a person to do. Every proposed question is marked as AI-proposed and has not been checked by anyone until a person reviews it; editing a question makes it the editor's. Nothing an AI proposes reaches a trainee until a QA person or approver signs the quiz. It respects your organization's AI switch, and it refuses to draft from a document version with too little text, rather than inventing questions about something it cannot see.
Why this classification: Classified “review” because it adds an AI-assisted authoring step to a workflow you may qualify. It is advisory by construction: the drafting step cannot approve a quiz, and a human signature is required before any question is used. If your AI use policy lists the functions where AI is permitted, this one should be added.
training-quiz.test.tsA database migration is required in this release: run `supabase/org_modules.sql`. Indelio can now record which modules an organization has, so a single module can be sold on its own. ⭐ Nothing changes for any existing customer: with no record, every module is on.
INDELIO NOW RECORDS WHICH MODULES AN ORGANIZATION HAS. Until now the product shipped as one suite and every tenant saw every module. A new append-only record says which modules each organization has, with the reason and the operator who decided it, set only from Indelio's own console and written to that organization's audit trail. ⭐ WITH NO RECORD, EVERY MODULE IS ON — so nothing about your system changes in this release. ⛔ TURNING A MODULE OFF WOULD STOP NEW RECORDS IN IT AND WOULD NEVER HIDE, DELETE OR LOCK RECORDS ALREADY THERE: reading, exporting and the audit trail are untouched, because your records must stay retrievable and readable for their whole retention period (21 CFR 11.10(b) and (c)). ⚠️ Where the entitlement record cannot be read, the answer is “unknown”, not “not in your plan”: a write refuses and says it could not confirm, and your dashboard shows every module with a note saying it could not be confirmed. A transient fault must never tell you that your own records are outside your plan. Today one write path consults this — a spreadsheet import refuses before writing if the module it would create records in is not yours. The other modules' write paths follow one by one, each in its own release.
Why this classification: Classified “review”, and platform-wide because it touches how the product decides what an organization may do. Your validated state is unaffected in behaviour — with no entitlement record every module stays on, which is the state of every organization when this release lands — but a new control now exists that can restrict which modules accept new records, and your quality function should know that before it is ever used on your tenant, and may want it in your supplier file or your access-control procedure. It is not “requalify”: no existing gate, role requirement, signature meaning or content hash changed, and no module's write path refuses anything it did not refuse yesterday except spreadsheet import, which now checks the module it imports into. ⚠️ The automated evidence covers the decision logic, the console path and the one wired consumer; the per-module write gates are not in place yet and each will be published as it lands.
entitlements.test.tsNo database migration is required in this release for an existing installation, and nothing in the product behaves differently. It corrects a seed file used only when Indelio itself is installed fresh: the vendor's own internal compliance register no longer arrives pre-filled with control statuses nobody had assessed.
THE VENDOR'S OWN INTERNAL COMPLIANCE REGISTER NO LONGER ARRIVES PRE-FILLED WITH UNASSESSED STATUSES. Indelio's platform console carries a register of the vendor's own SOC 2 controls. Its seed file set 13 of 19 controls to "Implemented" and recorded five recurring activities as performed on the day the module was written — statuses and dates that nobody had assessed, which then read as fact for three months. ⭐ It survived because of an asymmetry worth knowing about in any control register, including your own: a row that says "Implemented" prompts nobody, while only "Not started" and an overdue date produce a signal — so an understated control gets noticed and an overstated one does not. The seed now records which criteria are tracked and nothing about whether any is met; a status can only become "Implemented" when a person has looked and recorded the evidence. Re-running the seed never overwrites a register someone has filled in.
Why this classification: Classified “none” because it touches no customer-facing function, record, signature or schema. The register concerned is the vendor's own internal SOC 2 tracker, visible only to the Indelio platform console — it is not tenant data, no customer can read it, and no qualification check reads it either. It is recorded here because the release register records every change to a controlled file, and because the failure it fixes is worth naming: a recorded status is a claim, and a claim is not evidence.
compliance.test.tsA database migration is required in this release: run `supabase/import_verifier.sql`. The person who has to verify a spreadsheet import is now told on their dashboard, an import can name who should verify it, and Risk Management shows where an imported assessment came from.
THE VERIFIER OF AN IMPORT IS NOW TOLD, INSTEAD OF HAVING TO BE ASKED BY HAND. An import must be verified by somebody other than the person who ran it, and nothing told that person it was waiting — the transfer sat at “ready for QA verification” until somebody mentioned it. Imports awaiting verification now appear under “waiting on you” on the dashboard of anyone holding the qa or approver role who did not run the import. An import may also NAME who should verify it, chosen from the people who could; then only that person is told. ⭐ Naming somebody only decides who is TOLD — it is not a gate. Any qa or approver who did not run the import may still verify it, so naming a person who is then away cannot freeze a transfer, and the control that matters — the verifier is never the importer — does not depend on anyone being named. Every change of who is asked is recorded in the audit trail.
Why this classification: Classified “review” because it changes what your people are shown and adds a routing choice to a workflow you may have qualified, and because an obligation that now appears on a dashboard may belong in your procedure for how imports are verified. It is not “requalify”: no signature meaning, gate, role requirement or content hash changed. The verification itself is unchanged, including the refusal to let the person who ran an import verify it, and the new assignment is deliberately not consulted when signing.
import.test.tsRISK MANAGEMENT NOW SHOWS WHERE AN IMPORTED ASSESSMENT CAME FROM, AND OFFERS THE IMPORT FROM INSIDE THE MODULE. A risk assessment created by a spreadsheet import says so, naming the import and whether it has been verified, so the question an auditor asks — where did these risk items come from? — is answered on the record rather than only in the audit trail. If that cannot be read, the page says so rather than implying the items were typed in by hand. The risk register also links straight to the importer for anyone who may run one. One display fix with it: the import button counted the rows waiting rather than the rows that would actually be imported, so it read “Import 5 rows” beside a message saying two of them had problems to resolve first.
Why this classification: Classified “none” because nothing that operates under your quality system changed: this is what two pages show and where a link points. No record, signature, gate, role or hash is affected, and the provenance shown is read from the import that already existed. ⚠️ It is published rather than left out because it changes what a reader of a risk assessment sees on the record itself, which is the kind of thing a reviewer of your risk file should not meet unannounced.
import.test.tsA database migration is required in this release: run `supabase/risk_control_options.sql`. A risk item can now record which ISO 14971 §7.1 control option it uses and why the stronger options were not practicable, and every risk assessment page shows where it stands on the six steps of the ISO 14971 pathway. Nothing you already use behaves differently, and no existing risk assessment changes.
RISK CONTROL OPTION ANALYSIS (ISO 14971 §7.1) IS NOW RECORDED ON A RISK ITEM. §7.1 does not ask which control you applied; it asks which options you considered, and in what order: inherently safe design first, then protective measures in the device or the process, and only then information for safety — each used only where the ones above it are not practicable. Until this release a risk item held one free-text "Risk control / mitigation" field, which records what you did and not that the analysis happened. A risk item now also carries the option it uses, and — for anything below inherently safe design — why the stronger options were not practicable. Choosing a weaker option without that reason is refused when the item is saved, and the same check applies to an edit, so the reason cannot be removed later. ⚠️ Both fields are OPTIONAL on an item that does not have them: every risk item written before this release keeps exactly the content it had, and its assessment's content hash and signatures are untouched.
Why this classification: Classified “requalify” because a risk item's material content changed: which §7.1 option a control uses, and the justification for it, are part of the risk file a reviewer signs, so both are covered by the assessment's content hash when present. A new refusal also applies when a weaker option is chosen with no justification. ⭐ No existing record moves — both columns are nullable and are folded into the hash only when set, so an assessment signed before this release hashes exactly as it did and its signatures remain intact; the automated evidence asserts that directly. If your risk management procedure names the fields a risk item carries, it may need updating.
risk-pathway.test.tsrisk.test.tsEVERY RISK ASSESSMENT NOW SHOWS WHERE IT STANDS ON THE SIX STEPS OF THE ISO 14971 PATHWAY. A new panel walks the pathway in figure 1 of ISO 14971 — hazard identification; risk estimation and evaluation; risk control option analysis; finalizing, approving and implementing the controls; verification and validation of those controls; and production and post-production information — and for each step says what it is, what it looks like when it has been done properly, and where this assessment stands. ⭐ A step's state is worked out from what the assessment actually holds; there is no way to declare a step complete. Where a fact could not be read, the step says it could not be read rather than reporting the work as not done. ⚠️ This is a guide, not a gate: it blocks nothing, signs nothing and records nothing, and what Indelio enforces at review and approval is unchanged — your own procedure may order the work differently.
Why this classification: Classified “none” because it adds a read-only view over records you already hold. No function, gate, signature, permitted state or record changed, and the automated evidence asserts that no state transition or signature depends on the panel and that it writes nothing.
risk-pathway.test.tsA POST-MARKET READ THAT FAILED USED TO SHOW AS "NO REVIEW RECORDED". On a risk assessment page, the post-market review flags and the recorded post-market reviews were read in a way that treated a failed read as an empty list — so a database error at that moment showed the ISO 14971 §10 half of the risk file as clean, with no indication that anything had gone wrong. The page now names the source it could not read, as the rest of the page already did for risk items and signatures. A module whose post-market migration has not been run is unaffected: an absent table still reads as "no reviews yet", which is true.
Why this classification: Classified “review” because what a user and an investigator are shown changed, in a direction that previously understated what was unknown. No signature, content-hash definition, role, permitted state or database schema changed, and no stored record is affected.
risk-pathway.test.tsTHE IQ PROTOCOL'S STEP IQ-06 NOW EXPECTS 93 MIGRATION FILES, NOT 92. This release adds one migration, so the Installation Qualification step that checks every migration has been applied counts one more. ⚠️ If you are part-way through executing an IQ against the earlier copy of the protocol, your copy says 92; finish against the copy you started with and note which release it covers, or re-run IQ-06 against this one.
Why this classification: Classified “review” because a step in a qualification protocol a customer executes changed its expected result. The count is derived from `supabase/run-order.json` by an automated check rather than kept by hand, which is why it moved with the migration instead of falling behind it.
protocol-code-sync.test.tsNo database migration. Administrator-only actions — managing people and their roles, and the organization-wide settings — are now refused by Indelio's data layer as well as by the screen they are reached from. Nothing an administrator can do has changed.
ADMINISTRATOR-ONLY ACTIONS ARE ENFORCED IN THE DATA LAYER, NOT ONLY IN THE SCREEN THEY ARE REACHED FROM. Sixteen administrative actions checked for the Admin role in the page action and then called the underlying function, which accepted whoever it was given: inviting a colleague, changing their roles, deactivating them, resetting their password or clearing their second factor; the "attached evidence must be opened before signing" switch; the "a reviewer must state a review context" switch; the wording your signatures carry and the list of review contexts a reviewer may choose; document types and numbering; the AI on/off switch; the complaint-intake token; and recording a regulation change, which flags effective documents for periodic review. Every one of them now checks the Admin role where the work is done, before anything is read or written. ⚠️ No non-administrator could reach any of these through Indelio — each had exactly one route in, and that route checked. This closes the shape rather than an incident, and the check is the same one you saw before: what your administrators could do is unchanged, and what everyone else could do is unchanged.
Why this classification: Classified “requalify” because access restrictions changed — the conservative classification, and the honest one even though the enforced outcome is identical. Nobody gains or loses the ability to do anything: the same Admin role was already required for every one of these actions and no role, permission, signature, content-hash definition, lifecycle rule or database schema changed. What changed is where the restriction is enforced, so that it holds for any future route to the same function. A customer whose access-control qualification tested these actions through the screens will find the same results; one who tested them by calling the functions directly should re-run that part.
admin-only-in-lib.test.tsremoval-trail.test.tsNo database migration. Creating a record is refused, with a message asking to try again, when the codes already in use cannot be read — instead of failing with the database's own "duplicate key" message.
A NEW RECORD'S CODE IS NEVER WORKED OUT FROM A LIST THAT COULD NOT BE READ. Every module numbers its next record code — CAPA-004, RA-011, DHF-002 — from the codes your organization already holds. Before this release, if that list could not be read at that moment (a database timeout, for example), the next code was worked out as though you held none, so it came out as 001. No duplicate code was ever created: each code is unique per organization in the database, which refused to save the record. But what you were shown was the database's own message — "duplicate key value violates unique constraint" — for a read that had timed out. Creating the record is now refused with a message that says the existing records could not be read and asks you to try again. Numbering is otherwise unchanged, in all nineteen modules.
Why this classification: Classified “review” because the outcome of a failed read is unchanged — no record is created either way — while the message a user and an investigator see changed, in every module that numbers records. No signature, content-hash definition, role, permitted state or database schema changed.
record-code-fails-closed.test.tsNo database migration. The design module no longer says its traceability and sign-off gates are enforced in the database; they are enforced by Indelio's server when a signature is applied.
DESIGN PAGES STATE CORRECTLY WHERE THE SIGN-OFF GATES ARE ENFORCED. The design project list and each project's traceability coverage card said the traceability and sign-off gates were "enforced in the database". They are enforced by Indelio's server code when a verification or validation signature is applied — which is what makes them apply however the request arrives — but not by a database constraint. The wording now says server-side. The gates themselves are unchanged. The database controls Indelio does state — append-only records and the hash-chained audit trail, tenant isolation, one revision in progress per document — are unchanged and still described as database-enforced.
Why this classification: Classified “none” because only on-screen wording changed, to match the existing control; no function, gate, signature or record changed. A customer whose validation documentation quotes the old wording may want to update it.
copy-claims.test.tsA database migration is required in this release: run `supabase/spreadsheet_import.sql`. It adds Spreadsheet Import — a way to bring an existing risk analysis in from a CSV file as a new Draft risk assessment — and two provenance columns on risk assessments and risk items. Nothing you already use behaves differently.
NEW: SPREADSHEET IMPORT, for bringing an existing risk analysis in from a CSV file. Each row becomes a risk item in a NEW risk assessment, created as a Draft. You match the file's columns to Indelio's fields, every row is checked before anything is written, and a row with a problem cannot be imported until the file is corrected, the matching is changed, or the row is skipped with a recorded reason. ⛔ An import never supplies a value the file does not contain: a blank or unreadable severity or probability is a problem on that row, never a default score of 1. ⛔ Nothing is signed on anyone's behalf: the assessment is reviewed and approved in Indelio with e-signatures like any other, and an approval recorded in your previous system is not carried across as one. Rows are written through the risk module's own path, so the same sanitising, Cyber scoring rule, audit entries and content hash apply as for an item typed in by hand. Once the import is complete, a person with the qa or approver role — never the person who ran it — signs ONE verification of the whole transfer, with the new signature meaning “Import verified by”; that signature binds to the source file's hash, how it was read, the record created, and every row's imported values or skip reason. The source file is kept with the import and is included in your data export. Running an import needs the implementer, author or admin role.
Why this classification: Classified “review” because it is a new function that creates records in your risk management file and introduces a new e-signature meaning, which your quality function should assess against your intended use before relying on it — for example, whether your data migration procedure covers it and who may run it. It is not “requalify”: no existing control changed. The risk module's review and approval gates, its signature meanings and the definition of its content hash are unchanged; the two new columns on risk assessments and risk items record which import created them and are deliberately NOT part of the signed content, so no existing signature is affected. Until the migration is applied the import page cannot be used; the rest of Indelio is unaffected either way. ⚠️ The automated evidence covers the reading, checking, matching and manifest rules and pins the write path by inspection; the commit and verification steps themselves run against the database and are not executed by the automated suite.
import.test.tsexport.test.tsNo database migration. Adding a design input, output, verification, validation or review, or a management-review action, is refused — with a message asking to try again — if the existing items cannot be read, instead of numbering the new item 1.
ITEM NUMBERS ARE NEVER ASSIGNED OVER A LIST THAT COULD NOT BE READ. A new design input, output, verification, validation or design review, and a new management-review action, is numbered one above the highest existing number. Before this release, if the existing items could not be read when the item was added (a database timeout, for example), the new item was numbered 1 — even when an item 1 already existed — so two different items could carry the same number in the design history file, its traceability report, the management review and the audit trail. The addition is now refused in that case with a message asking to try again. When the items can be read, numbering is unchanged, except that it now always follows the highest existing number. Audit findings, project deliverables, risk items and CAPA actions already worked this way.
Why this classification: Classified “requalify” because adding a record item is now refused in a failure condition in which it previously went ahead with a number that could duplicate an existing one, as in the equivalent fix for audit findings, project deliverables and risk items. No signature, content-hash definition, role or database schema changed.
item-numbering-fails-closed.test.tsNo database migration. The design project page no longer shows lists it could not read as empty, or traceability coverage it could not check as complete; it says what could not be read. The design traceability PDF is not generated in that case.
THE DESIGN PAGE SAYS WHAT IT COULD NOT READ. A design project page shows its design inputs, outputs, trace links, verifications, validations, design reviews and signatures, and a traceability coverage summary built from them. Before this release, a list that could not be read when the page loaded (a database timeout, for example) was shown as empty — "No design inputs yet.", "No signatures yet" — and the coverage summary and traceability matrix were computed from the empty list, so the page could show every input as untraced, or none. Now the page names each list that could not be read, shows "could not be read" in its place, and reports the coverage summary as not checked — never as a count — whenever the inputs, trace links, verifications or validations could not be read. When everything can be read, the page is unchanged. The audit, project, risk, CAPA and human-factors pages already worked this way.
Why this classification: Classified “review” because what the design page displays changed in a failure condition: a list or a coverage conclusion that could not be established is now shown as such instead of as empty or complete. No control, signature or permitted action changed.
design-trace-gate.test.tspage-integrity-unreadable.test.tsTHE DESIGN TRACEABILITY PDF IS NOT GENERATED FROM AN INCOMPLETE READ. If any of a design project's inputs, outputs, trace links, verifications, validations, reviews or signatures cannot be read, the traceability PDF is no longer produced — it had printed the affected sections as empty and, when the inputs or trace links were unread, stated in green that every input was traced to an output. A message names what could not be read and asks to try again. The CAPA, audit and risk PDFs already worked this way.
Why this classification: Classified “review” because a record output is now withheld in a failure condition in which it was previously produced with false statements. When the reads succeed, the PDF is unchanged.
page-integrity-unreadable.test.tsNo database migration. The design verification and validation sign-offs are refused, with a message asking to try again, when the traceability they check cannot be read; and trace links, verifications and validations can only name the design project's own inputs and outputs.
DESIGN SIGN-OFFS NO LONGER PASS ON TRACEABILITY THEY COULD NOT READ. Before the verification sign-off, Indelio checks that every design input is linked to an output and that no verification is Pending or failed; before the validation sign-off, the same for validations. Those checks read the project's design inputs, trace links, verifications and validations. Before this release, a list that could not be read — a database timeout, for example — was treated as empty: if the design inputs could not be read, no input counted as untraced, and the verification sign-off was applied without the traceability check it exists for. (If the verifications or validations could not be read, the sign-off was refused, but with a message saying none existed.) Now, if any of those lists cannot be read, the sign-off is refused with a message saying so, and nothing is signed. When they can be read, the checks are unchanged.
Why this classification: Classified “requalify” because a design sign-off signature gate now refuses in a failure condition in which it previously signed, as in 2026.09.16.7. No signature meaning, content-hash definition, role or database schema changed.
design-trace-gate.test.tsDESIGN LINKS MUST NAME THE PROJECT'S OWN INPUTS AND OUTPUTS. A trace link, a verification (its input and output) and a validation (its user need) could name an input or output belonging to a different design project, because nothing checked it — and a trace to another project's output made an input count as traced for the verification sign-off. Each is now refused unless the input or output belongs to the same project, and refused with a message to try again if that cannot be checked. The audit-trail entry for a new trace link now names the input and output it joins, as the entry for removing one already did; linking a pair that is already linked no longer adds a second entry. Design-to-risk links already checked their design project.
Why this classification: Classified “requalify” because traceability records that were previously accepted are now refused, and what the audit trail records for a new trace link changed (21 CFR 11.10(e); ISO 13485 §7.3.6).
design-trace-gate.test.tsNo database migration. A project with no deliverables can no longer be signed complete.
PROJECT COMPLETION REFUSES A PROJECT WITH NO DELIVERABLES. Activating a project already required at least one deliverable, but deliverables can still be removed while a project is Active. Completion checked only that no deliverable was still open — and a project with no deliverables passes that. So a project whose deliverables had all been removed after activation could be signed complete, with a permanent "Project completed by" signature over nothing delivered. Completion now refuses a project with no deliverables and asks for at least one to be added. Projects with deliverables are completed exactly as before. The same gap was closed for risk review and approval in 2026.09.17.1.
Why this classification: Classified “requalify” because the completion signature gate now refuses in a condition in which it previously signed, as in 2026.09.17.1. No signature meaning, content-hash definition, role or database schema changed.
signature-gates-unreadable.test.tsNo database migration. A risk assessment with no risk items can no longer be reviewed or approved.
RISK REVIEW AND APPROVAL REFUSE AN ASSESSMENT WITH NO RISK ITEMS. Submitting a risk assessment for review already required at least one risk item, but items can still be removed while it is In Review. Review checked only that no item lacked an acceptability decision, and approval only that no item was marked Not acceptable — and an assessment with no items passes both. So a risk file whose items had all been removed after submission could be reviewed and then approved, releasing a signed risk management file with nothing in it. Review now refuses an assessment with no risk items and asks for at least one to be added; approval refuses it too, and says to cancel the assessment and open a new one, because an assessment awaiting approval can no longer be edited. Assessments with items are reviewed and approved exactly as before.
Why this classification: Classified “requalify” because a signature gate on the risk management file — review and approval — now refuses in a condition in which it previously signed, as in 2026.09.16.7. No signature meaning, content-hash definition, role or database schema changed.
signature-gates-unreadable.test.tsNo database migration. The audit, project, risk assessment, URRA, critical-task register and summative test pages no longer warn that a record was changed outside Indelio, or show its findings, deliverables, items, tasks, results or signatures as empty, when those could not be read. They say what could not be read instead, and the audit report and risk management file PDFs are not generated in that case.
RECORD PAGES NO LONGER REPORT A FAILED READ AS TAMPERING OR AS AN EMPTY LIST. Each of these six pages checks the record — its fields and its findings, deliverables, risk items, analysis items, critical tasks or task results — against the content hash stored with it, and shows a red warning if they do not match. Before this release, if those rows could not be read when the page loaded (a database timeout, for example), the check was made as though the record had none: the page showed the red warning that the record had been changed without going through Indelio, listed the rows as empty ("No findings recorded.", "No risk items."), and, if the signatures could not be read, showed "No signatures yet". Now the page says which of these could not be read and that the record's integrity was not checked, shows no red warning, and shows "could not be read" in place of the empty list. A record whose rows are read and genuinely do not match its stored hash still shows the red warning. The CAPA page has worked this way since 2026.09.16.1. What each record contains, and how its signatures are bound, is unchanged.
Why this classification: Classified “review” because what the record pages display about integrity changed in a failure condition: a check that could not be made is now shown as not checked instead of as a mismatch (21 CFR 11.10(a)). The integrity check itself, the content hash, signatures and every permitted action are unchanged.
page-integrity-unreadable.test.tsAUDIT REPORT AND RISK MANAGEMENT FILE PDFs ARE NOT GENERATED FROM AN INCOMPLETE READ. If an audit's findings or signatures, or an assessment's risk items or signatures, cannot be read, the PDF is no longer produced — it had printed "No findings recorded." or "No risk items recorded." and an integrity warning. A message names what could not be read and asks to try again. The CAPA record PDF has worked this way since 2026.09.16.1.
Why this classification: Classified “review” because a record output is now withheld in a failure condition in which it was previously produced with false statements. When the reads succeed, the PDFs are unchanged.
page-integrity-unreadable.test.tsNo database migration. Every database write in Indelio now checks that it worked before anything is recorded about it. Most visibly: a document release is recorded only when the new version is actually in force, and a failed release puts the previous version back; retraining that could not be assigned is recorded; and human-factors approvals, imports and report assembly, and document-migration steps, no longer go ahead — or record that they did — when a database step fails.
DOCUMENT RELEASE IS RECORDED ONLY WHEN IT HAPPENED. Releasing an approved version — on approval, or on its effective date for a future-dated approval — withdraws the version in force and puts the new one in force. Before this release, none of those steps checked whether it had worked. If the version in force could not be read (a database timeout, for example), it was not withdrawn; the database then refused to put a second version in force, and the audit trail still recorded "Approved version released" while the new version stayed unreleased and no retraining was assigned. A failed withdrawal or a failed update was ignored in the same way. Each step is now checked. If the version in force cannot be read or cannot be withdrawn, no version is withdrawn or put in force and the release is refused with a message asking to try again. If the new version cannot be put in force after the old one was withdrawn, the old one is put back in force, both steps are recorded in the audit trail, and the release is refused; if putting it back also fails, the message states that the document has no version in force. For a release on approval, the approval signature already applied stays on record and the version remains In Review, so approving again releases it. A future-dated release that fails is retried by the next daily run, as before, and is no longer reported as released. When every step succeeds, release behaves as before.
Why this classification: Classified “requalify” because a controlled-document lifecycle step is now refused in failure conditions in which it previously went ahead and was recorded inaccurately, and because a new audit-trail entry (the previous version put back in force) can now be written (21 CFR 11.10(e); ISO 13485 §4.2.4). No signature, content-hash definition, role or database schema changed.
write-results-checked.test.tsRETRAINING THAT DID NOT HAPPEN IS NOW RECORDED. When a revised document is released, Indelio cancels open training on the previous version and assigns retraining to everyone trained on it. Any failure in that step was silently ignored: if the training assignments could not be read, nobody was assigned retraining and nothing was recorded; an open assignment that could not be cancelled was recorded as cancelled; and an assignment that could not be created was skipped without a word. Retraining still never blocks the release — the document is already in force — but a failure is now recorded in the document's audit trail as "Document revised — retraining could not be fully reassigned; check training for this document", naming each step that failed, and a cancellation is recorded only when it happened. When every step succeeds, retraining behaves as before.
Why this classification: Classified “requalify” because what the audit trail records about retraining changed — a new entry for incomplete retraining, and no cancellation entry for a cancellation that failed (21 CFR 11.10(e); ISO 13485 §6.2). Who is assigned retraining, and when, is unchanged.
write-results-checked.test.tsHUMAN FACTORS: APPROVALS, CANCELLATIONS, IMPORTS AND ASSEMBLY NO LONGER PASS ON A FAILED DATABASE STEP. (1) Approving or cancelling a URRA, critical-task register, summative test or HFE/UE report did not check that the record's state actually changed, and the audit trail recorded the approval or cancellation either way. It is now checked. If an approval's state change fails, the approval signature — which cannot be removed — stays on record, the record stays in Draft, no approval is recorded in the audit trail, and the message says so. (2) If a URRA's, register's or summative test's items could not be read, Indelio treated the record as having none: approval was refused for the wrong reason ("add at least one item"), and after an edit the record's content hash — which the approval signature binds — was rewritten over an empty list. Approval and header edits are now refused with a message saying the items could not be read. After an item is added, edited or imported, the change is kept and recorded — the audit entry is now written before the hash is recalculated — and if the items cannot be read back, the hash is left as it was and the user is told. (3) Importing critical tasks from URRAs, or summative tasks from a register, reported "0 added" when the source could not be read, and imported every task a second time when the tasks already imported could not be read. Both are now refused, and a register that cannot be read is no longer reported as not found. (4) Assembling an HFE/UE report recorded a referenced record that could not be read as "missing", and one whose items could not be read as having none — in the snapshot the report's approval signs. Assembly is now refused and the previous snapshot is kept. When the database responds normally, each behaves as before.
Why this classification: Classified “requalify” because approval, cancellation, import and assembly of human-factors records are now refused in failure conditions in which they previously went ahead or were recorded inaccurately, and the order of an audit entry and a content-hash update changed — as in 2026.09.16.7 and 2026.09.16.8. No signature meaning, content-hash definition, role, permitted state or database schema changed.
write-results-checked.test.tsDOCUMENT MIGRATION: A STAGED FILE'S STATUS NOW FOLLOWS ITS DOCUMENT, OR THE USER IS TOLD. Committing a migrated document, or rolling one back, updates the staged file's status after the document is created or withdrawn. That update was not checked: a failure left a document in force whose file still read as not committed — outside the set QA verifies — or a withdrawn document that still counted as committed. The audit entry is now written as soon as the document is created or withdrawn, and a failed status update is reported: after a commit, with a message not to verify the batch and to contact Indelio support; after a rollback, with a message to roll it back again. Rolling back or skipping a file whose batch cannot be read is now refused — before, the QA-verified check was skipped in that case. Rolling back a whole batch is refused if its committed documents cannot be read, instead of reporting that there were none, and a failure to update a batch's status is reported.
Why this classification: Classified “requalify” because migration steps, including the QA-verified check, are now refused in failure conditions in which they previously went ahead, and the order of an audit entry changed. When the database responds normally, migration behaves as before. No signature, role or database schema changed.
write-results-checked.test.tsCAPA: RETURNING A REQUEST FOR UPDATES IS REFUSED IF THE CAPA CANNOT BE UPDATED. Returning a CAPA request to its owner updates the CAPA's last-changed time and records the reviewer's comments. A failure of that update was ignored and the return recorded anyway; the return is now refused with the database's message, and nothing is recorded.
Why this classification: Classified “review” because a routing step is now refused in a failure condition. The return is not signed and changes no record content; when the update succeeds it behaves as before.
write-results-checked.test.tsACCOUNT ADMINISTRATION AND DISPLAY PREFERENCES REPORT FAILURES. Setting a trial organization's end date when it is set up, stamping that a trial reminder was sent, and resetting your own dashboard module order each ignored a failed database write. Each failure is now reported (a trial reminder that could not be stamped is counted as an error in the daily run).
Why this classification: Classified “none” because these are Indelio's own account administration and a personal display preference, not functions that operate under your quality system.
write-results-checked.test.tsNo database migration. Every remaining removal now records in the audit trail what it removed — including linked records the database removes with it — and is refused, with a message asking to try again, if what it would remove cannot be read first. Document types gain an audit trail, and a design↔risk link on a closed design project can no longer be removed when the project's state cannot be read.
REMOVALS NOW RECORD WHAT THEY REMOVED, IN EVERY MODULE. Following 2026.09.16.9, every place Indelio deletes a record was checked. The audit-trail entry now carries the removed content for: CAPA action items (the entry held only an internal id); design inputs, outputs, verifications and validations (the entry named only the action); design trace links (the entry did not say which input and output they joined); human-factors URRA items, critical tasks and summative results; management-review actions arising; quality objectives; training requirements; review contexts; and attachments (the entry now also carries the file's size, type, content hash, uploader and upload time). Where the database removes or unlinks other records along with the one removed — a design input's or output's trace links and design↔risk links, a verification or validation that pointed at it, a risk item's design↔risk links, a training group's requirements and memberships — those are now read first and recorded in the same entry. In each case the removal is refused, with a message asking to try again, if the record or those linked records cannot be read first, so nothing is deleted unrecorded. Removing a design trace link now also reports a failed delete instead of recording it as done. An attachment's database record is now removed before its stored file rather than after, so a removal that fails part-way can no longer leave a record pointing at a file that has already been deleted. Who may remove what, and in which states, is unchanged.
Why this classification: Classified “requalify” because what the audit trail records for existing actions changed, and removals are now refused in a condition in which they previously went ahead — as in 2026.09.14.2 and 2026.09.16.9 (21 CFR 11.10(e)). No signature, content-hash definition, role, permitted state or database schema changed.
removal-trail.test.tsDOCUMENT TYPES NOW HAVE AN AUDIT TRAIL. Adding or removing a document type — the list that decides how controlled documents are numbered — wrote no audit-trail entry at all. Both now do, and a removal records the type's name, prefix and number width. Managing document types was already limited to admins by the settings page; the same check is now also made where the change is saved.
Why this classification: Classified “review” because a configuration change that affects controlled-document numbering is now recorded where it was not. Nobody who could manage document types before is refused now.
removal-trail.test.tsTRAINING GROUP MEMBERSHIP AND DESIGN↔RISK LINKS: A FAILED READ NO LONGER PASSES. Changing a training group's members read the current members first; if that read failed, the group was treated as empty — nobody was removed, everyone selected was recorded as added, and the audit entry said so. The change is now refused in that case, a member removal that fails is reported instead of being recorded as done, and if the change stops part-way the entry records only what actually happened. Separately, removing a design↔risk traceability link checks that the design project is not Closed or Cancelled; if the project could not be read, that check was skipped and the link removed. It is now refused. Resetting a signature meaning to its default is refused if the current meaning cannot be read, so the entry cannot misstate what it replaced.
Why this classification: Classified “requalify” because a membership change, a traceability-link removal on a closed project, and a signature-meaning reset are each now refused in a failure condition in which they previously went ahead or were recorded inaccurately. When the reads succeed, each behaves as before.
removal-trail.test.tsNo database migration. Removing a project deliverable or a risk item now records in the audit trail what the removed row contained, and removing a deliverable, audit finding or risk item is refused, with a message asking to try again, if the row cannot be read first.
REMOVING A DELIVERABLE OR RISK ITEM NOW RECORDS WHAT IT CONTAINED. A removed project deliverable or risk item is deleted, so its audit-trail entry is the only account of it that remains. Before this release that entry read only "Deliverable removed" or "Risk item removed" — not which one, and nothing of what it recorded. The entry now names it in its reason (for example "Risk item #3 removed (Free-flow on door open)") and carries the full content of the removed row. Audit findings already recorded this. Who may remove a deliverable or risk item, and in which states, is unchanged: deliverables while the project is in Planning or Active; risk items while the assessment is Draft or In Review.
Why this classification: Classified “requalify” because what the audit trail records for an existing action changed, as in release 2026.09.14.2: removal entries now carry the removed content (21 CFR 11.10(e)). No signature, content-hash definition, role, permitted state or database schema changed.
signature-gates-unreadable.test.tsA ROW THAT CANNOT BE READ IS NO LONGER REMOVED. Removing a deliverable, audit finding or risk item now reads the row first and is refused, with a message asking to try again, if it cannot be read — a database timeout, for example — so its content is never deleted unrecorded. Before this release a deliverable or risk item was removed without being read at all, and an audit finding that could not be read was removed anyway, with an entry saying its content could not be read. A removal of a row that does not exist is now refused rather than recorded.
Why this classification: Classified “requalify” because a record-handling step is now refused in a condition in which it previously went ahead. When the row can be read, removal behaves as before apart from the fuller audit entry above.
signature-gates-unreadable.test.tsaudit-finding-trail.test.tsNo database migration. After an edit to a project, audit or risk assessment, a record's content hash is no longer rewritten as though it had no deliverables, findings or risk items when those could not be read back; the edit is kept, recorded in the audit trail, and the user is told the hash was not updated.
PROJECTS, AUDITS AND RISK: THE CONTENT HASH IS NO LONGER WRITTEN OVER AN UNREAD LIST. Each of these records carries a content hash that covers the record and its deliverables, findings or risk items, and the electronic signatures on it are bound to that hash. After every edit, Indelio reads the record and those rows back and rewrites the hash. Before this release, if the rows could not be read at that moment — a database timeout, for example — the hash was rewritten as though the record had none; if the record itself could not be read, nothing was written and nothing was said; and a failure to write the hash was ignored. A signature applied next would then have been bound to content the record does not have, and read as broken once the hash was recalculated. Now, in any of those cases, the stored hash is left as it was, the edit is kept and recorded in the audit trail — the audit entry is now written before the hash is recalculated — and the user is told that the hash was not updated and that the record may show an integrity warning until the next saved change updates it. When the reads succeed, edits behave exactly as before. This is the same defect release 2026.09.16.1 fixed for CAPA.
Why this classification: Classified “requalify” because how record content hashes — which electronic signatures bind to — are maintained changed: in a failure condition the hash is now left unchanged and the user is told, where it was previously rewritten over incomplete content or silently not updated. When the reads succeed, the same hash is computed as before and no content-hash definition, signature meaning, role or database schema changed. The audit entry for an edit is now written before its hash is recalculated rather than after.
signature-gates-unreadable.test.tsNo database migration. Completing a project, closing an audit, and reviewing or approving a risk assessment are refused, with a message asking to try again, when the list each step checks cannot be read — instead of being signed as though that list were empty.
FOUR SIGNATURE STEPS NO LONGER SIGN WHEN THE LIST THEY CHECK CANNOT BE READ. Each of these steps checks a list before applying its electronic signature: completing a project checks that every deliverable is Complete; closing an audit checks that no Major or Minor finding is still Open; reviewing a risk assessment checks that every risk item has an acceptability decision; approving one checks that no risk item is marked Not acceptable. Before this release, if that list could not be read at the moment of signing — a database timeout, for example — the step treated it as empty, found nothing blocking, and applied the signature. Because signatures cannot be altered or removed, a signature applied that way would stay on the record. Each step now refuses in that case with a message asking to try again, and nothing is signed or changed. When the list can be read, every step behaves exactly as before. This is the same kind of defect that release 2026.09.16.1 fixed for CAPA action items.
Why this classification: Classified “requalify” because the behaviour of four electronic-signature steps changed: each is now refused in a condition in which it previously went ahead. When the checked list can be read, nothing that was permitted is refused, and no signature meaning, content-hash definition, role or database schema changed. A check of production records made for this release found no signature applied through these four steps while a blocker was present, so no existing record is affected.
signature-gates-unreadable.test.tsNo database migration and no change to how any module behaves. Three help topics described controls Indelio does not enforce, and have been corrected; several others now name every signature their module applies.
THREE HELP TOPICS NO LONGER DESCRIBE CONTROLS INDELIO DOES NOT ENFORCE. After the CAPA help correction, every workflow help topic was checked against the module it describes, and three said more than the system does. (1) Change Control said the approver can't be the person who raised the change. Indelio does not check that: it prevents the person who approved a change from also closing it, and nothing more. (2) Post-Market Trends said a trend disposition is signed. It is not an electronic signature: no password is asked for. The disposition is saved under the name of the person who records it, content-hashed, written to the audit trail and cannot be edited afterwards. (3) Documents said a reviewer is "not the author". Indelio allows a document's author to review it; what it refuses is the author approving it. Each topic now says what the system actually does, and where a procedure needs the stricter rule, that it must be followed procedurally. None of these behaviours changed in this release — the help was wrong. Automated checks now read each of these facts from the module itself.
Why this classification: Classified “none” because no control, record, signature, role or lifecycle rule changed; the help text now matches behaviour that was already in place. It is recorded because the previous text overstated three controls. If your procedures, training or validation relied on Indelio's help for any of them — that a change's requester cannot approve it, that trend dispositions are e-signed, or that a document's author cannot review it — Indelio does not enforce those, and they would need to be controlled procedurally.
help.test.tsHELP NOW NAMES EVERY SIGNATURE IN TEN MORE MODULES. Complaints now describes the "Investigated by" signature that comes before closure, and that the investigator can't sign the closure. Audits now says that closing an audit is an e-signature, "Audit closed by", by someone other than the person who issued the report. Change Control, Nonconformances, Design Controls, Projects, Management Review, Initial Impact Determination, Assessments, Corrections & Removals and Risk Management now name each of their signature meanings, including cancellation and reopening where those are signed, and the Getting started topic's list of records that refuse their author's signature now includes risk assessment approval and validation project completion. An automated check reads each module's signature meanings and fails if its help topic does not name one.
Why this classification: Classified “none” because these signatures and rules were already enforced; only their description was added.
help.test.tsNo database migration and no change to how any module behaves. The CAPA help topic now describes every signature a CAPA carries — it had left out the request review and the second action-plan approval — and help assistant answers show their headings, bold text and lists as formatting, and say so when they were cut short.
CAPA HELP NOW NAMES EVERY SIGNATURE A CAPA CARRIES. The CAPA help topic said that an approver signs to approve the action plan, and nothing more before closure. The CAPA module has required more than that since the request gate and dual plan approval were introduced: a new CAPA is raised as a request that a person with the approver or QA role must review ("CAPA request approved by", or "CAPA request rejected by"), and the action plan needs two approvals by two different people — "Action plan approved by (Approver)" and "Action plan approved by (QA)" — before implementation starts. Because /help and the help assistant read the same text, the assistant filled the gap when asked on 16 September to walk through a CAPA: it said a CAPA carries two signatures, named the plan signature "Change approved by" (a Change Control meaning), and said a CAPA starts in Draft. The topic now walks through each state and names all seven CAPA signature meanings — including cancellation and reopening — with the on-screen button for each step. An automated check reads the signature meanings out of the CAPA module itself, so a signature step cannot be added without the help naming it.
Why this classification: Classified “none” because no control, record, signature, hash, role or lifecycle rule changed: the CAPA workflow is exactly what it was, and the help now describes it. It is recorded here because the previous text understated the signatures the system requires, so a procedure or training material written from Indelio's help may describe fewer CAPA signatures than your records carry.
help.test.tsHELP ASSISTANT ANSWERS ARE SHOWN FORMATTED. The assistant writes its answers with headings, bold text and lists, and they were displayed with the markup characters showing — lines beginning with "#" and words wrapped in "**". They are now shown as headings, bold text, bulleted lists and numbered lists, using the same text renderer as Indelio's compliance documents. The documents render exactly as before: numbered lists are recognised only in assistant answers. Your own question is still shown exactly as typed.
Why this classification: Classified “none” because this is how one panel on the help page displays text. No record, control or answer content changed, and the help page holds no quality records. The renderer produces text only — never HTML from the answer — so displaying model output through it adds no way to inject content into the page.
markdown-lite.test.tsA HELP ANSWER THAT WAS CUT OFF NOW SAYS SO. Help assistant answers have a length limit. An answer that reached it stopped mid-way with no indication, so it read as a complete answer — for a CAPA walkthrough, possibly one that ended before the QA closure step. The limit is raised, and an answer that still reaches it ends with a note that it was cut short and a suggestion to ask about one part at a time.
Why this classification: Classified “none” because the help assistant is guidance on using the software, not a function under your quality system, and no record or control changed. The change makes an incomplete answer visibly incomplete.
help.test.tsDatabase migration required: supabase/risk_exploitability.sql (adds four optional columns to risk items). Cyber risks in Risk Management are now scored by exploitability instead of probability, with an optional CVSS reference. Existing risk assessments, their scores and their signatures are unchanged.
CYBER RISKS ARE SCORED BY EXPLOITABILITY. On a risk item whose design domain is Cyber, the form now asks for Exploitability and Residual exploitability (1 Very low to 5 Very high) instead of probability, and the risk level is severity × exploitability on the same 1–5 scale with the same bands (13 and above High, 5 to 12 Medium, below 5 Low). This follows FDA's premarket cybersecurity guidance, which assesses security risk by exploitability rather than probability. Your SOP defines what each exploitability level means. A CVSS score (0.0–10.0) and vector may be recorded on a Cyber risk for reference; they do not change the risk level or the acceptability decision. The risk-file PDF prints E instead of P for these items and marks the CVSS figures as reference only. RULES: adding a Cyber risk, or changing a risk's domain to Cyber, requires an exploitability; exploitability and CVSS are refused on a risk that is not Cyber; a residual exploitability needs an initial one. A Cyber risk recorded BEFORE this release keeps its probability score, and is shown as such, until an author enters an exploitability on an assessment that is still editable. Indelio scores cyber risks inside the risk file; it does not produce the separate security risk management plan and report that FDA describes (for example AAMI TIR57 or ANSI/AAMI SW96). VALIDATION DOCUMENTS: the IQ protocol step IQ-06 now counts 91 migration files (was 90); the URS adds requirement UR-FN-09, verified by automated OQ evidence and not yet by any PQ step.
Why this classification: Classified “review” because the risk estimation method for Cyber risks changed, and adding a Cyber risk without an exploitability is now refused where it was previously accepted. No existing record, score, signature or content hash changed: the new fields count toward an assessment's content hash only when they are filled in, and an automated check pins the hash of an assessment written before this release. If your procedure or your PQ describes probability as the likelihood measure for every risk, review it for Cyber risks.
risk-exploitability.test.tsrisk.test.tsseed-hash-parity.test.tsA SAVE NO LONGER RESETS A LIKELIHOOD SCORE THE FORM DID NOT SHOW. Found while building the change above: the risk-item save treated a field the form had not displayed as blank, which would have set a stored probability to 1 and cleared a stored residual probability. Before this release the form always displayed both fields, so no saved record was affected; the save now leaves any field the form did not send unchanged.
Why this classification: Classified “none” because no record was ever saved this way — the form always sent both fields until this release — and no control, signature or hash changed. It is recorded because the defect would have lost data the moment Cyber risks stopped showing probability.
risk-exploitability.test.tsTHE RISK MANAGEMENT PRODUCT PAGE NO LONGER SAYS THE SCORE DECIDES ACCEPTABILITY. It described the severity × probability band as showing “what's acceptable, ALARP, or unacceptable”, and listed “banded risk levels (acceptable / ALARP / unacceptable)”. The bands are Low, Medium and High; whether a risk is acceptable is a separate decision recorded on each item. The page now says so, and mentions exploitability scoring for Cyber risks.
Why this classification: Classified “none” because this corrects public product copy. No function under your quality system changed; the acceptability decision has always been separate from the band.
No database migration and no change to how any module behaves. Three corrections to the validation documents under Compliance & Validation, and one to the public security page.
THE 21 CFR PART 11 PACKAGE, THE VALIDATION PLAN AND THE PQ PROTOCOL WERE CORRECTED. (1) PART 11, §11.300(b) PASSWORD AGING: the traceability table said password aging was “Not enforced” and marked it as a gap, while its own gap register (§6, gap 4) already recorded it as closed. It IS enforced: a password older than 90 days forces a reset before the app can be used, and the user is told why. The row now says so, names its limit — aging runs from the date a password was last changed under the policy, so a password that predates the policy is not aged until its first change — and cites its automated evidence. (2) PART 11, §11.10(a) VALIDATION OF SYSTEMS: the row said IQ, OQ and PQ were all still “to execute”. IQ has been executed and signed against Indelio's own production instance — for its automated portion only, with the vendor as executor — OQ evidence is compiled but not signed, and PQ is not executed. The row now says that. (3) PQ PROTOCOL, STEP PQ-4.3: the expected result said a risk item records “hazard, hazardous situation, harm, and the control”, which reads as two separate fields for hazard and hazardous situation. Indelio records them in one field (“Hazard / hazardous situation”). The expected result now says so. (4) PART 11 AND VALIDATION PLAN: both said the Semgrep static-analysis scan runs on “every push”. It runs on every pull request. ⚠️ WHAT THIS MEANS IF YOU USED AN EARLIER COPY: if your PQ execution record expects two separate hazard fields at PQ-4.3, correct that expectation; if you cited the security-scan cadence from either document, cite “every pull request”.
Why this classification: Classified “review” because these are vendor documents you may have adopted or cited, and wording you may have relied on changed — one correction removes an overstatement (the scan cadence) and two remove understatements (password aging, IQ execution). No control, record, signature, hash, role or lifecycle rule changed; password aging was already enforced before this release.
validation-doc.test.tsvalidation-library-sync.test.tspassword-aging.test.tsscan-cadence-claims.test.tsTHE PUBLIC SECURITY PAGE SAID SEMGREP RUNS ON EVERY PUSH. It runs on every pull request — the workflow was changed on 26 August 2026 to stop scanning each change twice, and the page was not updated. The page now says “every pull request”, and an automated check now reads the workflow files so the stated cadence cannot drift from the real one again.
Why this classification: Classified “none” because this corrects a sentence on a public marketing page. No function under your quality system, and no record, control or signature, changed.
scan-cadence-claims.test.tsNo database migration and no change to how any module behaves. Risk Management help now states how a risk level is scored and what its bands mean, and a long answer from the help assistant no longer opens scrolled past its own first lines.
RISK HELP NOW EXPLAINS THE SCORING SCALE IT HAD BEEN SILENT ABOUT. The Risk Management help topic described what to enter but never what the result means. It did not say that risk level is severity × probability; that the bands are fixed at 13 and above High, 5 to 12 Medium, below 5 Low; that residual scores are recorded by you rather than calculated, and that an item falls back to its initial score until both residual fields are filled in; or that a control normally lowers PROBABILITY and not severity — so an item whose harm is Catastrophic scores at least 5 even at the lowest probability, can read Medium after a fully effective control, and cannot reach Low. Because /help and the help assistant read the same text, the assistant could not answer "why is this still Medium?" either: asked on 15 September it said so plainly, and then offered as its first explanation that the residual scores had not been updated — which, on an item already at the bottom of its scale, would send a user to change a severity that was correct. All four points are now in the help topic, and an automated check reads the thresholds out of the scoring function itself, so the numbers a user is taught cannot drift from the numbers the product applies.
Why this classification: Classified “none” because no control, record, signature, hash, role, lifecycle rule or score changed. The scale is exactly what it has always been; this is the first time the help text states it. It is recorded here rather than left silent because the previous omission could have led someone to adjust a severity that was not wrong.
help.test.tsA LONG HELP ANSWER NOW OPENS AT ITS FIRST LINE. The help assistant's message box scrolled to its own bottom whenever a message arrived. For an answer taller than the box, that showed the END of the answer and the reader had to scroll up to find where it began — which reads as a truncated answer rather than a scrolled one. A finished answer now opens with its first line at the top of the box; your own question and the "Thinking…" line still sit at the bottom, where the newest thing is the last line.
Why this classification: Classified “none” because this is the scroll position of one panel on the help page. No record, control, content or answer changed, and the help page holds no quality records.
No database migration. A CAPA whose action items cannot be read is no longer treated as a CAPA with no actions: signing, saving and completing implementation are refused with a message asking to try again, and the CAPA page and record PDF say what could not be read instead of reporting the record as changed or its approvals as broken.
CAPA: A SIGNATURE IS NO LONGER BOUND TO A PLAN WITH NO ACTIONS WHEN THE ACTIONS FAIL TO LOAD. A CAPA's action items are part of what its content hash binds, and part of what its plan-approval, cancellation, reopening and closure signatures bind. Before this release, if the action items could not be read at the moment of signing — a database timeout, for example — the CAPA was treated as having no actions. The signature was then permanently bound to a plan with no actions and read as broken once the actions loaded again; because signatures cannot be altered, it could not be repaired. The same read failure could write a content hash that left the actions out, show a plan-approval slot as open when it was already held, and complete implementation — moving the CAPA to its effectiveness check — while actions were still open. Each of these is now refused with a message asking to try again, and nothing is signed or saved. Where an action has already been added, changed or removed and only the step that follows it fails, the change is kept and recorded in the audit trail, the user is told the CAPA's content hash was not updated, and the next saved change to the CAPA's actions or details updates it. Removing an action that no longer exists now reports that it was not found, rather than recording a removal.
Why this classification: Classified “requalify” because the behaviour of CAPA electronic signatures and of a lifecycle step changed: signing, saving and completing implementation are now refused in a condition in which they previously went ahead. When the action items can be read, nothing that was permitted is refused, and no signature meaning, content-hash definition, role or database schema changed. A plan approval signed under the earlier behaviour while its CAPA's actions failed to load shows as broken on the CAPA page.
capa-actions-unreadable.test.tsCAPA PAGE AND RECORD PDF: WHAT COULD NOT BE READ IS NAMED. Before this release, if a CAPA's action items could not be read when its page was opened, the page warned that the record had been changed outside Indelio, showed its plan approvals as broken and said it had no action items, and the record PDF printed “No action plan recorded.” If its signatures could not be read, the page said “No signatures yet.” The page now shows a notice naming what could not be read, shows record integrity and signature bindings as not checked, and does not show those empty-list messages. In that case the record PDF is not generated; opening it shows a message asking to try again.
Why this classification: Classified “review” because what the CAPA page and record PDF state about record integrity and signatures changed when the underlying data cannot be read, and the PDF is now withheld in that case. When the data can be read, both are unchanged, and no control, signature or record changed.
capa-actions-unreadable.test.tsNo database migration. When a record is changed, its audit-trail entry now records the previous and the new value of each field that changed, in every module. A save that changes nothing no longer writes an entry, a change is refused if the previous values cannot be read, and replacing a draft document's file no longer deletes the file it replaces.
A CHANGE NO LONGER OBSCURES WHAT IT REPLACED (21 CFR 11.10(e)). Before this release, editing a record overwrote it in place and the audit-trail entry recorded, at most, the names of the fields the form submitted — not what they held before. Audit-finding edits were the only exception. This now applies everywhere a record's value can be replaced: record edits in every module (CAPA, CAPA actions, nonconformances, complaints, change control, suppliers, equipment and calibration assessments, audits and findings, risk assessments and risk items, design projects and their inputs, outputs, verifications and validations, human-factors records, assessments, determinations, field actions, management reviews, projects, the quality policy and objectives, document drafts), and the workflow steps that overwrite a field — for example re-approving a supplier, which replaces its approval date; re-pointing a link at a different CAPA, risk assessment or field action; completing a periodic review, which replaces the due date; and amending the quality policy. Each entry now carries `changes`, listing each field with its `from` and `to` values; `fields` now lists the fields that changed rather than every field submitted. The values are in the audit trail and in the records export (`records/audit_trail.csv`); record pages do not yet display them. Entries written before this release are unchanged and cannot be completed retrospectively.
Why this classification: Classified “requalify” because what the audit trail records changed on every record-changing action in every module, and three behaviours changed with it: a save that changes nothing no longer writes an audit entry; a change is now refused, with a message asking to try again, if the record's current values cannot be read first; and a failed save that previously still wrote an "updated" entry in several human-factors and link actions now reports the failure and writes nothing. Nothing that was permitted is refused in normal operation, and no signature meaning, content hash, role or database schema changed. If your qualification checked the content of audit entries for edits, the entries now contain more.
edit-trail-coverage.test.tschange-set.test.tsaudit-finding-trail.test.tsA REPLACED DRAFT FILE IS KEPT. Uploading a revised file to a draft document used to delete the file it replaced from storage, so nothing anywhere preserved it. The superseded file is now kept, and the REPLACE_DRAFT_FILE entry records its path, name, type, size and content hash alongside the new file's. This changes record retention: superseded draft files now accumulate in document storage rather than being removed.
Why this classification: Classified “requalify” because record retention changed: files that were previously deleted are now retained. No signature, content hash, role or lifecycle rule changed.
edit-trail-coverage.test.tsPART 11 VALIDATION PACKAGE: §11.10(e) "SHALL NOT OBSCURE" NOW MARKED MET, WITH ITS EVIDENCE AND LIMITS. Release 2026.09.14.1 corrected the package to mark this requirement as a gap. With the change above it is marked met, citing the tests that check it, and it states its limits: record pages do not yet show the recorded values, edits made before this release cannot be reconstructed, and two people saving the same record at the same instant could each record the other's value as the previous one. User requirement UR-AT-06 is added to the URS and the traceability matrix.
Why this classification: Classified “none” because this is the validation documentation for the change above; the change itself is classified separately.
validation-doc.test.tsrtm-sync.test.tsNo database migration and no change to how any module behaves. The 21 CFR Part 11 validation package under Compliance & Validation marked §11.10(e) as met, including the requirement that record changes not obscure previously recorded information. That requirement is not met when a record is edited, and the package now says so.
PART 11 VALIDATION PACKAGE CORRECTED — AN EDIT DOES NOT KEEP THE VALUES IT REPLACES. 21 CFR 11.10(e) requires that record changes not obscure previously recorded information. The package marked §11.10(e) as implemented and evidenced, including that requirement. It is not met for edits: when a record's details are edited, the record is updated in place, and the audit-trail entry records that the edit happened — at most, which fields were submitted — but not what those fields held before. Audit-finding edits are the one exception and record both. Where a record remains editable after it has been signed — CAPA, change control, complaints, nonconformances, suppliers, equipment, and the quality policy once amended — a material edit still shows the signature as no longer matching the record, so the change is detectable, but the content that was signed cannot be reproduced. Approved controlled documents are not affected: only a Draft can be edited, and an approved document changes through a new revision. The package now splits §11.10(e) into two rows — the append-only, hash-chained, time-stamped audit trail, which remains met, and the no-obscuring requirement, marked as a gap — and lists it as open gap 7 in §6. What the earlier row said is described in the package rather than removed. If your own validation relied on the earlier §11.10(e) row, reassess it against the corrected one. This release changes the document only; the system behaves as it did before.
Why this classification: Classified “none” because only the validation document changed — no control, record, signature, hash, role or lifecycle rule behaves differently. It is recorded because the document described a Part 11 control more strongly than the system enforces it, and a customer may have relied on that description in their own validation.
validation-doc.test.tsNo database migration. Regulatory citations to Quality System Regulation sections that the QMSR withdrew on 2 February 2026 have been removed from two calibration messages, the Design Controls help and two module descriptions, and replaced with the ISO 13485:2016 clauses the QMSR incorporates.
WITHDRAWN QSR CITATIONS CORRECTED. Since the Quality Management System Regulation took effect on 2 February 2026, most sections of the old 21 CFR Part 820 have been reserved, with their substance incorporated from ISO 13485:2016. Indelio still cited some of them as current. Corrected: the two messages refusing to accept an out-of-tolerance calibration record, or to save its assessment, without the impact on previous measurements now cite ISO 13485 §7.6 alone; the Design Controls help cites ISO 13485 §7.3 via 21 CFR Part 820; and the Human Factors and Calibration & Maintenance module descriptions cite ISO 13485 §7.3 and §7.6 respectively, under 21 CFR Part 820. Articles and pages that describe the QSR-to-QMSR change as history are unchanged, as are earlier entries in this register. ⚠️ WHAT THIS MEANS FOR YOU: if your own procedures, validation records or training material quote these messages or descriptions with the old citations, they quote text Indelio no longer shows. A new automated check now fails the build if a withdrawn section is cited as current anywhere a user or validator reads.
Why this classification: Classified “none” because only regulatory citations in text changed — no rule, message trigger, state, signature, role or record behaves differently. It is recorded because citations are what a quality reviewer checks a system against, and a citation to a reserved section is a finding in its own right.
withdrawn-qsr-citations.test.tsNo database migration. An out-of-tolerance calibration now takes the equipment Out of Service when the result is recorded, instead of when the record is accepted, and equipment cannot be returned to service while such a result is still awaiting acceptance.
OUT-OF-TOLERANCE EQUIPMENT LEAVES SERVICE WHEN THE RESULT IS RECORDED. Before this release, recording an out-of-tolerance calibration result changed nothing on the equipment: it stayed Active — green on the equipment register and absent from Needs attention — until a second person accepted the record, and acceptance is refused until the impact on previous measurements has been assessed, which can take days. SOP-QMS-014 v1.0 said so, and made the physical OUT OF SERVICE tag the control for that period. Now: recording an out-of-tolerance result takes Active equipment Out of Service immediately, with an audit entry naming the record. Acceptance still requires the impact assessment and a second person, and still clears the next calibration due date. Return to service is refused while any out-of-tolerance result on the equipment is still awaiting acceptance: accept it, or void it if it was entered in error, then return the equipment to service with a signature. Voiding does not return equipment to service by itself. Who may record, assess, accept and void a record is unchanged. SOP-QMS-014 is revised to v1.1. ⚠️ WHAT THIS MEANS FOR YOU: an out-of-tolerance result recorded before this release is not moved retroactively — that equipment stays Active until the record is accepted, as before — so check your register for any out-of-tolerance record still awaiting acceptance. If you adopted SOP-QMS-014 v1.0, its §5.4.2 describes behaviour Indelio no longer has. Anyone who can record a calibration result can now take equipment out of service; only an approver or QA can bring it back.
Why this classification: Classified “requalify” because a lifecycle rule changed in two places: equipment status now moves on a recorded, unsigned result and not only on an accepted one, and a signed action — returning equipment to service — is refused in a case where it was previously allowed. No signature meaning, content hash, role or database schema changed.
equipment.test.tsTHIS REGISTER NOW ORDERS RELEASE NUMBERS AS NUMBERS. This is the first release numbered with a two-digit suffix on its day (.10). The register had been ordering release numbers as text, which would have listed 2026.09.13.10 below 2026.09.13.2 to .9 — under releases it follows — and our own tooling recorded the previous release as the one covering this change. Both now order numerically. No release was ever displayed out of order: no two-digit suffix existed before this one.
Why this classification: Classified “none” because it changes only the order in which this register lists releases and how our release tooling identifies the newest one. No function operating under your quality system changed. It is recorded because the register is what you read to decide what to re-test, and an entry listed out of sequence is easy to miss.
releases.test.tsrelease-gate.test.tsNo database migration. The Post-Market Trends page could report no clusters or spikes when the records it examines had not been read at all. It now says what could not be read, and shows no all-clear while anything is unreadable.
POST-MARKET TRENDS NO LONGER SHOWS AN ALL-CLEAR FROM A FAILED READ. The page reads your complaints and nonconformances to detect trends. If either read failed — for example a database timeout — the failure was ignored and treated as having no records, so the page could show “No clusters or spikes in 0 counted records” without having examined anything. The trend dispositions and the open risk-review flags were handled the same way, so a failed read could make every signal look unreviewed and make open flags vanish from the page. Now: if complaints or nonconformances cannot be read, the page names them, says this is not the same as there being no trends, and does not show the all-clear; signals from a source that could be read still show, marked as partial. If dispositions or open flags cannot be read, the page says so in place of the list. ⚠️ WHAT THIS MEANS FOR YOU: an all-clear you saw on this page before this release was only as good as the reads behind it, and the page could not tell you when those had failed. The detection itself, its thresholds, and the counts-not-rates limitation are unchanged. The page's regulatory citation now reads ISO 13485 §8.4 (via 21 CFR Part 820) in place of 21 CFR 820.100, a section withdrawn when the QMSR took effect.
Why this classification: Classified “review” because a post-market surveillance screen could present an absence of trends that had not been examined; if your procedure relies on reviewing this page, what it tells you when data cannot be read has changed. No record, signature, hash, role, lifecycle rule or detection rule changed.
trends-unreadable.test.tstrends.test.tsNo database migration. Three new SOP templates — Post-Market Surveillance, Corrections & Removals, and Human Factors / Usability Engineering — so every module now has a template or a recorded reason it needs none. One defect fixed: a closed field action could not be reopened from its page.
THREE NEW SOP TEMPLATES. SOP-QMS-015 Post-Market Surveillance and Trend Review (Post-Market Trends). SOP-QMS-016 Corrections and Removals (Field Actions), covering the Initial Impact Determination, the HHE / Regulatory / PPMRM assessments and the field action through closure. SOP-QMS-017 Human Factors and Usability Engineering, one procedure covering all five Human Factors modules. Each is written against the module as it behaves, and each states what Indelio does not do: Post-Market Trends counts records and cannot compute rates, and a trend disposition is an attributed, audited record rather than an electronic signature; Indelio does not generate EU vigilance reports; and the Human Factors modules do not enforce the order of the work — critical tasks can be imported from an unapproved analysis and a report assembled from unapproved records, so the procedure makes approving each record before the next is populated from it your control.
Why this classification: Classified “none” because this adds optional templates and changes no control, record, signature, hash, role or lifecycle rule.
templates.test.tstemplates-sync.test.tstemplate-coverage.test.tssop-product-sync.test.tsA CLOSED FIELD ACTION CAN NOW BE REOPENED FROM ITS PAGE. Reopening a field action requires the approver or qa role and your password, and the server has checked that password since 25 August 2026 — but the Reopen form on the Corrections & Removals page had no password field. Every attempt to reopen was therefore refused as a wrong password, so no field action could be reopened from the screen. Nothing was reopened wrongly: the defect only ever refused. The form now asks for the password, and the reopening is signed with the meaning “Reopening approved by”, as it was always designed to be. ⚠️ If your procedure or validation records a failed attempt to reopen a field action, this was the cause. A new automated check now requires every form that submits a signature in Indelio to collect the password; it found no other form missing it.
Why this classification: Classified “review” because a signed action that could not be completed from the screen now can. The signing rule itself — role, password, signature meaning, and the state it returns to — is unchanged.
signing-form-password.test.tscorrections.test.tsOne database migration (supabase/nonconformance_supplier.sql). A nonconformance can now name the supplier it originates with, and when it does, its disposition cannot be approved until a supplier action is decided and signed with it. Supplier records now show the nonconformances that name them. This changes a signing rule on nonconformances.
NONCONFORMANCES CAN NAME THE SUPPLIER, AND A NAMED SUPPLIER NOW REQUIRES A SIGNED SUPPLIER ACTION. An NCR has a new Supplier field, a real link to a record in your supplier list. Once a supplier is named, approving the disposition is refused until a supplier action is recorded: No action (with its reason), Notify supplier, Recommend hold, or Recommend re-evaluation. A 'Return to supplier' disposition is refused until the supplier is named. The disposition-approval signature binds the supplier and the supplier action along with the disposition, so changing either after signing breaks that signature's binding. Naming or removing a supplier, and the signed supplier action, are also recorded on the supplier's own audit trail. ⚠️ A SUPPLIER ACTION NEVER CHANGES THE SUPPLIER. A recommended hold or re-evaluation is a decision recorded on the NCR; the hold or re-evaluation itself is still a separate e-signed act on the supplier record. ⚠️ EXISTING RECORDS: no existing NCR's content hash changed, so no existing signature's binding changed. An NCR already in Disposition with a 'Return to supplier' disposition cannot be approved until the supplier is named.
Why this classification: Classified “re-qualification recommended” because a signing rule changed: disposition approval on a nonconformance now has a precondition it did not have, and the signature binds two more fields. If your performance qualification covers nonconformance disposition, the affected scenario no longer describes the system.
ncr-supplier-escalation.test.tsnonconformance.test.tsisolation.test.tsSUPPLIER RECORDS SHOW THE NONCONFORMANCES THAT NAME THEM. Each supplier record lists the nonconformances naming it, with severity, state and supplier action. The Approved Supplier List and the on-hold list show each supplier's count of open nonconformances. Re-evaluation and hold release show the nonconformances since the supplier was last approved, next to the button. Where a signed nonconformance recommends a hold or a re-evaluation, the supplier record shows it until a later decision is signed there. That decision can be a hold, a re-evaluation followed by approval, a release, a disqualification, or a new signature, 'Supplier NCR recommendation reviewed by', which needs the approver or qa role and a recorded reason and leaves the supplier approved. If these lists or counts cannot be read, the page says so rather than showing none or zero.
Why this classification: Classified “review” because a new e-signature meaning was added to supplier records and the supplier pages show new information. No existing supplier signature, hash, state or lifecycle rule changed.
ncr-supplier-escalation.test.tssupplier.test.tsSOP TEMPLATES SOP-QMS-005 (NONCONFORMANCE) AND SOP-QMS-008 (SUPPLIER QUALIFICATION) UPDATED TO VERSION 1.1. SOP-QMS-005 now covers naming the supplier and deciding the supplier action. SOP-QMS-008 §5.4 now covers the nonconformances on a supplier's record and how their recommendations are handled. ⚠️ SOP-QMS-008 VERSION 1.0 SAID THAT COMPLAINTS ALSO FEED THE SUPPLIER FILE. Indelio does not link complaints to suppliers, and version 1.1 says so. If you adopted version 1.0, revise that clause in your procedure.
Why this classification: Classified “review” because a template you may have adopted described a supplier-file behaviour Indelio does not have, and the templates now describe a new control.
templates-sync.test.tssop-product-sync.test.tsNo database migration and no change to how any module behaves. Compliance & Validation now shows four more validation documents — the Validation Plan, the IQ protocol, the PQ protocol and the CSA implementation-plan template — and each of the four was corrected before being published there. If you hold an earlier copy of the CSA template or the PQ protocol, read the second change below.
FOUR MORE VALIDATION DOCUMENTS UNDER COMPLIANCE & VALIDATION. Alongside the 21 CFR Part 11 validation package, any signed-in member of your organization can now read the Validation Plan, the Installation Qualification (IQ) protocol, the Performance Qualification (PQ) protocol and the Computer Software Assurance (CSA) implementation-plan template, and download each as Markdown or save it as a PDF. Each document states its own status — what has been executed, what is a template, and what is still draft — so read that before relying on one. The existing download link for the Part 11 package still works.
Why this classification: Classified “none” because this makes existing vendor documents available in the application and changes no control, record, signature, hash, role or lifecycle rule. The documents served are checked against their maintained source on every change.
validation-library-sync.test.tsvalidation-doc.test.tsFOUR CORRECTIONS WERE MADE TO THOSE DOCUMENTS BEFORE PUBLICATION. (1) THE CSA TEMPLATE contradicted itself: its §4 correctly says Indelio's IQ protocol has been executed and signed against Indelio's production instance, but its closing Limitations section still said the IQ and PQ protocols were not executed. The Limitations section now matches §4. Its §0 and the model justification in §3.1 also still referred to “two” control switches; there are three, as §6 already stated. (2) THE PQ PROTOCOL said “Support hours are included in your onboarding.” That did not match our published pricing, which has never included PQ support in onboarding: if you want hands-on help with PQ, we introduce an independent validation consultant billed at their rate and passed through at cost, or you bring your own. (3) THE VALIDATION PLAN recorded the IQ protocol as “drafted; to execute”. It now records that IQ has been executed and signed against Indelio's own production instance, covering the automated portion only, with the vendor as executor, and that your instance requires its own execution. (4) THE IQ PROTOCOL's step IQ-33 was missing its Schedule cell, so in a table view its expected result appeared under the wrong column; the cell is now present and the expected result is unchanged. ⚠️ WHAT THIS MEANS IF YOU USED AN EARLIER COPY: if you copied the CSA template's §3.1 justification into your own plan, correct “two” control switches to three there too; if you planned PQ support on the basis of the earlier PQ protocol wording, raise it with us.
Why this classification: Classified “review” because these are vendor documents you may have adopted or cited, and wording you may have relied on has changed. No control, record, signature, hash, role or lifecycle rule is affected.
validation-library-sync.test.tscsa-template-config-surface.test.tsNo database migration and no change to how any module behaves. A Calibration & Maintenance SOP template is now available in the SOP library.
NEW SOP TEMPLATE: CONTROL OF MONITORING AND MEASURING EQUIPMENT (SOP-QMS-014). ISO 13485 §7.6 requires a documented procedure for monitoring and measuring equipment, and the library had none although the Calibration & Maintenance module exists. The template covers the equipment register and schedules, checks before use, recording calibration, maintenance and repair results, independent acceptance (the person who records a result or writes its impact assessment cannot accept it), out-of-tolerance results and the assessment of previous measurements, voiding records entered in error, return to service and retirement. ⚠️ IT STATES ONE LIMIT PLAINLY: Indelio takes equipment out of service when an out-of-tolerance record is ACCEPTED, not when it is recorded, and acceptance waits for the impact assessment. The template therefore requires the equipment to be physically tagged out of service as soon as the result is found, because until acceptance Indelio still shows it as Active.
Why this classification: Classified “none” because this adds an optional template and changes no control, record, signature, hash, role or lifecycle rule.
templates.test.tstemplates-sync.test.tstemplate-coverage.test.tssop-product-sync.test.tsNo database migration and no change to how any module behaves. The CAPA SOP template (SOP-QMS-002) described a CAPA lifecycle that differs from the one Indelio runs, and has been rewritten. If you adopted version 1.0, your CAPA procedure needs revising.
THE CAPA SOP TEMPLATE NOW DESCRIBES THE CAPA LIFECYCLE INDELIO ACTUALLY RUNS. Version 1.0 of SOP-QMS-002 was wrong in five ways: (1) it quoted the plan-approval e-signature meaning as “Plan approved by.” — Indelio records “Action plan approved by (Approver)” and “Action plan approved by (QA)”; (2) it said plan approval moves a CAPA to Effectiveness Check — Indelio moves it to Implementation, and a separate step moves it to Effectiveness Check once every action is done; (3) it described one plan approver — Indelio requires two approvals by two different people, one approver and one QA; (4) it omitted the request review a new CAPA starts with (approve, return for updates, or reject); and (5) it said the person who closes a CAPA cannot be the person who approved the plan — Indelio does not enforce that rule, which was removed in July 2026 on independent validation advice. What Indelio does enforce at closure is described instead: only QA can close, an effectiveness result or a documented override is required, and by default the closer must not have carried out any of the CAPA's actions. Version 1.1 walks through each step using the labels on screen. ⚠️ WHAT THIS MEANS FOR YOU: a procedure adopted from version 1.0 disagrees with your CAPA records — in its signature meanings, its stages and its approvers — and asserts a segregation-of-duties control the system does not apply. Revise it before your next audit.
Why this classification: Classified “review” because a procedure you may have adopted describes your CAPA records incorrectly, including a control that is not enforced. No control, record, signature, hash, role or lifecycle rule changed. ⭐ HOW IT WAS FOUND: a new automated check now compares every SOP template against the product — the signature meanings it quotes, the lifecycle states and transitions it names, and the buttons and pages it tells you to use. Its first run failed on this template.
sop-product-sync.test.tstemplates-sync.test.tsA correction to documents published earlier today. No change to how Indelio behaves. Release 2026.09.13 said the review-context switch had no control in the Settings page and could only be changed by Indelio. That was wrong: it has always been set from Settings.
CORRECTION: THE REVIEW-CONTEXT SWITCH IS SET IN SETTINGS, AND ALWAYS WAS. Earlier today, the CSA implementation plan template, CUEC-04 in the Complementary User Entity Controls register, SOP-QMS-012 (Validation of Quality System Software) and the release note for 2026.09.13 all said the “review context required” switch had no control in Indelio's Settings page and could only be changed by Indelio. That was false. An administrator sets it under Settings → ✍ Signature meanings & review contexts → “Require a review context on every review signature.” All three documents now say so, and the CSA template now names where each of the three switches is set. ⚠️ WHAT THIS MEANS FOR YOU: if you asked Indelio to change this setting, or recorded that you could not change it yourself, set the position you want and update that record. The same correction notes that document types and numbering (Settings → 🏷 Document types & numbering) are also tenant settings; they configure content, not what the system permits or refuses.
Why this classification: Classified “review” because documents you may have adopted or signed stated a limitation that did not exist. No control, setting, record or signature changed. ⭐ HOW IT HAPPENED: the claim came from searching the application code for the database column's name, which the Settings page never mentions because it calls a function instead. A search that finds nothing is not evidence of absence. The automated check behind the CSA template now confirms, for every switch, that the Settings page action which sets it exists behind the admin gate, and that the template names where it is.
csa-template-config-surface.test.tsNo database migration and no change to how any module behaves. A Quality Manual template is now available in the SOP library.
NEW TEMPLATE: QUALITY MANUAL (SOP-QMS-013). ISO 13485 §4.2.2 requires a quality manual, and the library had none. The template covers scope, exclusions and their justification, the quality policy and objectives (kept in Quality Policy & Objectives), organisation and responsibilities, a clause-by-clause map from ISO 13485 to the procedure and Indelio module that meets each requirement, how the processes interact, and the documentation structure. ⚠️ It does not pretend Indelio covers the whole standard: every clause with no Indelio SOP template — for example control of records, infrastructure, production and service provision, identification and traceability, monitoring and measuring equipment, post-market surveillance, and corrections and removals — is marked as a procedure you must write yourself before approving the manual.
Why this classification: Classified “none” because this adds an optional template and changes no control, record, signature, hash, role or lifecycle rule.
templates.test.tstemplates-sync.test.tstemplate-coverage.test.tsNo database migration and no change to how any module behaves. A new SOP template is available in the SOP library: SOP-QMS-012, Validation of Quality System Software — a procedure for validating Indelio itself for your intended use, as ISO 13485 §4.1.6 requires.
NEW SOP TEMPLATE: VALIDATION OF QUALITY SYSTEM SOFTWARE (SOP-QMS-012). Until now the library had procedures for eleven quality processes but none for the first thing a customer must do with Indelio — validate it. The template takes a risk-based approach consistent with FDA's Computer Software Assurance guidance: obtain and review Indelio's validation evidence and its current status, define your intended use, assess risk, select and justify assurance activities (including an explicit decision on scripted PQ), test high-risk controls by attempting the prohibited action, declare your organisation settings, implement the complementary user entity controls, and keep the validated state current by reviewing each entry in this release register. It runs the validation as a project in Projects, using its URS, Protocol, IQ, OQ, PQ and Report deliverables. ⚠️ It states plainly that Indelio is not validated for your use — no vendor can be.
Why this classification: Classified “none” because this adds an optional template and changes no control, record, signature, hash, role or lifecycle rule. Nothing you have already created from a template is affected.
templates.test.tstemplates-sync.test.tstemplate-coverage.test.tsTHE PERSON WHO CREATED A PROJECT CAN NO LONGER COMPLETE IT. Completing a project applies its only e-signature (“Project completed by”), and until this release a user holding the approver or qa role could create a validation project and sign it complete with nobody else involved. Completion now refuses the project's creator: “SEGREGATION OF DUTIES: the author of this Project can't also approve it — approval needs a second person.” Reopening is not affected. ⚠️ WHAT THIS MEANS FOR YOU: a project waiting to be completed by the person who created it now needs a second person. Completed projects are unaffected; nothing is reversed. This brings Projects into line with the other single-signature records, which received the same rule in August.
Why this classification: Classified “requalify” because a segregation-of-duties control on an e-signature now refuses something it previously allowed. If your qualification exercised project completion, re-run that step: the expected result for a creator attempting completion has changed from accepted to refused. No record, signature, hash or stored data changed. ⭐ WHY IT WAS MISSED: the August review that added this rule to nine single-signature records did not include Projects. It was found while writing the software validation SOP, whose final sign-off runs through this exact function.
four-eyes.test.tsTHE CSA IMPLEMENTATION PLAN TEMPLATE AND CUEC-04 NOW LIST THREE CONTROL SWITCHES, NOT TWO. Both documents told you Indelio has exactly two per-tenant control switches, and the CSA template uses that count in its reasoning for not performing a scripted PQ. Since 2026-09-09 there have been three: the AI features switch (Settings → AI features), which decides whether AI-assisted features are available in your organisation at all. Both documents now declare it, with its default (ON — an unset value means enabled) and its audit action. They also now say that the review-context switch has no control in the Settings page and can only be changed by Indelio, and that other tenant settings — signature-meaning wording, the AI provider key, the email-intake address — configure content or credentials rather than behaviour. ⚠️ WHAT THIS MEANS FOR YOU: if you adopted the CSA template or recorded CUEC-04 before today, add a declaration for the AI features switch.
Why this classification: Classified “review” because a document you may have signed stated a configuration count that became untrue four days ago, and the count supports a PQ justification. No control or setting changed. ⭐ WHY IT DRIFTED: the automated check that holds the template's switch list read only one migration file and only non-nullable booleans. The AI switch was added in a different file as a nullable column, and missed on both counts. The check now reads every migration and handles nullable switches.
csa-template-config-surface.test.tsNo database migration. Two changes to Risk Management, both about what counts as independent and what counts as traced. Indelio now refuses approval of a risk assessment by its author. And the Design-domain coverage table now counts a risk control as untraced unless a design output actually implements it, instead of accepting any text typed into the reference field. The Risk Management SOP template is updated to match.
THE AUTHOR OF A RISK ASSESSMENT CAN NO LONGER APPROVE IT. Before this release, Indelio blocked only the person who applied the review signature from also applying the approval signature. So an author could have a colleague review the assessment and then approve it themselves — two people involved, but the release applied by the author. Approval now refuses the author, with the message “SEGREGATION OF DUTIES: the author of this Risk Assessment can't also approve it — approval needs a second person.” An author may still review their own assessment, and may still cancel their own draft. ⚠️ WHAT THIS MEANS FOR YOU: an assessment written and waiting for approval by its own author now needs a different approver. Assessments already approved are unaffected; nothing is reversed. This brings Risk Management into line with Document Control and Management Review, which already enforced it. Earlier today's SOP template (1.1) described the author rule as procedural because the system did not enforce it; template 1.2 describes it as enforced.
Why this classification: Classified “requalify” because a segregation-of-duties control on an e-signed approval now refuses something it previously allowed. If your qualification or PQ exercised risk-assessment approval, re-run that step: the expected result for an author attempting approval has changed from accepted to refused. No record, signature, hash or stored data changed. ⭐ WHY IT WAS MISSED: the review that added author ≠ approver across the product in August reasoned that two-signature modules were already covered because the reviewer cannot also approve. That is a different rule — it guarantees two people, not that the author is not the one releasing.
four-eyes.test.ts“UNTRACED CONTROL” IN RISK MANAGEMENT NOW MEANS NO DESIGN OUTPUT IMPLEMENTS THE CONTROL. The Design-domain coverage table on a risk assessment counted an item as traced whenever its “Implemented by (ref)” field contained any text — it did not check that a design output was actually linked. It now reads the design ↔ risk links and counts a risk item as an untraced control when it states a risk control that no design output implements. Items with no stated risk control are no longer counted in that column. If the links cannot be read, the column shows “unknown” instead of a number. ⚠️ WHAT YOU WILL SEE: items where a reference was typed but the link was never made will now show as untraced. Nothing about your data changed — the table was reporting a traceability it had not checked. This is the same correction made to the risk Thread view on 2026-09-10, applied to the table that view was fixed to agree with.
Why this classification: Classified “review” because a traceability figure you may rely on or have reported can change, typically upward. No control refuses anything it previously allowed, and no record, signature or hash is affected; links live outside the risk content hash. ⭐ Traceability is a relationship, and a string describing one is not one — found when the demonstration risk file showed zero untraced controls in the Cyber domain directly above a design ↔ risk panel reporting that same Cyber control as not implemented.
risk-domain.test.tstemplates-sync.test.tsNo database migration and no change to how any module behaves. Two of the SOP templates in the SOP library were out of date with the product and have been rewritten: Management Review (SOP-QMS-011) and Risk Management (SOP-QMS-009). Documents you have already created from these templates are yours and have not been changed — read the notes below to decide whether to revise them.
THE MANAGEMENT REVIEW SOP TEMPLATE NOW DESCRIBES THE MANAGEMENT REVIEW MODULE. Version 1.0 told you to keep each management review as a controlled document in Document Control “until a dedicated Management Review module is available”. That module exists. Version 1.1 walks through it: opening the record, the nine review inputs and where each comes from in Indelio, recording conclusions and action items, approval (approver or qa role, and never the person who created the record — Indelio enforces this), and cancelling or reopening. ⚠️ IT ALSO STATES A LIMIT THE OLD TEMPLATE COULD NOT: once a review is approved its action items can no longer be edited, including their status, so the template tells you to track completion through the linked CAPA or Change Control and to record the final status in the next review's “Status of actions from prior reviews”. ⚠️ WHAT THIS MEANS FOR YOU: if you adopted version 1.0, your procedure points reviews at Document Control. Either keep doing that deliberately, or revise your SOP to use the module.
Why this classification: Classified “review” because a procedure you may have adopted told you to record management reviews somewhere other than the module built for them, and did not warn that approved action items are locked. No control, record, signature, hash, role or lifecycle rule changed; nothing you have already signed is affected. ⭐ WHY IT DRIFTED: templates are held identical to our SOP library by an automated check, but nothing held the SOP library to the product. The module shipped and the sentence promising it stayed.
templates-sync.test.tsTHE RISK MANAGEMENT SOP TEMPLATE NOW COVERS DESIGN DOMAINS, DESIGN ↔ RISK LINKS AND POST-MARKET REVIEW, AND ITS SEGREGATION-OF-DUTIES STATEMENT IS CORRECTED. Version 1.1 adds: classifying each risk item as Hardware, Software, Cyber, Usability or General; recording traceability as links in the Design Controls project (a design output implements a risk control; a hazard derives a safety design input); the three gaps the design ↔ risk weave reports; and the ISO 14971 §10 post-market loop — raising a flag, and recording a signed post-market review. ⚠️ TWO CORRECTIONS TO READ: (1) the Design-domain coverage table's “Untraced control” column counts risk items with nothing in “Implemented by (ref)”; a filled-in reference is not evidence of a design link, and the template now says so. (2) Version 1.0 stated only that the approver cannot be the reviewer. That is what Indelio enforces. The template now also requires, as a procedural rule, that the author does not approve their own assessment, and says plainly that this second rule is enforced by the procedure rather than by the system.
Why this classification: Classified “review” because a risk management procedure you may have adopted did not describe how design traceability is evidenced, and could be read as treating a typed reference as a trace. No control, record, signature, hash, role or lifecycle rule changed, and nothing you have already signed is affected. ⭐ We state the author-approval point as procedural on purpose: a template must not describe a control the software does not enforce.
templates-sync.test.tsdesign-risk.test.tsNo database migration and no functional change in this release. One correction, to a document we serve you: the Part 11 validation package stated that our automated installation check covers 16 of our IQ protocol's 49 steps. The protocol has had 50 steps since 31 August, so the figure was wrong by one step and had been for ten days. The corrected figure is 16 of 50. We also corrected several of our own validation documents that still described our IQ protocol as never executed, which stopped being true on 7 September.
THE PART 11 VALIDATION PACKAGE NOW STATES 16 OF 50 IQ STEPS AUTOMATED, WHERE IT SAID 16 OF 49. The denominator, not the numerator: our IQ protocol gained a fiftieth step on 2026-08-31 — a step that requires a human to sign in and open each module, deliberately not automatable — and the sentence in §6 that restates the coverage figure was not updated with it. The number of automated checks did not change and no control changed. ⚠️ WHAT THIS MEANS IF YOU ALREADY CITED THE OLD FIGURE: if your supplier file, risk assessment or validation rationale quotes “16 of 49” from a copy of this package downloaded before today, the correct figure is 16 of 50, and the step you were not told about is a manual one that falls to the executor. The direction of the error flattered us by one step, which is why it is recorded here rather than corrected silently.
Why this classification: Classified “review” because this package is vendor evidence you may have cited, and a figure you quoted has changed. No control, record, signature, hash, role or lifecycle rule is affected, and nothing you have signed is impacted. ⭐ WHY IT DRIFTED, STATED PLAINLY, BECAUSE THE MECHANISM MATTERS MORE THAN THE STEP. The authoritative table lives in our IQ coverage map, and an automated check holds that table's own totals to the rows it contains — so the map was right the whole time. What nothing held was the same figure RESTATED in four other documents. A restatement drifts independently of the table it restates, and every one of ours drifted. ⚠️ AND THE SAME SWEEP FOUND A SECOND, LARGER STALENESS RUNNING THE OTHER WAY: our IQ protocol's own status line still read “Template — not executed” three days after the second signed run. That understated what is on record. We are recording it because an understatement survives every review that is looking for a vendor overclaiming, and it is the harder direction to catch.
validation-doc.test.tsiq-coverage-map.test.tsTHE RISK THREAD PAGE NOW JUDGES A HAZARD “TRACED” FROM THE DESIGN LINK ITSELF, NOT FROM THE TEXT IN THE TRACEABILITY REFERENCE FIELD. Before today, the hazard list on a risk assessment's Thread view marked a hazard traced whenever its traceability reference field contained ANY text. It did not check that the named design element existed, that it was the right one, or that it still existed — so a typo, a renamed output or a deleted output all still read as “✓ traced”. It now reads the design↔risk link table and marks a hazard traced only where an 'implements' link puts the risk control on a real design output. ⚠️ WHAT YOU WILL SEE: hazards where somebody typed a reference but never made the link will change from “✓ traced” to “no design link”. Nothing about your data changed and nothing was deleted — the page was previously reporting a completeness it had not checked. The design-domain coverage view has always counted the structural links and is unaffected; this removes a disagreement between two screens describing the same assessment. ⭐ A third state now exists: if the link table cannot be read, hazards show “trace unknown” rather than defaulting to untraced, so a failed read is never rendered as a gap.
Why this classification: Classified “review” because what the screen tells you about your own risk file changes, and some hazards will move from traced to not traced. No record, signature, hash, role or lifecycle rule is affected — no risk assessment content changed and no e-signature is impacted, because the links live outside the risk and project content hashes precisely so that linking cannot orphan a signature. ⭐ WHY IT MATTERED: traceability is a relationship, and a string describing one is not one. A non-empty text box cannot be checked by anything, so the old indicator reported a conclusion it had no evidence for — the same defect class as a green “no trends detected” banner computed from data that cannot support it. Held now by four assertions and two mutation tests: one restores the free-text rule, one turns an unreadable link table back into a false “untraced”.
design-risk.test.tsunread-not-empty.test.tsNo database migration is required in this release. Two changes, both about the AI-assisted features. Your audit trail now records WHICH AI MODEL drafted or revised a controlled document, where before it recorded nothing — so after the fact you can tell whether a document was AI-assisted, and by what. And we have published a written assurance statement covering every AI feature: what it is for, what it cannot do, its known failure modes, how model and prompt changes are controlled, and the places where our answer is that we do not do that.
⚠️ YOUR AUDIT TRAIL NOW RECORDS WHICH AI MODEL DRAFTED OR REVISED A DOCUMENT. Until this release, if someone used the AI to draft a controlled document or revise a draft, nothing anywhere recorded that it had happened or which model produced it — after the fact you could not tell an AI-assisted draft from one somebody typed. That is now recorded, on the audit entry for the version: the model and the version of our drafting logic. A document created with AI assistance carries it on its CREATE_DOCUMENT entry; a draft revised by AI carries it on the EDIT_DRAFT entry, whose reason reads “Draft revised by AI” rather than “Draft content edited”, so the two do not read alike. ⭐ THE PROVENANCE IS IN THE AUDIT TRAIL AND DELIBERATELY NOT IN THE DOCUMENT: your SOP's content is the controlled record and its hash is what signatures bind to, so putting a machine stamp inside it would change the document of record. ⚠️ It records an EVENT rather than a property of the text — that an AI draft was produced for that version. An author may then rewrite every word, which is exactly why claiming the finished document is AI-authored would be false. ⚠️ No quality record, signature, hash, role or lifecycle rule changes, and nothing you already signed is affected — existing audit entries are untouched, and records created before this release carry no provenance because none was captured.
Why this classification: Classified “review” because it changes what your AUDIT TRAIL CONTAINS, and the audit trail is evidence you may present. Entries for AI-assisted document actions now carry an extra field, so anyone who reads or exports your trail will see something that was not there before, and any procedure of yours that describes what an audit entry contains may need a look. ⭐ WHY THIS WAS WORTH BUILDING, stated plainly because it was OUR gap and not a customer request. A quality professional listed, among the recurring complaints about every eQMS on the market, that vendors add AI features without saying how outputs are verified or how model changes are controlled. Writing our answer down forced us to check, and the check found that one of our own paths recorded nothing — the complaint-intake path had stamped its model and logic version onto the record and its audit entry since it was built, while the document-drafting path beside it had never recorded anything at all. The gap had been written in a code comment for weeks and had never reached anyone who could act on it. ⚠️ WHAT THIS DOES NOT GIVE YOU. It does not tell you how much of the final text came from the AI, because that is not knowable and we will not estimate it. It tells you that an AI draft was produced for that version, by which model, at which version of our drafting logic. If your procedures require knowing whether a controlled document was AI-assisted, that question is now answerable from your own audit trail; if they require knowing how much, it is not, and we would rather say so.
ai-model.test.tsWe have published an AI FEATURE ASSURANCE STATEMENT covering all seven AI-supported features: the intended use of each, what it is forbidden from doing, its known failure modes, what provenance is recorded, how model and prompt changes are controlled, where your data goes, and five responsibilities that sit with you rather than with us. ⚠️ IT STATES WHERE OUR ANSWER IS “WE DO NOT DO THAT”, because a statement listing only strengths is the thing customers are complaining about. In particular: no AI output is checked by the system against a reference — verification means a qualified person read it and accepted it; and we do not monitor whether AI output quality drifts over time or across model versions. ⛔ NOTHING IN IT CLAIMS ANY AI FEATURE IS VALIDATED FOR YOUR INTENDED USE. That determination is yours, which is why the statement ends with controls that transfer to you rather than with reassurance.
Why this classification: Classified “review”, and NOT because a controlled function changed — none did. It is published because this document is the kind of vendor evidence a supplier file cites and a risk assessment leans on, and because if you have already permitted or forbidden AI use in your quality system, this states the facts that decision should rest on. ⭐ THE STRONGEST CLAIM IN IT IS ONE YOU CAN CHECK RATHER THAN TRUST. Our model identifiers and prompts are single-sourced constants inside the controlled surface our release gate watches, so a model change or a prompt change cannot ship until a decision has been recorded about what it means for a customer maintaining a qualified state — the same gate that produced this entry. ⚠️ AND THE LIMIT: the statement describes design and control. It is not an executed validation of any AI feature, and it does not become one by being written down.
ai-model.test.tsrelease-gate.test.ts⚠️ THIS RELEASE ADDS A DATABASE MIGRATION — supabase/ai_policy.sql — and it must be applied BEFORE the release is deployed. ✅ YOU CAN NOW TURN THE AI FEATURES OFF. Settings → AI features carries an organisation-level switch covering SOP drafting and revision, complaint intake from email, document-metadata and citation extraction, and the help assistant. ⭐ IT IS ON BY DEFAULT, so nothing changes for anyone using these features today; turning it off is a deliberate act by an administrator, and from that moment every one of those features refuses — including the unattended email-intake route, which runs with nobody watching it. ⭐ SWITCHING IT IS RECORDED IN YOUR AUDIT TRAIL, in both directions, with the administrator's name and the time — so you can show an auditor when AI was permitted and when it was not. ⛔⛔ AND IT FAILS CLOSED: if the setting cannot be read, the AI features REFUSE rather than proceed, and the message says plainly that nothing was sent to the AI. ⚠️ The switch is all-or-nothing — there is no per-feature permission. ⚠️ No quality record, signature, hash, audit entry, role or lifecycle rule is affected, and nothing else in Indelio changes.
Why this classification: Classified “review” because it gives your quality function a CONTROL it did not have, and a control you have not yet made a decision about is one your procedures may need to speak to. ⭐⭐ WHY IT WAS BUILT, and it was our own gap rather than a feature request. Writing our AI assurance statement forced us to check what we actually enforced, and the answer was nothing: there was no per-feature permission and no organisation toggle, so any user who could author a document could use AI drafting. We published that as a disclosure — “if your procedures forbid AI use, that restriction is enforced by your procedures and your training, not by this system” — which was honest and was a bad answer. A quality function that has decided AI may not touch its controlled records wants a control, not a policy. So we built the control and the statement now says so. ⚠️ THE FAIL DIRECTION IS DELIBERATE AND IS THE OPPOSITE OF OUR EVIDENCE-VIEWED GATE. That gate treats an unreadable setting as ON, because failing closed keeps a gate enforced. This one refuses, because treating an unreadable setting as “AI enabled” would let a transient database fault silently re-enable AI in an organisation that had switched it off. A refused draft costs somebody a minute; an AI call inside an organisation that forbade it is a compliance event. ⚠️ WHAT IT DOES NOT DO. It is all-or-nothing: if you need drafting but not email intake, the switch cannot express that and you are back to procedure for the difference. And it does not retrospectively mark records created while AI was permitted — the audit trail already records which model drafted a document, and that is where the history lives.
ai-usage.test.tsNo database migration is required in this release, and no code that operates under your quality system changed. What changed is our own validation package — the 21 CFR Part 11 document you can download from Indelio — which described our validation position less favourably than the facts. It said no Installation Qualification had been executed or signed against our production instance. Seven IQ runs have in fact been recorded, and two of them are now signed — including one executed against the checklist version our independent validator confirmed. The document is corrected, and its open-gap table now states what exists, what it covers, and what it does not.
⚠️ OUR DOWNLOADABLE 21 CFR PART 11 VALIDATION PACKAGE UNDERSTATED OUR OWN VALIDATION POSITION, AND IS CORRECTED IN THIS RELEASE. Its open-gap table recorded gap #1 as “No executed/signed IQ/OQ/PQ — Open”. That was wrong on the IQ half: our platform console has recorded seven Installation Qualification runs against the production instance between 23 August and 8 September 2026, and two of them — 7 September and 8 September, both verdict `qualified` with zero critical failures — carry an executor's electronic signature. The gap is now recorded as PARTLY CLOSED, with the limits stated alongside it rather than left to be inferred: the signed record covers the AUTOMATED portion of our IQ protocol only — 16 of its 49 steps — the remaining executor and organizational steps are not executed, and the person who executed and signed it is us, not an independent party. ✅ The 8 September record is bound to the checklist version our independent validator confirmed; the 7 September record stays bound to that checklist as it stood while still marked PROVISIONAL, which is correct — a signed record is verified against the version it actually ran against. ⚠️ Operational Qualification evidence is compiled but not signed by an independent reviewer, and Performance Qualification is NOT EXECUTED AT ALL and is inherently specific to your processes. ⚠️ No quality record, signature, hash, audit entry, role, lifecycle rule or calculation is affected, and no screen you use to do quality work has changed. ⛔ NOTHING HERE MAKES INDELIO VALIDATED, AND WE ARE NOT CLAIMING IT DOES — that conclusion is your quality function's to reach for your own intended use.
Why this classification: Classified “review”, and the reason is narrow: NO CONTROLLED FUNCTION CHANGED. Not one rule, state transition, permission or calculation behaves differently, and you do not need to re-test anything on the strength of it. It is published because the document that changed is VENDOR ASSURANCE EVIDENCE — the kind of artefact a supplier file cites and a risk assessment leans on. If you downloaded the earlier version and recorded in your own supplier assessment that your vendor had executed no qualification of its production instance, that statement was based on something inaccurate, and you may want to refresh it. ⭐⭐ THE PATTERN THIS BELONGS TO, stated because it is the one this company keeps finding and because the direction is the counter-intuitive part. A document described the system inaccurately — here by UNDERSTATING it, claiming less assurance than actually existed. That is a defect in exactly the same way as overstating, and it is the direction almost nobody audits for, because an understatement reads as conservatism. It is not: a supplier file built on it is built on a false fact, and the moment the true position is discovered the whole document's accuracy is in question. ⚠️ HOW LONG IT WAS WRONG, because that is the honest measure of the control that failed: the first IQ run was recorded on 23 August and the package still said none existed on 8 September — roughly two weeks in which our system was ahead of its own record. ⚠️ AND WHAT WE HAVE NOT DONE. Our independent validator reviewed the checklist's three newest checks and confirmed it fit at version 0.3 on 7 September, so the checklist is no longer marked provisional — but he signed off THE CHECKLIST, not any run we executed against it, and that distinction is deliberate: he specifies what must be verified, we execute it, and blurring the two would cost the independence that makes his review worth anything. ⚠️ The FIRST signed qualification record we held was executed and signed hours BEFORE that confirmation arrived, and it stays permanently bound to the checklist version stored on it, because a signed record is verified against the checklist it actually ran against rather than whatever the current one says. Our records are append-only and cannot be restated after the fact. A signed record against the confirmed checklist therefore required a fresh run and a fresh signature — and that run was executed and signed on 8 September 2026. ⚠️ What it still does NOT give you is an independently executed qualification: our validator confirmed the checklist, we ran it, and no run of ours has an independent executor.
validation-doc.test.tsoq-evidence-sync.test.ts⚠️ A STEP IN OUR OWN INSTALLATION QUALIFICATION TOLD THE PERSON RUNNING IT TO RECORD SOMETHING THE SYSTEM GAVE THEM NO WAY TO RECORD, AND THAT IS FIXED IN THIS RELEASE. One check in our IQ checklist (“log in and confirm each module opens”) is a thing no automated check can do — it needs a human to look. Its own wording said it must be “performed and recorded by the executor before signing”, but there was no field, box or note anywhere to record it in, so it reported NOT PERFORMED on every run no matter what had actually been done. Every signed qualification record we hold therefore carries an instruction that was provably not followed, and a reader cannot tell whether the person who signed it had opened every module or had never logged in. There is now a confirmation the person running the check ticks before running it, recorded against their name and the server’s time and bound into the record’s content hash. ⚠️ It is recorded as ATTESTED, never as PASS, and the report prints it with its own mark: it is a person’s word, not something the software verified, and signed evidence must let you tell those apart. Leaving it unticked still records NOT PERFORMED, which is the honest answer when the step was not done. ⚠️ This affects OUR qualification records, not your instance: no quality record, signature, hash, audit entry, role or lifecycle rule changes, and no screen you use to do quality work is different.
Why this classification: Classified “review” only because it changes what our INSTALLATION QUALIFICATION EVIDENCE looks like, and that evidence is something your supplier file may cite. If you have downloaded one of our IQ reports, future ones will carry a status you have not seen before — ATTESTED, printed as [ATTD] — and you should know what it means before you read it as a pass. It is not a pass. It means a named person stated, at a recorded time, that they performed a manual step, and the software is telling you it did not verify that itself. ⭐ WHY WE DID NOT AUTOMATE IT INSTEAD, since that would look stronger. The step asks whether each module opens for a human being. We could have made the check fetch those pages and report a green PASS, and it would have been more impressive and less true — a page that returns successfully to a server is not the same fact as a person confirming the module is usable. Manufacturing a machine result for a human observation would have been the worse defect. What was actually missing was somewhere to put the answer. ⚠️ AND THE LIMIT, stated plainly: an attestation is a self-declaration by the person running the qualification. It is exactly as strong as their word, which is why it is labelled as their word, attributed to them by name, timestamped by our server rather than their browser, and covered by whatever signature is later applied to the record. That is the same standing a manual step has in any paper qualification protocol. ⛔ We cannot and do not backfill: our records are append-only, so runs already signed keep reporting NOT PERFORMED for this step, permanently and correctly, even where the work was done. The gap is closed going forward, not rewritten backwards.
iq-check.test.tsThe six figures in the Audit program panel — Planned, In progress, Report issued, Overdue, Open findings, Closed — are now clickable, and clicking one shows just those audits. Clicking it again, or the “Show all” link beside the heading, puts the full list back. The filter is held in the page address, so a filtered view can be bookmarked or sent to a colleague and it will open the same way for them. ⚠️ One number behaves differently from the other five, deliberately, and the page now says so on screen: Open findings counts FINDINGS, not audits, so an audit with two open findings makes that figure read 2 while the filtered list correctly shows one audit. The heading states both — “Open findings — 1 audit with 2 open findings between them” — rather than leaving a reader to conclude the filter is broken. Nothing is hidden that was not already visible: this filters a list you can always restore, and no record, signature, state or permission changes.
Why this classification: Classified “none”: this is a view over records you could already see, and it changes no rule, calculation, state transition, permission or signature. You do not need to re-test anything, and no controlled function behaves differently. ⭐ IT IS PUBLISHED ANYWAY BECAUSE A NUMBER THAT DISAGREES WITH WHAT IT FILTERS TO IS THE FAILURE MODE HERE, and it fails silently. Before this change the six figures were calculated in one place and the list was drawn in another; making the figures clickable would ordinarily mean writing the same six conditions a second time, and two copies of a rule drift apart without anything erroring — the panel says 1 Overdue, you click it, and the table is empty. So the conditions are now defined ONCE and both the figure and the rows come from that single definition, with an automated check holding the two together for every one of the six. ⚠️ AND THE ONE PLACE THEY LEGITIMATELY DIFFER IS STATED ON THE SCREEN rather than hidden, because a figure of 2 sitting above a single row looks exactly like the bug this was built to prevent, and a reader who cannot tell the two apart learns to distrust every number in the panel. ⚠️ An unrecognised filter in a hand-edited or stale address is ignored and the full list is shown — never an empty register, which is the way a filter turns into “you have no audits”.
audits.test.tsOn the Inspection Readiness scorecard, the headline figures — “1 needs attention · 1 to review before an inspection” — are now links, and each one takes you to the records it counted rather than to the module those records happen to live in. Previously only the rows beneath it were clickable, and even those landed on the whole register: “CAPAs awaiting QA closure · 1” opened the CAPA list showing all four CAPAs, with nothing marking the one it meant. Clicking that figure now shows that one CAPA on its own, headed with the wording the scorecard used and a “Show all” link to restore the full register. The overdue figure behaves the same way on the reminders page, showing the overdue items alone rather than surrounded by the “waiting on you” and “due soon” sections. ⚠️ Where you arrive this way, the back link at the top of the page returns you to Inspection Readiness instead of the modules list, so you are not left retracing your route by hand. Arriving at those pages any other way is unchanged. ⚠️ Nothing is hidden that you cannot restore in one click, and no record, signature, state, permission or calculation changes.
Why this classification: Classified “none”: these are views over records you could already reach, and no rule, calculation, state transition, permission or signature behaves differently. Nothing needs re-testing. ⭐ IT IS PUBLISHED BECAUSE THE FAILURE MODE IT REMOVES IS A NUMBER THAT DOES NOT LEAD TO WHAT IT COUNTED, and that is a quiet way for a scorecard to lose a reader's trust. A figure saying “1 to review” that opens a register of four, with the one unmarked, is truthful and useless in the same breath: it answers “which module?” when the reader clicked to find out “which record?”. ⭐⭐ THE STATES A FILTERED VIEW SHOWS ARE READ FROM THE SIGNAL ITSELF, never restated in the page. The scorecard defines “awaiting QA closure” once; the register asks it what that meant. A second copy would drift the first time a state is renamed, and the two would disagree silently — the register showing rows the scorecard did not count, or none at all. An automated check fails the build if a figure advertises a filtered view that the page it points to does not actually apply, because a promise broken silently is worse than one never made. ⚠️ A stale or hand-edited link showing an unrecognised signal displays the FULL list rather than an empty one: filtering to nothing would render “you have no CAPAs” over a register that has them, which is the way a filter turns into a false all-clear.
readiness.test.tsreminders.test.tsThe two figures in the Inspection Readiness headline now count RECORDS rather than rows of the scorecard. It previously read “1 needs attention” above a row reading “Overdue items … 5” — the 1 meaning one row of the scorecard was red, the 5 meaning five records were overdue. Both were correct, under different denominators, and nothing on the page told you which you were looking at. Now that the figure is a link to those very records, clicking a 1 and being shown five things would have been the obvious next complaint, so the headline reads “5 need attention”. ⚠️ A signal that could not be checked is never counted as zero here: it stays in the separate “could not be checked” figure, because an unchecked signal is not a clear one. ⚠️ No record, signature, state, permission or calculation changes.
Why this classification: Classified “none”: nothing operating under your quality system changed, and no controlled function behaves differently. ⭐ IT IS PUBLISHED BECAUSE A HEADLINE FIGURE IS THE PART MOST READERS ACT ON, and this one was answering a different question from the one it appeared to answer. A scorecard whose summary counts its own rows while its rows count your records is not wrong so much as unreadable — and the moment the summary became clickable, the two denominators collided in a way a reader would reasonably call a bug. ⚠️ WHAT THE NUMBER IS, STATED PRECISELY, because it would be easy to over-read: it is the total number of flagged occurrences in that severity, NOT the number of distinct records. Two signals can flag the same record — a CAPA that is both due soon and awaiting QA closure is counted by each — because this scorecard is a list of concerns rather than a deduplicated worklist. That limit is recorded in an automated test so it is not later “corrected” into a claim the figure cannot support.
readiness.test.tsNo database migration is required in this release. If your password reaches its maximum age, Indelio has always required you to change it before continuing — but it never told you why, and it left the navigation on screen. Every link you could see sent you straight back to the same page, silently, with no message. Signing in and finding the application apparently stuck in a loop was the only symptom. This release explains the block on the page and removes the navigation that could not be used. Nothing about the ageing rule itself has changed: the interval is the same, no password expired earlier or later than before, and no record, signature or audit entry is affected.
⚠️ OUR TERMS OF SERVICE AND PRIVACY POLICY PREVIOUSLY DESCRIBED DELETION BEHAVIOUR THE SYSTEM CANNOT PERFORM, AND BOTH ARE CORRECTED IN THIS RELEASE. They said your data would be made available for export “before deletion in the ordinary course” and that we would “delete or de-identify” Customer Content once an account ended. That is not what Indelio does and never was. Quality records, electronic signatures and the audit trail are append-only: database controls refuse modification and deletion to every account, including our own administrative accounts, and that does not change when a subscription ends. The documents now say so plainly, including that we cannot selectively delete a quality record and will not claim to have done so, and that an erasure request which conflicts with that retention will be answered with what specifically can and cannot be removed rather than silently. ⚠️ The Privacy Policy also now names RESEND, the service that sends account, credential, notification and reminder emails. It was in use and was missing from the list; it receives names, email addresses, record identifiers and one-time passwords, and it does not receive the contents of your records. A new public page at /subprocessors carries the full list with what each party receives and what it does not. ⚠️ BECAUSE BOTH DOCUMENTS CHANGED, THEIR EFFECTIVE DATE MOVES TO 1 SEPTEMBER 2026, AND EVERY USER WILL BE ASKED TO ACCEPT THEM ONCE MORE. ⚠️ No quality record, signature, hash, audit entry, role or lifecycle rule is affected, and no module behaves differently — the SYSTEM did not change here, the DESCRIPTION of it did.
Why this classification: Classified “review”, and the reason is unusual enough to state precisely: NO CODE THAT OPERATES UNDER YOUR QUALITY SYSTEM CHANGED. Not one rule, calculation, state transition or control behaves differently. What changed is what our legal documents SAY about how long your data is kept and who else processes it — and that is worth your review because a retention statement is something your own procedures and your own privacy notices may depend on, and because if you had read the earlier wording and planned around deletion, that plan was based on something untrue. ⭐⭐ THE PATTERN THIS BELONGS TO, because it is the one this company keeps finding rather than a one-off. A document described the system more strongly than the system enforced it — here in the opposite direction to usual: the document promised something WEAKER (deletion) than the system actually does (permanent, tamper-evident retention), which is a defect in the same way. The fix was applied in the same order used every time: make the claim true FIRST, then improve the system if it needs improving. ⚠️ WHAT WE HAVE NOT DONE, stated because a sub-processor page reads like the paperwork that usually accompanies it: there is still no standalone Data Processing Agreement, and the /subprocessors page says so rather than implying one exists. An EU or UK controller generally requires one; ask us and we will tell you where we stand rather than send a document we have not had reviewed. ⚠️ AND THE LIMIT ON ALL OF THE ABOVE: these documents are our own drafting and have not been reviewed by external counsel. This release makes them describe the system accurately. It does not make them legally reviewed, and we are not claiming that it does.
subprocessors.test.tslegal-acceptance.test.ts⚠️ THIS RELEASE ADDS A DATABASE MIGRATION — supabase/legal_acceptance.sql — and it must be applied BEFORE the release is deployed. Everyone who signs in is now asked, once, to read and accept the Terms of Service and the Privacy Policy before reaching the application. What changes for you: the first time each of your users signs in after this release they see the two documents in full on one page and must tick a box to continue; after that they are not asked again unless the documents' effective date changes. Nothing else about signing in changes, and the request comes AFTER any forced password change and after two-factor authentication, so nobody is asked to agree to anything before they are fully signed in. ⚠️ Their acceptance is a permanent record: it is stored append-only, with the person's name, the date, and a fingerprint of the exact wording they were shown, and it appears in your tenant data export like any other record you own. It cannot be edited or withdrawn — only superseded by a later acceptance. ⚠️ No quality record, signature, hash, audit entry, role or lifecycle rule is affected, and no module behaves differently.
Why this classification: Classified “review”, and NOT because it is a Part 11 control — it is not one, and should not be recorded as one. Nothing here gates a signature, a record, a state transition or a role; it is a commercial agreement. It is classified “review” for two narrower reasons. FIRST, IT BLOCKS ACCESS: a user who does not accept cannot reach the application, so it changes what standing between a person and their work, and a customer whose qualification covers user access should know that. SECOND, THE FILE IT CHANGES: the request layer that enforces it also enforces the idle-session timeout, the two-factor step-up, password ageing and the signed-in/public boundary, all of which ARE Part 11 controls. None of them is altered and the automated suite covering them passes unchanged — but the file was touched, and that judgement is yours rather than ours. ⭐⭐ WHAT IS RECORDED IS THE TEXT, NOT A VERSION NUMBER, and this is the part worth understanding. Each acceptance stores a fingerprint of the exact Terms and Privacy wording the person was shown, the same way an electronic signature is bound to the content of the record it signs. A record saying somebody “accepted version 1” is worth nothing once version 1's words have been edited; this way, the question “which words did they agree to?” stays answerable. ⚠️ AND THE HONEST LIMIT OF THAT. People are asked to accept again when the EFFECTIVE DATE changes, not whenever a word changes — deliberately, because re-prompting everybody for a typo correction teaches people to click through legal documents without reading them, which is the exact behaviour the record exists to evidence against. That leaves a gap (edit the words, leave the date, nobody is asked again), and the gap is closed on our side rather than yours: our build FAILS if either document's wording changes without the effective date changing. ⚠️ ONE THING STATED PLAINLY BECAUSE IT AFFECTS WHAT THE RECORD IS WORTH: the Terms and Privacy Policy are our own drafting and have not been reviewed by external counsel. Recording acceptances does not make their contents correct; it makes them dated and attributable.
legal-acceptance.test.tsThe address to contact us has changed to info@voxelioai.com, and it now appears in one form everywhere: in the help centre, on the enquiry form, in the Privacy Policy and Terms of Service, and as the reply-to address on every email Indelio sends you. The previous address still receives mail, so nothing you have already sent is affected and no reply is lost — but from this release the address printed in the product and in the legal pages is the same one. Separately, the enquiry form on our website now states that submitting it means agreeing to our Terms of Service and Privacy Policy, and links BOTH documents; it previously linked only the Privacy Policy and stated no agreement. What changes inside your quality system: nothing. No record, signature, hash, audit entry, role, rule or calculation is affected, and no screen you use to do quality work has changed.
Why this classification: Classified “none”, and it is the honest classification: nothing operating under your quality system changed. The files touched are our support-contact copy, the help assistant's instructions and the outbound-email module — none of which enforce a control, gate an action, or take part in a signature, a record hash or the audit trail. You do not need to re-test anything on the strength of it. ⭐ IT IS PUBLISHED ANYWAY, FOR TWO REASONS. The first is that a support address is the route by which you would report a problem to us, and an address that is wrong somewhere in the product is a route that fails silently — there is no error when a person emails an address nobody reads. The second is that the reply-to on emails Indelio sends your users is customer-visible behaviour, and we do not change customer-visible behaviour without saying so. ⚠️ WHAT WE DID NOT RELY ON. The address lived in seven files as seven separate pieces of text, and changing it was a search-and-replace; keeping it changed is not, because the next place we print a contact address could reintroduce the old one and nothing would fail. So it is now checked automatically — no retired address may appear anywhere in the application or its libraries, including the suspended mailbox that would bounce. That check earned itself immediately: while it was being written, an unrelated command reverted one of the seven files, and the check is what reported it rather than the change quietly shipping six-sevenths done. ⚠️ ON THE CONSENT LINE, because the wording matters more than it looks. A statement that someone agrees to a document is only meaningful if they can READ that document at the moment they agree — so the check requires not merely that both are linked, but that both pages are readable WITHOUT SIGNING IN, read from the file that actually decides that. A link to terms that sends a visitor to a login screen would be an agreement collected to a document we did not let them see.
public-contact-and-consent.test.ts⛔ THREE SCREENS COULD REPORT A CLEAN RESULT WHEN THEY HAD NOT BEEN ABLE TO LOOK. When Indelio counts rows — how many audit entries a record has, how many items are open before an inspection, how many rows one tenant can see of another — a database that cannot answer returns an empty answer rather than an error. Until this release three of those counts treated the empty answer as the number zero. What changes for you: the Inspection Readiness page now shows a signal as UNKNOWN, in grey, where it previously showed a green zero it had not established — so a page that could not check no longer reads as a clean bill of health. The instance self-check now reports that a tenant-isolation reading could not be taken, instead of recording that no other tenant's data was visible, which was the result it was looking for. And adding a finding to an audit is now REFUSED, with a message, if Indelio cannot first establish how many findings that audit already has — because numbering a new finding over an existing one cannot be undone in an append-only system. ⚠️ No record, signature, hash or audit entry was altered by this, and nothing you have already filed changes. If you have a saved or printed Inspection Readiness view from before this release, the zeros on it were not necessarily measured.
Why this classification: Classified “review” because it changes what three screens are willing to STATE, and in one case whether an action is allowed to proceed — and a customer's procedures may treat the Inspection Readiness page as a pre-audit check. No control changed: the readiness page gates nothing, the isolation control itself is enforced by the database and is unaffected, and audit findings are numbered exactly as before whenever the count succeeds. What changed is the answer given when a count cannot be taken. ⭐⭐ THE REASONING, because “a null became a zero” sounds like a rounding error and is not. Each of these three sat in front of a claim somebody relies on: readiness is the screen checked BEFORE an inspection; the isolation reading is the strongest claim the self-check makes and an executor signs it; and a duplicated finding number makes a signed audit's content hash depend on the order rows come back in, which is a record-integrity consequence rather than a display one. ⚠️ AND THE HONEST PART: eighteen further places in the product still read a failed count as zero. They are recorded, individually, with what each one would misreport — the three fixed here were chosen by consequence, not swept — and a new one cannot be added without a decision being taken. The right answer differs per place: refuse, degrade, or say plainly that the figure is unknown.
count-query-guard.test.tsreadiness.test.tsiq-check.test.ts⛔ EVERY RECORD PAGE COULD STATE THAT THE SYSTEM HELD ZERO AUDIT ENTRIES. Each record — CAPA, complaint, audit, change, field action, equipment, NCR, project, risk assessment, supplier, document — ends its audit trail with a sentence of the form “this record's 12 events of 1,443 total across the system”. That system-wide total was counted by the same query written separately in twelve places, and each of them treated a count that could not be taken as the number zero. A page describing a signed quality record's audit trail could therefore state, in print, that the system held no audit entries at all. What changes for you: when the total cannot be read, the page now says so — “the system-wide total could not be read” — instead of showing a number. When it can be read, nothing about the page changes. ⚠️ No record, signature, hash or audit entry is altered by this, and the audit trail itself was never affected: the entries always existed and the hash chain was always intact. What was wrong was the sentence describing them. ⚠️ If you hold a saved or printed record page from before this release showing “of 0 total across the system”, that figure was not necessarily measured.
Why this classification: Classified “review” rather than a control change: nothing this figure feeds gates an action, and the audit trail, its hash chain and every signature are untouched. It is corrected at this weight anyway because 21 CFR 11.10(e) is about the accuracy of the audit trail, and this sentence is the product's own statement about that trail on the page an auditor opens. A confident zero is a specific false claim, and it is worse than a blank. ⭐ THE ENGINEERING NOTE, because it changes what a reader should conclude from the previous entry: those twelve places were not twelve judgements. They were one query copied twelve times, byte for byte, with the same coercion in each — so they became a single guarded function rather than twelve separate fixes. ⚠️ THIS SUPERSEDES THE FIGURE IN THE PREVIOUS ITEM: “eighteen further places” was accurate when written and is now eight, because nine files left that list in this release. The remaining eight are still recorded individually, with what each would misreport, and still need reading one at a time — the right answer genuinely differs between them. ⚠️ One further thing worth stating plainly: this change altered a value's type from a number to “a number or nothing”, and the compiler reported no error on any of the eleven pages that display it, because a missing value renders as empty space rather than as a fault. Each page was corrected explicitly and the check for it is now automated, since the toolchain cannot see that class of mistake.
count-query-guard.test.tsThe browser is now told exactly which scripts Indelio expects to run. A Content-Security-Policy is a list a web page gives your browser saying which code it is allowed to execute; anything not on the list is refused. Indelio's has always been restrictive — no plugins, no framing by another site, no loading code from other domains — with one gap: it permitted scripts written directly into the page, because the web framework Indelio is built on writes several of its own and there was no way to distinguish those from any others. This release closes that gap. Every page now issues a fresh single-use token, the framework's own scripts carry it, and the browser refuses any script that does not. What changes for you: nothing you can see. No screen, record, signature or report is affected, and no setting changes. The value is that IF a defect ever allowed text from a document or a complaint to be treated as code, the browser would now refuse to run it rather than depending solely on Indelio having escaped it correctly. ⚠️ This is a second line of defence, not a fix for a known problem: the first authenticated dynamic security scan of the signed-in application, run 29 August 2026, found no vulnerability — this was the one hardening item in its report.
Why this classification: Classified “review” rather than “no change” because the file it changes — the request layer — also enforces the idle-session timeout, the second-factor step-up, password ageing and the signed-in/public boundary, all of which are Part 11 controls. Nothing about those controls is altered, and the automated suite covering them passes unchanged; but a customer whose qualification covers session handling should know the file was touched, and that judgement is theirs rather than ours. ⭐ THE FAILURE MODE IS WORTH STATING PLAINLY, because it is not an error message: if the token failed to reach the framework's scripts, pages would arrive looking completely normal and simply not respond — the styling applies, the text is right, and nothing works. So it was verified by signing in through the real form on a built copy of the application, which cannot succeed unless those scripts ran, rather than by a test asserting the policy string. ⚠️ Two further notes recorded deliberately. The policy still permits inline STYLES, because every visual style in this product is written as an element attribute rather than a stylesheet and a token cannot cover those; removing it would leave the application unstyled, and inline styling is a materially weaker concern than inline code. And the policy is now issued per request rather than fixed at build time, which means it must be issued in exactly one place — two such headers are not merged by a browser, they are each enforced, so a second one would quietly reinstate what this removed. That is asserted automatically.
csp.test.tszap-rules.test.tsThe AI that drafts documents has been moved to the current generation of the model, and it now tells you when it could not finish. Indelio's drafting assistant was running on the previous generation of Anthropic's Opus model; it is now on the current one, at identical cost. What changes for you: drafts are produced by a newer and more capable model, and two situations that previously produced a confusing message now produce a clear one. If the AI declines a request — which the newer model can do, for its own safety reasons — you are told it declined, rather than being told that it returned nothing and to try again, which was misleading because trying again with the same text does not help. And if a document is too long to finish in one pass, it is now refused with an explanation instead of being silently saved half-written. ⚠️ That second one matters more than it sounds: a partially drafted procedure that stops mid-section looks like a complete draft once it is in the system, and would have travelled the review and approval workflow looking whole. ⚠️ No existing document, signature, record or audit entry is affected, and nothing you have already drafted changes. The AI remains advisory: every draft still enters as a Draft and still requires human review, approval and signature before it becomes effective.
Why this classification: Classified “review” because the model is part of the configuration a customer qualifies, and because a customer's own validation may name the model in use. Nothing about the control around the AI changed — the output is a Draft, a human reviews it, a human signs it, and that sequence is what the automated evidence covers. ⭐⭐ THE ENGINEERING NOTE, because it is the reason this is not a one-line change. Between the two model generations the DEFAULT for internal reasoning inverted: the previous model did not reason unless asked, the new one reasons unless told otherwise, and the token ceiling for a response covers the reasoning as well as the answer. So a route whose ceiling had been sized tightly around its answer could now spend that ceiling before writing anything and return an empty or truncated result, with no error raised. One route in this product was configured that way. Every AI request now states its reasoning mode and effort explicitly rather than relying on a default, so that a future model change cannot alter behaviour underneath the code again, and the ceilings were re-sized. ⚠️ ALSO WORTH RECORDING: the previous model had been superseded for some time and nothing in the product said so — it was written as three separate literal values with no version beside them, so there was no name for the thing that had gone out of date. It was noticed by looking at a supplier console, not by a notification. Both models are now defined in one place with a version stamp, which is what makes the next change a recorded one. ⛔ AND THE HONEST LIMIT: no formal comparison of output quality between the two models has been performed, and the effort setting has not been tuned by measurement — a starting value was chosen and documented as such.
ai-model.test.tsai-intake.test.ts⛔ TWO RECORD-NUMBERING FAULTS AND ONE LIMIT THAT DID NOT HOLD. Continuing through the remaining places where Indelio counts rows, three turned out not to be display problems at all. (1) Adding a risk item to an assessment, and (2) adding a deliverable to a project, both numbered the new row by counting the existing ones — and a count that could not be taken was treated as zero, so the new row could be given a number another row already had. Because a signed assessment's and a signed project's content hash is computed over their rows IN NUMBER ORDER, two rows sharing a number make that hash depend on the order the database happens to return them, which can show a signed record as no longer matching what was signed. Both now REFUSE to add the row, with a message, rather than number it unsafely — the same answer already applied to audit findings earlier in this release. (3) The founding-partner limit checked how many spots were taken and compared it to the maximum; an unreadable count was treated as zero, and zero is never at the limit, so the check GRANTED the spot instead of refusing. It now refuses. ⚠️ No existing record was altered by any of this, and nothing you have filed changes. If you hold a risk assessment or project where two items share a number, tell us — it can be corrected, but only deliberately, because rows cannot be renumbered silently in an append-only system.
Why this classification: Classified “review” because two of the three touch RECORD INTEGRITY rather than display: a duplicated row number changes what a signed content hash is computed over, which is the binding a customer's e-signature relies on. The third is a commercial limit rather than a quality control, and is corrected here because its failure direction was to allow rather than to refuse. ⭐⭐ THE FINDING WORTH RECORDING IS NOT THE FAULTS THEMSELVES — IT IS THAT ALL THREE WERE ALREADY ON A LIST, DESCRIBED WRONGLY. Indelio keeps a register of places where a failed row count is not yet guarded, each with a note saying what it would misreport. Three of those notes described a harmless display problem while the code was doing something else entirely: two were the numbering fault above, and one was the limit that failed open. A fourth named a place that no longer existed. The notes are prose, and prose cannot fail a test — so the register stayed green while the descriptions in it were wrong. Each of the three is now covered by its own automated check and by a deliberately introduced fault that the check must catch, which is a claim that cannot rot the way a sentence can. ⚠️ The count of places still unguarded moves from eight to two, and both remaining ones are guarded in fact — they are listed only because the automated detector cannot see a guard written inside a helper function. That is a weaker statement than the register previously made, and it is recorded as such rather than presented as completion.
count-query-guard.test.ts⚠️ AI DRAFTING NOW HAS A YEARLY LIMIT WHEN IT RUNS ON INDELIO'S AI ACCOUNT. Drafting or revising an SOP with AI is an expensive call, and until now Indelio funded an unlimited number of them for every organization that had not connected its own Anthropic API key. There was no counter and no limit of any kind. From this release, an organization running on Indelio's account may make 100 AI drafts or revisions per rolling year; the 101st is refused with a message naming the two ways forward. ⭐ AN ORGANIZATION USING ITS OWN ANTHROPIC KEY IS NOT COUNTED AND NOT LIMITED — connect a key under Settings → Integrations and AI runs on your account with no cap, which is also the arrangement that puts the AI provider under your own supplier qualification rather than ours. The limit is set well above a normal quality system's needs: a complete SOP library for a small manufacturer is typically twenty to forty documents, so a full build-out plus revisions sits comfortably inside it. Nothing else about AI drafting changed — the draft is produced the same way, is still advisory, and is still reviewed and signed by a person.
Why this classification: Classified “review” rather than “none” because a request that previously always succeeded can now be refused, and a customer procedure describing how a document is drafted may assume AI is always available. No control changed: the document lifecycle, the review and approval signatures, the audit trail and the content hash are all untouched, and a draft produced before or after this release is identical. What changed is availability, and only for organizations running on Indelio's AI account. ⭐ WHY THIS IS IN THE REGISTER AT ALL RATHER THAN TREATED AS BILLING. The refusal is a behaviour a user meets while doing controlled work, at a moment when they may reasonably think the product is broken — so it is described here in the words the user will see, along with the way out. ⚠️ AND THE HONEST PART: the previous state was not a deliberate allowance, it was an absence. Nothing counted these calls, so nothing bounded them; the check that existed handled the AI provider's own rate limiting, which is the provider pushing back rather than Indelio setting a limit.
ai-usage.test.ts⛔ A PASSWORD PAST ITS MAXIMUM AGE BLOCKED YOU WITH NO EXPLANATION, AND THE WAY OUT WAS NOT OBVIOUS. Indelio requires a password to be changed once it passes its maximum age (21 CFR 11.300(b)). When that happened you were sent to the Change password page — with no message saying why, the ordinary “All documents” link still on the page, and the full navigation bar above it. Following any of them returned you to the same page immediately and without comment, because none of those destinations is reachable until the password is changed. There was nothing on screen to distinguish a working security control from the application having broken. You now see a plain statement that your password has reached its maximum age and must be changed before you continue, and the navigation that cannot be used is not shown. The form itself is unchanged and still asks for your current password. ⚠️ The same silent-bounce applied to anyone required to change a temporary password after an administrator reset — that is fixed by the same change.
Why this classification: Classified “review” rather than “none” because it changes what a user SEES at the moment an access control stops them, and because a customer’s procedures may describe how a user regains access after a password expires. No control changed: the maximum age, the accounts it applies to, and the point at which it fires are all exactly as before, and a password that would have been accepted yesterday is accepted today. What changed is that the refusal now explains itself and stops offering routes it will not honour. ⭐ WHY THAT IS WORTH A REGISTER ENTRY AT ALL. A control the user cannot tell apart from a fault is one they work around — they try another browser, they ask an administrator to reset them, or they conclude the system is down. Every one of those responses is a worse outcome than the control intended, and none of them leaves a record that says so. ⚠️ AND THE HONEST PART: this shipped in that state for months and no test could see it. The tests asserted the message was present; nothing they could assert knew that the navigation drawn above it was unusable. It was found by signing in and looking.
password-aging.test.tsA database migration is required in this release: run `supabase/equipment_void.sql`. It supplies three columns and a record state that the calibration module's code has always depended on but that no committed migration created. On indelio.io they were already present, so nothing you have done in Calibration & Maintenance behaved differently and no record, signature or audit entry was affected — this closes a gap between the migration set we publish as installation evidence and the system it is meant to describe. Separately, and needing no database change: a document brought into Indelio through Document Migration now records on the document itself which system it came from. It never has — the value was read from the wrong record and was blank on every migrated document since the module shipped. Nothing was lost; the source system was, and still is, recorded on the migration batch.
⚠️ THE DURABLE FIX FOR THE COVERAGE-REPORT CORRECTION PUBLISHED EARLIER TODAY. That correction fixed the specific query that was failing. This changes what happens when ANY of the reads behind the design-to-risk coverage cannot be completed — an unapplied migration, a permission, a transient refusal, not only the one column that was wrong. Where a source cannot be read, the Coverage PDF now says so at the top, before the scope section and before any number, and it will NOT state “No open gaps” at all — it records that whether there are open gaps is UNKNOWN, and tells you not to file it. The project page shows the same warning above the coverage panel. The same treatment is applied to the record thread and the risk thread: each connection arm that cannot be read is now named at the top of the page, instead of that section simply appearing empty. ⛔ If you generated a Coverage PDF before this release and it stated no open gaps, that sentence was printed without checking whether the data behind it had been read. Re-generate it.
Why this classification: Classified “review” because it changes what a DOCUMENT you may file will say. No control, record, hash, signature, link or audit entry is affected, and nothing you had linked was ever lost — the links were always stored correctly. What changes is the conclusion the report is willing to state, and in one direction only: it will now decline to state one where it previously stated a clean finding. If your procedures treat the Coverage PDF as evidence, a report generated after this release either carries a conclusion or says plainly that it cannot, and there is no longer a third possibility where it carries one it did not compute. ⭐⭐ THE REASONING, because the classification looks conservative for what is mostly an error path. A blank panel invites a question; a report does not. The sentence at issue — every stated risk control is implemented and every Safety design input traces back to a hazard — is a regulatory finding under ISO 14971 clause 7, printed in green, and a reader has no way to tell it apart from the same sentence backed by data. That is why the all-clear is SUPPRESSED rather than qualified: there is no wording that truthfully says “no gaps” over a source that was not read, so none is printed. ⛔ AND ONE THING WORTH RECORDING, because it is the honest part. While building this we found the same defect inside the fix for it. The thread arms were written to catch a failure and carry on — but our database RETURNS a refusal rather than raising one, so those arms caught nothing and a refused query still became an empty section. It was found by a test written to prove the fix, not by reviewing it. Every read is now checked explicitly.
unread-not-empty.test.tsdesign-risk.test.ts⛔ GLOBAL SEARCH COULD NOT RETURN A DOCUMENT, and it said “no matches” rather than failing. If you have used the search box to look for an SOP, a work instruction or any other controlled document by its code or title, it found nothing — not because nothing matched, but because the query Indelio built for the documents table named a column that does not exist, and the database refused it. The refusal was discarded and the module simply contributed no results. Every other module searched normally throughout, which is why the box appeared to work. It now returns documents, and where a module cannot be searched at all the results page says which one and stops claiming that nothing matched — “no records match” is a statement about your whole tenant, and it was being made over a module nobody had read. ⚠️ Search results for a document do not show a state badge, and that is deliberate rather than a remnant: a document does not have one state — its versions do — so naming a state for the document would mean choosing a version to speak for it. Open the document to see its versions and their states.
Why this classification: Classified “review”, and the reason is not that a control changed — none did. No record, content hash, signature, lifecycle state or audit entry was affected in any way, and every document has been present, correct and reachable through the Documents module and every link to it for the whole period. What changed is a FINDING aid, and the reason this is not “none” is the direction in which it was wrong: it returned an empty result that looked like an answer. If anyone in your organisation searched for a document and concluded from the empty result that it did not exist — while preparing for an audit, checking whether a procedure was already written, or answering a question about what you hold — that conclusion was drawn from a failed query, and it is worth knowing before you rely on it again. ⚠️ Being precise about the boundary: nothing was hidden, deleted or altered, and no other module was affected. ⭐⭐ THE SHAPE, recorded because this register has now drawn it several times in one month: a read that returned nothing, rendered as a real answer. Here the query was refused by the database and the error was thrown away, so “that query was rejected” and “nothing matched” reached the screen identically. Two things changed as a result. A refused search is now written to our logs naming the module, so it can no longer be invisible; the surrounding behaviour is unchanged and deliberately so, because one module that a customer has not yet migrated must not take the other fifteen down with it. And the underlying cause — table and column names written as ordinary text where our compiler cannot check them — is now checked by an automated ledger across every such list in the codebase. ⛔ That ledger is also how this was found, and it was written because a note in our own source claimed the compiler already covered these lists. It did for four of them and not for seven, and the note had been true when it was written. The comment sitting directly above the broken entry read “Verified against the schema”.
registry-columns.test.ts⛔ IF YOU ARE EVALUATING INDELIO IN A SANDBOX, THIS ONE AFFECTED YOU. Your dashboard tells you that to complete a two-person step — you cannot approve your own work, that is the segregation-of-duties control — you should sign in as a named colleague, and it gave you a password. For evaluation sandboxes that password was never the one those accounts had: they were created with random passwords that were never printed anywhere. So the walkthrough the panel exists to enable was the one thing it made impossible, and the only way through was a route nobody was told about. It now shows a password that genuinely works, unique to your sandbox. If you tried to sign in as a colleague and concluded the account was broken, it was not — the instruction was wrong. Try it again.
Why this classification: Classified “none”, and the reason matters more than the classification: this only ever affected EVALUATION AND DEMO SANDBOXES, never a customer instance. A customer’s dashboard has never shown a password for one of their users and never will — their people set their own, and a panel asserting one would be stating a credential that is not ours to state. That guarantee was already in place and is unchanged; what was wrong sat entirely on the sandbox side of it. No control, record, signature or audit entry was affected in any tenant. ⭐ Published anyway, for two reasons. It is the honest thing to tell an evaluator who may have concluded from a failed sign-in that a control was broken when the instruction was simply wrong. And the shape is worth recording, because it is one this register has drawn before in other places: THE PRODUCT ASSERTING SOMETHING IT DOES NOT ENFORCE. A fix to this same panel earlier in August corrected which colleague it names and who is shown it — and left the password untouched, because nothing compared what the screen SAYS against what provisioning DOES. That comparison now exists as an automated check, verified by putting a hard-coded password back and confirming it fails. ⚠️ One design note stated plainly because a reader may wonder why it was not simpler: each sandbox now gets its OWN password rather than one shared across all of them. A single shared value would have been less work and would have put a working credential for one evaluator’s sandbox on another evaluator’s screen — the colleague addresses are derivable from a prospect’s own email address. Tenant isolation would still have held; the credential would not have. Nothing is stored either way: the password is computed from the sandbox’s identifier and a server secret whenever it is needed.
sandbox-credentials.test.tstenant-type.test.ts⚠️ A NEW VENDOR CAPABILITY YOU SHOULD KNOW ABOUT, because it reaches into your tenant. An Indelio platform administrator can now reset a password for a user in your organization, from our side. It exists because the alternative was worse: until today, a tenant whose only administrator had lost access had nobody who could restore it — our support console could not even show us which login address an account used, let alone reset it. This is recovery of last resort. It does not replace your own administrator, who should always do it where one is available. ⛔ What it does NOT do: it cannot read anything, it cannot sign anything, and the temporary password is never emailed to anyone — it is shown once to the administrator performing it and handed over out of band. Every use requires a written reason, is recorded in YOUR audit trail attributed to the named Indelio administrator, and emails the account holder to tell them it happened. Separately, our support console now shows each tenant’s users and their login addresses, which it previously did not.
Why this classification: Classified “review”, and deliberately not “none” even though nothing you do inside Indelio behaves differently, because a new way for someone outside your organization to change a credential inside it is exactly the kind of thing your quality function should decide how to treat. Some customers will want it recorded in their supplier file or their access-control procedure; some will want to be told before it is ever used on their tenant; you may want neither. It is your call, and you can only make it if we say the capability exists — which is why this entry is here rather than folded into a maintenance note. ⭐ What CONSTRAINS it, so the decision can be made on facts. The caller must be on our platform-administrator allowlist, and that is re-checked in the data layer rather than trusted from the page that called it. The target user must genuinely belong to the tenant named, so a mistaken identifier cannot write an entry into a different customer’s trail. A reason of real substance is required — padding it with invisible characters is refused. The administrator must re-enter their own password at the moment of doing it. The account holder is emailed, and whether that email succeeded is recorded in the entry, because “we notify the user” is only a control if it can be shown to have happened. And the reset forces a password change at that person’s next sign-in, so the temporary credential is a handover rather than something anyone keeps. ⚠️ One honest limitation: the audit entry is written after the password is changed. If that write were to fail, the operation reports loudly that the change happened and was NOT recorded, rather than claiming nothing changed — an unrecorded privileged action is the thing worth shouting about. ⛔ THE INCIDENT BEHIND IT, published because it is the more useful half. An evaluator was given a login address that had no account behind it. She could not sign in, and “Forgot password” on an address with no account returns exactly the same reassuring sentence it returns for one that has an account — that sameness is deliberate, and it is what stops a stranger using the page to discover who has an account with us. The cost is that a person with a genuine problem cannot tell the difference either, and neither could we, because the console showed no login addresses. She waited thirteen days. Nothing was broken; nothing failed; no alarm could have fired. The anti-enumeration behaviour is correct and stays. What was missing was any way for us to look, and that is what this release adds.
platform-users.test.tspassword-reset.test.ts⚠️ A SECOND VENDOR CAPABILITY IN THE SAME FAMILY, and this one closes a gap in our own records rather than opening a new door. Every organization on Indelio carries a declared type — Customer, Evaluation, Demonstration or internal Test. It is not a label: it decides whether two-factor authentication is MANDATORY for administrators in that tenant, and whether sign-ins in that tenant are reported to Indelio. Until today an Indelio administrator changed that value directly in the database, because our own support console told them to. That route wrote NO audit entry, so a change to your access-control policy and to who can see your staff's sign-ins could be made with no record of who did it, when, or why. It is now an action in the console: it requires a written reason, the administrator must re-enter their own password, and it is written to YOUR audit trail attributed to them by name. ⛔ To be exact about what this does and does not mean for you: the value on your organization is unchanged, this does not let us do anything we could not do before, and we have no reason to change a customer's type. What changes is that if it ever happens, you will see it.
Why this classification: Classified “review” on the same footing as the password reset above, and for the same reason: nothing you do inside Indelio behaves differently, but a vendor-side action that reaches into your tenant and moves an access control is something your quality function should decide how to treat — in your supplier file, your access-control procedure, or your change-notification expectations. It is your call, and you can only make it if we say the capability exists. ⚠️ We are deliberately NOT classifying it “none”, even though the honest description is that an untracked action became a tracked one, because the underlying capability is what your assessment is about, not our tooling around it. ⭐ THE SECOND HALF, which matters if you are evaluating Indelio in a sandbox. An evaluation or demonstration tenant cannot show segregation of duties unless two people in it hold the approver role — you cannot approve your own work, so a sandbox with one user reaches “submitted for review” and stops. Provisioning has seeded those colleague accounts since 27 August. Changing a tenant's type by hand did not, so a tenant made into an evaluation sandbox after it was created arrived in exactly that unusable state. The console action now seeds them as part of the change, re-reads the tenant afterwards to confirm it really can complete a two-person approval, and refuses to write the new type if it cannot. ⛔ AND THE SHAPE, recorded because this register keeps drawing it: THE RULE LIVING SOMEWHERE THE SYSTEM CANNOT REACH. The seeding rule first lived in a script somebody had to remember to run — two evaluators got an unusable sandbox for two weeks and nothing went red. That was fixed by moving it into the code that creates a tenant. It then lived in a database console our own interface pointed people at, and nothing went red there either. The same rule, the same silence, one step to the left. It is now in the one place both routes pass through.
platform-tenant-type.test.tstenant-type.test.ts⚠️ A LIMIT WE WERE APPROACHING WITHOUT KNOWING IT, corrected before anyone met it. Several parts of Indelio need to read the list of user accounts — to show your administrator who is in your organization, to refuse a second account for an address that already has one, to keep training off people you have deactivated, and to address complaint-triage notices and the daily reminder digest. Every one of those asked our authentication service for the FIRST PAGE of accounts and treated it as the complete list. That service returns at most 200 accounts per page. ⛔ THE IMPORTANT PART FOR YOU IS THAT THE 200 IS COUNTED ACROSS THE WHOLE PLATFORM, NOT PER ORGANIZATION — so a ten-person customer would have started to be affected because OTHER organizations grew, which is not a limit anyone could have anticipated from their own use. Measured on the day of the fix: 51 accounts of 200, so nothing had reached it and no record, signature, assignment or audit entry was ever wrong. Every one of those reads now pages through the whole list and then checks what it read against the count the service itself reports, refusing to answer at all rather than answering from a partial list.
Why this classification: Classified “review” rather than “none”, and the reason is what WOULD have happened rather than what did. Nothing in your system behaved incorrectly, because the threshold was never crossed. But the failures waiting on the other side of it are not the kind that announce themselves: your user-administration page would have shown a person with a blank email address and marked a DEACTIVATED account as Active; training would have been assigned to people you had deactivated, which is precisely what that check exists to stop; a reminder digest would have quietly dropped recipients; and onboarding a new organization would have failed with an error about creating an account rather than about the lookup that was actually wrong. Each of those is a controlled function reading a partial list and reporting a confident answer, so the decision of whether your qualified state covers it is yours to make on the facts rather than ours to wave through. ⭐ ONE BEHAVIOUR GENUINELY CHANGES TODAY, and it is worth stating plainly: where the account list cannot be read at all, the user-administration page now reports an error instead of rendering. It previously discarded the failure and drew the page from an empty list — which showed every person with no email address and every account as active. A page that will not load is recoverable; a page that confidently lists the wrong people is not, and an administrator changing roles or resetting a password from that screen is acting on it. ⛔ THE PART WORTH RECORDING, because it is why this took a real fix rather than a one-line one. The obvious repair — follow the “next page” marker until it says there are no more — does not work against our authentication service: measured directly, that marker never says there are no more, it counts up and then wraps back to the first page, and the “last page” number it reports is simply wrong. A walk written that way reads for as long as you let it and never finishes; ours reads until a page comes back short and then verifies the result against the total the service reports, which was the one figure it got right. The measurement is recorded in the automated evidence rather than the reasoning, so it can be re-run rather than believed.
auth-directory.test.tsconsole-tenancy.test.ts⛔ THE USER & PRIVILEGES TABLE COULD SHOW ROLES THAT WERE NO LONGER TRUE, and it had a working Save button beside them. The table keeps an editable copy of each person's roles while you tick boxes. That copy was taken when the page first loaded and it took priority over anything the server said afterwards — so if a person's roles changed by any route other than that table, the checkboxes kept showing the old ones until the page was fully reloaded. ⚠️ The consequence is the part that matters: pressing Save on a row that looks wrong would have written the STALE roles back, removing permissions the system had granted, and your audit trail would have recorded that an administrator meant to do it. The table now shows what the server holds and keeps only the ticks you have actually made. Fixed in both places it exists — your own Admin page and our support console.
Why this classification: Classified “review” because it could have caused a WRONG ROLE ASSIGNMENT — an access-control change — that looked deliberate in the audit trail. No signature, record, hash or lifecycle rule was affected, nothing was lost, and the roles actually stored were always correct; what could be wrong was the screen an administrator reads before deciding. ⚠️ Worth checking rather than assuming it never happened: if anyone has pressed Save roles on a row shortly after a role change made somewhere else — a second administrator working at the same time, or an account we adjusted from our side — the saved set may have been the older one. Your audit trail records every role change with who made it and when, so this is answerable from your own records rather than ours. ⭐ HOW IT WAS FOUND, because it says something about where the risk sits. It was found by a person clicking through the product on 2026-08-29, not by a test — the screen reported two approvers in one panel and one in the table directly below it, on the same page, and the table was the wrong one. The automated suite could not see it because both numbers came from correct code; only the screen showed the contradiction. Four defects were found in that one walkthrough, of which this is the only one with a controlled consequence; the other three were an unreadable confirmation message, a button that looked clickable while disabled, and a grammatical error.
admin-tables.test.tsA development control, published because it is the kind a validation reviewer asks about and because one customer-visible defect came out of it. Indelio’s code is now checked against the database schema by the compiler: a full description of every table, column, type and enum is generated from the same migration set we publish as installation evidence, and the database client is typed with it. The practical effect is that a query naming a column the database does not have, a stored value of the wrong type, and — the case that prompted this — code reading a field off a record that does not carry it, stop being things that run and start being things that cannot be built. Two smaller corrections fell out of it and are already in this release: the migrated-document source system (below), and a document download whose stored file name is empty now falls back to a sensible name instead of offering the file as “null”. Nothing else changed in how any module behaves.
Why this classification: Classified “none”, and the classification is the honest one even though the work was substantial: no function operating under your quality system was changed. Every record, rule, signature, state transition and calculation behaves exactly as it did. This is a change to how our code is checked before it reaches you, which is a matter for our development controls rather than for your validated state — you do not need to re-test anything on the strength of it. ⚠️ Two things stated plainly, because a control described only by what it catches is easy to over-read. FIRST, WHAT IT DOES NOT COVER. It compares our code against the MIGRATION SET, not against a running database; where the two disagree, this cannot see it, and on 2026-08-28 they genuinely did (three columns existed in production that no committed migration created — see this release’s calibration entry). It also cannot see through a type assertion, a construct our code still uses in many places, and where one is used the old exposure remains exactly as it was. SECOND, THE PART THAT IS ACTUALLY DURABLE. A generated description of a database is a hand-maintained list the moment somebody adds a column without regenerating it — and it would go on reporting success while checking against a schema that no longer exists. So the file is not the control: a merge-gate check that regenerates it and fails on any difference is, and that check was itself verified by adding a column to a migration and confirming it went red. ⭐ The lesson this closes, recorded because it is the third form of one shape. A read that returns nothing can be rendered as a real answer: first a query naming a column that does not exist (the design↔risk weave), then a report asserting a clean finding nothing had computed, and now code reading a property a query never returned. The first two were caught by checks we wrote. This one could not be — knowing which columns a record is carrying at the line that reads it is a question about program structure, not about text, and it needed a compiler rather than another search. It was found by generating the schema description, applying it, and reading what the compiler rejected: 113 objections, of which one was a real defect.
db-types-drift.test.tsselect-columns.test.tsmigration.test.tsThe source system of a migrated document — “MasterControl”, “SharePoint”, “paper”, whatever you typed when you created the batch — is now recorded on the document version it produces, and appears beside the version on the document page as “· from MasterControl”. Until this release it was never recorded there. The value was taken from the staged file rather than from the batch, and a staged file has no such field, so the document was written with a blank source system every time and the line on the document page never appeared. ⚠️ This can only be recorded at the moment a document is committed, so documents you migrated before this release keep a blank source system on the document itself. Their provenance is not lost and never was: the batch page has always shown the source system, and every migrated document remains linked to the batch it came in through — that is where to look for anything migrated earlier. Separately, committing a staged document now stops with a clear message if its batch cannot be read, instead of proceeding as though the batch were open.
Why this classification: Classified “review” because migration is how legacy records enter your quality system, and where a record came from is part of what makes it a controlled record rather than a file. If you have migrated documents and your procedures expect the source system to be visible on the document — for example in a DHF index, a document register you export, or evidence you prepared for an audit of your migration — that expectation was not being met and you may want to check what you produced from it. ⚠️ Being precise about the blast radius, because it is narrower than the entry may sound. No document content, hash, signature or audit entry was affected; the file, its version, its approver and its effective date are all exactly as they were. The migration audit trail has always recorded the source system correctly at batch level, so the fact was captured — it was one field on the document that was not carrying it. Nothing needs re-migrating, and no existing record is wrong; it is incomplete in one field, and only forward. The second part is a fail-closed correction: a batch whose state could not be read was previously treated as an open batch, which meant a database read that failed could let a document be committed into a batch your QA had already verified and closed. It now refuses. ⭐⭐ The lesson, recorded because it is a new form of one this register has drawn twice this month. In August we published a defect where a query asked the database for a column that does not exist and the resulting error was read as an empty answer. This is the step after it: the query was correct, and the CODE THEN READ A FIELD THE QUERY NEVER RETURNED. In JavaScript that is not an error — it is the value “undefined”, which the code turned into a blank and wrote without complaint. The check we built in August reads every query against the columns the database actually has, and it could not see this, because the query was fine. It was found by generating a full description of the database from our migration set, applying it to the application's own code as a type definition, and asking the compiler which fields the code reads that the database does not have. That is now a technique we have, not a check that runs by itself, and we are stating that limit rather than implying broader coverage than we have.
migration.test.tsselect-columns.test.tsWithdrawing (voiding) a calibration or maintenance record entered in error has been part of the Calibration & Maintenance module since it shipped, and worked on indelio.io. The database migration that creates what it needs — a `Voided` record state and the reason, who and when columns behind it — was never committed to our migration set. The consequence was confined to a system built from that set rather than from the running database: the Void control appeared on the record exactly as it does today, and failed only when pressed, with the message “Run the Void migration in Supabase first”. That file did not exist. It does now, it is safe to re-run, and it has been verified by building a database from every committed migration in order and comparing it column by column against the running system.
Why this classification: Classified “review”, and the reason is worth reading because it is narrower than the entry may sound. No controlled function changed: voiding behaved on indelio.io before this release exactly as it does after it, the rules around it are untouched — an ACCEPTED record still cannot be voided, voiding still needs the approver or qa role so that whoever entered a record cannot withdraw it themselves, and the record is still never deleted — and no data, signature or audit entry was affected in any way. What changed is our INSTALLATION EVIDENCE. The migration set is what an installation qualification is performed against, and it is how you would rebuild or re-create an instance; it did not describe the running system, so the two disagreed for the lifetime of the feature. If you have relied on our IQ material, this is a defect in it that is now corrected. ⭐⭐ The lesson, recorded because it is the same shape this register has drawn before from the opposite direction: our checks confirmed that every committed migration applies cleanly in order, and they did. Nothing asked whether the RUNNING system held anything the migrations do not create. It was found by building a database from the migrations alone and diffing it against production — 95 tables on each side, and these three columns were the only difference in either direction.
sql-run-order.test.tsequipment.test.tsA database migration is required in this release: run `supabase/audit_chain_lock.sql`. It corrects a fault in which concurrent writes to the audit trail could cause the daily integrity check to report the trail as tampered with when it had not been — no record was ever altered or lost, but if you have investigated an integrity alert, re-read it in that light. Separately: three more separation-of-duties checks — complaint closure, NCR closure and audit closure — now refuse to proceed when they cannot be performed. This is the same correction published yesterday for risk, change control and design validation, and it means yesterday's entry understated which modules were affected. If you acted on that entry, please re-read this one: the review it recommended needs to cover three further record types. Separately, the validation package no longer claims 21 CFR 211 (drug GMP) within the scope it validates — no requirement was ever traced against it. If you hold a copy taken before today, replace it.
⚠️ CORRECTION TO THE ENTRY BELOW, PUBLISHED THE SAME DAY. The design-to-risk coverage figures — on the project page and in the new Coverage PDF — were not counting the design side at all. Since the design↔risk weave was first built, the query that reads design inputs asked for a column that does not exist on that table, so it returned an error rather than rows. The error was treated as an empty result. The consequence: “Safety design inputs” and “Safety inputs with no hazard behind them” were reported as ZERO on every project, always, and the picker for linking a hazard to a design input had nothing to offer. The risk side of the coverage — controls stated but not implemented, and controls implemented without a passing verification — was counted correctly throughout and is unaffected. ⛔ The reason this is published as its own entry rather than folded into the one below: for a few hours today the Coverage PDF could state “No open gaps — every Safety design input traces back to a hazard.” That sentence was not a computed result. If you generated a Coverage PDF today, discard it and generate it again; if you filed one, replace it. No record, signature or audit entry was affected, and nothing you had linked was lost — the links were always stored correctly, they were not being read.
Why this classification: Classified “review” because a report you may have filed as design-review evidence could assert a clean finding the system had not calculated, and only you know whether you generated or relied on one. The remedy is small and entirely in your hands: regenerate. ⚠️ Being precise about the blast radius, because it is narrower than it may sound. Nothing was written incorrectly — no link, record, signature or audit entry is wrong, and no data was lost. This was a READ that failed and was reported as an empty answer instead of as a failure, so the effect is confined to what the coverage figures and the report displayed. Any design-risk link you created is intact and will now appear. ⭐⭐ The lesson worth recording for anyone weighing our automated evidence, because it is the same one this register has drawn before in a new form: our database interface answers a request for a column that does not exist with an ERROR, and the calling code turned that error into an empty list — so the feature returned nothing, convincingly, while every automated check stayed green. The checks all ran against fixtures rather than against the schema, so none of them could see it. A new check now reads every query in the application against the columns the migrations actually create, and it was verified by reintroducing this exact defect. ⚠️ It was not found by a test. It was found by a person trying to use the feature with real data and noticing that nothing printed.
select-columns.test.tsdesign-risk.test.tsThe design-to-risk coverage that was already on the design project page can now be exported as a PDF — a “Coverage PDF” link on the Design ↔ risk weave panel. It answers, per design domain, the question ISO 14971 clause 7 asks: which risk controls are stated but not implemented in the design, which are implemented but have no PASSING verification behind them (§7.2), and which Safety design inputs have no hazard behind them. Every count is followed by the individual items it is made of, naming the risk assessment, the item number, the hazard and the control, so a finding can be acted on rather than only counted. The report states its own limits in the document: it is derived from the links recorded in the system, and cannot tell you whether the right hazards were identified, whether a control is adequate, or whether a passing verification was an adequate test. ⚠️ The coverage NUMBERS are unchanged — this is the same calculation the screen has always shown, now printable.
Why this classification: Classified “review” although no existing behaviour changed, because the change is additive in a way that can enter your evidence: a customer may choose to file this PDF as design-review or audit evidence, and whether it belongs in your validated scope is a decision only you can make. Nothing you have already collected is affected. ⚠️ Being precise about what did and did not change internally, because it bears on whether you need to re-check anything: the per-domain coverage calculation was REFACTORED so that the summary counts and the itemised list are produced by one function rather than two. The three conditions that define a gap are unchanged, and the thirty existing automated checks over that calculation pass unmodified — the numbers on your screen before and after this release are the same numbers. The reason for the refactor is worth stating plainly: had the report re-implemented those conditions separately, a summary could say four while the list showed three, and nothing would say which was right. A count a reviewer cannot reconcile to its own rows is worse than no count. ⭐ Needs no database change and no action on any existing instance.
design-risk.test.tsEntries in the audit trail are linked to one another by a hash, so that any later alteration is detectable. Under concurrent use — two people, or a person and a scheduled job, writing to the same organization at the same instant — two entries could be written claiming the same predecessor. The daily integrity check requires each entry to name the one before it, so it would have reported the trail as BROKEN, which is its way of saying it had been tampered with. No record was ever altered, forged, hidden or lost by this, and nothing about who may write to the audit trail changed: the trail remains append-only, and the database continues to refuse UPDATE and DELETE on it for every account including our own service account. What could go wrong was the DETECTION reporting a fault where there was none. Audit-trail writes for a single organization are now serialized so two entries cannot claim the same predecessor. ⚠️ We have no evidence this ever occurred on any instance, and it would have announced itself loudly rather than silently if it had. ⚠️⚠️ If you have ever seen an audit-trail integrity alert on your instance, re-examine it in this light before treating it as evidence of tampering — and if you investigated one and found no cause, this is a likely explanation. A database migration is required: run `supabase/audit_chain_lock.sql`.
Why this classification: Classified “review” rather than “none” for two reasons, and deliberately not “requalify”. It is not “none” because the audit-trail integrity check is a controlled detective function and its behaviour changed, and because the change is not effective on your instance until you run the migration named above — an entry that requires an action on your side is never a no-change entry. It is not “requalify” because no preventive control was weakened or altered: the append-only enforcement, the signature bindings, the access restrictions and the content of every existing record are all exactly as they were, and no evidence you have already collected is invalidated. What to review is narrow and specific: whether your procedures treat an audit-trail integrity alert as presumptive tampering, and whether any alert you have already acted on should be reconsidered. ⚠️ Being plain about what we cannot tell you: because a broken link and a raced write leave the same trace, we cannot determine from your data which one you saw. We can tell you that the raced write requires two audit entries for the same organization within the same instant, so it is far more likely on an instance with several active users or several scheduled jobs than on a quiet one. ⭐⭐ Recorded with the reason it was found, because that bears on how much weight to place on our automated evidence: nothing detected this. It was found by reading the database source ahead of an external security review, and the existing test suite passed throughout — a hash-chain test that feeds the checker a well-formed chain proves the checker walks a chain correctly, and says nothing about whether two writers can produce a chain that is not well-formed. The suite now proves the consequence first, by feeding it a forked chain and asserting it reports tampering, before asserting the fix. ⭐ The fix is also held in POSITION and not merely in presence: the lock one line later would satisfy a naive check while serializing nothing, so its ordering is asserted and was verified against that exact mutation.
audit-chain-lock.test.tsClosing a complaint, closing an NCR and closing an internal audit each look up who signed the previous step, so that the same person cannot sign both. If that look-up FAILED — a momentary database problem rather than a normal result — the check was skipped and the signature was allowed. All three now stop and say the check could not be performed. ⚠️⚠️ This is the identical defect corrected yesterday in risk assessments, change controls and design validations, and yesterday's register entry told you those were the only three affected. That was wrong. Six checks had the fault, not three. ⚠️ The everyday behaviour is unchanged in all three: where the look-up works, exactly the same people are allowed and refused as before. Only the failure path changed. Specifically: the investigator of a complaint still cannot sign its closure; whoever approved an NCR disposition still cannot close the NCR; whoever issued an audit report still cannot close the audit.
Why this classification: Classified “review” for the same reason as yesterday's correction: a controlled function changed behaviour in a failure mode rather than in normal use, and no record, signature or audit entry was altered, with nothing changed about access or storage. ⚠️⚠️ The reason to look rather than file this away, and the reason to look AGAIN if you already looked yesterday: for the period before this release it was possible, though it required a database read to fail at the exact moment of signing, for one person to sign both sides of these separations too. Indelio cannot tell you whether that happened on your instance, because the skipped check left no trace. If you performed the review recommended yesterday, it covered risk assessments, change controls and design validations only — extend it to complaints, NCRs and internal audits, checking the signature history for cases where the same person signed both steps. Every signature is in your audit trail and in your records export, so this remains answerable from your own data without our help. ⭐⭐ The distinction worth recording for anyone maintaining a qualified state is about how the first three were found and why the second three were not. The check that found them derives its own scope from an internal register naming, per module, the guard that enforces each published claim. For these three modules that register named the ROLE requirement (closure needs a quality or approver role) and not the identity check sitting beside it, so all three fell outside what was examined — while the automated check reported itself as covering the area. The register now grades each entry against the code it points at, and refuses an entry that records the weaker of two controls. ⚠️ Stated plainly because it bears on how much weight to place on our automated evidence generally: the check was correct and its scope was wrong, and a scope error of that kind reports as a pass. Needs no database change and no action on any existing instance beyond the review above.
copy-claims.test.tsThe validation package no longer states that 21 CFR 211 (drug GMP) is within the scope it validates. The Validation Plan, the User Requirements Specification and the Part 11 Validation Summary each opened by naming Part 11, 21 CFR 820 (QMSR / ISO 13485) and 21 CFR 211 as the regulations the system is qualified against. Part 211 did not belong in that list: none of the 65 requirements in the URS is written or traced against it, and the one record type that is specific to drug GMP — batch records / device history records — is already marked out of scope in the module coverage map. All three documents now name Part 11 and 820/QMSR only, and each states explicitly that drug GMP and EU Annex 11 are outside the package. ⚠️ Individual Part 211 section numbers still appear elsewhere in the product — §211.198 beside ISO 13485 §8.2.2 on complaints, for example. Those are unchanged and are correct: they name the drug-side analogue of a device requirement the system does implement, so a reader can orient themselves. They were never a claim of validated scope, and the plan now says so in as many words. ⚠️⚠️ A SECOND CORRECTION IN THE SAME DOCUMENT, AND THE MORE SERIOUS OF THE TWO: the Part 11 traceability matrix (§11.10(g), authority checks) listed “plan-approver ≠ closer” among the segregation-of-duties rules enforced for CAPA. That rule was REMOVED in July 2026 on independent validation-review advice — it is not required by regulation and can block closure outright in a small QA team — and this document went on describing it for six weeks afterwards. CAPA closure is restricted to the QA role, and QA independence is what carries that clause. The row now lists the separations actually enforced, module by module, with a note recording what was removed and why rather than deleting the claim quietly. It also now distinguishes which separations are enforced by IDENTITY and which by ROLE, since a role check is satisfied by one person holding both roles and a reviewer needs to know which kind each module has.
Why this classification: Classified “review” although no software behaviour changed at all, because the document that changed is the one a customer uses to decide what our evidence covers, and it previously claimed more than it delivered. ⚠️⚠️ If you hold a copy of the validation package taken before today, its scope statement is wrong in a way that matters: it names a regulation against which nothing was ever tested. If you cited that scope statement in your own validation file, in a supplier assessment, or to an auditor, replace it with the current version — take a fresh copy from the Validation page. ⚠️ Nothing you validated has become less true; the qualification evidence itself is unchanged, and the Part 11 and 820/QMSR coverage it describes is exactly what it always was. What was removed was a claim to a THIRD regulation that no requirement backed. ⭐ Recorded here rather than treated as a documentation tidy-up because it is the same failure this register described earlier today in the software: an asserted scope with nothing behind it, where the assertion reads as evidence to anyone who does not go looking for the trace. A pharmaceutical manufacturer intending to qualify Indelio for a Part 211 use case would need to establish that scope themselves; we do not and did not.
validation-doc.test.tsAn exported copy of your quality system now contains the uploaded files themselves, not only a list of them, and the export now checks your stored files against the records that refer to them. Separately, that list was incomplete: it named evidence attached to records, but not your uploaded controlled documents or your migration source files. Any export taken before this release understates how many files your organization holds.
When you export your organization's records, the archive contains a file called files-index.csv that lists every uploaded file — its name, size, storage location and the record it belongs to. That list covered only ONE of the three places this system stores a file. Evidence attached to a quality record was listed. Your uploaded controlled documents — the ones you attached a PDF or Word file to, which for most organizations means your SOPs themselves — were NOT listed, and neither were the source files staged by a bulk migration. Measured on the live system before the correction, the index named 3 of the 79 files actually held. ⚠️ The archive also stated, in both its README and its manifest, that the index named "every uploaded file", and reported the export as complete. So the shortfall was not visible from inside the export: an incomplete list was indistinguishable from a complete one, which is the specific failure this export was designed to prevent. The index now covers all three sources and carries a `source` column naming which one each file came from; if any source cannot be read, the archive names it as failed per source and reports the export as incomplete, rather than omitting those files silently. The file contents themselves are still not included in the archive — that is unchanged, and stated plainly rather than implied.
Why this classification: Classified “requalify” rather than “review” because the correction is retrospective: an export you already took and filed is inaccurate, not merely different from what you would get today. If you hold an exported archive as your organization's copy of its records — under 21 CFR 11.10(b), which requires the ability to generate accurate and complete copies for inspection — that archive's file inventory understates what you hold, and the archive itself asserts it is complete. ⚠️ No records, signatures, audit-trail entries or files were lost, altered or deleted; nothing about how your data is stored or reached changed, and no action is required on the instance. What was wrong was the INVENTORY of files inside the export, and the claim of completeness printed beside it. The conservative course is to take a fresh export and to treat any previously-filed archive's files-index.csv as understating your file holdings — the records CSVs in those archives were not affected. ⭐ The distinction worth recording for anyone maintaining a qualified state is one this register has now drawn several times: a control can be correct in the place it was written and absent from an adjacent place nobody scoped. The table inventory in this export was derived from the database schema and checked in both directions on every build; the FILE inventory was a single table name written inside a function, and no check compared it to where files actually live. It is now derived and checked the same way, so a future change that adds a place files are stored fails the build unless the export is told about it.
export-files.test.tsexport.test.tsExporting your organization's records (Settings → Export your records) now places the uploaded files in the archive, under a files/ folder arranged by the record each belongs to. Previously the archive listed your files and you retrieved them one at a time from the records they were attached to; a bulk import had no matching bulk export. ⭐ Every file remains listed in files-index.csv whether or not its contents are present, and an "outcome" column states which — there are exactly three reasons contents can be absent, and each names itself against the file it applies to: bulk-migration SOURCE files are excluded by decision (on commit the same file becomes a controlled-document version, which IS included, so including both would place every migrated document in your archive twice); the archive holds at most 250 MB of file contents, and anything beyond that is listed as over the limit with its record still exported in full; and a file that could not be retrieved from storage is listed as failed, with the reason. ⭐⭐ Where a controlled document's contents are included, the export re-computes the file's SHA-256 and compares it to the value that document's electronic signatures are bound to, recording whether they match. This lets you establish from the archive alone — with no access to Indelio and no cooperation from us — that the file you hold is the file that was signed. A file that does NOT match is reported as a mismatch, the export is reported as incomplete, and the discrepancy is stated in the archive's notes.
Why this classification: Classified “review” because the behaviour of a controlled function changed — what a records export contains is now materially different — while no quality record, signature, audit entry or access rule was altered, and nothing about how your data is stored or reached changed. ⚠️ It is not “none”: if your procedures describe what a records export produces, or if a performance qualification exercised the export, both now describe an archive of a different shape, with a files/ folder, additional columns in files-index.csv, and additional fields in manifest.json. Check whether that falls inside the intended use you qualified. ⭐ The distinction worth recording for anyone maintaining a qualified state is about the SIGNATURE CHECK, because it changes what the export can be used to prove rather than only what it contains: previously an archive let an independent reader re-walk the audit hash chain; it now additionally lets that reader verify a signed document file against the hash its signature was taken over. That is evidence you can act on without the vendor, which is the property that matters if we are not here. ⚠️ A mismatch reported by that check is a serious finding requiring immediate assessment, not a scheduled one — it means stored bytes and signed bytes disagree. ⚠️ Note the 250 MB ceiling explicitly: an organization holding more than that receives a deliberately partial archive which SAYS it is partial and names every file left out. It is never a silent truncation, but it does mean a very large organization needs more than one export to hold every file, and that is a fact to account for in a records-retention procedure. Needs no database change and no action on any existing instance.
export-files.test.tsexport.test.tsAn export now also checks your stored files against the records that refer to them, in both directions, and reports the result in the archive (storage-reconciliation.csv). Two things were previously invisible: a file sitting in storage that no record refers to, and a record that refers to a file which is no longer stored. ⚠️ The two are reported differently on purpose. A record pointing at a file that is not there is a gap in your records, so the export is reported as INCOMPLETE. A stored file that nothing refers to is not — nothing in your quality system points at it, so the archive was never meant to contain it; it is named so you know it is there, and its contents are not included because there is no record to file it under. ⭐ If the check cannot be completed — including for an organization holding more stored folders than the check will walk in one pass — the archive reports NOT VERIFIED rather than reporting nothing found. Those are different statements and only one of them is reassuring.
Why this classification: Classified “review” because what a records export reports about itself changed, while no quality record, signature, audit entry or access rule was altered and no stored file was moved, changed or removed — this check only reads. ⭐ The distinction worth recording for anyone maintaining a qualified state is that this is the first check Indelio performs of your STORED FILES against your RECORDS. Until now every completeness statement an export made was derived from the database alone, so a file that had gone missing from storage could not be detected by reading the records — the records were self-consistent. If your procedures rely on the export as evidence that your file holdings are intact, that evidence now exists where it previously did not, and an export that reports a missing file is reporting a real discrepancy that warrants investigation rather than a formatting change. ⚠️ An export that reports NOT VERIFIED for this check is NOT evidence that your files are intact; it is evidence that the question was not answered. Needs no database change and no action on any existing instance.
export-files.test.tsThree separation-of-duties checks now refuse to proceed when they cannot be performed, instead of allowing the action through unchecked. Approving a risk assessment, closing a change control, and signing design validation each look up who signed the previous step, so the same person cannot sign both. If that look-up FAILED — a momentary database problem rather than a normal result — the check was skipped and the signature was allowed. A failure to check was being treated the same as a successful check that found nothing. All three now stop and say the check could not be performed. ⚠️ The everyday behaviour is unchanged: where the look-up works, exactly the same people are allowed and refused as before. Only the failure path changed. The same treatment was already in place for CAPA effectiveness signing and Corrections closure, which is why those two were not affected.
Why this classification: Classified “review” because a controlled function changed behaviour — in a failure mode rather than in normal use — while no record, signature or audit entry was altered and nothing about access or storage changed. ⚠️⚠️ The reason to look rather than file this away: for the period before this release it was POSSIBLE, though it required a database read to fail at the exact moment of signing, for one person to sign both sides of one of these three separations. Indelio cannot tell you whether that happened on your instance, because the skipped check left no trace — that is precisely what was wrong with it. If your quality system relies on any of these three separations, the conservative course is to review the signature history on risk assessments, change controls and design validations for cases where the same person signed both steps; every signature is in your audit trail and in your records export, so this is answerable from your own data without our help. ⭐ The distinction worth recording for anyone maintaining a qualified state is one this register has drawn before in other words: a check that did not run is not a check that passed, and a control which cannot distinguish those two states is not enforcing anything in the moment it matters most. Needs no database change and no action on any existing instance beyond that review.
copy-claims.test.tsThe published order in which this system's database migrations must be applied contained one file in the wrong position, and following it as written would have stopped part-way through installation. Corrected, and a complete installation onto an empty database is now performed automatically on every build.
The automated installation check now DEMONSTRATES that one organization cannot read another organization’s records, by actually attempting it: the check reads, through the signed-in person’s own session, records belonging to a different organization on the same instance, and requires that nothing comes back. Until now the check read the access rules and confirmed they were written to confine each organization to its own data. Those are different claims — the rules can be correct and not be the rules in force, because switching row-level security off on a table leaves every rule listed against it exactly as before. ⚠️ The check reports NOT VERIFIED rather than a pass in three situations, each of which produces the same empty result a correctly-isolated instance produces: an instance with only one organization, an instance whose other organization holds no records, and — the one that matters — a session that cannot see its OWN organization’s records either, because a session that is not properly signed in sees nothing belonging to anybody and is otherwise indistinguishable from perfect isolation. The check reads only; it creates no organizations, no users and no records.
Why this classification: Classified “review” because no controlled function changed — tenant separation behaves exactly as before, and nothing about how your data is stored or reached was altered. What changed is that the evidence now demonstrates the separation operating on the instance being qualified, rather than reporting that it is configured correctly. ⚠️ It is not “none”: if this check FAILS on an instance, that instance is disclosing one customer’s quality records to another, which is the most serious finding this system can produce and would require immediate assessment rather than a scheduled one. ⭐ The distinction worth recording for anyone maintaining a qualified state is the same one drawn for the append-only rules above — a configuration reading and a behavioural demonstration are different pieces of evidence — with one addition specific to a negative test: an empty result is only evidence when it is known that there was something to find and that the reader could see anything at all. An existing automated test does perform a cross-organization read, but against a disposable database built for testing, which is a different database from the one being qualified. Needs no database change and no action on any existing instance.
iq-check.test.tsiq-coverage-map.test.tsThe automated installation check now confirms that the database rule preventing anyone — including the system's own privileged account — from altering an audit-trail entry or an electronic signature actually TAKES EFFECT, by attempting exactly that and confirming it is refused. Until now the check confirmed only that the rule was registered against the table. Those are different claims: a rule can be registered and switched off, and the registry still lists it, so the previous check would have reported an instance whose audit trail was fully writable as sound. ⚠️ This is the only check that attempts a write, so its safety is designed rather than assumed: each attempt runs inside its own nested transaction which is deliberately abandoned whether the attempt is refused OR succeeds, so the path where the rule is missing is the very path that undoes the write. The attempt also sets a value to itself, so even a hypothetical completion would change no data. ⚠️ A table with no rows is reported as NOT VERIFIED rather than as a pass — the rule operates per row, so an attempt that matches nothing demonstrates neither that it works nor that it is broken.
Why this classification: Classified “review” because no controlled function changed — the append-only rules and the privileged account behave exactly as before. What changed is that the evidence now demonstrates the control operating rather than recording that it is configured. ⚠️ It is emphatically not “none”: if this check FAILS on an instance, that instance permits its audit trail or its signature records to be altered, which is the single assumption every other control in the system rests on, and it would be a finding of the most serious kind. ⭐ The distinction worth recording for anyone maintaining a qualified state is that a configuration reading and a behavioural demonstration are different pieces of evidence, and only the second answers the question the installation protocol actually asks. The same distinction applies to controls beyond this one. Requires a new database function, supabase/iq_probe_append_only.sql, to be installed before the next installation check; without it this check records itself as NOT VERIFIED rather than passing.
iq-check.test.tsiq-coverage-map.test.tsThe automated installation check now confirms that every state value the migrations define is actually present on the instance — for example, that a controlled document can be placed in every state the system knows about. Nothing had ever examined these. ⚠️ Closing this required correcting the installation protocol itself, not only adding a check: the protocol listed six document states by name and the system has seven. A state added by a later migration was never written back into the protocol, so somebody performing an installation qualification against a perfectly correct instance would have found a state that was not on their list, and would have had to decide on the spot whether to raise it as a problem or tick it anyway. The protocol no longer lists the states in prose at all; the check derives them from the migrations, so a future migration that adds a state updates the expectation automatically.
Why this classification: Classified “review” because no controlled function changed and any instance built by applying the migrations in the published order already has every state. What changed is that the evidence now examines them, and that a stale sentence in the protocol was corrected. ⚠️ It is not “none” because a FAILURE here would mean a real one: an instance missing a state cannot put a record into it, so a lifecycle path the procedure describes would simply not be available. The realistic case is an instance that received one migration and not a later one — the table is there and every column is there, so the two existing schema checks both stay green and only reading the state list can see it. ⭐ The lesson worth recording is that this was a documentation defect discovered while building a check, not a software defect: the safeguard against it recurring is that the expected values are now generated from the migrations rather than typed into a document, because a typed list goes stale the moment somebody adds to the system without re-reading the prose. Two configuration tables were also added to the list the check verifies; they had simply never been on it. Requires supabase/iq_run.sql to be re-applied before the next installation check, as with the two preceding items.
iq-check.test.tsiq-coverage-map.test.tsschema-manifest.test.tsThe automated installation check now confirms that the shared secret the four scheduled jobs use to authenticate is actually configured. It was already being read and reported, and no check ever examined it. ⚠️ This is recorded as a critical check, and the reason is the opposite of what it looks like: with the secret missing, the scheduled endpoints do not become open to the public — they refuse every request, including the scheduler's. So nothing is exposed; instead all four scheduled jobs stop, silently. The one that matters most is the daily job that moves an approved document to effective on the date it was scheduled for: a document given a future effective date would simply never take effect, and nothing anywhere would report it. A refusal returned to an automated scheduler looks like silence rather than like a failure.
Why this classification: Classified “review” because no controlled function changed — the scheduled jobs and their authentication behave exactly as before. What changed is that the installation evidence now examines a setting it had always collected and never looked at. ⚠️ It is not “none” because if this check FAILS on an instance, that instance has four dormant scheduled jobs and at least one document-lifecycle rule that has quietly not been running, which is a finding requiring its own assessment rather than a documentation matter. ⭐ The pattern is worth recording because it is not a missing check so much as an unfinished one: somebody wrote the code to gather the fact, which is the harder half, and never wrote the line that judges it. A reader of the evidence would see the setting listed and reasonably assume it had been checked. Unlike the two preceding items this needs no database change and no action on any existing instance.
iq-check.test.tsiq-coverage-map.test.tsThe automated installation check now confirms that the two database rules preventing a document from having more than one effective version, and from having more than one revision under way at a time, are not merely present but are defined with the right scope. Neither had ever been examined by any automated check. ⚠️ The one that matters is the second. That rule was originally written to cover revisions that were in draft or under review; it was later widened so that an approved revision waiting for its effective date also holds the document's slot. Both versions carry the SAME NAME, so an instance that received the earlier version and not the later one looks correct to any check that asks whether the rule exists — while a second revision could in fact be started underneath one that is already approved and awaiting release. The check now reads the rule's scope, not only its presence. ⚠️ ACTION: re-apply supabase/iq_run.sql to your instance before your next installation check, as with the previous item; without it this check records itself as NOT VERIFIED rather than passing.
Why this classification: Classified “review” because no controlled function of the product changed — the rules themselves are exactly as they were, and any instance built by applying the migrations in the published order already has the wider version. What changed is that the installation evidence now examines them. ⚠️ It is not “none” because if this check FAILS on an instance, that instance genuinely permits something the document control procedure says it does not, and that would be a finding requiring its own assessment rather than a documentation matter. ⭐ The reason it is recorded at this length is the failure mode it addresses: a rule that exists under the right name with the wrong scope. A check asking “is it there?” passes; a check asking “what does it cover?” does not. The same shape has now been found four times in this system — a control that was present, named correctly, and narrower than the thing it was believed to enforce. ⭐ Three further ways of being wrong are graded rather than assumed, because each fails differently: a rule of the right name that is not enforced as unique constrains nothing at all; a rule with no scope restriction whatsoever would be the opposite failure and a far more visible one, since it would prevent any document from ever being revised; and an instance that cannot report its rules at all is recorded as NOT VERIFIED rather than as passing.
iq-check.test.tsiq-coverage-map.test.tsThe automated installation check now confirms that the database function which identifies a signed-in user's organisation is not merely present but is defined with the elevated privilege it needs in order to work. Previously the check confirmed only that a function of that name existed. That function reads the user's organisation from a table that is itself access-restricted, so without the elevated definition it returns nothing at all — and every access rule written in terms of “the organisation this user belongs to” then matches no records. The failure mode is silent: not an error message, an empty result everywhere. ⚠️ ACTION FOR ANYONE WHO HAS ALREADY PERFORMED AN INSTALLATION QUALIFICATION: re-apply supabase/iq_run.sql to your instance before your next installation check. An instance still carrying the previous version of that routine cannot report the new fact, and the check will record itself as NOT VERIFIED — which blocks a qualified verdict in the same way a failure does, deliberately, because “we could not look” is not “we looked and it was fine”.
Why this classification: Classified “review” rather than “requalify” because no controlled function of the product changed — the system behaves exactly as it did. What changed is how much of its own subject the installation qualification evidence covers, and that is something a reader of that evidence is entitled to know. ⚠️ It is listed as “review” rather than “none” for two reasons: an existing instance will report NOT VERIFIED until the routine above is re-applied, and if the new check FAILS on any instance then that instance carries a real defect in how tenant separation is enforced, which would be a finding requiring its own assessment. ⭐ The defect worth recording is not the missing check, which is a few lines. It is HOW it stayed missing. The check that existed was titled with the function's name, so anyone comparing the qualification protocol against the automated checks saw the name in both places and concluded the step was covered. It had been covered on paper and never in fact, on every instance ever checked. This was found by deriving a coverage map from what each check's code ACTUALLY ASSERTS rather than from what it is called — a comparison that also showed the qualification protocol's forty-nine steps to be seven fully automated rather than the nineteen a count of checks suggests. ⚠️ A second finding came out of testing the fix and is recorded because it is the same shape a third time: the rule distinguishing “the instance did not report this” from “the instance reported that nothing qualifies” was written inline where it could not be tested, and deliberately breaking it changed no test result. Confusing those two would have invented a critical failure out of a reading that never happened. It was moved somewhere it could be tested, and is.
iq-check.test.tsiq-coverage-map.test.tsThe check that a CAPA's effectiveness is signed off by somebody other than the person who carried out the actions now considers EVERYONE who ever marked an action complete, not only the most recent person to do so. Previously that was recorded in a single field, and completing an action a second time overwrote it. So if one person marked an action complete, the action was reopened, and a second person marked it complete, the first person was erased from the record — and could then sign the effectiveness check with no challenge and nothing written down. ⚠️ This CHANGES BEHAVIOUR: somebody who was previously allowed to sign that check without comment may now be asked either to hand it to a colleague or to record why they are signing it themselves.
Why this classification: Classified “requalify” because a qualification step that closes a CAPA may now meet a challenge it did not meet before, and because the control this restores is a segregation-of-duties default (ISO 13485 §8.5.2). Nobody is barred — the default may still be overridden by recording a reason, exactly as before, and a person who never completed an action is unaffected. ⛔ The part worth reading is HOW it failed. The module already refused to erase the completer when an action was reopened, and the reason was written down in the code: blanking the field would hand somebody back an independence they had not earned by pressing a button twice. That reasoning was right, and it guarded the door it was pointed at. A second person completing the same action walked through a different one, and the outcome was worse than a refusal would have been: a refusal is visible, whereas this left the closure looking independent and wrote no exception anywhere, because the note recorded in the audit trail is decided by the same test that failed. ⭐ The set is maintained by the database itself rather than by application code. Everywhere else in this system the application is the control and the database is a second line of defence; here the requirement is that the record may only ever grow, and expressing that as read-then-write in application code invites precisely the lost update being prevented. ⚠️ No signature is affected: what a signature is bound to does not include who completed an action, so no existing record's seal moves. ⭐ This was found by verifying a backlog note that described it as a bookkeeping detail. It was not one. Two further defects were caught inside the fix while it was being written — one that would have challenged every signer whose identity could not be read, and one where the automated check for the database rule would have accepted a rule that had been renamed and therefore no longer ran.
capa-effectiveness-independence.test.tscapa.test.tsThe list stating the order in which this system's database migrations must be applied contained one file in the wrong position, and an installation following that list would have stopped part-way through with an error rather than completing. The file concerned removes two retired notification triggers that this product no longer uses, and one of the triggers it removes is attached to the audit trail table — which the list placed five files further on. Removing a trigger from a table that does not yet exist is an error rather than something the database skips, so installation halted there. The file has been moved to run after the table exists, which is the whole of the correction to the order. ⚠️ No customer instance has ever been provisioned from this list, and no deployed instance is affected: every existing instance was built up incrementally as the modules were written, so none of them ever executed the published order. What was wrong was the procedure, not any installed system.
Why this classification: Classified “requalify” because this list is the authority the installation qualification protocol cites, and it is the specific thing that protocol asks an executor to confirm. Anyone performing an installation qualification against it would have been unable to complete the installation, so a protocol referring to it rests on a procedure that did not work. ⭐ The defect worth recording is not the file's position, which is a one-line correction. It is that nobody had ever performed the installation. The order was checked by a program that reads each migration and works out what it needs to already exist — and that program understood five kinds of dependency and not a sixth. A trigger's target table was invisible to it, so it reported the order clean, confidently and in writing. It had done the same thing three times before, for three other kinds of dependency it did not model at the time; each was found by a person reading the output and asking why a particular file had sorted to the front. ⚠️ The correction is therefore two things, and the second is the one that matters: the file was moved, and the automated build pipeline now performs a complete installation of every migration onto an empty database on every single run. An incomplete dependency model still returns a clean answer. An installation that does not complete cannot. ⛔ This defect was found on the first run of that new installation check, which is the plainest available statement of how long it would otherwise have survived. ⭐ A second, smaller thing was corrected in the same work and is recorded because it would have been the more damaging of the two had it shipped: the first version of the widened dependency check treated a trigger on a table belonging to the underlying platform as though it were one of ours, and reported that the system depended on a table no migration creates. That is the strongest finding this check is capable of producing — it means the repository cannot install itself — and it would have been false.
sql-run-order.test.tsci-schema-coverage.test.tsThe automated adversarial test that attempts to forge an electronic signature now runs against every one of the twenty-three record types that hold signatures whenever it runs in the build pipeline. Previously the disposable database that pipeline uses had been set up by hand and carried only one of them, so the test reported on one record type and recorded the other twenty-two as absent. A shortfall in that coverage is now a failure of the build rather than a note in its output.
Why this classification: Classified “review” because no control changed and no customer instance is involved — what changed is how much of its own subject our evidence actually covers, which a reader of the previous release's deviation record should know about. ⚠️ That deviation listed as complete an action reading “the adversarial test was widened from one signature table to all twenty-three”. Widening the test was done. The pipeline running it reached one, printed that it had reached one, and passed anyway — so the evidence the pipeline produced covered a twenty-third of what the action claimed, and the only thing standing between that and a passing build was somebody reading a line of output. The action has been amended to say so in those terms. ⭐ Two things now prevent it recurring: the pipeline installs the complete schema before testing, so every record type exists to be attacked; and coverage is graded rather than reported, so a record type that is absent fails the build unless an exception is recorded by name with a reason. The exception list is empty and is asserted to be empty. ⭐ This is the same defect the deviation itself describes — something believed to be in force because a document said it was — found this time in our own evidence rather than in the product.
attack.test.tsci-schema-coverage.test.ts⚠️ DEVIATION. A control described in the release of 2026-08-20 does not hold in practice, and this entry records that rather than leaving it in correspondence. That release stated that the person signing must type their password at each signature, and that credential filling by a browser or password manager is suppressed on every signature field. The suppression is implemented as described. It is not sufficient. A web page can ask a browser not to fill a field; it cannot require it, and the signal we use suppresses automatic filling rather than the password manager's offer — so on the first signature on a freshly loaded page, which is the ordinary case, a stored password can still be offered and accepted with a single click. We have observed this repeatedly on our own instance, including one occasion where no device check of any kind was presented because the password manager's vault had been unlocked earlier in the session.
Why this classification: Classified “requalify” because a signing step that a customer qualified on the understanding that the password was typed by the signer cannot be assumed to have been performed that way, and because the shortfall goes to the second identification component itself. ⛔ Stated plainly, and with a measurement rather than an argument: on our own instance a valid signature was applied to a controlled document twelve minutes and twenty-two seconds after the signer had last proven their identity, with no challenge presented and nothing supplied that only that person could supply. Identity came from the browser session; the password came from a vault unlocked minutes earlier; the signer contributed a single click. We do not believe that meets the requirement for two distinct identification components. ⛔⛔ AND THE RECORD CANNOT SHOW WHICH SIGNATURES WERE AFFECTED. A signature where the password was typed and one where it was filled by a password manager are identical in our tables and in the audit trail, and there is no field that could ever separate them. That is a limit of the evidence and not a reason to assume the best: it means any signature made since 2026-08-20 may have been made this way, and we cannot tell you which. ⚠️ A device biometric check appears in some of these cases and MUST NOT be read as mitigation. It is the signer's own operating system releasing a stored credential to the browser; this system performs no biometric verification anywhere, and our server receives the same string it would have received had the password been typed. It is not our control and we do not claim it. ⭐ Follow-up actions, with status. (1) COMPLETE — disclosed in full to our independent validation reviewer, with the measurements and the reproduction, together with a correction to two statements in our own earlier draft that were stronger than the evidence supported. (2) COMPLETE — the interval between a document being delivered to a signer and that signer signing it is now recorded, so an implausible gap is visible on the record rather than invisible. (3) COMPLETE — claims that overstated this control have been removed from our own documentation, including any suggestion that a device biometric is a control of ours. (4) OPEN — a security key with user verification is the only mechanism we know of where the server can verify that a person was checked at the moment of signing. Whether our authentication provider actually enforces that property is undocumented; we have written a test to measure it and it cannot run until the capability is enabled. Until it returns a result we will not describe that approach as evidence a person was checked. (5) ANSWERED IN PART, 2026-09-01 — our independent validation reviewer ruled on the target control: document the residual as a limitation and leave enforcement to the customer. That is what the customer-control register already does, so nothing about the control changes; what changes is that the position is now RULED rather than proposed, and it is his rather than ours. ⚠️ The rest of the question stays open. Whether our sign-in page should stop inviting the browser to save the password at all was not addressed, and we have not changed it on our own initiative — it is a sign-in behaviour every user meets, and we would rather leave it as it is than alter it on our own reading of a ruling that did not mention it.
password-clearing.test.tsdocument-delivery.test.tsReopening a closed record now requires an electronic signature, in all nine modules where a closed record can be reopened — corrective and preventive actions, change control, complaints, corrections and removals, design controls, management review, nonconformance, projects and risk assessments. Reopening already required an approver or quality role and a written reason, and was already recorded in the audit trail; what it did not do was bind a named person to the act with their password re-verified at that moment, as every other disposition in those modules does. ⚠️ This CHANGES BEHAVIOUR: a person who could previously reopen a record by giving a reason must now also enter their password, and the reopening is recorded as a signature against the record.
Why this classification: Classified “requalify” because a qualification that exercised reopening will find an additional step, and because reopening is now evidenced differently — there is a signature on the record where previously there was only an audit entry. Nothing that was permitted is now forbidden: the same people may still reopen the same records, for the same reasons. ⭐ The reason this was found is worth recording, because it was not found by a test. The Part 11 backlog carried it as a single item about one module. Sweeping for the pattern rather than the reported instance showed it was nine — every module that can reopen anything. That is the third time in a week that a defect recorded as one instance turned out to be a whole class, and it is now the default assumption when one is reported. ⛔ One module was materially worse than the other eight and it is the one where it matters most. The corrections and removals module — field actions, which is to say recalls — presented the signer with a password box and the words “re-enter your password”, and then discarded what they typed. A wrong password reopened a closed field action exactly as a correct one did. Every other module verified it. We are recording that as its own statement rather than folding it into the general improvement, because a control the product ASSERTS and does not perform is a different fault from one it never claimed: the first misleads a user into believing they have been authenticated, and it is the fault we have found most often when checking our own claims against our own behaviour. ⚠️ Signatures are applied before the record's state changes, so a refused or failed signature leaves the record closed rather than reopened-but-unsigned.
reopen-signature.test.tscapa.test.tsThe file that enforces this system's inactivity timeout, two-factor step-up, password expiry and public/private page boundary has been renamed, from `middleware.ts` to `proxy.ts`, because the web framework this product is built on has deprecated the former name. No behaviour changed: the contents are the same, the controls are the same, and the running system does exactly what it did before. ⚠️ It is recorded here because of how it appears in this system's own change record — the controlled-file list published with each release shows one controlled file removed and a different one added, which is the same shape a genuine removal of a control would take. It was not a removal.
Why this classification: Classified “review” because nothing a customer could have qualified behaves differently: no step changes, no screen changes, and nothing that was permitted or forbidden has moved. It is not classified “none” because two things a reviewer actually reads will differ and would otherwise be unexplained — the controlled-file manifest shows a file disappearing, and the Part 11 traceability matrix now names a different file for four requirements. ⭐ The rename was made as its own release item rather than folded into other work, because the file carries four Part 11 controls and a change touching it should be visible on its own. ⭐ One property of it is worth recording, because it is the failure it could have caused rather than the one it did. The framework locates the function this file must export by NAME, and the name it looks for depends on the filename — so renaming the file without renaming the function inside it yields a system that typechecks, passes its test suite, deploys successfully, and then fails every single request, with no session handling, no timeout, no two-factor step-up and no page protection at all. We established which check catches that by deliberately introducing the fault rather than reasoning about it: the production build refuses it with an explicit error, and our first expectation — that only the running system would notice — was wrong. ⛔ Checking that also exposed something the rename did not cause and did not depend on. Every existing automated check on this file reads its CONTENTS; nothing anywhere asserted which requests reach the file in the first place, and that is decided by a single pattern near the bottom of it. Adding one page prefix to that pattern switches off all four controls for the affected pages, with every existing test, the typecheck and the build all still passing, and the only symptom being that a control silently does not fire. A new automated check now holds that pattern to a whitelist — framework internals and static files only — so any page added to the exemption has to be argued for rather than slipped in. That gap predates this release and was not introduced by it; it is recorded here because this is when it was found.
proxy-reach.test.tsidle-timeout.test.tsThe automated installation check now confirms that this system's privileged database credential — the one that can read and write every organization's records — is not configured in a way that would send it to the web browser. Nothing had ever examined this, and it is the single most consequential thing in the installation protocol that was going unchecked. ⚠️ There are two separate ways such a credential can reach a browser, and they have different owners, so this is closed by two separate mechanisms. The first is the software itself, and is checked automatically on every build, by examining the finished files a browser would download rather than by reading the source that produced them. The second is the instance's OWN configuration: a setting whose name begins with the reserved public prefix is copied into the web page by design, so a person configuring an instance can publish the credential without any change to the software at all. That second one is what the installation check now examines, on the instance being qualified, and it is complete for that route because the public prefix is the only way a setting reaches the browser. ⚠️ The check reports NOT VERIFIED rather than a pass if it can see no public settings at all, because a correctly configured instance always has at least one, so seeing none means the reading failed rather than that the instance is clean. ⚠️ It reports the NAME of any offending setting and never its value, because this evidence record is signed and retained.
Why this classification: Classified “review” because no controlled function changed — nothing about how records are stored, reached or protected behaves differently, and no correctly configured instance is affected in any way. What changed is that the installation evidence now examines a route that was previously left to somebody remembering to look. ⚠️ It is emphatically not “none”: if this check FAILS on an instance, that instance has published a credential that bypasses every separation rule in the system, and any visitor to the site can read and alter every organization's quality records while leaving audit entries indistinguishable from legitimate activity. That is not a finding to schedule — it requires the credential to be replaced at the database provider immediately, and an assessment of what was reachable while it was published. ⭐ The distinction worth recording for anyone maintaining a qualified state is that this step is recorded as PARTIALLY automated rather than fully, deliberately: the two mechanisms together cover the software and the configuration, but neither one inspects the delivered files of the specific instance being qualified. The remaining portion stays with the person performing the installation, and the coverage record says so rather than letting the two halves read as a whole. ⭐ The wider lesson is about the shape of the requirement rather than the check: the step reads as one question and is two, with two different owners, so a single check would have closed whichever half its author happened to have in mind and would have read, from its title, as closing the requirement. Needs no database change and no action on any existing instance.
iq-check.test.tsscan-client-bundle.test.tsiq-coverage-map.test.tsThe warning that appears when a record's content does not match its stored fingerprint now says what it actually checked, on every module. It previously said an electronic signature was broken, which is not what that check looks at.
On every record screen, the red warning shown when a record fails its integrity check has been reworded. It said the current content “no longer matches what was signed — the e-signature binding is broken”, or that the record “was altered after signing”. Neither is what the check measures: it compares the record against its own stored fingerprint and never against any signature. It now says the content does not match its own stored fingerprint, which means a field was changed without going through Indelio, and points to the E-signatures table where each signature's own binding is reported. Nineteen screens changed. No rule, record, signature or permission is affected.
Why this classification: Nothing about what the system does changes, and nothing that was accepted is now refused — which is why this is “review” and not “requalify”. It is recorded because a qualification script may check for particular words on screen, and because the previous words were wrong about a Part 11 control in a way that changes what a reader would do about it. Told that an e-signature binding is broken, a quality reviewer investigates the signature. The signature is fine; every in-app edit rewrites the record and its fingerprint together, so the check can only fail when something changed the row WITHOUT going through the application — a direct database edit, a restore, a migration run by hand. That is the more serious reading of the two, and it was the one nobody was being given. The same defect had already been corrected on the CAPA screen when scoped signature bindings shipped, and the other nineteen were left behind; both halves are now checked by an automated test that reads the rule out of the shipped code first and then asserts the wording against it, so a future relaxation cannot leave the copy behind again.
copy-control-claims.test.tsDesign Controls: where a design project has no state-independent fingerprint recorded, the integrity check now reports that it cannot be checked, instead of reporting the record as changed. Affects any instance where supabase/design_material_hash.sql has been applied but the accompanying backfill has not yet been run. The PDF traceability report carries the same three outcomes.
Why this classification: Classified “requalify” because a qualification script that recorded a red integrity warning against a design project, or a traceability PDF filed as evidence carrying the WARNING line, was recording a finding that did not exist — and after this change the same project reports differently. The mechanism is worth stating plainly. A design project's signatures bind to a fingerprint that includes the lifecycle state, frozen in that shape so that existing signatures are not orphaned. Advancing the state deliberately does NOT recompute that fingerprint, because recomputing it would break every signature on the record. A separate state-independent fingerprint was introduced so the integrity banner would stop false-tripping on lifecycle transitions, and the banner uses it — but where that value had never been recorded, the code fell back to the old comparison, which cannot hold once the state has moved. So a sound design project displayed as altered, and the fallback was reachable by an ordinary route: the migration only adds the column, and the backfill that populates it is a separate step. A fingerprint we do not hold cannot be compared, and “we cannot tell” must never be presented as “somebody changed this”. It is now a third outcome, worded as not comparable and coloured as information rather than as a finding, in both the screen and the PDF — the same treatment already given to signatures made before scoped bindings existed. ⚠️ No records were affected on the vendor's own instance, where the backfill had been run; that is a fact about one instance and not about the shipped code.
copy-control-claims.test.tsdesign-controls.test.tsThe application framework was upgraded two major versions (Next.js 14 to 16) and the user-interface runtime with it (React 18 to 19). This closes eleven published security advisories against the framework and four against a component it bundles, including one allowing unauthenticated disclosure of internal server-function endpoints; the dependency audit reports no known vulnerabilities after the upgrade, where it previously reported two high-severity findings that no patch on the previous major could resolve. No quality function, rule, signature, role or record behaviour was changed as part of this work.
Why this classification: Classified “requalify” because the software the qualified system runs on has changed by two major versions, and installation and performance qualification are executed against a specific deployed build. Nothing here is a change to a control, and the automated operational-qualification suite passes unchanged — the same 1,735 checks, including every access-control, signature, hash-chain and tenant-isolation test — which is the evidence that behaviour is preserved. But an executed IQ or PQ naming the previous build is evidence about a build that is no longer deployed, and that judgement belongs to whoever maintains it. What actually changed in the code is one thing, applied about a hundred and sixty times: the framework made reading cookies, headers and URL parameters asynchronous, so the data-access layer that every module reads through became asynchronous with it. The compiler identified every affected call site and the change was driven to zero compiler errors, which is why a change of that breadth was safe to make mechanically. Two configuration keys were relocated as the framework required; one of them controls whether the PDF and Word text extractors are bundled or loaded at runtime, and leaving it in its old position would have been accepted silently while reintroducing a defect that presented as PDF text extraction returning empty in production. ⚠️ One deprecation is deliberately NOT actioned in this release: the framework has renamed the request-interception file convention and warns on the old name. That file enforces the idle timeout, second-factor step-up, password ageing and the public-route boundary, and renaming it also moves it within our own controlled-file list, so it is being done as its own change rather than folded into an upgrade. The old name continues to work. ⭐ Verified beyond the test suite: a production build completes, the sign-in page renders and hydrates with no console errors, the automatic sign-out explanation still appears when the timeout flag is present — which exercises the asynchronous parameter change on a Part 11 access control — and an unauthenticated request to a protected module is still redirected to sign-in, which exercises the request interception and the cookie read. ⭐⭐ It was also verified by opening the upgraded build while signed in, because the failure this change could produce is not an error. A data read that lost its await renders an EMPTY LIST, which is indistinguishable from a module holding no records, so neither the compiler nor the test suite can stand in for looking at a screen. Controlled documents returned fourteen records with their versions and lifecycle states, corrective and preventive actions four with state, owner and due date, design projects four with their computed input and output counts, and the training matrix its assignments with overdue status. A design project was opened and both of its electronic signatures report their binding intact, which is the record-level integrity check reading a stored fingerprint back through the changed data layer. Where a list WAS empty — the signed-in user's own training assignments — other reads on that same page returned rows, and that is what separates a record set that is genuinely empty from a read that has broken.
idle-timeout.test.tsattack.test.tsCorrections & Removals now expects a field action to be closed by someone other than the person who approved its assessment. Where nobody else is available the same person may still close it, and records why.
The automated installation check now verifies that every column the database migrations add is actually present, and names any that are missing. It previously checked that tables, three database functions and two audit-trail columns existed, so a migration that only adds columns to existing tables could be missing entirely and the check would still report a clean instance. A database migration is required to enable it: run supabase/iq_columns.sql. Until it is run, the new check reports NOT CHECKED rather than passing.
Why this classification: Nothing about how records behave changes, and no signature, gate or permission is affected — this adds a check to the installation qualification evidence. It is classified “review” because the automated IQ report gains a step, and anyone whose installation qualification cites that report's contents should re-run it and re-file the output. The reason it exists is worth recording plainly, because it is a gap we found the hard way. A migration was committed with its code and never applied to the running instance. It added only columns, to tables that already existed. The consequences were invisible for a day: one action in the CAPA module failed every time it was attempted, because the write set a column that was not there, and a closure control degraded to half its intended scope — by design, so that closure would not become impossible, and therefore silently, because closure still succeeded and looked identical. The automated check would have reported the instance healthy throughout, since nothing in it had ever verified a column, and a migration that only adds columns is the ordinary case. Two properties deserve a validator's attention. The check reads `ALTER TABLE … ADD COLUMN` from the migrations and nothing else — it does not parse table definitions, so a pass means every column a migration adds is present, and never that the schema as a whole is correct; the wording in the report says so. And it FAILS TO “NOT CHECKED”. If the column map cannot be read — most likely because supabase/iq_columns.sql has not been run — it reports that it could not look, rather than reporting that nothing is missing. A clean result derived from a failed read is the precise failure this check was written to catch, and it would have been indefensible to reproduce it here. The check is not marked critical: a missing column is a real defect, reported by name, but it does not make the instance unsafe to operate the way a missing integrity trigger does, and making it critical would block qualification over a drift an executor can correct in a minute.
schema-manifest.test.tsiq-check.test.tsOn the CAPA screen, the confirmation or refusal shown after an action now disappears as soon as you edit that form, instead of staying on screen until the next time you submit it. Nothing about what is recorded changes; this only affects how long a message remains visible.
Why this classification: No rule, record or signature changes, and nothing is refused that was previously allowed — but it is worth recording because a qualification script may check for a message on screen, and a message that used to persist now clears when the form is touched. The reason for the change is the direction that matters. A result was held until the next submit, so it survived every edit made in between and went on describing a form state that no longer existed. Stale in one direction that is merely untidy: a refusal that names a person you have since removed from the form makes you look twice. In the other direction it is a false assurance — sign something, receive “recorded and signed”, then start filling the form in for the next one, and the confirmation sits beside your edited form reading as though that second act had succeeded. It had not. This was seen on a live system with a green “action plan approved” rendered directly beneath a red notice saying that approval no longer covered the plan, in the same card, with the reassuring message being the false one. The message now clears on the first edit after a submit, so a confirmation only ever describes the submission that produced it. ⚠️ Applied to the CAPA screen in this release. The same pattern exists on other record screens and is being changed module by module rather than all at once; no claim is made here that it is fixed elsewhere.
signing-feedback.test.tsA CAPA's dual plan approval now counts an approval that still covers the plan, rather than one that merely exists. If the plan is edited after somebody has approved it — which is possible, because the plan stays editable until both approvals are in — that approval's slot reopens and shows who approved the earlier version. The CAPA no longer advances to Implementation until both slots are held by approvals that cover the plan as it currently stands. The earlier signature is kept on the record. No migration is required beyond the one released with the scoped-binding change above.
Why this classification: This changes what it takes to move a CAPA from Investigation to Implementation, so a qualification script can now stall where it previously proceeded. That is why it is classified “requalify”. The specific sequence that changes: approve the plan as QA, then edit the root cause, the problem statement, an action or the effectiveness plan, then approve as Approver. Until now the second approval advanced the CAPA, because the check asked only whether a signature row with that meaning existed. It never asked whether the approval still covered the plan. The result was a CAPA in Implementation carrying two approvals of which only one had been given to the plan as it actually read — four-eyes satisfied on paper and not in fact. There was also no way to put it right: the first approver could not re-approve, because the slot already carried their name and the system replied that they had no open slot to sign. Both halves are fixed together. A slot is now held only by an approval whose binding still holds, so a broken one reopens the slot and the original approver can sign again; the CAPA advances only when both slots are genuinely held, by two different people, exactly as before. One property matters to anyone assessing this and is deliberate: an approval that CANNOT BE JUDGED holds its slot. Signatures made before scoped bindings existed recorded a hash of the whole record, and whether the plan changed underneath them is not recoverable. Treating those as broken would have reopened plan approvals on every CAPA in the instance the moment this shipped and demanded re-approval of work that was almost certainly sound, on no evidence at all. Only a definite break reopens a slot. Two smaller properties worth recording: signatures are append-only, so a repaired slot keeps the superseded approval visible in the E-signatures table rather than replacing it, and the panel now names who approved the earlier version instead of showing the slot as simply “pending”, because “nobody has approved this” and “the approval no longer covers the plan” are different facts and the second is the more urgent one.
capa.test.tsOn a CAPA, each e-signature is now checked against the fields that signature actually approved, rather than against the whole record. Recording the effectiveness result no longer shows the earlier action-plan approvals as broken. The Binding column gains a third state, “not comparable”, used for signatures made before this change — they recorded a hash of the whole record, and what their own fields held at the time cannot be recovered. Nothing is refused that was permitted before, and no existing signature is altered or re-verified. A database migration is required: run supabase/capa_signature_scope.sql.
Why this classification: No signing rule changed, nothing that was accepted is now refused, and the stored signatures themselves are untouched — which is why this is “review” and not “requalify”. What changed is what the record TELLS you about its own signatures, and that is worth a validator's attention because until now it told you something false. A CAPA's binding was checked by asking whether a signature's hash still equalled the record's hash. But a CAPA's content grows legitimately as it moves: recording the effectiveness result is required before closure, and doing so changed the hash that the action-plan approvals had bound to. The consequence was that every CAPA which reached closure displayed both of its plan approvals as broken, on a plan nobody had touched. It was found by walking a real record: an Approver signature was applied, the effectiveness result was saved six minutes later, and the signature then read broken. ⚠️ A tamper indicator that fires on one hundred per cent of correctly-handled records is not detection. It is worse than absent, because it teaches whoever reads it to ignore the one occasion it means something — which is the opposite of what 21 CFR 11.10 asks a binding to do. Each signature meaning now carries the set of fields it covers: approving a raised request covers the problem as stated, approving the action plan covers the problem, the root cause, the actions and the effectiveness plan, and closing the record covers all of it including the result. That set is hashed and frozen on the signature at the moment of signing, and re-checked against the record as it stands. So altering the root cause after the plan was approved still breaks that approval, and adding or rewriting an action still breaks it, while recording the effectiveness result correctly leaves it alone. Two properties deserve stating. The first is the third state: a signature made before this change cannot be judged either way, because a whole-record hash cannot separate “someone altered what this signature covered” from “the CAPA moved on”. Those are shown as “not comparable” and are deliberately not shown as intact or as broken; computing a value for them would fabricate evidence about a Part 11 control, and they will remain in this state permanently. The second is that an unrecognised signature meaning is bound to the whole record rather than to nothing, so a signature added in future breaks readily instead of silently reporting itself intact. The wording of the record-level warning above the panel has also been corrected. It compares the record against its own stored hash and never against any signature, so it can only indicate a change made outside Indelio; it previously said the e-signature binding was broken, which was not what it had measured. ⚠️ This applies to CAPA only, and the reason it was needed only here is worth stating, because an earlier version of this entry stated it wrongly. Twenty other record types do still compare each signature against the whole record. That was described here as a known limitation certain to produce the same false “broken” elsewhere; a module-by-module review on 2026-08-24 found that it does not, and the earlier wording is withdrawn. Every one of those modules refuses a change to any hashed field once the record leaves the state it is editable in — by an editable-state helper, by refusing anything but Draft, or by naming the signed state and refusing it — and the signature is applied at or after that transition, so no hashed field can move underneath a signature. CAPA is the exception because its lifecycle REQUIRES a hashed write after signing: the effectiveness result must be recorded before closure. No other module has a mandatory post-signature write of a hashed field. ⚠️ Two limits on that review, stated so it is not read as more than it is. It establishes that no module can produce the false “broken” CAPA produced; it does NOT make whole-record binding as precise as scoped binding, and a genuine edit in any of those modules still breaks every signature on the record rather than only the ones whose fields moved. And it covers the SIGNATURE binding only — a write that changed a hashed column without recomputing the record's own hash would show on the record-level integrity indicator, which is a different check and was not part of this review. If the migration is not applied, signing is deliberately NOT blocked — new signatures are recorded without a scope and shown as “not comparable”, because refusing signatures on an un-migrated instance would be a worse failure than an unanswerable column.
capa.test.tsA field action is now closed by default by someone other than the person who approved its assessment — the health-hazard evaluation, the recall classification and the 21 CFR 806 reportability decision. If you approved that assessment, closing the same record is refused unless you record why you are closing it yourself; having done so, you may close it. The reason is stored on the record and the closure signature is unchanged in every other respect. Closure still requires the QA role, and still requires a recorded effectiveness result or a documented rationale for not performing one.
Why this classification: A refusal now exists where none did before, at the point a field action is closed, so a workflow you qualified may stop and ask for something it did not previously ask for. That is why this is classified “requalify” and not “review”: if your performance qualification includes closing a field action, and the same person performs the assessment approval and the closure in that script — which is likely, because until now nothing prevented it — the script will now be refused at the final step unless a reason is recorded first. Re-executing that part is the conservative course. What was there before is worth stating plainly, because it is the reason this changed. Corrections & Removals had no identity control of any kind. Every step checked a ROLE and stopped there, so one person holding the QA role could raise a field action, approve its health-hazard evaluation, set the recall classification, decide whether FDA had to be told, and then close the record — alone, on a recall. Every comparable module in the product has step-to-step separation; this one did not, and the product's own module reference described a segregation-of-duties control it did not have. That wording was corrected on 22 August; this change makes the original claim true instead. ⚠️ This is a default Indelio chose, NOT a regulatory requirement, and nothing in the product should be read as saying otherwise. 21 CFR Part 806 requires the correction or removal to be reported; ISO 13485 §8.3.3 requires nonconforming product detected after delivery to be acted on. Neither imposes an independence rule on whoever closes the record, and Indelio cannot be cited for lacking one. It is shipped as a default worth having, in the same shape as the CAPA effectiveness change of 21 August and on the same reasoning given then by our validation reviewer: default the control on, allow a recorded exception, and never remove the rule — because a hard refusal in a two-person quality team does not make the record safer, it makes closure impossible and the rule gets switched off. The exception is deliberately a written justification rather than a permission setting, so the decision lands on the record an auditor reads rather than in a configuration screen nobody looks at. One property is worth a validator's attention: the check FAILS CLOSED. If the list of assessment signatures cannot be read, closure is refused and says so, rather than resolving to “nobody approved the assessment, so this person is independent” — which would clear the very check being made, on a recall record. A database migration is required: run supabase/corrections_closure_independence.sql. Until it is applied, recording the exception is refused with a message naming that file, and closure by the assessment approver stays blocked.
corrections.test.tscopy-control-claims.test.tsThe 30-minute inactivity auto-logout now applies wherever you are in Indelio, including the signed-in dashboard at the site root. Previously the check was skipped on pages marked public — and because the root is both the marketing page and the dashboard, arriving at indelio.io skipped the check while still resetting the inactivity clock. The practical effect was that the auto-logout did not fire for anyone entering the site normally. You will now be returned to the sign-in page after 30 minutes of inactivity, with a notice saying the session ended on a timeout rather than a fault.
Why this classification: This is an access control that was not operating, and is now. It is classified “requalify” because behaviour you may have qualified changes in a way people will notice: sessions that previously persisted indefinitely will now end after 30 minutes of inactivity, and any script or procedure that assumed a session stayed open across a long gap will stop where it did not before. If your performance qualification includes a step performed after a break, re-execute it. What was wrong is worth stating precisely, because the control was recorded as working and was not. The inactivity check was skipped on any page marked publicly reachable. The site root is on that list — necessarily, since it must serve the marketing page to signed-out visitors — but for a signed-in user the same route renders the dashboard. So the one page every user enters through was exempt from the check, while the code that refreshes the inactivity timestamp was not similarly scoped and stamped it forward anyway. Arriving at indelio.io therefore turned a stale session into a fresh one: the page that could not expire you was renewing you, and the next page you opened saw activity seconds old. Because that is the only entry point, the auto-logout had effectively never fired. Both halves are corrected together, and the correction is that the check and the refresh now share exactly one condition — a page that cannot log you out cannot keep you logged in either. Pages where a redirect would be wrong remain exempt and are named explicitly rather than inferred: the sign-in page itself, which is where an expiry sends you and would otherwise loop; the forced password-change flow, which must survive an expiry or a mandatory change deadlocks the account; and API and authentication callbacks, which cannot act on an HTML redirect. No database migration is required. ⚠️ This was reported by a user asking why the site opened straight into his previous session, not by our test suite — which asserted that the timeout logic was correct without ever asserting which pages it ran on. Tests covering that scope, and three injected mutations reproducing the original defect, ship with this change.
idle-timeout.test.tsA record that has never been signed no longer displays the warning that its electronic-signature binding is broken. Thirteen modules showed that banner whenever the stored content hash did not match the record, without first checking that any signature existed — so a brand-new draft could be shown, in red, that an integrity control on it had failed. Separately, a newly created field action now stores a content hash that describes the record as saved. It previously described the record as submitted, which differs whenever the database supplies a default the form did not, so field actions were created with a mismatched hash and displayed that warning immediately.
Why this classification: No control changed. Nothing that was permitted is refused, no signature, role or lifecycle rule moved, and no record's integrity was ever actually at risk from either half of this. It is classified “review” rather than “none” because what was wrong was an INTEGRITY REPORT, and a quality function may reasonably have acted on it. If anyone investigated a record because Indelio told them its signature binding was broken, that investigation was chasing a display fault; the underlying signatures were bound correctly to whatever the record held when they were taken. The first half is the banner. It was rendered on the hash comparison alone, so any record whose stored hash did not match — including one with no signatures at all — was told its binding had failed. That is worse than cosmetic: the message says an integrity control has failed, and firing it where nothing has been signed teaches people to disregard the one warning that must never be ignored. Five modules already had the correct form; the other thirteen have been brought into line, and a check now requires every page that renders this banner to gate it, so a new module cannot arrive without one. The second half is why the mismatch existed at all on field actions. The content hash was computed from the values being submitted and the record was then inserted, at which point the database applied its own column defaults — reportability defaults to “Undecided” when the form leaves it unset. The stored hash therefore described a row that had never existed, one where reportability was null. The hash is now recomputed from the row the database returns and settled before the create completes, which is correct for every column default rather than for the one that was noticed. Existing records are unaffected in substance; a field action created before this change carries the old hash until it is next edited, at which point it is recomputed correctly through the normal audited update. ⚠️ Found by a user creating a field action and reading the screen, not by the test suite — which asserted the hash function was correct without ever asserting that the value stored alongside the record described that record.
corrections.test.tsui-surface.test.tsThree database migrations that had never been committed are now part of the shipped installation: row-level security on the organisations table, and the table definitions for per-organisation settings and per-user dashboard preferences. A running instance is unaffected — all three already exist there, and the migrations are written to change nothing where they are already in place. What changes is what a NEW installation produces. Two further consequences: the installation qualification protocol's migration count moves from 79 to 82, and the tenant data export now names per-organisation settings and per-user preferences as deliberate exclusions, each with its reason, rather than not mentioning them.
Why this classification: This is recorded as requalify because for any instance built from the shipped migrations, a tenant-isolation control that was absent is now present, and the installation qualification report gains steps. On the existing running instance nothing changes at all, and no record, signature, role or lifecycle rule is affected anywhere. The reason it matters is the direction of the gap. The running instance had row-level security enabled on the organisations table, but no committed migration ever enabled it — it had been switched on by hand, with no migration, no record and no change control on a tenant-isolation control. Two further tables, per-organisation settings and per-user preferences, existed on the instance with no definition in the shipped migrations at all; three separate migrations had been adding columns to a settings table that, as far as the shipped installation was concerned, did not exist. An organisation installing from the shipped migrations would therefore have got an installation that stopped partway, and — had it continued — one whose organisations table had row-level security switched off. A table in that state is readable with the public application key, which by design reaches the browser, so organisation names, tenancy types and trial dates would have been exposed. The step that says all migration files are applied would have been confirmed against an instance whose tenant isolation was never installed. ⛔ THE SECOND FINDING IS THE SERIOUS ONE, AND IT WAS FOUND ON THE RUNNING INSTANCE. Checking what access rules those hand-made tables actually carried showed that per-organisation settings granted every signed-in member of an organisation full read AND WRITE access to their own organisation's settings row, directly, using the public application key that by design reaches the browser. That row holds two switches which govern when an electronic signature may be taken: whether attached evidence must be opened before signing, and whether a reviewer must state a review context. Both could therefore be switched off by anyone in the organisation, whatever their role — including a role that deliberately carries no signing authority at all — without passing through the application, and so WITHOUT THE AUDIT ENTRY the application writes whenever either switch is changed. A control governing signatures could be disabled with no record of who disabled it, which is the property Part 11 exists to guarantee. The same rule exposed the organisation's encrypted AI key and the stored hash of its complaint-intake token to the same audience. This release removes that access rule outright rather than narrowing it to read-only: all twelve places in the application that touch this table use the privileged server connection, which is not subject to these rules at all, so removing it costs no function and returns the table to refusing everything. A check is included that refuses to install if any access rule is present on that table under any name, so it cannot return quietly. NOTHING IS KNOWN TO HAVE BEEN CHANGED THIS WAY — the audit trail carries every switch change made through the application, and no evidence of any other route was looked for or found; what is being reported is that the route existed and would have left no trace. Anyone whose qualification relies on those two switches having held their recorded value should consider that when reviewing this release. Two smaller notes for a validator. The organisations table is given no read rule at all, because nothing but the privileged connection reads it, and that is stated in the migration so it is not later mistaken for an omission. And the per-user preferences rule was compared against the live instance — name and expression both — before being written down, rather than reconstructed from intent; that comparison had to be run by hand, because the installation check counts these rules without reading them, which is precisely why a rule granting full write access sat beside a correct one and looked identical to it. ⚠️ Found by comparing the running instance's row-level security against every such statement in the shipped migrations. No test found it, and none could have: every check in the suite reasons about the migrations, and the gap was between the migrations and the database.
export.test.tsschema-manifest.test.tsprotocol-code-sync.test.tsiq-check.test.tsThe automated installation check now reads what each database access rule actually permits, instead of only counting how many exist, and fails the installation if any rule lets an ordinary signed-in user WRITE to a table. A database migration is required to enable it: run supabase/iq_policies.sql. Until it is run, the new check reports NOT CHECKED rather than passing.
Why this classification: No record, signature, role or lifecycle rule changes; this adds a step to the installation qualification evidence, and anyone whose installation qualification cites that report's contents should re-run it and re-file the output. The reason it exists is worth recording plainly. The two access-rule checks that already existed asked whether protection was switched on for every table, and whether each table had at least one rule. Neither ever asked what a rule SAID. A rule granting every signed-in member of an organisation full read and write access to their own organisation's rows scored exactly the same as one granting read-only access, and the release above is the case in point: a rule of the first kind sat one row away from a rule of the second kind, both checks reported the installation clean, and what found it was a person running a query by hand. An installation check that cannot distinguish those two is not evidence about access control; it is evidence about arithmetic. The new check reads the rules and fails if any of them grants a write to a role the browser can reach. Three properties deserve a validator's attention. It treats the wildcard ALL as a write, which is the specific thing that was missed — a wildcard rule reads as a scoping rule and is one, it simply scopes writing as well as reading. It ignores rules scoped only to the privileged server connection, which is correct, because that connection is not subject to these rules at all. And it FAILS TO NOT CHECKED in two separate ways: if the rules cannot be read, and — this is the one worth noting — if the rules come back EMPTY while the older count says rules exist. Two readings of the same objects that disagree is not a clean result, and an empty list would otherwise have graded as “nothing to object to”, which is the precise failure this check was written to catch and would have been indefensible to reproduce. ⚠️ SCOPE, STATED SO IT IS NOT OVERSOLD: it asks who may write, not whether a rule's tenant-scoping expression is correct. A rule that grants read access using the wrong expression is a different defect and this check will not find it. It is also worth recording that the shipped migrations declare twenty tables with wildcard rules that the write-lockdown migration does not name; whether those are in force on any given instance is now something the installation check answers on every run, rather than something someone has to think to ask.
iq-check.test.ts⛔ SECURITY. Forty-three database access rules across thirty-seven tables allowed any signed-in member of an organisation to CHANGE records directly, using the public application key, without going through the application. Nine of those allowed INSERTING A ROW INTO A SIGNATURE TABLE — that is, applying an electronic signature that the application never authorised, never bound to the record, and never recorded in the audit trail. All forty-three are removed by supabase/rls_lockdown.sql, which must be run. Reading is unaffected: twenty of the removed rules also served reads, and each has been replaced by a read-only rule carrying the identical scoping expression.
Why this classification: This is the most serious finding recorded here and it should be read in full. The application writes every row through a privileged server connection that is not subject to these access rules; the browser's connection is used for reading only. An access rule granting the browser permission to write is therefore never load-bearing and always an exposure — it is a second way into the data that goes around the code where authorisation, segregation of duties, the binding of a signature to the record's content, and the audit entry all live. THE NINE SIGNATURE TABLES ARE THE PART THAT MATTERS. On the affected tables the only condition on inserting a signature was that the row named your own organisation. No role was checked, no password was re-entered, nothing was bound to what was being signed. The append-only protection on those tables does not prevent it: it is present on all nine, and it blocks changing or deleting an existing signature, which is a different act from creating one that never happened. Nor is the consequence confined to display. The design-controls module READS one of those tables to enforce that a verifier and a validator are different people, so a fabricated row naming somebody else does not merely appear genuine — it feeds that decision and inverts the control, making the named person appear to have signed and changing who the system will then permit to sign. In Part 11 terms this reaches signature/record linking and the signature-components requirement, and the audit trail would show nothing, because the write never reached the code that writes audit entries. NOTHING IS KNOWN TO HAVE BEEN DONE THIS WAY. The audit trail records every signature applied through the application; a signature applied by this route would not appear there, so its absence is not proof either way, and no evidence of use was sought or found. What is being reported is that the route existed. Anyone relying on signatures in the design, equipment, management-review, human-factors, assessment or usability records should take that into account when reviewing this release. The modules listed are derived from the affected tables rather than chosen: assessments, CAPA (action items), design controls, document configuration, equipment, the five human-factors modules, management review, risk review, and trend disposition. HOW IT SURVIVED THIS LONG IS THE PART WORTH FIXING. An earlier migration closed exactly this hole and closed it correctly — for the twenty tables that existed when it was written — and an automated attack suite proves it live across six. Both were true and both were narrow. Every module shipped since added its own rules and joined neither list. A hand-maintained list fell behind the schema, and nothing compared the list to the schema. Two checks now do, and they cover different things on purpose: one derives every write-granting rule the migrations declare and refuses to merge unless a lockdown file removes it, so a new module cannot ship this shape; the other reads the LIVE instance during installation qualification and refuses to qualify it while any such rule is in force. The second is what found these — the two older access checks, which ask whether protection is switched on and whether each table has at least one rule, both reported the instance clean. ⚠️ THE REPLACEMENT READ RULES ARE NOT COSMETIC: twenty of the removed rules were the only rule on their table and served reads as well as writes. Removing them without replacement would have taken those modules offline for every tenant. All twenty were confirmed to carry the identical scoping expression before the replacements were written, and a check refuses this migration if any table it strips is left unreadable. ⭐ RECORDED AS A DEVIATION, WITH FOLLOW-UP ACTIONS. Independent validation review confirmed on 2026-08-24 that for a defect of this kind, a register entry carrying the deviation and its associated follow-up actions is a sufficient record, and that no separate corrective-action file is required. THE DEVIATION: for a period ending 2026-08-24, an electronic signature could be recorded against a quality record without passing through the application — and therefore without the identity re-verification at the moment of signing, the segregation-of-duties checks, the binding of the signature to the record's content, and the audit entry that the application applies to every signature. There is no evidence that this occurred. By the nature of the defect there would be no evidence either way: a write taking that route leaves no audit entry, which is the reason it cannot be ruled out rather than a reason to assume it happened. That is stated here rather than left for a reader to infer. FOLLOW-UP ACTIONS AND THEIR STATUS. (1) COMPLETE — the removal migration is now GENERATED from the schema instead of hand-maintained. Two hand-written versions preceded it and both fell behind as new modules shipped, which is the mechanism that allowed this to accumulate rather than any single mistake. (2) COMPLETE — the installation qualification gained a check that reads what each access rule PERMITS and refuses to qualify an instance where any rule lets a browser-reachable role write, plus a second check that every read rule is scoped to a single organisation. The two checks that existed before asked only whether protection was switched on and whether each table had at least one rule; both reported this instance clean throughout. (3) COMPLETE — the adversarial test that runs against a live instance was widened from one signature table to all twenty-three. It had been cited as evidence that signature forgery by direct write is blocked, and it tested that on a single table. ⚠️ AMENDED 2026-08-25, because COMPLETE was too strong and the way it was too strong is worth stating. Widening the test was done. The automated pipeline that runs it, however, reached only ONE of the twenty-three, because the disposable database that pipeline uses had been built by hand from three files and carried only that one table. The remaining twenty-two were recorded on every run as absent and the run passed regardless, so the evidence the pipeline actually produced covered a twenty-third of what this action claimed. Nothing was wrong with any individual check. What was missing was anything that judged how much of the subject had been reached. The pipeline now installs the complete schema from the migrations before it tests, so all twenty-three exist and are attacked there, and a shortfall in coverage is now a failure rather than a line in a log. That is the same defect this deviation records — a control believed to be in force because a document said it was — found this time in our own evidence rather than in the product. (4) COMPLETE — a repository check now compares the access rules the migrations leave in force against the rule that no browser-reachable role may write, so a new module cannot ship this defect and remain green. (5) OPEN — nine tables are deliberately excluded from the tenant-isolation check, each with a written reason, and that exclusion list has not been reviewed by anyone independent. Our validation reviewer has recorded that assessing it calls for information-security expertise rather than quality-systems expertise. An independent security review of those nine exclusions is outstanding and is the remaining open action on this entry.
rls-lockdown.test.tsiq-check.test.tsThe installation check's list of tables that are private by design — closed to everyone except the server's privileged connection — now records WHY each one is on it, names them all on the report rather than passing over them silently, and fails the installation if an access rule ever appears on one of them. Six tables were added to that list after their access paths were read: the two vendor compliance tables, the failed-login lockout table, the signup queue, the per-organisation settings table and the organisations table.
Why this classification: No record, signature, role or lifecycle rule changes; this affects the installation qualification report, so anyone whose qualification cites it should re-run and re-file. Three things changed and the second is the one that matters. First, the list was a bare set of table names, and the check's PASS read "every tenant-facing table has at least one access rule" without ever saying which tables it had waived. An exemption nobody can see is indistinguishable from a check that is not looking, so each entry now carries its reason and the report names every table it waived. Second, being on that list is a CLAIM — that nothing reads the table except the privileged server connection, and therefore that having no access rule is correct rather than an omission. The check now enforces the other half of that claim: if an access rule appears on one of those tables it FAILS, because the claim has been broken by hand, outside the migrations. That is precisely what happened to the settings table, where a hand-made rule granted every tenant member full write access and sat unnoticed because the check only ever asked whether a rule existed — and one did. Third, the six additions were made only after reading every place in the code that touches each table: twenty-four for the organisations table, twelve for settings, and all of them use the privileged connection. ⚠️ Being on this list REMOVES a table from a tenant-isolation check, so it is not a way to quiet a failure, and the reason recorded beside each name is what an independent reviewer should challenge.
iq-check.test.tsThe order in which the database migrations must be applied is now written down, in supabase/run-order.json, and the installation protocol cites it. It is NOT alphabetical — applying them in the order a directory listing shows them will fail. Two corrections come with it: one file that was being counted as a migration to apply is a historical record that says DO NOT RUN and contains no executable statements, so the number of files to apply is 83 rather than 84; and the per-organisation settings migration written the previous day is now pinned ahead of the three migrations that add columns to it.
Why this classification: No record, signature, role or lifecycle rule changes. It is classified review because it changes an installation qualification step that a customer's executor performs, and because the step as previously written could not be performed correctly. The protocol said the migrations are "applied in order" and never said what the order was, so the real order existed only in the builder's head while the protocol asked somebody else to confirm the result. It is not alphabetical by a wide margin: under an alphabetical listing, 172 files need a table, function or type that a later file creates. The core migration defines the tenancy tables and the function nearly every access rule calls, and does not sort first. So the order is declared in a file, the protocol names that file as the authority, and a check holds the two together — every migration must be either in the order or on a short exclusion list with a stated reason, and none may need something a later one creates. Two things surfaced while building it, both worth recording. One file in the migrations folder is a HISTORICAL RECORD rather than a migration: it is entirely inside a block comment, states DO NOT RUN, and preserves the definitions of two retired privileged functions so the removed objects stay describable. It was being counted among the files to apply, so the stated count was 84 when the number an executor should actually apply is 83; running it would have been harmless, but the instruction was wrong. And the per-organisation settings migration released the previous day had not finished the job it was written for — it fixed three migrations that altered a table nothing created, and simply MOVED the hazard, because it must now run before those three and nothing said so. ⚠️ THE HONEST CAVEAT IS ABOUT THE CHECK ITSELF. Its dependency model was incomplete three times over, and each time it returned a confident clean answer against an order that could not have installed — first ignoring foreign keys, then function calls, then enum types. Every one was found by reading the generated order and asking why a particular file was first, never by the check reporting a problem. It now models tables, functions and enum types, and it models nothing else; a pass means no migration needs one of those three things too early, and never that the order is correct in every respect.
sql-run-order.test.tsprotocol-code-sync.test.tsThe installation check now also verifies what each READ rule is limited to, not only who is allowed to write. A rule letting the browser read a table fails the installation unless it names the current organisation, compares it to the row's organisation, is not an unconditional "allow everything", and is not widened by an OR. Nothing on the running instance fails it — all eighty-six rules are already correctly limited — so this prevents a future mistake rather than correcting a present one.
Why this classification: No record, signature, role or lifecycle rule changes, and no rule on the running instance changed; this adds a step to the installation qualification evidence, so anyone whose qualification cites that report should re-run it and re-file the output. It exists because the check added earlier in this release asks only who may WRITE, and the two failures are different. A write rule lets somebody create or alter a record inside their own organisation, going around the application. A badly limited READ rule hands one customer another customer's records, which is the single guarantee a shared system cannot afford to get wrong. Each of the four conditions is there because a rule can satisfy the others and still leak: an unconditional "allow everything" reads as a rule and exposes the whole table; naming the current organisation but comparing it to the wrong column limits nothing; and an OR only ever widens what is visible. A STRICTER rule passes — the per-user preferences table additionally limits each person to their own rows — and write rules are left to the other check rather than double-reported. ⚠️ THE LIMIT IS WORTH STATING PLAINLY, AND IT IS SHARPER THAN THE OTHER CHECKS'. This proves a rule NAMES the tenancy condition and is not obviously widened. It does NOT prove the condition is correct: a rule that joins to the wrong parent table would satisfy every one of the four and still be wrong. It narrows the range of possible mistakes, it does not eliminate them, and an OR being reported is a request that a person read the rule rather than an assertion that it is faulty. Verified against the running instance when written: eighty-five of the eighty-six rules are the same single expression, the eighty-sixth is the stricter one, and the check reports nothing.
iq-check.test.tsThe two database access-lockdown migrations are replaced by ONE, and it is now generated from the schema rather than written by hand. Applying it requires a short one-time step first, because twenty access rules created yesterday revert to their original names; the rules themselves are unchanged. No rule gains or loses any permission — this was verified against the running instance, rule by rule, before the change was made.
Why this classification: No record, signature, role or lifecycle rule changes, and no permission changes anywhere; what changes is how the lockdown is maintained. It is classified review rather than none because it alters a migration a customer applies and renames twenty access rules, so an installation qualification that lists them by name should be re-run and re-filed. The reason is the pattern rather than the file. There were two lockdowns. The first was written by hand for the twenty tables that existed when it was written. The second existed ONLY because the first had fallen behind a schema that kept growing — by the time anyone looked, forty-three access rules across thirty-seven tables let ordinary signed-in users write, nine of them into signature tables. Writing a third by hand would have gone stale in exactly the same way, for exactly the same reason, so the list is now derived: a script reads every access rule the migrations declare, finds the ones still in force that permit writing, and emits the removals along with a read-only replacement for any table that would otherwise be left unreadable. A check asserts the committed file is current, so a new module that ships a permissive rule and forgets to regenerate cannot merge. Two safeguards deserve a validator's attention. Each read-only replacement carries the ORIGINAL rule's own tenancy expression, copied rather than defaulted, so what each tenant can read is unchanged — and the generator REFUSES to produce a file at all if an expression cannot be read, rather than substituting a plausible one, because a guessed tenancy condition applied to a customer's database would silently widen or narrow access with nothing to show for it. And the result was checked against the running instance before being adopted: eighty-six rules before, eighty-six after, every one matching on table, permission and tenancy condition, with the only differences being the twenty names.
rls-lockdown.test.tssql-run-order.test.tsPowerPoint decks and Word documents stored as controlled documents can now be read on screen inside Indelio, so a reviewer can see the document they are signing instead of downloading a file. Where that happens the system records that it was displayed to that person, which is a different and stronger record than a download — and is still not a record that they read it. In Design Controls, a design review now records who took part and the function each of them represented, and the rule that the signer could not be the person who created the design project has been withdrawn. In CAPA, the effectiveness check now expects to be signed by someone other than the person who carried the actions out.
An organization administrator can now download the whole of their quality system from Settings, as a zip containing one CSV per table — every record, the complete audit trail with its hash columns intact, and every electronic signature. The archive states what it contains and what it does not: file CONTENT is not included, and an index names every uploaded file, its size, and the record it belongs to. The audit hash chain in the export can be re-walked by anyone, using nothing but the CSV, with no access to Indelio and no cooperation from us. The download is itself recorded in the audit trail, including whether it came out complete.
Why this classification: Nothing you previously qualified behaves differently, nothing that was permitted is now refused, and no signature, role or lifecycle rule changed — so this is not a requalification. It is classified “review” rather than “none” for two reasons, and both are worth a quality reviewer’s attention. The first is that it addresses a Part 11 clause our own validation package records as only partially met. §11.10(b) requires the ability to generate accurate and complete copies of records, in human-readable and electronic form, suitable for inspection and copying by the agency; until now the only routes were per-record PDFs, one record at a time, which is a poor answer to an inspector asking for the complete set. Whether this clause should now be assessed differently is a judgement for your validator and ours, not one we make by shipping the feature — the status in our Part 11 matrix is deliberately left where it is pending independent review. The second is that a new bulk export of every quality record is a data-egress capability your own procedures may need to speak to. It is restricted to the Administrator role and every download writes an EXPORT_ORG_DATA entry to the immutable audit trail naming who took it and when, so the act is visible; but if your quality system controls who may remove records from the system, that control is now yours to state. It is deliberately NOT gated on a signing role: taking your own records out is an administrative act rather than a quality decision, and it would be perverse to make the vendor the gatekeeper of your route out of the vendor. Three properties determined how it was built, and each exists because the opposite failure is the dangerous one. An export that quietly omits something is worse than no export, because it is indistinguishable from a complete one — so a table that cannot be read is recorded as FAILED in the manifest and its CSV carries the failure text, never an absent or empty file, and the archive marks itself incomplete whenever that happens or the audit chain does not verify. The set of tables exported is checked against the database schema on every build, in both directions, so a table added in future that nobody adds to the export breaks the build rather than silently going missing from customers’ archives. And one table carrying an organization identifier is deliberately excluded and named as excluded: the vendor’s own signup queue, whose rows hold other prospects’ contact details and whose organization link is stamped after provisioning rather than being a tenancy anchor. Finally, values are neutralised against spreadsheet formula injection, because this artefact exists to be opened in a spreadsheet, which is exactly where a stored value beginning with an equals sign stops being text. No database migration is required; the export reads what is already there.
export.test.tsSix statements in the product that described a segregation-of-duties control Indelio does not enforce have been corrected — in the in-product help, the module reference, the FAQ, the admin page, the document signing panel, and SOP-QMS-001 in the SOP template library. Nothing about how the product behaves has changed. What changed is that it now describes the controls it actually has. Two corrections matter most. Indelio does NOT prevent the author of a document from signing a REVIEW of their own document — it prevents them approving it — and five places said otherwise, including the panel where that review is signed and the Document Control SOP template. And Corrections & Removals is NOT subject to a segregation-of-duties rule: one person holding the QA role can raise a field action, approve its health-hazard evaluation, recall classification and 806 reportability decision, and close it.
Why this classification: No controlled function changed, nothing is refused that was permitted before, and no signature, role or lifecycle rule was altered. It is classified “review” rather than “none” because the statements being corrected are statements about CONTROLS, and you may have relied on them. If you wrote either of the two above into your own procedures on the strength of ours, that is worth correcting on your side, and if your quality system requires design or document reviews to be independent of the author, or requires a field action to be closed by someone other than the person who assessed it, those are now procedural controls you state and evidence rather than ones the system refuses for you. How this happened is worth stating, because it is the more useful part. Neither statement was wrong when written. Each described a control that was later relaxed on deliberate advice — the CAPA closer-versus-plan-approver rule in July 2026, and the document author’s review signature in August 2026, the latter because the stricter form could leave a document that nobody was able to approve. In both cases the rule was changed and the sentences describing it were not, and nothing in the build could notice: automated tests assert what the code does, and until now nothing asserted that what the product SAYS matches it. A test suite has been added that derives each rule from the shipped function and checks the wording against it, including one check deliberately written to fail if a segregation-of-duties control is ever added to Corrections & Removals, so that the wording is updated as part of that change rather than afterwards. The SOP-QMS-001 template has been updated; a template seeds a DRAFT that runs your own review and approval, so no procedure you have already approved changed under you, but if you adopted an earlier copy of it the paragraph on segregation of duties and the Author and Reviewer role rows are the ones to re-read.
copy-control-claims.test.tsfour-eyes.test.tstemplates-sync.test.tsClosing a CAPA now expects the effectiveness check to be signed by someone other than the person who carried the actions out — either the owner of an action or whoever marked it Done. If that is you and nobody else is available, you can still close it, but you must record why; the reason is kept with the CAPA and printed on its report. Completing a CAPA action now also records who completed it, on the action itself. Two statements about CAPA that were not true have been corrected at the same time: the in-product help said the person who closes a CAPA cannot be the one who created it, and the module reference said the effectiveness signer must differ from the person who approved the plan. Neither was enforced.
Why this classification: A closure gate was added (ISO 13485 §8.5.2). A CAPA that you would previously have closed may now be refused until either another QA user closes it or you record a reason for closing it yourself, so if your performance qualification exercises CAPA closure, add the case: close a CAPA whose actions you owned, confirm the refusal, record the reason, confirm the closure. State plainly what this is and is not. It is an Indelio DEFAULT, not a regulatory requirement: §8.5.2(f) requires the effectiveness of a corrective action to be reviewed and sets no independence rule for who reviews it, and the wording you see says so — we will not tell you a standard obliges something it does not. It was recommended by an independent regulatory consultant (RAC) reviewing the module, whose advice was to default the control on and allow a recorded exception rather than choose between enforcing it and dropping it. Three properties matter to a quality reviewer. The gate reads who OWNED an action and who MARKED IT DONE, because ownership is an assignment rather than a record of doing, and a gate reading only ownership is satisfied by assigning the work to someone else and doing it yourself. Reopening a completed action does not erase who completed it. And where a CAPA’s actions cannot be read, the closure is REFUSED rather than allowed — a read that failed must never resolve to “no implementers, therefore this signer is independent”. Separately, the two corrected statements: neither described anything Indelio enforced. The data layer stores who created a CAPA and has never compared it to the person closing it, and the rule that the closer differed from the plan approver was deliberately relaxed in July 2026 on validation-review advice, because it is not required by regulation and can block closure in a small quality team — QA closing the record independently is the control that matters, and it is unchanged. If you wrote either statement into your own procedures on the strength of ours, that is worth correcting on your side too. ⚠ This release requires a database migration (supabase/capa_effectiveness_independence.sql). Until it is applied, existing records are unaffected, but marking a CAPA action Done will fail and the new closure gate will read only action ownership.
capa-effectiveness-independence.test.tscapa.test.tsA design review in Design Controls now records who took part and the function each of them represented, and will not be signed until it does. You are recorded as a participant of the review you sign and state your own function alongside the others; a participant does not need an Indelio account, so a contract engineer, consultant or clinician can be named. Where nobody else took part, the review is still accepted — but you must record why, and that reason is kept and shown with the review. At the same time, the rule that a design review could not be signed by the person who created the design project has been WITHDRAWN: that person may now sign a design review, provided the participants are recorded. Both changes appear on the design page and in the Design History File report.
Why this classification: Two controls changed in opposite directions, and both belong in your assessment. A restriction was REMOVED: until this release Indelio refused a design review signed by the creator of the design project, and it no longer does. The grounds are that the requirement it was enforcing does not exist — legacy 21 CFR §820.30(e) is RESERVED under the QMSR, and ISO 13485 §7.3.5 sets no independence rule for design reviews — so the check enforced a good practice as though it were a regulatory requirement being failed, which is not a defensible position for a system to take on your behalf. The removal was reviewed by an independent regulatory consultant (RAC) and authorised by name and date by Indelio’s founder before it was made. It is NOT a relaxation of segregation of duties elsewhere: the four-eyes rule on document release, and the rule that the validation signer cannot be the verification signer, are untouched. A requirement was ADDED in its place, and it is the one the standard actually states: ISO 13485 §7.3.5 requires the review record to include the identification of the design under review, the participants involved, and the date. Indelio recorded the design, the date, the outcome and the single signer — so it recorded the signature and called it the review, and there was nowhere to put anybody else. A design review is now refused until the participants and their functions are recorded. If your quality system requires design reviews to be independent of the designer, that requirement is yours to keep: state it in your design control procedure and evidence it through the participants now recorded on each review, because Indelio will no longer refuse the signature for you. Two things a quality reviewer should know about how it behaves. The single-participant case is an exception rather than an exemption — the review is accepted, the reason is mandatory, and it is stored and displayed only where it was actually taken, so a review with several participants can never appear to have claimed one. And where the participants of a review cannot be READ, Indelio says exactly that, on the page and in the DHF report, rather than showing the review as having none — an unreadable record must never be presented as an incomplete one next to a signature. ⚠ This release requires a database migration (supabase/design_review_participants.sql). Until it is applied, existing records are unaffected and unchanged, but a new design review cannot be recorded and the product will say so.
design-review-participants.test.tsfour-eyes.test.tsdesign-controls.test.tsA Word (.docx) controlled document can now be read inside Indelio. Its text, headings, lists, tables and pictures are drawn on screen, and the page header and footer are shown with it — so a reviewer or trainee can read the document without downloading it or having Word installed. Where the whole document is shown, that is recorded as a delivery in the same way viewing a PowerPoint deck is, and it satisfies the requirement to have been given the document before signing. If any part of the document cannot be shown, Indelio says which part, shows what it has, and records nothing. Legacy .doc and .ppt files are unchanged: Indelio cannot display them, they still save to your downloads, and the product now says that saving them as .docx or .pptx would let them be shown on screen instead. ⚠ Corrected shortly after release on the same day: for about an hour, a table in a Word document was drawn as an empty grid with its cell contents shown as ordinary paragraphs above it. The document's words were all present and none were altered, but a table did not read as a table. If you looked at a Word document containing a table in that window, look again.
Why this classification: A control changed, in the same way and for the same reason as the PowerPoint viewer earlier in this release: the precondition for signing a file-backed controlled document — that the document reached the signer — can now be satisfied a second way for Word files, by the document being displayed, in addition to the file being downloaded. The requirement itself is unchanged and no signature rule, role or lifecycle rule was altered, but the evidence a signature rests on is now produced by a path that did not exist before, so re-executing the part of your performance qualification covering review and approval of uploaded Word documents is the conservative course. One property is worth stating for a quality reviewer, because it determined how this was built. The conversion library Indelio uses renders the document body and discards headers and footers WITHOUT reporting that it has done so — confirmed by testing a file that carried text in those places. On a controlled document that is a serious omission to make silently, because the document number, revision, effective date and any print-control statement typically live there, and they are what a reviewer checks to confirm they are looking at the right document. Indelio therefore extracts and displays the header and footer itself, and decides whether a document was shown in full from its own examination of the file rather than from the library reporting success: comments, footnotes and endnotes are detected and named where present, tracked deletions are detected because a deletion does not render and the reader would otherwise see the document as if every deletion had been accepted, and any element the renderer does not recognise is recorded rather than skipped. Anything not shown is listed on the page and prevents the delivery record being written, so a partial rendering can never satisfy the signing gate. As with the deck viewer, this records that the document was displayed to a named person, from an identified set of bytes, at a stated time — it is not evidence that they read it, and the record is made against a signed-in session rather than against a pair of eyes.
docx-render.test.tsdocument-delivery.test.tsslide-viewer.test.tsA revision that was started and is not going ahead can now be discarded. Nothing is deleted: the version is kept and marked “Discarded”, the reason you give is recorded in the audit trail, and any signatures already applied to it stay exactly where they are. Until now there was no way to do this — the system refused a second revision with “Finish or discard it first” and offered no discard, so an abandoned revision held the document’s revision slot permanently and the only way out was to take an unwanted version through review and approval. The Effective version in force is untouched, and the document can be revised again afterwards. QA can discard any abandoned revision; the person who started one can discard their own only while nobody has signed it.
Why this classification: A lifecycle state was added and a new disposition of a controlled record with it (21 CFR 11.10(a), (e); ISO 13485 §4.2.4). Documents can now hold a version in a state your qualification has not seen, and a revision can leave the in-progress slot without having been approved, which is a path through the lifecycle that did not previously exist. If your performance qualification exercises the revision lifecycle, add the case: start a revision, discard it, confirm the Effective version is still the one served and that the document can be revised again. Three properties are worth stating for a quality reviewer. First, discard is not deletion and cannot be made into one — the version, its content hash and its signatures are retained, and the audit trail records who discarded it, from which state, whether it had been signed, and the reason they gave, which is mandatory. Second, “Discarded” is deliberately not “Obsolete”: an obsolete version was in force and has been superseded, while a discarded one was never in force, and collapsing them would attribute a history to the document that it never had. Third, an Approved version awaiting its effective date CANNOT be discarded by this — withdrawing a release that has already been signed off has different consequences for training and for anyone expecting it, and it is deliberately not covered here. ⚠ This release requires a database migration (supabase/discard_revision.sql). Until it is applied the rest of the product is unaffected and no existing behaviour changes, but attempts to discard a revision will fail.
doc-control-phase1.test.tsrelease-gate.test.tsEach electronic signature on a document stored as an uploaded file now also states how long before the signature the document was last put in front of that signer — for example “shown in Indelio, 4 seconds before signing” or “the file was sent, 2 days before signing”. The interval was always in the delivery record and was never shown. Separately, the slide viewer now states plainly that the record it writes is made against your signed-in session and cannot tell whether you, or software acting on your behalf, was at the screen.
Why this classification: No control changed. Nothing is refused that was previously permitted, no signing rule was altered, and the records are the ones the system has been writing since delivery was first recorded. It is classified “review” rather than “none” because a signature manifestation gains a clause, which a qualification script asserting that text will notice, and because the second half states a limitation of the evidence that a validation reviewer should see rather than infer. That limitation is the reason for the change and is worth stating here in full. A delivery record evidences that a document was displayed to a signed-in session. It cannot evidence who was at the screen: software operating a user’s own browser carries that user’s session, address and browser identity, and produces a delivery record identical to a person reading. Indelio does not attempt to detect that and makes no claim to. Attempting it would mean presenting a guess as evidence, and restricting delivery by address or by a time window would prevent none of it while obstructing ordinary use. What the system can do is report the interval, which is a fact rather than an inference, and let your quality function weigh it: a signature applied seconds after a long document was rendered and one applied two days later are different circumstances, and the record now shows which occurred. Nothing here is evidence that the signer read the document, and none of this wording says so — the electronic signature remains the signer’s own attestation, and this changes nothing about their accountability for it.
document-delivery.test.tsEach electronic signature on a document stored as an uploaded file now states how that signer received the file: shown on screen in Indelio's viewer, sent to them as a file, or both. It appears beside the signature, alongside the time, the content hash and the signer's IP address. The system already recorded this for every delivery; until now it was held in the database and shown nowhere, so the difference could only be retrieved with a direct database query. Where no delivery is on record the signature says so, and where the record could not be read it says that instead — those are different statements and are worded differently.
Why this classification: No control changed. Nothing is refused that was previously permitted, no rule about who may sign or what must precede a signature was altered, and the underlying records are the same ones the system has been writing since delivery was first recorded — this release only reads them back and displays them. It is classified "review" rather than "none" because it changes what a signature manifestation shows, and that is material to how you evidence document review: a qualification script that asserts the text beside a signature will now find an additional clause, and an auditor asking how you know a reviewer saw a document can be answered from the record rather than from a database query. Two properties are worth stating for anyone assessing it. First, it does not claim the signer READ the document, and no wording in it does — being shown a document and reading it are different, the system can only evidence the first, and that distinction is deliberately preserved here. Second, a delivery record that cannot be read is displayed as unreadable and never as an absence: an empty result beside a signature would read as evidence the signer was never served the document, which is a materially different and potentially damaging claim. Signatures applied before delivery was recorded at all will show that no delivery is on record; that is accurate and is not a defect in those signatures, which were applied under the rules in force at the time.
document-delivery.test.tsCreating a controlled document now requires the 'author' role. Until this release there was no role check on any of the three ways a document comes into being — typing one, drafting one with AI, uploading a file, or starting a revision of an existing document — so anybody who could sign in could bring a document under control and be recorded as its author. Reviewing, approving and signing are unchanged. The places that offer document creation now match the rule: Document Control's write / AI-draft / upload panels, the “Create revision” control, and the “Use this template” form on each SOP template are shown only to someone holding the role, and each says which roles you do hold instead of simply disappearing. A template's text remains readable by anyone — reading a procedure is not authoring one. An administrator who needs to write documents can grant themselves the role, and that grant is recorded in the audit trail and notified to the account holder.
Why this classification: An access control was added where there was none (21 CFR 11.10(d) — limiting system access to authorised individuals). No signature control was broken before this release, and that distinction matters when you assess it: the author of a document still could not approve it, every named reviewer still had to sign, and the delivery and four-eyes gates were all operating. What was missing was the authority to originate the record at all, which the signing gates were never asked to cover. Behaviour changes in a way a qualification script will notice, and it is the kind that fails closed rather than open: a user who holds admin, reviewer or approver but not author can no longer create a document, upload one, or start a revision, and any step that did so as such a user will now be refused. Check who in your organisation holds 'author' before upgrading — a new organisation's first user is provisioned with it, but a user created later may not have been given it, and the people most likely to be affected are administrators who have been writing documents without it. The role is grantable by any administrator. Records already created are untouched and remain valid; this governs who may create new ones. Bulk migration is unaffected and keeps its own separate authority, content-confirmation step and QA verification.
four-eyes.test.tsOpening a document from the training panel now always serves the Effective version — the one the trainee is assigned to read — rather than whichever version the document is currently showing. Previously, while a revision was open as a Draft with a replaced file, a trainee was sent that draft. Since the acknowledgment requires being shown the version being acknowledged, and the draft is a different version, the trainee could not complete their training at all: they were told to open the document, they opened it, and nothing changed. Reviewing and approving a document is unaffected and still shows the version under review, which is the version a reviewer needs to see.
Why this classification: No training rule changed, and nothing that was previously accepted is now refused: the acknowledgment still requires that the exact assigned version was shown to the trainee, and it still records only that the document was put in front of them. What changed is which version the training panel serves, and therefore whether that requirement can be met at all while a revision is in progress. Classified "review" rather than "requalify" because it removes a block rather than altering a control — but it is not "none", because the behaviour is observable and a qualification script may cover it: a step that revises a document and then checks that trainees on the current version can still complete their training would previously have failed, and a step that asserted the training link opens the same version as the document page will now find it opens the Effective one. Two related points for anyone assessing this. First, the version served is stated on the viewer itself, so a trainee can see which version they are reading. Second, this is the second half of the training-record correction recorded above: that entry stopped a trainee being shown one version and recorded as having read another; this one gives them a route to the version they are actually assigned. Requesting the version in force fails closed — a document with no Effective version serves nothing rather than falling back to a draft.
training.test.tsslide-viewer.test.tsWhen a document has an Effective version and someone is part-way through revising it, opening that document sends the revised DRAFT — and until this release the training system recorded the trainee as having read the EFFECTIVE version instead. Because a training assignment is bound to the effective version's content, that record then satisfied the requirement to open the document, and the trainee could apply a "read and understood" signature for a version the system had never put in front of them. The record of a trainee opening a document is now bound to the bytes that were actually sent to them. If those are not the bytes their training is about, nothing is recorded and the acknowledgment stays refused until they are shown the right version.
Why this classification: ⚠ THIS AFFECTS TRAINING RECORDS ALREADY IN YOUR SYSTEM. A completed read-and-understand acknowledgment carries an electronic signature asserting that the trainee read a stated version (21 CFR 11.10(e), 11.50; ISO 13485 §6.2). Where the conditions below held, the view record underlying that assertion describes a different set of bytes from the ones the trainee was shown, and both the view record and the signature are append-only, so neither can be removed or amended. The conditions are specific and you can check them: the document had an Effective version; a revision of it existed as a Draft; that draft's file had been REPLACED, so it was no longer the byte-for-byte copy of the effective file a draft starts as; the trainee held an open assignment on that document; and they opened it from the training panel between 18 August 2026, when the requirement to open a document before acknowledging it was introduced, and this release. An unmodified draft is not affected — it carries the same content as the effective version, so the record was accurate. If any of your completed training meets all of those conditions, the conservative course is to re-assign that training on the current effective version and have it acknowledged again, rather than relying on the existing record; superseding it is the correct treatment, not deleting it. Nothing about who must be trained, on which version, or what the acknowledgment binds to has changed, and no acknowledgment that would previously have been accepted on the correct version is now refused. The cause is worth recording for anyone assessing this: two parts of the system were each deciding on their own which version of a document was the relevant one, by different rules, and the fix was to have the part that sends the file state which version it sent rather than have the other part work it out again.
training.test.tsslide-viewer.test.tsdocument-delivery.test.tsA PDF controlled document is shown on its own page in an embedded preview. Until this release, that preview counted as the document having been delivered to whoever loaded the page — so the requirement to open a PDF before signing it could be satisfied by arriving at the page, without the reviewer opening anything, with only the first page visible in the frame. It no longer is. The preview now records nothing and says so beneath itself; what satisfies the requirement is asking for the document, which is the button beside the signing boxes or "Download original".
Why this classification: A Part 11 control was not operating as described, and this release makes it operate as described. The requirement itself never changed — a signature on a file-backed document still requires that the document reached the signer — but for PDFs the record evidencing it could be produced by a page load rather than by any act of the signer. Two consequences a quality reviewer has to weigh, and the second is the one that cannot be undone. First, behaviour changed in a way a qualification script will notice: a step that loads a PDF document's page and then signs it will now be refused, and must be updated to open the document first; a PowerPoint deck, a Word file and a typed document are unaffected, as is the download path for every type. Second, and more important: delivery records written for PDF documents between 2026-08-21, when the delivery requirement was introduced, and this release may have been produced by a page view rather than by a deliberate act, and the audit trail is append-only so those records cannot be removed or amended. If you held a PDF controlled document in that window, the conservative course is to treat any delivery record on it as unproven, re-execute the review or approval of that document, and re-run the part of your performance qualification covering signature of uploaded PDF documents. Indelio can tell you which records fall in that window on request. The defect existed for one day and arose from a combination rather than a single change: the embedded preview predates the delivery requirement by two months, and neither was wrong on its own.
document-delivery.test.tsOn a document stored as an uploaded file, the button beside the signing boxes now matches what will actually happen. A PDF opens in a new tab. A PowerPoint (.pptx) deck opens Indelio's slide viewer. A Word file or a legacy .ppt — which nothing here can display — is labelled "Download the document" and states that it saves to your downloads folder. Previously the button read "Open the document" in every case and always saved the file, including for PDFs that could have been displayed.
Why this classification: The requirement that a file reach a signer before they can sign it, and who may sign, are unchanged. It is classified "review" rather than "none" because the wording on the button is observable and may be written into a qualification script: a step reading "click Open the document" will find that button labelled "Download the document" for a Word file and "View the slides" for a PowerPoint deck, and should be updated to expect it. ⚠️ A Word document and a legacy binary .ppt still cannot be displayed inside Indelio; only .pptx decks and PDFs can. The delivery record continues to evidence that the document was put in front of a named person at a stated time, and does not evidence that they read it.
document-delivery.test.tsslide-viewer.test.tslayout.test.tsA PowerPoint (.pptx) controlled document can now be read inside Indelio. Its slides are drawn in the browser — text, pictures, tables and layout — so a reviewer or approver can see the deck without downloading it or having PowerPoint installed. Anything the viewer cannot draw is marked in place on the slide it belongs to, and is never silently left out. If any slide fails to draw, if any picture cannot be shown, or if any text would be drawn in the same colour as what is behind it, Indelio says so, does NOT record the document as having been delivered to you, and you must obtain the original file before you can sign.
Why this classification: A control changed. The precondition for signing a file-backed controlled document — that the document reached the signer — can now be satisfied a second way, by the slides being displayed, in addition to the file being downloaded. The requirement itself is unchanged and no signature rule, role or lifecycle rule was altered, but the evidence a signature rests on is now produced by a path that did not exist before, so re-executing the part of your performance qualification that covers document review and approval of uploaded decks is the conservative course. Two further points a quality reviewer should weigh. First, the delivery record now states HOW the document reached the person — 'viewer' where the slides were drawn on their screen, 'download' where the bytes were sent — because those are different facts and an auditor asking how you know an approver saw a deck deserves the difference; records written before this release are marked 'download', which is what they were. Second, and unchanged in principle: neither value is evidence that anybody READ the document. Someone can be shown a deck and look past it. The system records that the document was put in front of a named person, from an identified set of bytes, at a stated time. ⚠️ The viewer is Indelio's own rendering and is not PowerPoint: themes, animation, charts and SmartArt are not drawn, and the original file remains the record of what was signed. One consequence is worth stating for a reviewer assessing the evidence: the viewer checks that text is not being drawn in the same colour as its own background, and treats a slide where that happens as not shown — because a slide the reader cannot read is not a slide they were shown. It does not make that judgement about text placed over a photograph, where deciding readability would mean interpreting the picture.
slide-viewer.test.tspptx-render.test.tsdocument-delivery.test.tsThe slide viewer opens on a preview — the first three slides — so you can confirm a deck is the one you are about to sign or download without waiting for a hundred slides to be drawn. A button shows the whole deck. Viewing all of it is what gets recorded; the preview is not, and the page says so in place.
Why this classification: The recorded fact is unchanged: a delivery is written only when the whole document has been drawn for that person. What changed is that the viewer no longer draws the whole document by default, so a reviewer who opens it and stops at the preview has NOT satisfied the signing gate and will still be refused — which is why the preview states that plainly, and why the buttons leading to it say "view all the slides" rather than "view the slides". Classified "review" rather than "requalify" because no control moved: the same act produces the same record as it did before. A qualification script that steps through reviewing an uploaded deck should be checked for a step that assumes the viewer shows everything on arrival.
slide-viewer.test.tspptx-render.test.tsViewing a PowerPoint deck in Indelio also satisfies the training read requirement for that document, in the same way that downloading it already did.
Why this classification: Same reasoning as the change above, applied to the training acknowledgment gate: a trainee's read-and-understood acknowledgment is still refused until the exact effective version has been served to them, and that condition is unchanged — but it can now be met by the slides being displayed as well as by the file being downloaded. Classified conservatively because training records are the evidence of competence an inspector asks for first.
slide-viewer.test.tsWhen a signature or a submission for review is refused because the file has not yet been delivered, the message now directs you to the document button at the top of the Actions panel rather than naming that button by its label.
Why this classification: The refusal reasons and the conditions producing them are unchanged; only the sentence a blocked user reads is different. It was corrected because the message named a button by a label that the same release changed, so it directed people to a control that no longer existed under that name. Classified "review" because the refusal text is observable and a qualification script may assert it word for word.
document-delivery.test.tsIn the Versions table of a document, a two-word state such as "In Review" is no longer split across two lines, which had broken its coloured background into two separate rectangles.
Why this classification: Presentation only. No state, rule, permission or record changed — the same states are shown for the same versions, and only how the label is drawn is different.
layout.test.tsThe forgotten-password screen was found to disclose whether an email address has an account, and to report success when the reset had in fact failed — both corrected. Separately, people are now told when an administrator changes their roles, suspends or restores their account, or creates an account for them, and when a second factor is added to or removed from their own account.
When you request a password reset, the confirmation message is now the same whether or not that email address has an account. Previously the two cases were worded differently — an address with no account received "If that email has an account, a reset link is on its way", while an address with one received "Reset link sent — check your inbox" — so the reply itself revealed which addresses belong to your users. Anyone could do this without signing in. No reset link is sent to an address without an account, and none is sent to a deactivated account; neither is now distinguishable from a successful request.
Why this classification: The behaviour of a function available to unauthenticated visitors changed, and the text a user sees changed with it. No access rule was altered: who may reset a password, what the link grants, and how a new password is set are all unchanged, so nothing that previously succeeded now fails. The reason it is not classified "none" is that the confirmation wording is observable and may be written into a qualification script — a step reading "request a reset for a valid account and confirm the screen reports the link was sent" will now see the neutral wording instead, and should be updated to expect it. If your assessment covers disclosure of account existence to unauthenticated visitors, this is the change to point at.
password-reset.test.tsIf a password reset cannot be carried out, the screen now says so and states that no email was sent, instead of reporting that a link is on its way. Previously every failure — the authentication service being unreachable, an expired credential, a rate limit, a timeout — produced the same reassuring confirmation as a successful request, and the reason was discarded rather than recorded. Failures are now written to the server log with their cause.
Why this classification: A function that could previously fail while reporting success now reports the failure. Nothing that worked before behaves differently: a successful reset request is unaffected. The classification is "review" rather than "none" because the failure mode was invisible — if password recovery had stopped working for your organization, every user would have been told a link was coming and no record would have existed of it having failed, so an outage could not be evidenced after the fact. If your qualification recorded a pass on the forgotten-password path, that pass could not have distinguished a working path from a broken one; it can now.
password-reset.test.tsPeople are now emailed when an administrator changes their roles, suspends or restores their account, or creates an account for them — and when a second factor is added to or removed from their own account. Previously only two administrative actions were notified: a password reset and an administrator clearing someone's second factor. Role changes, suspensions and account creation were recorded in the audit trail but announced to nobody. Nothing about who may do what has changed, and no action is refused that was previously allowed.
Why this classification: A detective control was widened; no preventive control was altered. The reason it is not "none" is that it changes what your people receive and what your records contain: each of these actions now also records whether the notification was actually sent (`user_notified`), which appears in the audit entry's details. If your procedures describe how a user learns their access changed, or if your training or SOPs state that role changes are communicated manually, both should be reviewed against the system now doing it. Of the actions covered, the role change is the one to look at first — granting Approver or QA confers the ability to sign records, and it was the only such action that was both self-serviceable by an administrator and unannounced.
account-notices.test.tsThe email sent to a newly created user does NOT contain their temporary password. It tells them the account exists, who created it, which roles they hold, and that their administrator will supply a temporary password separately. The administrator continues to receive that password on their own screen exactly as before, to hand over by whatever means they judge appropriate.
Why this classification: The account-creation flow is unchanged in what it creates and in how the administrator receives the temporary password; what is new is that the person also gets a notice. The password is deliberately excluded from it. A newly created account may already carry Approver or QA, so its first credential can confer signing authority, and a credential delivered by email comes to rest in that mailbox, its backups, and every device it syncs to. If your onboarding procedure assumed the user receives nothing until the administrator contacts them, that is no longer true and the procedure should say so.
account-notices.test.tsPeople are now emailed when read-and-understand training is assigned to them, and when a document they were trained on is revised and their training is automatically re-assigned. Previously neither was notified: the assignment appeared on the training matrix and in the trainee's own training list, but nothing told them it existed. One email covers everything assigned in a single action rather than one per document. Whether each person was actually reached is recorded in the audit trail.
Why this classification: No training rule changed. Who must be trained, on which version, what the acknowledgment is bound to, and the requirement to open the document before signing are all exactly as before, and nothing that was previously accepted is now refused. The classification is "review" because your procedures may describe how a person learns training has been assigned to them — if an SOP says a supervisor informs them, or if your qualification recorded that the system does not notify, both should be checked against the system now doing it. Note also that the daily reminder digest still omits assignments that have no due date, and the due date on the manual assignment form is optional; the notice on assignment is now the reliable channel, and the digest remains a reminder rather than a first notification.
training-notice.test.tsA document that was UPLOADED as a file — a PDF, Word document or PowerPoint — can no longer be reviewed, approved or periodically reviewed until the system has actually delivered that file to the person signing. Opening the document with "Download original" satisfies it. Documents whose content is typed into Indelio are unaffected, because their content is displayed on the page above the signing box. If the file is replaced while the document is still a draft, everyone who is to sign must open it again.
Why this classification: A control was added to the electronic signature on a controlled document (21 CFR 11.10(a), 11.50). Signatures that would previously have been accepted are now refused until the file has been opened, so this changes behaviour you may have qualified. It was added because a file-backed document could be taken from upload to Effective with valid review and approval signatures from someone who had never been sent a byte of it — the same system already refuses a trainee's read-and-understand acknowledgment until the exact version has been delivered to them, and refuses a signature on twelve other record types until attached evidence has been delivered. If your performance qualification exercises review or approval of an uploaded document, that script now needs a step that opens the file before signing, and a document currently part-way through review will require its reviewers to open it again. Signatures already applied are unaffected and are not re-opened. The check records that the file was delivered; it is not evidence that it was read, and must not be described as such.
document-delivery.test.tsFor a document that was uploaded as a file, the document itself is now offered at the top of the Actions panel — an "Open the document" button beside the signing boxes, which says whether you have been sent this version and what is still required. It was previously a "Download original" link below the versions table. The author must also open an uploaded file before they can send it for review, and everyone named as a reviewer is now emailed that they have been asked, with whether each was reached recorded in the audit trail.
Why this classification: No signing rule changed — what is refused and what is permitted are exactly as released earlier today. What changed is where the document is offered and who is told. Two behaviours are new and may affect a qualification script: sending an uploaded document for review is now refused until the author has opened it, and being named as a reviewer sends an email. If your procedures describe how a reviewer learns they have been asked, or if a script sends a document for review immediately after uploading it, both should be checked. The reason for the change is worth recording: a reviewer who had been correctly refused reported that the download link was below the fold, that it was not clear opening it was required, and that a downloaded file is not the same as having seen the document. The first two are addressed here. The third is not — for anything other than a PDF the file still lands in the reader's downloads rather than on screen, and the wording on the panel now says plainly that the record shows the file was sent, not that it was read.
document-delivery.test.tsA new "Waiting on you" list shows each person the records held up until they sign — documents where they are an assigned reviewer, documents awaiting their approval, and training assigned to them. It appears on the dashboard, at the top of Reminders, and as a count in the navigation. Nothing about who may sign what has changed; this only shows people what was already true.
Why this classification: No control changed and nothing is refused that was previously allowed — this is a view over obligations the system already recorded and enforced. It is classified "review" rather than "none" because it changes how a person learns work is waiting for them, and your procedures may describe that. Until now the only list of outstanding items was organization-wide and driven by due dates, so a document sitting in review with a named reviewer against it appeared nowhere at all: a signature obligation has no due date. If an SOP says a reviewer is told by their supervisor, or if a qualification recorded that the system does not surface pending signatures, both should be checked against the system now doing so. One property is worth stating for anyone assessing it: if a source cannot be read, the list says so by name and the "nothing is waiting on you" message is suppressed entirely, so an empty list is never shown over a failed query.
waiting-on-you.test.tsThe "Waiting on you" list released earlier today duplicated an existing dashboard notice, so a reviewer saw the same document announced twice. The two have been merged into one list. The older notice has been removed, and the merged list now uses the stricter of the two calculations: a document is offered for your approval only once every assigned reviewer has actually signed it, where the newer list had offered any document sitting in review. Documents you authored yourself are not offered to you for approval, and a reviewer who has since been reassigned is no longer chased.
Why this classification: No signing rule changed, and nothing is refused that was previously allowed — both lists were views over obligations the system already enforced, and the approval gate itself was never in question. What changed is what the two lists claimed. The reason to record it rather than treat it as cosmetic is that the earlier of the two could name a document as awaiting your approval before its reviewers had signed; clicking through, you would have been refused at the point of signing, correctly, but the list had told you the work was yours to do. It now agrees with the gate because it asks the gate rather than re-deriving the answer. If a qualification script counted the items in this list, or recorded that a document appears there at the moment it enters review, both should be re-checked — a document now appears for approval later than it did this morning, and appears once rather than twice.
waiting-on-you.test.tsdocument-reviewers.test.tsTwo access controls were found not to be working as documented and were fixed, the tenant classification two controls depend on became a recorded property instead of a guess, two documents we publish were found to be out of date against their own sources, and an administrator who loses the device holding their authenticator can now be restored to access without leaving the system.
On a CAPA action plan, someone who has already signed one half of the dual approval is now told the other half needs a different person, instead of being offered a signing form that would be refused. The rule itself has not changed: the Approver and QA approvals still have to be signed by two different people, and that is still enforced when the signature is submitted.
Why this classification: The refusal now happens before the signing attempt rather than after it. The outcome is identical — the same person cannot fill both approval slots — but a qualification step written as "attempt to sign both slots as one person and verify the system refuses" will now find no form to submit, and will record no refused attempt. The server-side check is unchanged and still runs on every submission.
signing-feedback.test.tsThe idle session timeout now works after a browser restart. Previously the cookie tracking your last activity was discarded when the browser closed, while the sign-in itself persisted — so on any device that had restarted its browser, the inactivity logout never took effect and the session stayed open indefinitely. The interval is 30 minutes.
Why this classification: An access restriction changed (21 CFR 11.10(d), 11.300(d)). Sessions that previously remained open indefinitely will now be terminated after 30 minutes of inactivity, and the user is returned to the sign-in page. If your qualification tested the inactivity logout and recorded a pass, it was most likely exercised within a single browser session, which is the case that did work; the case that did not was resuming after closing the browser. Our published documentation stated 20 minutes while the system used 30 — the documentation has been corrected to match the system, not the other way round.
idle-timeout.test.tsEach electronic signature now records how long ago the signer last authenticated interactively, and that interval is kept as an append-only record. No signing rule changed — nothing is refused that was previously accepted, and the signature itself is unaltered.
Why this classification: The signing act now produces an additional record, so the behaviour of a controlled function changed even though no rule did. It was added because a browser password manager will re-supply a stored signing password for a period after one biometric check, and that period belongs to the manager rather than to Indelio — previously the interval could not be measured at all, because the signing check itself overwrote the only timestamp available. Recording it does not control it; it makes it observable.
signing-auth-age.test.tsWhether an organization is treated as a paying customer, an evaluation, a demonstration or an internal test tenant is now a property recorded on that organization and chosen when it is created. It was previously worked out each time by looking for words like "demo", "evaluation" or "northwind" in the organization's displayed name. Two things depend on the answer: whether multi-factor authentication is mandatory for administrator accounts, and whether sign-ins are notified to Indelio. An organization with no recorded value is treated as a customer, which is the stricter setting for both.
Why this classification: The scope of an access control changed (21 CFR 11.10(d), 11.300). The MFA requirement on administrator accounts is unchanged in substance, but which organizations it applies to is now determined by a recorded property rather than by the text of the organization's name. An organization whose name contains one of those words was previously outside the requirement and outside the sign-in-notification exclusion; if that describes yours, administrator MFA now applies to it and its sign-ins are no longer notified to us. Names are not otherwise used, so renaming an organization can no longer move it in or out of either.
tenant-type.test.tsmfa-policy.test.tsTrial expiry reminders are now sent reliably. The daily sweep read only the first page of accounts, so on a large installation the administrators of some organizations were never emailed their 7-day and 1-day warnings, and the organization was recorded as having been reminded regardless. An organization whose administrators have no reachable email address is now reported by name in the daily run instead of being skipped silently. When trials run out, access is suspended exactly as before — that behaviour is unchanged.
Why this classification: No function operating under your quality system was changed. This affects courtesy notifications about the commercial state of a trial account, not any record, signature, lifecycle rule or access restriction. Suspension of a lapsed trial — the part that does affect access — is untouched, and reminders were already best-effort by design. Listed because the register is a complete record of releases, not only of controlled changes.
trials.test.tsThe 21 CFR Part 11 Validation Package available under Compliance & Validation was out of date and has been corrected. The copy being served was generated on 9 August and was never regenerated after the source document was updated, so it stated that the idle session timeout was 20 minutes when the system uses 30, and it did not reflect the administrator multi-factor requirement. Its “last updated” date was fixed at 9 August by the build itself and could not move. The date now comes from the document, and the served copy is held to its source automatically. If you have downloaded this package, replace your copy.
Why this classification: No function operating under your quality system changed — this is a correction to a document that describes them, not to a control. It is recorded because the document is one you may have filed as part of your own assessment of this system, and it was wrong about a Part 11 access control interval. The behaviour of the idle timeout itself is covered by its own entry above; this entry concerns only what our documentation said about it.
validation-doc.test.tsidle-timeout.test.tsThe Supplier Qualification SOP template in the template library was an earlier draft than the one we maintain, and has been brought up to date. Its section on placing a supplier on hold now states the two ways a hold is closed more precisely, and adds that suppliers on hold are reviewed at management review. The other ten templates were unaffected. A template seeds a Draft in your document control and is reviewed and approved by you before it can become Effective, so no document already in force is changed by this.
Why this classification: No function operating under your quality system changed, and no controlled document was altered — a template is starting content, not a record, and it enters your document control as a Draft that your own review and approval must pass before it takes effect. It is recorded because the library is content we publish and it was out of date. If you seeded Supplier Qualification before this release, compare it against the current template when that document next comes up for periodic review.
templates-sync.test.tsRemoving or editing a finding on an internal audit now records what the finding said. Previously the audit trail entry read only “Audit finding removed” — it did not record which finding, its classification, or its text, so the trail could show that a major nonconformity had been raised during an audit and then deleted while preserving nothing about it. The entry now carries the finding’s number, classification and content, and an edit records the prior values alongside the new ones. Who may remove a finding is unchanged: only while the audit is Planned or In Progress, never after the report is signed.
Why this classification: The audit trail is a controlled function (21 CFR 11.10(e)) and what it records for these two actions changed, although no rule about who may do what changed with it. Nothing that was permitted is now refused. If your qualification checked the wording of the audit-trail entry for removing or editing a finding, the text it will now find is different and more complete.
audit-finding-trail.test.tsCorrected the register itself. The changes listed above shipped on 20 August but were initially recorded under the 19 August release, because that was the only release in the register at the time and entries were added to it rather than to a new one. They are now filed under the date they shipped. No entry's content was altered — only which release it sits under.
Why this classification: No function operating under your quality system changed. It is recorded because this register exists so you can decide whether a given release affects your qualified state, which makes the date each change shipped part of what you are relying on. If you assessed the 19 August release before this correction, the eight entries above were in what you read and are now attributed to 20 August.
releases.test.tsCorrections to the qualification material we publish, after four of our own documents were found to describe a product that had moved on. The PQ protocol template asked you to verify two behaviours the system no longer has: that an author is blocked from reviewing their own document, and that whoever approved a CAPA action plan may not close the record. Both rules were deliberately removed on independent validation-review advice — the first in August, the second in July — and executing those steps as written would have recorded a deviation against a control we took out on purpose. Both steps have been rewritten to expect what the system does, and the document-lifecycle scenario now demonstrates the four-eyes rule as it actually stands: an author may review their own document, and may not approve it. Separately, our OQ evidence index cited a test suite that exercised an internal model the product never ran, and that model had the review rule backwards; the model has been removed and the affected rows re-pointed at the suites covering the shipped code. A requirements traceability matrix is now generated from the requirements specification and published with it, replacing a hand-maintained copy that had fallen six requirements behind.
Why this classification: No function operating under your quality system changed — this is a correction to documents that describe them, and to internal code that was never executed. It is recorded because the PQ protocol is material you may have adapted and executed against your own instance: if you did, steps 1.4 and 2.10 as you received them will produce failures against a system that is behaving correctly, and any deviation raised against them should be reassessed rather than investigated. The behaviour of the two controls concerned is unchanged by this release and was last changed in the entries dated 19 August and in July respectively.
rtm-sync.test.tsfour-eyes.test.tsengine.test.tsThe password box on every electronic-signature field now refuses a pasted or dragged password, and asks each password manager that publishes an opt-out not to offer to fill it. You must type your password to sign. Signing in is unchanged and still works normally with a password manager, as does changing your password — this applies only to signature fields.
Why this classification: How a signer supplies the second component of an electronic signature changed (21 CFR 11.200(a)(1), 11.200(a)(3)). Nothing that was accepted is now refused at the server: the same password, typed, is accepted exactly as before. What changed is that the browser and password managers are no longer asked — and in the case of paste, no longer permitted — to supply it for you. If your qualification recorded a signing step performed by autofilling or pasting the password, that step now requires typing. ⚠️ Stated precisely, because the distinction matters to your assessment: this SUPPRESSES credential filling, it does not disable it. A web page cannot disable a password manager — browsers deliberately ignore the standard instruction to do so on password fields, and the additional attributes we send are honoured by each manager at its own discretion. It raises the bar on the ordinary path; it is not a guarantee that a signature component was supplied by the person, and we do not claim it as one.
password-clearing.test.tsAn administrator can now clear another person's two-factor authentication, so someone who has lost the device holding their authenticator can enrol a new one and sign in again. It removes the enrolled factor only — the password is unchanged, and anyone whose role requires a second factor is asked to enrol a new authenticator at their next sign-in. A reason must be given, the action is written to the audit trail, and the account holder is emailed to say it happened and why. Nobody can clear their own factor. Where an organization has only one administrator, and so nobody inside it can perform the recovery, Indelio can perform it from the platform console; that is recorded in your own audit trail, attributed to the Indelio administrator who did it.
Why this classification: An access restriction changed, and a new one was added (21 CFR 11.10(d), 11.300(b), 11.300(c)). §11.300(c) requires procedures to deauthorize a lost or compromised token and to issue a replacement under suitable, rigorous controls; until this release Indelio had no such path, and a lost authenticator on an administrator account could only be resolved by direct database access outside the audit trail. Your access-control procedure should now say who is permitted to perform this recovery and what they must establish before doing so — the system requires a stated reason and the acting administrator's own password, but it cannot confirm that the person asking is who they claim to be, and that verification is yours to define. If your qualification asserted that a second factor could not be removed from an administrator account, that assertion is no longer true and should be re-executed against the new behaviour.
mfa-recovery.test.tsTurning off two-factor authentication on your own account now tells you truthfully whether it was turned off. Previously, if the removal was refused — which happens when you are signed in through a password-reset link, or before entering your 6-digit code — the screen still said “Two-factor authentication disabled” and an entry saying so was written to your audit trail. The factor remained active throughout. Refusals are now shown as refusals, explained in plain language, and recorded as refusals.
Why this classification: ⚠ THIS AFFECTS RECORDS ALREADY IN YOUR AUDIT TRAIL. Any `MFA_DISABLED` entry written before this release may describe a removal that did not take place, because the error returned by the removal was discarded and success was reported regardless (21 CFR 11.10(e) — the audit trail as an accurate record of operator actions). If your trail contains such entries, verify against the account whether a second factor is in fact enrolled before relying on them, and supersede any that are wrong rather than deleting them — the trail is append-only by design. The underlying access control was never weakened: the removal was correctly refused every time, and no account was left less protected than it appeared. What was wrong was the reporting and the record. Two further changes come with it: a refused removal is now itself recorded (`MFA_DISABLE_REFUSED`), which did not previously appear anywhere, so a qualification that counted audit entries for this action will see entries it has not seen before; and a partial removal is reported as partial rather than as complete.
mfa-disable-honesty.test.tsmfa-policy.test.tsEvidence attached to a quality record is now retained once it has been signed over, and must be opened before the record is signed.
Attached evidence can no longer be deleted from a record once a signature has been applied to that record after the file was attached. Previously the rule was based on the record's state, so reopening or cancelling a signed record made its evidence deletable again, and three record types (Corrections & Removals, the Design History File, and the Summative Usability Test) had no retention rule at all.
Why this classification: Record retention behaviour changed (21 CFR 11.10(c)). Evidence that could previously be removed can no longer be, and attempted removals are now recorded in the audit trail. If your qualification covered removing an attachment from a closed or reopened record, that step will now be refused.
attachment-lock-coverage.test.tsA signature on a record that carries attached evidence is now refused until each attached file has been opened by the person signing. Records with no attachments are unaffected. The requirement can be switched off per organization by an administrator, under Settings.
Why this classification: A new precondition was added to the signing act itself. Any qualification step that signs a record carrying attachments will now require the file to be opened first, and will fail if it is not. This records that a file was delivered to the signer; it is not a record that anyone read or understood it.
attachment-lock-coverage.test.tsA document signature is now refused if your organization's configured signature meanings and review contexts cannot be read, instead of silently falling back to the built-in defaults.
Why this classification: The signature meaning is bound into the signature itself (21 CFR 11.50). Previously a failed settings read produced a signature stating wording your organization had not configured, and could switch off a configured mandatory-review-context policy without saying so. Signing now stops and says the policy could not be read.
pg-errors.test.tsThe password field used for electronic signatures is now cleared as soon as the form is submitted, whether the signature succeeded or was refused.
Why this classification: Previously the field kept its value, so a subsequent signature on the same screen could be applied without the signer re-entering either component. Signing now requires the password each time. No signature already applied is affected.
Responsive layout rules were being discarded by the browser and are now applied correctly. Affected the signed-in header on narrow screens and page breaks when printing the metrics page.
Why this classification: Presentation only. No record, signature, calculation, or control was involved.
inline-css.test.tsThe person who created a record can no longer apply its final approval signature. A second qualified person must sign. This affects assessments, change-notification determinations, management reviews, the quality policy, and all five human-factors records — the record types that previously reached their approved state on a single signature.
Why this classification: Segregation of duties on the approving signature changed (21 CFR 11.10(d), ISO 13485 §5.5.1). A qualification step in which one person both created and approved one of these records will now be refused and will need a second signer.
four-eyes.test.tsA controlled document now carries a named list of reviewers. Only a named reviewer may apply a review signature, and every named reviewer must have signed before the document can be approved. Documents already in review when this was released were not retro-fitted with a reviewer list.
Why this classification: The document lifecycle gained a precondition on approval (21 CFR 11.10(d), ISO 13485 §4.2.4). A document could previously reach Approved with no review signature recorded at all; approval is now refused and names the reviewers who have not signed. A qualification that approved a document without assigning reviewers will fail at that step.
document-reviewers.test.tsThe author of a controlled document may apply a review signature to it, but still may not apply its approval signature. Previously the author was blocked from every signature on their own document.
Why this classification: Who may sign what changed (21 CFR 11.10(d)). Together with the reviewer list above, the previous blanket restriction could leave a document that nobody was able to approve whenever its author was among the named reviewers. The four-eyes control on the approval signature itself is unchanged.
document-reviewers.test.tsMulti-factor authentication is now required of every administrator, and an administrator can no longer switch it off for their own account. An administrator who has not enrolled is held at an enrolment prompt before continuing.
Why this classification: An access control changed (21 CFR 11.10(d), 11.300). The policy already stated that administrators required MFA, but it was not enforced; it is now enforced at the point of use. An administrator account with no enrolled authenticator will not be able to proceed until it enrols.
mfa-policy.test.tsThe password field used for electronic signatures no longer asks the browser to offer a saved password, so a stored credential is not filled into it automatically. The signer types it. This does not prevent a password being pasted, and it does not make the password usable only by its owner.
Why this classification: The signing interaction changed, though no signing rule did. A browser password manager could previously supply the password component of a signature without the signer entering it. This reduces the chance the browser supplies it; it is a mitigation and not a control over who holds the password.
password-clearing.test.tsA public release register was added at /releases, listing each release, what changed in it, our assessment of the validation impact of each change, the reasoning behind that assessment, and the automated suites covering it.
Why this classification: No function operating under your quality system was changed. It adds a published record so each release can be risk-assessed; the assessment it carries is ours, and the decision it feeds is yours.
releases.test.tsThis register begins August 19, 2026. Changes released before that date were not recorded in this form, and we have not written them up after the fact.