BrainService::http() was building a PendingRequest with no auth
header, so when Qdrant has auth enabled (the production lthn.sh
deploy does) every upsert/lookup returned 401. The circuit breaker
logged the 401 via Cache::store('file'), which was the red-herring
cache-write error chased in the first #97 iteration.
Changes:
- BrainService loads + trims a Qdrant api key from
config('brain.qdrant.api_key') in the constructor.
- New qdrantHttp() helper returns a PendingRequest with the
api-key header when the key is non-empty, or the plain client
otherwise. Ollama + Elasticsearch call sites still use http()
(separate auth shapes).
- php/config.php adds a brain.qdrant.api_key entry reading
env('BRAIN_QDRANT_API_KEY').
- Good/Bad/Ugly Pest tests cover: configured key → header sent,
unset → header absent, empty-string → header absent.
Closes tasks.lthn.sh/view.php?id=97
Co-authored-by: Codex <noreply@openai.com>
Co-Authored-By: Virgil <virgil@lethean.io>
Introduce a pipeline metadata surface that enforces "no body content
ever reaches pipeline decisions". MetaReader is an interface with four
methods (getPRMeta, getEpicMeta, getIssueState, getCommentReactions),
each returning a readonly DTO carrying only structural fields —
state, mergeability, SHAs, branches, reaction counts, child linkage.
ForgejoMetaReader projects raw Forgejo API payloads into these DTOs
and drops body/description/review text before the caller can see it.
Unit test mocks rich Forgejo payloads containing body, description,
review_text, and comment_body, then asserts the DTO toArray output
never exposes those keys — the regression fence for the RFC rule.
Downstream callers (ScanForWork, ManagePullRequest) still use the
raw ForgejoService today; that refactor lands under Mantis #90.
Closes tasks.lthn.sh/view.php?id=89
Co-authored-by: Codex <noreply@openai.com>
Co-Authored-By: Virgil <virgil@lethean.io>