feat(glitchtip): C1 — emit() infrastructure + DEV-trigger skeleton#12
feat(glitchtip): C1 — emit() infrastructure + DEV-trigger skeleton#12MANFahrer-GF wants to merge 2 commits into
Conversation
Spec: docs/spec/v0.9.3-glitchtip-emit-sites.md LE6 Slice C1 (SPEC ACCEPTED R2).
Erste der 9 C-Slices (C0=Spec, C1=Infra, C2..C8=Sites, C9=Release).
C1 baut die komplette emit()-Pipeline + DEV-Test-Infrastruktur, OHNE
einen einzigen echten Site-Aufruf in lib.rs. Damit ist die Akzeptanz-
Verifikation in C2..C8 (Live-Trigger gegen tip.kant.ovh) von Anfang
an moeglich (R2-P1-2-Fix: DEV-Panel kam in R1 zu spät).
Was kommt rein:
1. `sentry_init.rs` (+ ~380 Zeilen) — die EINZIGE öffentliche Schnitt-
stelle für Custom-Events ab v0.9.3:
- `pub(crate) mod tag_keys` — 13 Compile-Time-Konstanten 1:1 mit
`allowed_tag_keys()`. Aufrufer müssen `tag_keys::ERROR_KIND` etc.
statt roh-Strings nutzen → keine versehentlichen Underscore-vs-Dot-
Drifts (Spec R0-P0). #[allow(dead_code)] solange C2..C8 noch nicht
ergänzt haben.
- `RATE_LIMIT_DEFAULTS` — 9 Sites mit Token-Bucket-Limits aus Spec LE2
(1/min für update_check_failed, 10/min für hard_landing, etc.). Plus
DEFAULT_LIMIT (10/60s) für unbekannte error_codes.
- `try_consume_token(error_code, now: Instant)` — Clock-Injection
macht Tests deterministisch ohne sleep(60s) (Spec R1-P2-2).
- `pub(crate) fn emit(level, error_code, message, tags)` mit
4-Gate-Sequenz (Spec R2-P1-1):
Gate 1: Consent
Gate 2: Client bound + is_enabled() — kein Token-Verbrauch
wenn DSN fehlt oder nach Opt-Out
Gate 3: Rate-Limit
Gate 4: Send via sentry::with_scope mit explizitem
`scope.set_fingerprint(Some(["aeroacars", error_code].as_slice()))`
→ Gruppierungs-Vertrag „1 Issue pro error_code" garantiert
(Spec R1-P1-3 + R2-P1-3).
- Die 2 alten dead-code `capture_message`/`capture_error`-Wrapper
wurden entfernt — die fehlende Verwendung war Teil des „GlitchTip
ist Theorie"-Problems das v0.9.3 löst.
2. `sentry_init.rs#[cfg(test)] mod c1_rate_limit_tests` — 7 Schicht-1-
Mechanik-Tests:
- token_bucket_lets_first_n_through
- token_bucket_drops_after_limit
- token_bucket_resets_after_window_without_sleep (Clock-Injection)
- known_error_code_uses_specific_limit (sim_disconnect = max 1)
- unknown_error_code_gets_default_limit
- emit_returns_false_when_no_client_bound (R2-P1-1-Regression-Guard)
- consent_off_returns_false_without_consuming_token
Alle nutzen `TEST_LOCK`-Mutex für Serialisierung (statisches BUCKETS
geteilt zwischen parallel laufenden Tests sonst flaky).
3. `sentry_init.rs#[cfg(test)] mod c1_mock_transport_smoke` — 1 Schicht-
2-Smoke-Test mit MockTransport-Helper:
- smoke_emit_passes_all_gates_and_lands_in_mock_transport
Beweist dass ein Envelope durch alle 4 Gates kommt + im MockTransport
landet + die Tags die Allowlist (redact_event) überleben. Plus
Fingerprint-Vertrag ["aeroacars", error_code] verifiziert.
4. `lib.rs` — `#[cfg(debug_assertions)] fn dev_trigger_glitchtip_event(
site: String)` Tauri-Command mit Catch-All-Match (Err(unknown_site)).
Match-Arms werden in C2..C8 pro Site ergänzt. Plus cfg-gated
Registration im invoke_handler-Set. Production-Build hat den Command
NICHT (doppelter Schutz: cfg + import.meta.env.DEV).
5. `SettingsPanel.tsx` — `<GlitchTipDevTriggerPanel />` nur unter
`import.meta.env.DEV`. 9 Buttons in Grid-Layout (S1..S9), pro Button
wird der letzte Status als `<small>` daneben angezeigt (R2-Frage-10:
unknown-site-Errors sauber zeigen statt crashen). Tree-shaked in
Production-Bundle.
Tests + Verify:
- `cargo check --all-targets` ✓
- `cargo test -p aeroacars-app --lib sentry_init` → 8/8 ✓
- `tsc -b` ✓
- `npm test` → 56/56 ✓ (47 SinkrateForensik + 9 UpdateButton-Guards)
KEIN echter Site-Aufruf in lib.rs in C1 — emit() wird in Production-
Code-Pfaden erst ab C2 (S1+S2: PIREP-Submit-Fail) gerufen. #[allow(
dead_code)] an emit() bleibt — schadet nicht.
KEINE Wire-Format-Änderung. KEINE Privacy-Allowlist-Erweiterung
(LE7). KEINE neue NPM-Dependency.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 646695c63f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| static SMOKE_LOCK: StdMutex<()> = StdMutex::new(()); | ||
|
|
||
| #[test] | ||
| fn smoke_emit_passes_all_gates_and_lands_in_mock_transport() { | ||
| // Test-Isolation: SMOKE_LOCK + frischer Bucket + frischer Hub. | ||
| let _g = SMOKE_LOCK.lock().unwrap_or_else(|p| p.into_inner()); | ||
| BUCKETS |
There was a problem hiding this comment.
Serialize all sentry_init tests with one shared lock
The smoke test uses its own SMOKE_LOCK, but c1_rate_limit_tests uses a different TEST_LOCK while both modules mutate the same globals (BUCKETS, CONSENT, and hub binding). Because Rust tests run in parallel by default, these tests can interleave and clear/flip shared state underneath each other, causing nondeterministic failures (for example, token-bucket assertions racing with BUCKETS.clear()). Use a single lock shared across both test modules (or move all shared-state tests into one serialized module) to avoid flakiness.
Useful? React with 👍 / 👎.
Thomas-Code-QS-R1 fand P2 (kein P0/P1): die zwei lokalen Test-Locks
(TEST_LOCK in c1_rate_limit_tests, SMOKE_LOCK in c1_mock_transport_smoke)
schuetzten nur INNERHALB ihres jeweiligen Moduls. Beide Module teilen
aber drei globale Statics: CONSENT (Atomic), BUCKETS (Static-Mutex),
und den Sentry-Hub. Ein Test aus Modul A konnte parallel zu einem
Test aus Modul B laufen und CONSENT umkippen → flaky CI moeglich.
Fix:
1. Shared file-level `SENTRY_TEST_LOCK` (#[cfg(test)]) — serialisiert
ALLE Sentry-Init-Tests modul-uebergreifend.
2. `SentryTestEnv`-Drop-Guard mit Pre-Clean + Post-Clean:
- `lock()`: holt den shared lock + setzt CONSENT=false +
bind_client(None) + leert BUCKETS-Map. Recover-from-poisoned-Mutex
damit ein Panic-Test nicht alle folgenden Sentry-Tests blockiert.
- `drop()`: gleicher Cleanup wieder. Naechster Test bekommt
garantiert sauberen Default-Zustand.
3. Alle 7 Tests in c1_rate_limit_tests + den 1 Smoke-Test in
c1_mock_transport_smoke umgestellt von `let _g = LOCAL_LOCK.lock()...
; fresh_buckets();` auf `let _env = super::SentryTestEnv::lock();`.
8 Stellen ersetzt.
4. Lokale `TEST_LOCK` + `SMOKE_LOCK` Statics + `fresh_buckets()`-Helper
entfernt — nicht mehr noetig.
5. Manuelles `sentry::Hub::current().bind_client(None)`-Cleanup am Ende
vom Smoke-Test entfernt (war nicht genug — resettete nur Hub aber
nicht CONSENT/BUCKETS) → Drop-Guard macht das jetzt vollstaendig.
Tests:
- cargo test -p aeroacars-app --lib sentry_init → 8/8 ✓
- cargo check --all-targets ✓
Macht den Pfad sauber bevor C2 (S1+S2 PIREP-Sites) kommt — die werden
weitere MockTransport-Tests in c1_mock_transport_smoke ergaenzen, alle
nutzen jetzt automatisch die saubere Test-Isolation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Erste Slice der v0.9.3 GlitchTip-Emit-Sites-Implementierung. Spec: docs/spec/v0.9.3-glitchtip-emit-sites.md (SPEC ACCEPTED R2). C1 = Plumbing ohne echte Site-Aufrufe. 8/8 sentry-Tests gruen, 56/56 vitest gruen, cargo check sauber. Detail siehe Commit-Message.