QA — Quality Gates & Acceptance Criteria
Источник:
dev/fera/project-design/context/QA.md(read-only; сгенерировано scripts/sync-docs.mjs)
QA — Quality Gates & Acceptance Criteria
Section titled “QA — Quality Gates & Acceptance Criteria”Companion docs: ADR.md, RFC.md, TECHSPEC.md, OQ.md.
This document defines what “done” and “safe to merge” mean for each subsystem. Intended for both future-self review and AI agent self-checks before considering a workstream complete.
Cross-cutting gates (apply to every module/PR)
Section titled “Cross-cutting gates (apply to every module/PR)”- No code path in the UI/domain layer reads directly from the network; all reads go through local storage (ADR-010). Verified by: a test that runs the app with network access blocked and confirms cached history still renders.
- Any new trait crossing a
nylon-ringmodule boundary at high/unbounded frequency has a batch-shaped API and, if it’s a read-heavy path, a host-side cache in front of it (ADR-005). Verified by: boundary-crossing count under the fixed benchmark scene does not scale linearly with UI-visible item count. - Any new persisted data type has an explicit answer to “which encryption tier” (metadata vs content, ADR-016) recorded in code comments or the relevant ADR addendum — not left implicit.
- Any new multi-state data (status, connection state, etc.) is modeled as an enum or typestate, not a combination of independent booleans/Options (ADR-015), unless a documented reason exists for the exception.
- Contract test suite exists and passes for any trait with more than one implementation (ADR-014) before the second implementation merges.
- No inter-actor message is serialized (ADR-007) — raw types only, unless the message is actually crossing a module/process/network boundary.
Protocol layer
Section titled “Protocol layer”-
send_messageand other authenticated operations are not callable onProtocolClient<Unauthenticated>— verified at compile time, not by a runtime check (typestate, ADR-015). - Reconnect backoff includes jitter; a test simulates N=100 simultaneous reconnect events and confirms requests are not synchronized to a single instant.
- Health-check completes faster than the MTProto request timeout in the simulated-network-loss test case.
- Session data is only ever written/read through the Session/Auth actor — no ad-hoc file access to session data elsewhere in the codebase.
Domain / Actor layer
Section titled “Domain / Actor layer”- Every actor has at least one
ractorharness test covering: normal message handling, and behavior under a simulated crash/restart. -
ChatActorReadStatetransitions are covered by tests for all three states (Synced,Pending,Offline) and their valid transitions; invalid transitions do not compile or are rejected. - Outbox entries persist across a simulated app restart while in
Pendingstate, and are retried on reconnect — covered by an integration test that kills and restarts the process mid-flight.
Storage & Search
Section titled “Storage & Search”-
StorageEnginecontract test suite passes againstsanakirja. - Search results from the milli-fork-on-
sanakirjaare verified equivalent (same ranking, same result set) to upstream milli-on-LMDB for a fixed reference corpus, before the fork is trusted for anything beyond experimentation. - Two-phase regex search returns identical results to a naive full-scan
PCRE2 baseline on the reference corpus (correctness), and is
measurably faster on a corpus above a defined size threshold
(performance) — threshold TBD, see
OQ.md. -
Encrypted<T>values are never observed in plaintext outside the decrypt boundary — enforced by the phantom-type design; additionally spot-checked by a test that inspects raw on-disk bytes for known plaintext content and asserts absence. - Argon2id parameters (memory/iterations) are documented with the rationale for the chosen cost, not left at library defaults without review.
Media pipeline
Section titled “Media pipeline”-
MediaDecodercontract test suite passes against the pure-Rust baseline decoder. - Sticker mmap cache: a test confirms that re-displaying a previously cached animated sticker does not re-invoke the decoder.
- Fixed benchmark scene (parameters per
OQ.md) runs undercriterionfor bothtlottieandvelatobackends before a default is chosen; the decision and its measured basis are recorded inADR.mdas an addendum once made. - Optional FFmpeg module: a test confirms the app functions correctly (falls back to “format unsupported” UI, not a crash) when the module is absent.
- Predictor-issued prefetch requests are verified, under load, to never
delay an explicit user-driven fetch already queued or arriving
concurrently (priority ordering test at the
FetchActorqueue).
-
update: &mut State, Msg -> Vec<Effect>has unit test coverage for everyMsgvariant, runnable with no GPUI context. - Pixel-diff regression suite exists per Style module, run against
reference screenshots from the corresponding official OS client, with
a documented acceptable diff threshold (TBD, see
OQ.md). - Swapping the Engine module (even to a stub/no-op implementation for
testing) does not require changes to
ui-elmor domain-layer code — verified by actually performing such a swap in a test build. - Feature-module local state (e.g. scroll physics) is confirmed to
not appear in the central
Statetype — code review gate, not automatable directly. - Native axis absent or failed to load: every slot falls back to an own-renderer equivalent and the app runs (no crash) — explicit test per platform that has a Native module.
- Native text-input slot passes a basic IME interaction test on each platform where the slot is implemented.
- Frame-rate budgets are independent: under the fixed benchmark scene, each path (UI ~24 fps, message updates ~45 fps, stickers 60–75 fps) meets its own budget, and a saturated sticker path does not degrade UI-chrome frame timing.
Module system / trust
Section titled “Module system / trust”- Loading a module with a mismatched hash produces a non-blocking UI warning and the module still loads (ADR-004) — explicit test for both halves of this behavior (warning shown AND load proceeds).
- Loading a module with an incompatible declared version falls back to the built-in default implementation (where one exists) rather than crashing the app — explicit test.
- A module can be hot-reloaded while a concurrent stream of calls is
in flight, with zero dropped calls (relies on
nylon-ring’s draining guarantee — write a test that actually exercises this, not just an assumption that the library provides it). - The trust manifest itself is signature-verified before any of its contents (including the update endpoint) are trusted — a test confirms a tampered-but-unsigned-modification manifest is rejected.
- Adding/removing a trusted key via the in-app UI persists correctly and takes effect without requiring an app restart.
- Translator rights only narrow: a translator attempting to widen the original plugin’s capabilities yields rights = original ∩ user-granted (no escalation path exists).
- Level 1 translator chains are statically validated at load: cyclic and over-depth chains are rejected before any plugin code runs.
- Level 2 behavioral translation is fail-closed: without an explicit per-capability user opt-in, flagged functions are not behaviorally translated (the plugin runs untranslated or is refused — never silently behavioral).
- The plugin-rights UI shows the original plugin’s requested rights, not translator-mapped ones — code review gate.
- Manifest signature covers the entire entry: a
tampered-but-unsigned modification of any field (including
host.max_version,fallback,depends_on) is rejected. - Downgrade protection: a module below the manifest’s minimum allowed version is refused even with a valid signature; an expired manifest is surfaced, not silently honored.
- Multi-variant artifacts (
avx2/ baseline) select correctly by CPU detect at launch. - Struct-layer load order follows the manifest dependency graph
(
layer+depends_on): a module whose dependencies are not yet loaded is never initialized (topological-order test).
Launcher / self-update
Section titled “Launcher / self-update”- Launcher successfully replaces an outdated core module on a clean restart, on all three target OSes.
- Update process is non-blocking: the app remains usable (on the previous core version) while a new core module downloads in the background; the user is prompted to restart, not forced.
- Explicit test: no plaintext, user-editable file controls the update endpoint — confirm the endpoint is only ever read from the signature-verified manifest.
Voice/video calls
Section titled “Voice/video calls”-
rstgcallsbindings are isolated behind their own module boundary; a build without the calls module compiles and runs the rest of the client normally (calls feature is not load-bearing for anything else).
Definition of “architecture skeleton complete” (this document’s own
Section titled “Definition of “architecture skeleton complete” (this document’s own”scope milestone)
This QA document, together with ADR.md/RFC.md/TECHSPEC.md, is
considered a complete architectural baseline once:
- Every ADR has a corresponding section in
RFC.mdand/orTECHSPEC.md(no decision left undocumented downstream). - Every “open question” flagged inline across these docs is also
present in
OQ.md(no orphaned TODOs). - Phase 1 of the sequencing plan (
TECHSPEC.md§10) is achievable using only crates/decisions already recorded here — i.e., no hidden unresolved dependency blocks even the first implementation step.