Skip to content

Search is only available in production builds. Try building and previewing the site to test it out locally.

Four QA gates are wrong by construction, and the corpus's own completeness gate fails today

Источник: dev/fera-research/docs/oc1/014-qa-gates-wrong-by-construction.md (read-only; сгенерировано scripts/sync-docs.mjs)

Four QA gates are wrong by construction, and the corpus’s own completeness gate fails today

Section titled “Four QA gates are wrong by construction, and the corpus’s own completeness gate fails today”
  • kind: problem

  • area: ui / storage / modules / perf

  • evidence — QA.md is README.md:19’s “quality gates” doc; it defines “what ‘done’ and ‘safe to merge’ mean”. Four of its gates cannot be satisfied or test what they claim:

    1. “Zero dropped calls” contradicts the straggler kill. QA.md:129-132 — “A module can be hot-reloaded while a concurrent stream of calls is in flight, with zero dropped calls”. OQ.md:739-742 — “Host-function TTL: 1 minute, then kill — and the TTL timer starts only after the epoch has ended… kill stragglers at 60 s.” A deliberate bounded-drop policy exists; the gate demands an absolute. (And per my finding 009 the gate measures call accounting, not code liveness, which is the thing that actually breaks.)

    2. The signature gate has a hole in the load-bearing field. QA.md:149-151 — “signature covers the entire entry: a tampered-but-unsigned modification of any field (including host.max_version, fallback, depends_on) is rejected.” ADR.md:781-783 — “The manifest declares layer: shared | types | impl and depends_on; load order is derived topologically”; ADR.md:795-796 — “manifest layering and the dependency graph become load-bearing metadata (load-order correctness), not decoration.” layer is not in QA’s list, so an unsigned tamper of layer passes this gate. QA.md:157-159 separately tests that load order follows layer+depends_on, but asserts correctness, not rejection.

    3. The regex gate demands a threshold the decision replaced with a matrix. QA.md:69-72 — “measurably faster on a corpus above a defined size threshold (performance) — threshold TBD, see OQ.md”. OQ.md:288-304 — “The threshold is a decision matrix, not one number: (corpus size) × (narrowing quality)… No literals extractable from the pattern → always naive. Candidate count > K (hard cap, % of corpus) → naive… modeled as a typestate plan (RegexSearchPlan::Naive | TwoPhase{…})”. A pure size threshold cannot express either branch. Calibration was gated on OQ-006 being frozen (OQ.md:303); OQ-006 froze 2026-10-02 and no calibration is recorded — so the gate is now stale-blocked, not TBD. PREDICT.md:60 adds a third mechanism (a cost model with calibrated constants) layered on the typestate plan.

    4. The Lottie gate requires a benchmark that is no longer the deciding mechanism. QA.md:87-90 — “runs under criterion for both tlottie and velato backends before a default is chosen; the decision and its measured basis are recorded in ADR.md as an addendum once made.” OQ.md:96-101 — “Resolved (2026-09-30) → sequencing decision. tlottie is the default, implemented first. velato testing is deferred to near v2.” The gate demands a pre-default benchmark; the decision was made by sequencing. And the obligation to record it in ADR.md is un-runnable: grep -n -i "sticker\|tgs" ADR.md → 0 hits — no ADR mentions stickers at all. OQ.md:92-94 also states a precondition both docs omit: coverage must be confirmed before benchmarking (“a faster-but-visually- wrong renderer is not a real candidate”).

    • The encryption-tier gate cites the wrong ADR and is looser than it. QA.md:22-24 — “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”. ADR.md:633-635 (ADR-016) is the ADR that lacks the answer (“flagged as an open question”); ADR.md:684-686 (ADR-031) has it and requires more: “New persisted fields require an audit-table entry before merge”. So the gate (a) points at the empty ADR and (b) accepts a code comment where ADR-031 demands a table row. Two data classes currently have no tier at all: the sticker frame cache (absent from both lists, ADR.md:648-664; RESEARCH-OQ-003.md:132-133 argues it needs none but no doc says so) and Turso’s .ttso files (my finding 020).

    • Two of the corpus’s own completeness gates fail as written: QA.md:186-187 — “Every ADR has a corresponding section in RFC.md and/or TECHSPEC.md”. Verified mention counts (RFC / TECHSPEC / QA): ADR-006: 0/0/0 · ADR-024: 0/0/0 · ADR-025: 0/0/0 · ADR-026: 0/0/0 · ADR-027: 0/0/0 · ADR-030: 0/0/0 · ADR-031: 0/0/0 — 7 of 31. QA.md:190-192 — “Phase 1 … achievable using only crates/decisions already recorded here”: phase 1 (TECHSPEC.md:454-456) needs the protocol layer, whose only recorded decision is the ADR-026 fork with no crate in TECHSPEC.md:20’s layout, and step 3 specifies “minimal GPUI rendering” on the substrate ADR-028 rejected. QA.md:188-189 — “Every ‘open question’ flagged inline … is also present in OQ.md” is self-defeating: QA never cites an OQ number (QA.md:72, :87, :104 all say bare “OQ.md”), so a linter cannot run it, and 6 of the referenced entries are now Resolved while the referring text still says TBD.

    • One gate with no ADR behind it at all: QA.md:163-164 — the launcher replaces the core module “on all three target OSes”. The phrase “three target OSes” appears once in the entire corpus; no document states the target OS set.

    • Gates that cannot fail: QA.md:116-119 (frame budgets) — see my finding 008; QA.md:77-79 (Argon2id “with the rationale for the chosen cost”) — pure prose; QA.md:108-110 and :147-148 are self-declared review gates.

  • severity: mid

QA.md is the doc that decides whether the project is allowed to ship, and a meaningful fraction of its gates have drifted out of sync with the decisions they police. The common cause is the same one behind findings 001–003, 008 and 011: a decision moved, the gate did not. When the answer changes in an ADR and the gate keeps the old premise, the gate either becomes unsatisfiable (case 1, 3, 4) or silently stops testing the thing that matters (case 2, 5).

Case 2 is the one worth escalating. ADR-020 made layer and depends_on load-bearing for load-order correctness — a tampered layer reorders the module graph. The signature gate enumerates the fields it protects and omits one. The omission is harmless only while the ordering test catches it, and that test asserts the result is correct, not that the tampering is rejected — so an attacker who controls the manifest but not the signature gets a different failure mode (wrong load order, possibly a module loaded before its dependency) instead of a clean rejection. For a trust manifest whose entire purpose is authenticity, that is the wrong trade.

Case 5 is structural: QA’s cross-cutting “every persisted type has a tier” gate is the mechanism that keeps ADR-031’s audit table true, but it points at the ADR that has no answer, accepts a weaker form of evidence, and two persisted data classes have no entry at all.

Варианты решения (tradeoffs)

Section titled “Варианты решения (tradeoffs)”
  • (A) Rewrite the four gates against current decisions, add layer and the two missing tiers, and make the completeness gates mechanical. ~1 day of editing, no architecture work.
  • (B) Add a “gate ↔ decision” traceability column to QA.md (each gate names the ADR/OQ it enforces, plus the date of that decision), and a review rule: changing a decision obliges re-reading the gates citing it. Highest leverage against recurrence; slightly more process.
  • (C) Generate the mechanical gates as a script (crates/-adjacent tooling): ADR-coverage check, OQ-resolution-marker check, manifest field-coverage check. Catches 3 of the 6 defects automatically.
  • (D) Leave QA as prose intent. Reject.

(A) + (C), with (B) as the durable rule.

  1. Rewrite QA.md:129-132 to match OQ.md:739-742: zero dropped calls within the drain window, stragglers killed at 60 s, plus a new gate for the real hazard (no thread executing in a retired mapping, after many swap cycles) — see finding 009.
  2. Rewrite QA.md:69-72 to test the decision matrix, not a threshold: “for a corpus of N and a pattern with/without extractable literals, the chosen RegexSearchPlan branch matches the OQ-011 matrix, and the two-phase path is faster than naive on the branch where it is chosen” — that is falsifiable without calibration constants.
  3. Rewrite QA.md:87-90 to match OQ.md:96-101: tlottie is default; velato A/B is a near-v2 obligation; and add the coverage-first precondition (:92-94) as its own gate. Record the sticker format decision in a new ADR once OQ-003 closes (it currently has zero ADR coverage — grep confirms 0 sticker mentions in ADR.md).
  4. Add layer to QA.md:149-151’s field list, and add a gate that unsigned tampering of every manifest field is rejected (field enumeration generated from the schema, not hand-maintained).
  5. Fix QA.md:22-24 to cite ADR-031, require an audit-table row (not a code comment), and add the two missing entries (sticker cache, .ttso files) — the first needs a tier decision (finding 013), the second needs an ADR (finding 020).
  6. Make the completeness gates runnable (QA.md:186-192): a script that fails when an ADR has no downstream mention, when a doc says “TBD/open question” about an OQ marked Resolved, or when a referenced OQ-NNN number does not exist. All three checks are ~15 lines and would have caught findings 001, 002, 003, 008 and 011 on their own.
  7. State the target OS set in one place and point QA.md:163-164 at it (see my finding on the platform matrix).