feat(mobile+api): KI-Foundation Phase 1 RC2–RC18 — semantic search wireup #445

Merged
pm-bot merged 49 commits from wireup/ai-foundation-phase1 into main 2026-06-08 19:12:26 +02:00
Collaborator

Summary

49 Commits über RC2..RC18 — die on-device semantic-search-Foundation kommt produktreif rüber:

  • Embedding-Pipeline (RC2–RC16): Tokenizer-Bypass, Xenova-Mirror, UA-Header, 3-Input-ONNX-Signatur → end-to-end auf Device validiert (4 Quellen → 1 Chunks extrahiert · 1 indexed · 0 Fehler · embedder: ready in Maestro phase1-indexing-smoke).
  • Mutation-Observer-Wireup (RC17): useCreateList/Update/Delete rufen onSourceChange-Callback im onSuccess. Mobile-Bootstrap fängt das ab und publisht in Shopping-List- und Note-DataSources — Index füllt sich inkrementell.
  • Test-Seam für die Mutation-Kette (RC18): 5 deterministische Unit-Tests in apps/mobile/__tests__/mutation-observer.test.tsx lösen den fragilen Phase2-Maestro-Flow ab. Code-Level-Verkabelung ist damit reproduzierbar geprüft.
  • X-Dev-User Bypass (RC18): JwtAuthGuard akzeptiert X-Dev-User: <sub> nur wenn NODE_ENV !== 'production'. Mobile sendet den Header automatisch wenn auth === null. Stellt die in den Code-Kommentaren dokumentierte Dev-Passthrough-Semantik wieder her — der no-Keycloak-Mobile-Build kann jetzt echte Mutationen gegen dev.api absetzen.

Why now

Die Phase2-Validierung der Mutation→Publish→Indexer-Kette war auf dev.api durch 401 blockiert (Backend hat Keycloak, Mobile-Dev hat keinen Token). Mit X-Dev-User + Test-Seam ist beide Lücken geschlossen.

Risk

  • API: Header-Bypass ist NODE_ENV !== 'production'-gated, Prod-Deploy bleibt unverändert. 4 Guard-Tests inkl. Prod-Bypass-Blocker.
  • HttpClient-defaultHeaders: Authorization aus getToken überschreibt Defaults — bestehende Auth-Flows nicht betroffen.
  • Diagnostik-Counter (bumpA/B/C/D, ctx-Probe, mutation-trace.ts) entfernt — −67 Zeilen Diagnose-Overhead.

Test plan

  • Unit-Tests grün (448 API + 198 Mobile + 6 api-client = 652 Tests)
  • Typecheck + Lint sauber
  • Maestro phase1-indexing-smoke auf Device (rc16 done)
  • Nach Merge: deploy-dev triggert → dev.api hat X-Dev-User-Bypass → rc18 APK auf rpi5 installieren → phase2-mutation-observer-smoke (sollte jetzt durchlaufen)

Refs #122 KI-Assistent Epic

## Summary 49 Commits über RC2..RC18 — die on-device semantic-search-Foundation kommt produktreif rüber: - **Embedding-Pipeline** (RC2–RC16): Tokenizer-Bypass, Xenova-Mirror, UA-Header, 3-Input-ONNX-Signatur → end-to-end auf Device validiert (`4 Quellen → 1 Chunks extrahiert · 1 indexed · 0 Fehler · embedder: ready` in Maestro phase1-indexing-smoke). - **Mutation-Observer-Wireup** (RC17): `useCreateList/Update/Delete` rufen `onSourceChange`-Callback im `onSuccess`. Mobile-Bootstrap fängt das ab und publisht in Shopping-List- und Note-DataSources — Index füllt sich inkrementell. - **Test-Seam für die Mutation-Kette** (RC18): 5 deterministische Unit-Tests in `apps/mobile/__tests__/mutation-observer.test.tsx` lösen den fragilen Phase2-Maestro-Flow ab. Code-Level-Verkabelung ist damit reproduzierbar geprüft. - **X-Dev-User Bypass** (RC18): `JwtAuthGuard` akzeptiert `X-Dev-User: <sub>` nur wenn `NODE_ENV !== 'production'`. Mobile sendet den Header automatisch wenn `auth === null`. Stellt die in den Code-Kommentaren dokumentierte Dev-Passthrough-Semantik wieder her — der no-Keycloak-Mobile-Build kann jetzt echte Mutationen gegen dev.api absetzen. ## Why now Die Phase2-Validierung der Mutation→Publish→Indexer-Kette war auf dev.api durch 401 blockiert (Backend hat Keycloak, Mobile-Dev hat keinen Token). Mit X-Dev-User + Test-Seam ist beide Lücken geschlossen. ## Risk - API: Header-Bypass ist `NODE_ENV !== 'production'`-gated, Prod-Deploy bleibt unverändert. 4 Guard-Tests inkl. Prod-Bypass-Blocker. - HttpClient-defaultHeaders: Authorization aus getToken überschreibt Defaults — bestehende Auth-Flows nicht betroffen. - Diagnostik-Counter (bumpA/B/C/D, ctx-Probe, mutation-trace.ts) entfernt — −67 Zeilen Diagnose-Overhead. ## Test plan - [x] Unit-Tests grün (448 API + 198 Mobile + 6 api-client = 652 Tests) - [x] Typecheck + Lint sauber - [x] Maestro phase1-indexing-smoke auf Device (rc16 done) - [ ] Nach Merge: `deploy-dev` triggert → dev.api hat X-Dev-User-Bypass → rc18 APK auf rpi5 installieren → phase2-mutation-observer-smoke (sollte jetzt durchlaufen) Refs #122 KI-Assistent Epic
chore(release): v0.6.6-rc1 — KI-Foundation Phase 1 device-test build
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
1728f15c88
Pre-release tag for on-device verification of PR #444 / epic #122. Roadmap
entry added so the `gitea-release` Drone step can resolve notes. APK lands
in F-Droid repo + Gitea release; install on a physical Android device and
walk the smoke-test checklist in apps/mobile/docs/ai-foundation-verification.md.

Not for general release — `status: "next"`, will flip to `"done"` only if
the device pass succeeds and we cut a regular v0.6.6.
Production code never calls dataSource.publish() yet, so the index sits
empty on a real device. This temporary yellow bar on the Suche route
exposes ports.debugIndexAll(), which fans out 'created' events for every
shopping list and note the user owns. Lets us validate the embed +
sqlite-vec + search path end-to-end before the api-client mutation-
observer wireup lands.

Refs #122
chore(release): v0.6.6-rc2
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
c9dbccc69c
Synchronizes all 11 workspace packages and ships the Suche debug-index
button so we can validate the Phase-1 stack on a real device while the
mutation-observer wireup is still pending.

Refs #122
chore(e2e): add maestro-pi wrapper + parameterize appId in smoke flows
All checks were successful
continuous-integration/drone/push Build is passing
e724473854
scripts/maestro-pi.sh rsyncs apps/mobile/.maestro/ to the rpi5 host and
runs `maestro test` against the USB-tethered phone — gives us a fast e2e
loop during development without going through the Drone Android-emulator
pipeline. The wrapper auto-detects which mrrmlab flavor is installed
(release `de.mrrm.mrrmlab` vs dev `de.mrrm.mrrmlab.dev`) and passes it
through as the APP_ID env var.

The three smoke flows now reference `appId: ${APP_ID}` so they work
against either flavor; the existing Drone maestro-test step pins it to
the dev flavor since build-debug-apk produces that one.
The release-build APK strips Hermes console.log, so silent failures in
the embedder / vector-store pipeline have no visibility on a real
device. The debug bar now subscribes to ports.indexing.onProgress and
shows running totals: chunks indexed, error count, last error message
(source-type + reason). Lets us diagnose why search returns no hits
without needing a debuggable variant.

Refs #122
chore(release): v0.6.6-rc3
Some checks reported errors
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build was killed
d1d4ced7bb
chore(release): retrigger v0.6.6-rc3 build
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
8cbea0b5f1
Drone build #951 hung at bundle-mobile (killed after ~1h timeout).
Re-tagging the same diagnostics commit onto a fresh empty commit to
retrigger the publish-apk pipeline on a fresh runner.

Refs #122
After rc3 reported 0 chunks indexed AND 0 errors on a real device, the
prime suspect is the embedder silently hanging on its ~120 MB ONNX
model download (or failing to load). Routes the embeddingService
lifecycle (idle / downloading N% / loading / ready / error) through a
new EmbedderStatusPort on SearchPorts so the debug bar can render
exactly where things sit. Keeps the lazy native-load boundary intact
(the route doesn't import embeddingService directly).

Refs #122
chore(release): v0.6.6-rc4
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
9468ce3314
rc4 showed 'embedder: idle' on-device after the debug button — meaning the
embed() call was never reached. Most likely cause: extractChunks returning
[] (empty shopping list, wrong list.type, empty note body) → for-loop in
IndexingService.indexSource doesn't iterate → embedder never starts.

Two diagnostics added:

- IndexingService emits 'chunks-extracted' after every extractChunks call,
  including count=0. Debug bar shows 'X Quellen → Y Chunks extrahiert' so
  '2 → 0' is distinguishable from 'still processing'.

- EmbeddingProgress now carries bytesWritten + bytesTotal alongside the
  percentage. Debug bar renders 'downloading 23.4 / 120.0 MB', making
  '0/0 MB' (no Content-Length), '0/120 MB' (request accepted, no bytes)
  and '12/120 MB' (slow) distinguishable — all three rendered as '0%'
  in rc4.

Refs #122.
chore(release): v0.6.6-rc5
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
b6cc6f7d88
Folge-RC zu rc4 mit chunks-extracted + bytes diagnostics.
Two changes that together let future RCs be self-verified.

embedding-service: 'loading' was hiding two sequential steps —
AutoTokenizer.from_pretrained (transformers.js fetches tokenizer.json from
HuggingFace, no caching because useFSCache=false) and InferenceSession.create
(onnxruntime compiles the 120 MB ONNX). rc5 stayed in 'loading' on the
phone, which left us blind to which of the two is the bottleneck. Now:
'loading-tokenizer' → 'loading-session' → 'ready', surfaced in the debug
bar as 'embedder: loading tokenizer' / 'loading session'.

apps/mobile/.maestro/phase1-indexing-smoke.yaml: drives the temp debug
button on the Suche route, waits up to 3 min for the embedder to reach
'ready'. NO clearState so the cached ONNX stays on disk between runs
(30s warm-start instead of 3-min cold-start). Runs via the existing
scripts/maestro-pi.sh — `scripts/maestro-pi.sh phase1-indexing-smoke`.

Refs #122.
chore(release): v0.6.6-rc6
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
7cd934d914
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
rc6 confirmed via the Maestro phase1 flow that the embedder hangs inside
AutoTokenizer.from_pretrained — neither error nor progress, just an
indefinite stall in transformers.js on Hermes. To tell a network outage
from a library-internal hang, fetch tokenizer_config.json directly first
and surface the outcome as a new 'loading-tokenizer-probe' status.

A probe failure now throws with the HTTP status code rather than letting
the lib hang silently.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
chore(release): v0.6.6-rc7
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
761034195b
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
rc7 confirmed via the Maestro phase1 flow that Hermes' JS fetch() hangs
indefinitely against huggingface.co. createDownloadResumable (native,
expo-file-system) works fine against the same host — it already pulls
the 120 MB ONNX file successfully.

Pull all four tokenizer JSONs the same way, and install a globalThis
.fetch wrapper that resolves every huggingface.co/<repo>/... URL by
reading the corresponding file from the on-disk cache. transformers.js
keeps thinking it's fetching from HF; the bytes come from disk.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
chore(release): v0.6.6-rc8
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
1600bcca8f
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
rc8 stalled on the new ensureModelFilesDownloaded tokenizer loop with no
visible progress and no error. Emit per-file status with filename and
bytes flowing, race each download against a 45s timeout, and send a
User-Agent header in case HF gates on it.

This is diagnostic, not yet a fix — but it tells us which of the four
tokenizer JSONs is hanging and whether any bytes ever arrive.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
chore(release): v0.6.6-rc9
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
5060112365
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
rc9 confirmed native tokenizer downloads work, but AutoTokenizer.from_pretrained
hangs again at the same point as rc6. Hypothesis: transformers.js hits HF URLs
that don't match HF_BASE (e.g. /api/models/<repo>/tree/main) before the
/resolve/main/* file fetches — those pass through to origFetch and hang on
Hermes.

rc10 catches every huggingface.co URL: known /resolve/main/<file> reads from
disk, anything else returns a synthetic 404 so transformers.js falls through
cleanly instead of waiting on a dead socket. Per-fetch status emit makes the
hanging URL attributable in the debug bar ("fetch#3 api/models/...").

Refs #122.
chore(release): v0.6.6-rc10
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/tag Build is passing
8ddc339ceb
Includes the maestro phase1-indexing-smoke drawer-open step that was used
to run rc7-rc9 device validations but never committed.

Refs #122.
rc10 device test confirmed the fetch interceptor was never invoked — the debug
bar showed 'embedder: loading tokenizer' without the per-call 'fetch#N <path>'
suffix the rc10 wrapper would have emitted on any HF request. The runtime-
patch approach (globalThis.fetch = interceptor inside initialize()) was likely
defeated by @huggingface/transformers capturing the original fetch at module
evaluation.

rc11 moves the interceptor into its own module 'hf-fetch-interceptor.ts' with
a top-level side effect, and imports it BEFORE '@huggingface/transformers' so
the patched fetch is the one transformers closes over. The interceptor exposes
setHfLocalPath() for the downloader to register on-disk paths and onHfFetch()
so embedding-service can surface each invocation in the debug bar.

A '[awaiting from_pretrained]' marker is emitted right before the await on
AutoTokenizer.from_pretrained. The on-device behaviour is now diagnostic:

  - debug bar updates to 'fetch#1 ...' → transformers IS hitting our wrapper;
    inspect which URL is the hang
  - bar stays at '[awaiting from_pretrained]' → from_pretrained hangs
    synchronously before its first fetch and the pivot off @huggingface/
    transformers becomes unavoidable

Tests split: hf-fetch-interceptor.spec.ts covers the wrapper in isolation
(disk read, query-string strip, 404 for unknown HF paths, passthrough,
listener events). embedding-service.spec.ts loses the 3 interceptor tests
(moved) and stops touching globalThis.fetch in beforeEach.

Refs #122.
chore(release): v0.6.6-rc11
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/tag Build is failing
e10a0a1656
fix(embedding): drop unused clearHfLocalPaths import + bump to rc12
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
0709229a17
rc11 tag build failed at lint — clearHfLocalPaths is only used in the spec,
not the production module. Same code path otherwise.

Refs #122.
RC12-Device-Test belegte: AutoTokenizer.from_pretrained hängt synchron
auf Hermes BEVOR überhaupt ein fetch ausgeht. Der Fetch-Interceptor
konnte deshalb nie greifen.

Bypass: PreTrainedTokenizer (Constructor) wird direkt mit den beiden
auf Disk vorhandenen JSONs (tokenizer.json + tokenizer_config.json)
instanziiert. Damit entfällt der gesamte from_pretrained-Code-Path
und auch der hf-fetch-interceptor.

- hf-fetch-interceptor + spec entfernt
- AutoTokenizer-Import entfernt, PreTrainedTokenizer importiert
- TOKENIZER_FILES auf zwei Files reduziert (rc8-Leftovers raus)
- ensureModelFilesDownloaded gibt ModelFiles-Struct mit beiden URIs zurück
- initialize() liest beide JSONs synchron, konstruiert Tokenizer direkt
- Spec: tokenizerFromPretrained-Mock durch PreTrainedTokenizer-Constructor-
  Mock ersetzt; Lifecycle-Test blockt jetzt auf readAsStringAsync

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
chore(release): v0.6.6-rc13
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
336632c269
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
RC13 device-finding: tokenizer hang gone (PreTrainedTokenizer-Bypass
funktioniert), aber ORT failed mit "Protobuf parsing failed" beim Laden
der ONNX-Datei. Ursache: `intfloat/multilingual-e5-small` hostet
`onnx/model_quantized.onnx` NICHT (404). expo-file-system speichert
stillschweigend den 15-Byte-Error-Body als `model_quantized.onnx`, ORT
versucht das als Protobuf zu parsen und stirbt.

Fix: Wechsel auf `Xenova/multilingual-e5-small` — den
transformers.js-Convention-Mirror, der die Standard-Pfade tatsächlich
hostet (model_quantized.onnx 118 MB + tokenizer.json + tokenizer_config.json
am Root). Cache-Verzeichnis auf `embedding-e5-small-xenova` umbenannt,
damit die kaputte rc13-Datei nicht weiterverwendet wird.

Spec-Test sperrt die URLs gegen den Xenova-Mirror, damit ein Repo-Swap
nicht erneut still regressiert.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
chore(release): v0.6.6-rc14
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
f62f945d16
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
RC14 device test: tokenizer-bypass funktioniert, URL ist korrekt
(Xenova-Mirror), aber ONNX-Download hängt bei 0 Bytes gegen
cas-bridge.xethub.hf.co. Tokenizer-Downloads (gleiche CDN, gleicher
Redirect-Pfad) gehen durch — einziger Unterschied: tokenizer setzt einen
User-Agent-Header, das Model-Request nicht.

Fix:
- Gleicher UA-String für den ONNX-Request (mrrmlab/0.6.6 expo-file-system).
- Post-Download-Sanity-Check: <1 MB → File löschen + actionable Error mit
  URL. Wäre die saubere Fehlermeldung in rc13 gewesen (15-Byte-404-Body
  wurde als ONNX gespeichert → "Protobuf parsing failed" weiter unten).
- Tests: UA-Header gesperrt + Size-Check throw-Verhalten.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
chore(release): v0.6.6-rc15
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2b269a7d1f
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
RC15 device test war ein Meilenstein: embedder erreicht "ready",
Maestro phase1-indexing-smoke geht durch bis zur Assertion. ABER:
session.run() failed mit "input 'token_type_ids' is missing in 'feeds'"
beim ersten echten Embedding.

Ursache: Der Xenova-ONNX-Export behält die Standard-BERT-3-Input-Graph-
Signatur (input_ids + attention_mask + token_type_ids). E5/XLM-RoBERTa
nutzt zwar in der Praxis keine Segment-IDs, aber der Graph hat den Input.

Fix: Zeros-BigInt64-Tensor in derselben Shape wie input_ids als
token_type_ids. RoBERTa-Stil — ein Segment, alle 0.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
chore(release): v0.6.6-rc16
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/tag Build is passing
a6e8522116
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
docs(roadmap): rc16 done — e2e indexing pipeline validated on device
All checks were successful
continuous-integration/drone/push Build is passing
a42a946460
Maestro phase1-indexing-smoke shows "4 Quellen → 1 Chunks extrahiert ·
1 indexed · 0 Fehler · embedder: ready". The rc13→rc16 fix chain is
fully validated end-to-end: tokenizer bypass + Xenova mirror + UA header
+ 3-input ONNX signature.

Follow-up: 3/4 sources produced 0 chunks — chunk extraction for empty
lists/notes needs investigation in a separate issue.

Refs #122
feat(feature-lists): onSourceChange prop wires mutations to indexing (#85)
All checks were successful
continuous-integration/drone/push Build is passing
784a297831
Adds an optional `onSourceChange(event)` prop to `<ListsApiClientProvider>`.
Every mutation hook in feature-lists (`useCreateList`, `useUpdateList`,
`useDeleteList`, `useAddListItem`, `useUpdateListItem`, `useDeleteListItem`)
now invokes the callback inside `onSuccess` with `{ sourceType: 'list',
sourceId, kind }`.

The mobile bootstrap supplies a callback that fans the event out to the
shopping-list and note data sources — extractChunks gates by `list.type`,
so publishing to the wrong source is a cheap no-op. Data sources are
constructed eagerly via a new `data-source-bundle` singleton so the
mutation path doesn't require the search route to have rendered first
(native vector store + embedder remain lazy).

With this, lists/items created or edited on-device flow into the
semantic-search index incrementally — the debug "Jetzt indexieren"
button is no longer the only way to fill the index.

Tests:
- new `data-source-bundle.spec.ts` (5 tests) locks the singleton + publish
  routing
- existing 185 mobile tests still pass

Closes #85
chore(release): v0.6.6-rc17
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
13dc5346ad
test(maestro): phase2-mutation-observer-smoke flow
All checks were successful
continuous-integration/drone/push Build is passing
682fa28406
Validates the rc17 wireup: opens /search first to wake the IndexingService,
then mutates a list, then returns to /search and asserts the stats line
shows non-zero indexed chunks with no errors.

Refs #85
Phase2-Mutation-Observer-Flow überarbeitet:
- Pre-Assertion auf "0 Quellen" (statt embedder=ready), Post-Assertion auf
  Quellen >= 1 — embedder bleibt idle solange keine embeddable content da ist,
  und ein frisch leerer Listen-Mutate erzeugt nur den Publish, nicht den Embed.
- Drawer-Tap durchgängig auf optional+point-tap-Fallback (8%,12%), weil die
  hamburger-id auf manchen Launches verschwindet.
- waitForAnimationToEnd + waitToSettleTimeoutMs an mehreren Stellen, um den
  Race zwischen Maestro-Tap und Tamagui-Layout zu entschärfen.

rc17 deviceFinding ergänzt: Code-Level ist solide, aber phase2 e2e auf Device
ist auf Maestro-UI-Fragilität geblockt (inputText auf Tamagui-Input + OAuth-
Intent-Picker) — drei Iterationen ohne Durchbruch, das ist UI-Drift, nicht
Code-Defekt. Markiert offene Validierungsschuld für #85.
ci: publish-debug-apk pipeline + install/run scripts for rpi5 test device
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is failing
1c3c7d3314
Neue Drone-Pipeline `publish-debug-apk` (custom-event-trigger): baut die
Mobile-App mit APP_VARIANT=development → de.mrrm.mrrmlab.dev (eigenes
Package, kollidiert nicht mit der Release-Variante) und ohne EXPO_PUBLIC_KC_*
— `app/login.tsx` ueberspringt OIDC, `useAuthState` returnt direkt
{ isAuthenticated: true }. Damit laufen Maestro-Flows ohne OAuth-Intent-
Picker, was beim phase2-mutation-observer-Flow auf rc17 geblockt hat.

Output: ueberschreibt idempotent die Gitea-Release `debug-rpi5`
(prerelease=true). Vom rpi5 abgegriffen via neuem
`scripts/install-debug-apk-pi.sh` (SSH → curl → adb install -r).

`scripts/maestro-pi.sh` praeferiert jetzt `de.mrrm.mrrmlab.dev` ueber die
Release-Variante, falls beide installiert sind — die .dev-Variante ist die
designed-fuer-Maestro Variante.
ci: serialize publish-debug-apk vs e2e-mobile + reduce -Xmx for arm64 debug
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is failing
534b001881
Build #986 zeigte zwei Probleme: (a) publish-debug-apk und e2e-mobile rannten
beide auf event=custom und kollidierten am gradle-cache-Host-Volume
("Timeout waiting to lock journal cache"). (b) Mein Build OOMte ~16min
spaeter ("Gradle build daemon disappeared") trotz -Xmx4g — vermutlich
arm64-NDK-Compile zusammen mit 4g JVM-Heap ueberschreitet die Host-RAM.

Fix:
- e2e-mobile triggert jetzt nur noch auf cron (war: cron+custom). Manuell via
  Drone-UI "Run now" am Cron-Job triggerbar.
- publish-debug-apk: -Xmx2g + free-h-Diagnose vor Build (kopiert von publish-apk).
ci: simplify publish-debug-release shell (no line continuations, no unicode)
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
continuous-integration/drone/tag Build is passing
ae927dbc57
Build #988 build-debug succeeded, aber publish-debug-release brach mit
'/bin/sh: syntax error: unexpected "||"' — busybox-sh in node:24-alpine
mag die Kombination aus YAML-Literal-Block-Fortsetzung (\) + nachfolgendem
|| true nicht. Workaround: ein-Zeilen-Curls + AUTH/API in Vars vorbereitet,
keine Unicode-Chars (→/—) im Release-Body.
ci(publish-debug-apk): assembleRelease statt assembleDebug
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
continuous-integration/drone/tag Build is passing
f3bafe94c4
Build #990 lieferte zwar einen installierbaren APK, aber der startete in den
expo-dev-client-Launcher ("DEVELOPMENT SERVERS", "npx expo start"-Prompt)
und blieb dort haengen, weil kein Metro-Server lief. assembleDebug linkt
den dev-launcher und erwartet JS via Metro.

Fix: assembleRelease — bundelt JS via Hermes ab Build-Zeit, App startet direkt
im RootLayout. Signing mit dem prod-Keystore-Secret (gleicher Key, anderer
packageName de.mrrm.mrrmlab.dev — kein Konflikt mit der Release-Variante).
JVM-Heap zurueck auf -Xmx1g (publish-apk-bewaehrt fuer assembleRelease+arm64).
fix(search): persistent IndexingStats so DebugIndexBar survives /search remount
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
continuous-integration/drone/tag Build is passing
a786c76a78
Why: phase2 Maestro flow asserts "[1-9]\d* Quellen" after navigating
/search -> /listen -> create list -> /search. The chunks-extracted events
fire on the create, but DebugIndexBar's React state resets on remount, so
the counter shows 0 and the assertion fails.

Move the accumulator into IndexingService (sourcesProcessed,
chunksExtracted, chunksIndexed, errorCount, lastError). DebugIndexBar
seeds its useState from getStats() on mount. resetStats() keeps
"Jetzt indexieren" semantics consistent.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
debug: trace mutation observer chain via console.warn for logcat
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
continuous-integration/drone/tag Build is passing
2f35dc8c59
diag: rawEventsReceived counter to trace mutation observer chain in DebugIndexBar
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
continuous-integration/drone/tag Build is passing
7ef898a791
diag: per-stage mutation trace counters surfaced in DebugIndexBar
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
continuous-integration/drone/tag Build is passing
edc4685005
diag: add A counter (useCreateList.onSuccess) + ctx identity probe via globalThis
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone Build is passing
continuous-integration/drone/tag Build is failing
d005c45572
Replaces blocked Phase2 Maestro e2e (dev API has Keycloak enabled, dev
mobile build has no auth → 401) with deterministic unit tests on the
api-client-context wireup. Renders each mutation hook inside a
ListsApiClientProvider with a spy onSourceChange, asserts the spy fires
with the correct event kind, and proves a failing mutation does NOT fire.

Strips the diagnostic counters (bumpA-D, ctx probe, mutation-trace.ts,
trace line in DebugIndexBar) — the wireup is now covered by tests so
the on-device probes are no longer needed.

Refs #85 (Mutation-Observer in api-client-context)
Restores the documented dev-passthrough semantics for clients that talk
to a Keycloak-configured backend without their own token. Guard accepts
`X-Dev-User: <sub>` as a stand-in for a verified JWT iff NODE_ENV !==
'production'. Mobile dev build (no Keycloak) now sends `X-Dev-User:
dev-user` via the api-client's new `defaultHeaders` option.

- jwt-auth.guard.ts: header check after the `enabled` short-circuit;
  ignored in production
- api-client/http.ts: `defaultHeaders` config that merges into every
  request; Authorization from getToken still wins
- mobile/services/api.ts: opt-in header only when auth === null
- 4 guard spec tests + 2 http.spec tests covering precedence

Unblocks the Phase2 Maestro flow on the rpi5-tethered no-Keycloak build.
chore(release): v0.6.6-rc18
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
continuous-integration/drone/pr Build is passing
81080c8aaa
pm-bot merged commit 5c98189b95 into main 2026-06-08 19:12:26 +02:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
admin-mrrm/mrrmlabapp!445
No description provided.