Phase 3 lane: queueable Job that resolves a profile via ProfileSelector,
posts POST {gateway}/v1/responses to the chosen Hermes gateway, persists
ticket_id/profile_id/response_id/run_id/status in agent_dispatches, and
chains CaptureDispatchResultJob.
Migration 2026_04_25_000003 creates agent_dispatches table (FK-free,
Postgres-compatible).
HermesClient: thin Laravel HTTP wrapper around the Hermes /v1/responses
endpoint with Authorization header + JSON body.
DispatchMantisTicketJob behaviour:
- Resolves profile via ProfileSelector::pickFor()
- Null-profile → log warn + ->release(60) requeue
- Otherwise POSTs to gateway, persists AgentDispatch row, queues
CaptureDispatchResultJob
AgentDispatch Eloquent model with minimal $fillable.
Pest Feature test (Http::fake): verifies request shape, persisted row,
downstream capture-job queueing, and the no-profile requeue path.
Test file conditionally aliases minimal stubs for sibling-lane services
so this file remains runnable before #826/#828 fully land in dev.
Codex note: php -l clean; pest skipped (no vendor/).
Closes tasks.lthn.sh/view.php?id=827
Co-authored-by: Codex <noreply@openai.com>
Phase 3 lane: queueable Job that normalises Hermes fetch/event payloads,
extracts the first commit SHA via ShaExtractor (matches both forge URL
and bare 7-40 hex forms), builds a close-note with optional
?\Mod\AgentProfile profile reference, posts the Mantis note via
MantisClient->note(), then transitions the ticket to closed/fixed via
MantisClient->close().
ShaExtractor: extract($modelOutput): {?sha, ?repo, ?forge_url} regex
matcher for forge.lthn.sh commit URLs + bare SHAs.
MantisClient: thin Guzzle wrapper around tasks.lthn.sh REST API
(note + close), Authorization header, base URL via config.
Pest Feature test: 8 tests / 17 assertions covering forge URL parsing,
bare SHA parsing, note/close ordering, repo-hint fallback, missing-SHA
RuntimeException path. Verified via temp Composer harness (no checked-in
vendor/ at this repo level).
Closes tasks.lthn.sh/view.php?id=828
Co-authored-by: Codex <noreply@openai.com>
Foundation slice for Mantis #841 php/Mod/Agent RFC implementation:
* CompleteTask now wraps in DB::transaction with idempotent credit awards
and safe current_task_id clearing
* Credits/{Award,GetBalance,GetCreditHistory} updated for agent_id +
fleet_task_id ledger support and richer balance totals
* GenerateCommand canonical agentic:generate wiring; legacy duplicate
no longer registered
* Boot wires brain:clean / brain:prune / brain:reindex
* EmbedMemory exits early when memory already indexed
* 3 follow-on fleet migrations reconcile fleet_nodes pointer column,
fleet_tasks/credit_entries fk/index hygiene, fleet+credit constraints
* 4 foundation tests under php/tests/Feature/Mod/Agent/
php -l clean on all modified files. pest unrunnable in sandbox (no vendor/).
Foundation slice only: remaining model/action parity, full MCP tool/
service sweep, fleet controller auth-context, and 41-tool/45-action
surface left for follow-up tickets.
Co-authored-by: Codex <noreply@openai.com>
Closes tasks.lthn.sh/view.php?id=841
Inverse of the EmbedMemory job (#56): removes a memory from Qdrant (and
the future Elasticsearch index) when brain_forget fires or a memory is
soft-deleted.
- php/Jobs/DeleteFromIndex.php — Laravel Job, 3 retries with backoff
- BrainService: qdrantDelete() private→public and now throws on HTTP
failure (was silent Log::warning — wouldn't trigger Job retry)
- elasticDelete() stub added (fills in with the ES integration ticket)
- php/tests/Feature/Jobs/DeleteFromIndexTest.php — success + HTTP-failure
paths via mocked Http
Co-authored-by: Codex <noreply@openai.com>
Closes tasks.lthn.sh/view.php?id=57
Implements the async-embedding pipeline's worker side:
- php/Jobs/EmbedMemory.php — Laravel Job that calls BrainService::embed()
+ qdrantUpsert() and sets indexed_at on success
- php/Migrations/…_add_indexed_at_to_brain_memories.php — nullable
timestamp + index, portable across pgsql/mariadb (hasColumn guard)
- BrainMemory: +indexed_at fillable + datetime cast + PHPDoc
- BrainService: qdrantUpsert() private→public so the Job can use it;
elasticIndex() stub added (to be implemented by the ES ticket)
- php/tests/Feature/Jobs/EmbedMemoryTest.php — Pest tests for success
path and Qdrant-failure path
Co-authored-by: Codex <noreply@openai.com>
Closes tasks.lthn.sh/view.php?id=56