Commit graph

3 commits

Author SHA1 Message Date
Snider
b6565263f3 fix(agent/brain): lock forget+supersede paths against late index writes
Cache::lock keyed by memory id wraps the delete path in BrainService::
forget(); supersede cleanup in remember() lifted to the same idiom.
forget() now ALWAYS queues DeleteFromIndex on a successful delete
(was previously skipped when indexed_at was null — left late writes
from stale preloaded models a window to land entries after the
underlying memory was gone).

Index write paths (qdrantUpsert / elasticIndex) re-check that the
memory row still exists before writing — defence-in-depth against any
future caller that holds a stale model reference past a forget.

Pest coverage extended in SupersedeForgetIndexCleanupTest:
- never-indexed forget queues cleanup
- late stale-model index writes are skipped after forget
- never-indexed supersede cleanup queues deletion
- late stale-model index writes are skipped after supersede

Co-authored-by: Codex <noreply@openai.com>
Closes tasks.lthn.sh/view.php?id=999
2026-04-25 18:04:55 +01:00
Snider
6832d40587 fix(agent/brain): batch — org maxLength + retry semantics + forget index cleanup
Codex 5.5 batch lane processed 8 brain Mantis tickets. 4 implemented,
1 stale, 3 deferred.

Tickets implemented:
- #313 — MCP schemas (BrainRemember/Recall/List): org field maxLength=128 with runtime validation; recall filter.org also bounded; pest test coverage added
- #314 — BrainList: removed withCircuitBreaker('brain') from DB-only handler; CircuitBreakerTest updated to assert no breaker call
- #315 — BrainService.retryableHttp(): now retries 408 (request-timeout), 429 (rate-limit), and 5xx; honours Retry-After header; focused retry tests added
- #326 — BrainService.forget(): dispatches DeleteFromIndex only when row has indexed_at (was unconditional); SupersedeForgetIndexCleanupTest covers never-indexed case

Tickets stale-fixed: #316 (RememberKnowledge already rejects missing/deleted supersedes target before dangling retry)
Tickets deferred: #121 (cross-surface audit), #311 (retry-inside-breaker architectural redesign), #312 (no authoritative org claim in MCP request context yet)

Co-authored-by: Codex <noreply@openai.com>
Closes tasks.lthn.sh/view.php?id=313
Closes tasks.lthn.sh/view.php?id=314
Closes tasks.lthn.sh/view.php?id=315
Closes tasks.lthn.sh/view.php?id=326
2026-04-25 14:55:40 +01:00
Snider
c616ff1e32 fix(brain): close openbrain audit gaps — org scoping + index cleanup + reindex flags + MCP schemas + circuit layer
Closes the 5 PARTIAL items flagged in docs/AUDIT-openbrain-20260424.md.

- Gap A (org scoping persisted on writes): new migration adds `org`
  nullable+indexed column to brain_memories; BrainMemory fillable;
  RememberKnowledge action forwards org; BrainService::remember
  persists it.

- Gap B (supersede/forget Elastic cleanup): BrainService::forget
  dispatches DeleteFromIndex (handles both Qdrant + Elastic); supersede
  path dispatches cleanup for the old memory id before replacing it.
  DeleteFromIndex itself untouched — already handled both indexes.

- Gap C (brain:reindex flags): --org, --project, --stale (null OR
  >14d old), --dry-run (count+stop), --elastic-only added to the
  artisan command.

- Gap D (MCP schemas expose org): brain_remember, brain_recall,
  brain_list now accept `org` in input schema + forward into
  action/service.

- Gap E (resilience uneven): brain_list now wrapped in
  withCircuitBreaker('brain', ...) matching the pattern used by
  BrainRemember/Recall/Forget. BrainService gains retryableHttp()
  helper — 100/300/900ms exponential backoff, retries only on 5xx +
  connection errors, not on 4xx. Qdrant calls route through it;
  Ollama left alone (EmbedMemory job has its own retry).

Tests (Good/Bad/Ugly per gap):
- Feature/Brain/OrgScopingTest.php
- Feature/Brain/SupersedeForgetIndexCleanupTest.php
- Feature/Brain/ReindexFlagsTest.php
- Feature/Mcp/BrainSchemaOrgTest.php
- Feature/Brain/CircuitBreakerTest.php

php -l clean on all 13 files. Pest binary not in this checkout —
CI path validates the full suite.

Closes tasks.lthn.sh/view.php?id=107

Co-authored-by: Codex <noreply@openai.com>
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-24 08:14:06 +01:00