GLMCHAT V2.2.1 — FINAL CODE AND BUILD-SPEC AUDIT Audit date: 22 July 2026 Audit type: Source, deployable runtime, documentation and retained-evidence audit Baseline: GLMCHAT_V2.2.1_VERIFIED_HANDOVER_22072026180358 Verdict: NO-GO FOR PRODUCTION EXECUTIVE FINDING The supplied release is structurally coherent and its existing automated checks pass, but it does not meet the documented build specification. The audit confirmed three release-blocking implementation defects, four additional functional defects, and unresolved external production-acceptance gates. No runtime source, schema, data, manifest, service worker, styling or configuration was changed during this audit. The only additions are this report and the mandatory audit diff/verification record. ============================================================ CONFIRMED RELEASE-BLOCKING DEFECTS ============================================================ AUD-P0-001 — Privacy export and full reset are incomplete Severity: P0 / Critical release blocker Affected files: - app/server/lib/DataManagement.php:44-62 - app/server/lib/DataManagement.php:156-199 - source/frontend/30-screens.js:108 Evidence: 1. createExport() exports settings, conversations, messages, workflows, workflow_stages, cost events, prompts, projects, project files, conflicts and tombstones only. 2. The application contains additional user-generated or user-associated tables that are omitted, including automation_definitions, automation_templates, workflow_snapshots, workflow_workers, workflow_events, workflow_checkpoints, tool_calls, tool_approvals, workflow_interventions and message_attachments. 3. The Privacy screen states that the export contains definitions, but automation definitions are absent. 4. deleteAllContent() does not delete automation_definitions or automation_templates. 5. resetApplication() calls deleteAllContent(), then separately deletes authentication/settings records. If the second phase fails, deleted content cannot be restored even though the operation reports failure. Impact: - A data export is incomplete and contradicts the user-facing privacy claim. - “Delete all content” and “Reset application” can leave agent, team, workflow-definition and template data behind. - A newly configured owner could encounter previous-owner definitions after reset. - Reset is not atomic and can fail after irreversible partial deletion. Required remediation: - Define the authoritative privacy-export data model. - Export all user-generated definitions, templates, attachments and detailed workflow execution records, or explicitly disclose and justify each exclusion. - Include all user-content tables in delete-all/reset semantics. - Stage filesystem deletion and database deletion so failure can roll back safely. - Reseed required default settings/templates after a successful reset. - Add integration tests proving export completeness, complete deletion and failure rollback. AUD-P0-002 — Recent-PIN verification can be brute-forced without lockout Severity: P0 / High security release blocker Affected files: - app/server/lib/Auth.php:133-171 - app/server/routes/data.php:19-21 - app/server/routes/settings.php:58-59 Evidence: 1. Auth::login() uses RateLimiter before calling verifyCurrentPin(). 2. Auth::verifyCurrentPin() has no independent rate limiting. 3. /api/data/reauthenticate and /api/settings/api-key call verifyCurrentPin() directly. 4. An authenticated session can therefore submit repeated six-digit PIN guesses to recent-PIN endpoints without the documented login lockout. Impact: - Recent-PIN protection for destructive privacy operations and API-key replacement is materially weaker than documented. - A stolen authenticated session can attempt an unrestricted online PIN brute force. Required remediation: - Add a distinct server-side reauthentication rate-limit scope. - Record failures atomically and apply temporary lockout by account/session and source address. - Clear the reauthentication failure state only after successful verification. - Return consistent retry-after metadata without revealing whether the PIN was close or otherwise valid. - Add concurrent and sequential brute-force regression tests. AUD-P0-003 — Offline mutation idempotency is not atomic Severity: P0 / High data-integrity release blocker Affected file: - app/server/lib/Sync.php:72-101 Evidence: 1. Sync::apply() checks the mutation ledger. 2. It then commits or performs the entity/filesystem mutation. 3. Only afterwards does it insert the sync_mutations ledger record. 4. The filesystem lock prevents concurrent workers but does not protect against process termination between the entity write and ledger insertion. 5. Deterministic identifiers make some create operations repeatable, but do not make update, delete, settings or file mutations safely replayable after that failure window. Impact: - A retry can return a conflict or not-found result instead of the original successful result. - Ordered offline replay can stall or create an incorrect user-visible conflict after an operation already applied. - The documented idempotent replay guarantee is not met for all supported mutation types. Required remediation: - Execute database entity mutations and ledger insertion inside one database transaction. - For filesystem mutations, stage files and coordinate commit/rollback with the database using a recoverable journal or compensating operation. - Persist the exact original result before exposing success. - Add forced-crash tests at every check/apply/ledger boundary for each mutation type. ============================================================ ADDITIONAL CONFIRMED DEFECTS ============================================================ AUD-P1-004 — Offline settings accepted by the UI fail during replay Severity: P1 / High Affected files: - source/runtime/offline-runtime.js:481-489 and 573 - app/server/lib/Sync.php:281-308 - app/server/routes/settings.php:19-48 Evidence: - The normal settings route accepts model_chat, model_vision, reasoning_preserved_default, TTS pricing, monthly cost limit, warning percentage, currency code and other fields. - The offline runtime queues arbitrary settings patches. - Sync::updateSettings() allows only a smaller subset. - The online route accepts reasoning values off, auto, high and max; the sync route accepts high and max only. Impact: - A setting can appear saved offline, then repeatedly fail when the queue reconnects. - The client and server can diverge and the queue can exhaust retries. Required remediation: - Use one shared settings schema and normalisation path for online PATCH and offline replay. - Reject unsupported settings before optimistic local commitment. - Add replay tests for every setting exposed by the UI. AUD-P1-005 — Two manifest shortcuts are consumed but never handled Severity: P1 / Medium Affected files: - app/manifest.webmanifest - source/runtime/native-android.js:6 and 104-147 - source/frontend/40-app.js:74-89 Evidence: - native-android.js consumes new-chat and projects from the URL before React reads the query string. - It dispatches a glmchat-shortcut event later. - No listener for glmchat-shortcut exists in the supplied source. - Other manifest shortcuts are not consumed and therefore remain available to the React mount logic. Impact: - Installed-PWA shortcuts for “New chat” and “Projects” can open the app without performing the requested action. Required remediation: - Either leave the query parameter intact for React, or register and remove a tested glmchat-shortcut listener in the application lifecycle. - Add installed-PWA shortcut tests for all manifest shortcuts. AUD-P1-006 — “Speak” does not prefill the Voice screen Severity: P1 / Medium Affected files: - source/frontend/40-app.js:949-965 and 1023-1024 - source/frontend/30-screens.js:106 Evidence: - The chat action stores voiceSeed when routing to Voice. - VoiceScreen is rendered without voiceSeed. - VoiceScreen always initialises text to an empty string and does not consume a seed prop. Impact: - Selecting “Speak” on an assistant response opens an empty TTS form rather than the selected response. Required remediation: - Pass the selected text into VoiceScreen and initialise/update the text safely. - Clear stale seed state after consumption. - Add a chat-to-TTS interaction test. AUD-P2-007 — Health checks persist an internal setting Severity: P2 / Low Affected file: - app/server/routes/auth.php:44-58 Evidence: - /api/health inserts or replaces settings.__health__ to test write access. - It does not delete the probe record. Impact: - Repeated operational health checks mutate persistent application data. - The internal record can appear in settings storage and privacy exports. Required remediation: - Perform the write probe in a transaction that is always rolled back, or use a dedicated temporary health table/operation. ============================================================ UNRESOLVED EXTERNAL PRODUCTION GATES ============================================================ AUD-P0-008 — Production acceptance remains incomplete Severity: P0 / Release blocker Not available in the audit environment: - PHP sqlite3, curl, mbstring and zip extensions. - Production-equivalent LiteSpeed root and nested-path hosting. - Live Together API credentials and provider acceptance. - A private clone of actual production data for upgrade and rollback. - Physical Android Chrome, Samsung Internet and TalkBack testing. - Formal approval of the React 16 and six-digit-PIN residual risks. Required completion evidence: - Passing target-host acceptance runner with all required PHP modules. - Live text, vision, function-calling, streaming, cancellation and TTS evidence. - Real-data migration/rollback counts and integrity evidence. - Physical-device browser, offline, PWA update, download and accessibility evidence. - Signed dependency and authentication threat-model decisions. ============================================================ CHECKS ACTUALLY PERFORMED ============================================================ Passed: - Fresh ZIP compressed-data integrity. - SHA-256 verification of the supplied handover. - Deterministic frontend build. - TypeScript compatibility check. - Lint. - PHP static contracts: 34 passed. - Node automated tests: 7 passed. - Fresh migration, clone migration, rollback and schema 12-to-13 tests. - Controlled PHP HTTP preflight. - PHP syntax: 44 of 44 files. - JavaScript syntax checks. - Package, PWA, security and SQLite structural validator: 281 checks. - Static inspection of authentication, privacy/data management, synchronisation, settings replay, shortcuts, TTS routing, service-worker and health paths. Not run or incomplete: - Full backend execution because required PHP extensions are absent. - Live LiteSpeed acceptance. - Live Together provider acceptance. - Production-data migration/rollback. - Physical Android browser and TalkBack acceptance. - Headless Chromium UI run: Chromium did not complete in this sandbox and produced no valid DOM evidence. IMPORTANT INTERPRETATION The existing automated suite passing does not override the confirmed defects above. The suite primarily proves build determinism, syntax, selected static contracts, migration mechanics and package structure. It does not currently exercise export/reset completeness, recent-PIN brute-force resistance, crash-safe sync idempotency, the complete offline settings surface, installed-PWA shortcut dispatch or chat-to-TTS prefilling. ============================================================ FINAL VERDICT ============================================================ NO-GO FOR PRODUCTION. Safe disposition: - Retain this build for controlled staging only. - Repair AUD-P0-001 through AUD-P0-003 before any production deployment. - Repair AUD-P1-004 through AUD-P1-006 before claiming full feature compliance. - Complete AUD-P0-008 with retained external evidence. - Rerun the complete build, automated, live-host, migration, device and accessibility suites. - Repeat a final source-to-documentation compliance audit and require a GO verdict.