Commit graph

115 commits

Author SHA1 Message Date
Snider
bad718da8d revert: remove domains array, keep single domain config
Some checks failed
CI / PHP 8.3 (push) Failing after 2s
CI / PHP 8.4 (push) Failing after 2s
Multi-domain handled at app level via config override.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-03 17:08:52 +00:00
Snider
b75fa0ba57 feat: add mcp.domains config for multi-domain portal support
Some checks failed
CI / PHP 8.3 (push) Failing after 2s
CI / PHP 8.4 (push) Failing after 2s
New 'domains' key accepts comma-separated MCP_DOMAINS env var,
falling back to single MCP_DOMAIN value for backward compatibility.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-03 16:59:28 +00:00
Snider
8efd939ce4 fix: derive MCP portal domain from APP_DOMAIN
Some checks failed
CI / PHP 8.3 (push) Failing after 2s
CI / PHP 8.4 (push) Failing after 2s
Instead of hardcoding mcp.host.uk.com as the default, derive it from
APP_DOMAIN env var so it works automatically per environment without
needing a separate MCP_DOMAIN override.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-03 16:51:16 +00:00
Snider
1ef8157822 fix: truncate oversized sections and clear DB on fresh ingest
Some checks failed
CI / PHP 8.3 (push) Failing after 2s
CI / PHP 8.4 (push) Failing after 2s
- Truncate content to 3800 chars before embedding (embeddinggemma has
  a 2048-token context, ~4K char limit). Eliminates all 73 Ollama 500
  errors from oversized plan sections.
- Clear brain_memories DB table when --fresh is used, keeping DB rows
  in sync with Qdrant vectors.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-03 16:02:11 +00:00
Snider
331796c1da feat: add dedicated brain database connection for remote MariaDB
Some checks failed
CI / PHP 8.3 (push) Failing after 2s
CI / PHP 8.4 (push) Failing after 2s
Brain memories can now be stored in a separate database, co-located
with Qdrant vectors on the homelab. Defaults to the app's main DB
when no BRAIN_DB_* env vars are set (zero-config for existing installs).

- Add brain.database config with BRAIN_DB_* env var support
- Register 'brain' database connection in Boot.php
- Set BrainMemory model to use 'brain' connection
- Update BrainService transactions to use brain connection
- Update migration to use brain connection, drop workspace FK
  (cross-database FKs not supported)
- Add migration to drop FK on existing installs
- Update default URLs from *.lthn.lan to *.lthn.sh

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-03 15:14:01 +00:00
Snider
ad0ee04b83 chore: make TLS skip detect any non-public TLD, not just .lan
Some checks failed
CI / PHP 8.3 (push) Failing after 2s
CI / PHP 8.4 (push) Failing after 2s
Prepares for lthn.lan → lthn.sh migration. Once real certs are
deployed, verifySsl will always be true and this logic becomes
a no-op safety net for .lan/.lab/.local/.test domains.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-03 13:56:06 +00:00
Snider
02cc11d2cf chore: update default brain URLs to *.lthn.lan convention
Some checks failed
CI / PHP 8.3 (push) Failing after 2s
CI / PHP 8.4 (push) Failing after 2s
Ollama and Qdrant defaults now use ollama.lthn.lan and
qdrant.lthn.lan to match the homelab service mesh naming.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-03 13:19:59 +00:00
Snider
9623e1e0b5 feat: add brain:ingest command for comprehensive knowledge archival
Some checks failed
CI / PHP 8.3 (push) Failing after 2s
CI / PHP 8.4 (push) Failing after 2s
Discovers markdown across 4 source types:
- memory: ~/.claude/projects memory files
- plans: docs/plans across repos + ~/.claude/plans
- claude-md: CLAUDE.md repo instructions
- tasks: core/tasks research and ideas

Supports --fresh to clear collection, --dry-run for preview,
and --source to target specific types.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 11:10:37 +00:00
Snider
9d49fc601b refactor: build HTTP client in single call, not conditional mutation
Some checks failed
CI / PHP 8.3 (push) Failing after 2s
CI / PHP 8.4 (push) Failing after 2s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 10:54:57 +00:00
Snider
b6823538d5 feat: skip TLS verification for .lan domains in BrainService
Some checks failed
CI / PHP 8.3 (push) Failing after 2s
CI / PHP 8.4 (push) Failing after 2s
Adds a private http() helper that wraps Http::timeout() with
conditional withoutVerifying() for self-signed .lan certs behind
Traefik. Boot singleton auto-detects .lan URLs at construction.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 10:53:00 +00:00
Snider
20a0b584ae fix: remove glob path from docblock that broke PHP tokenizer
Some checks failed
CI / PHP 8.3 (push) Failing after 2s
CI / PHP 8.4 (push) Failing after 2s
The `*/` in `projects/*/memory/` was closing the docblock comment
early, causing PHP to see `for` as a keyword on the same line.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 10:49:30 +00:00
Snider
31e2aae980 chore: rename package from host-uk/core-agentic to core/php-agentic
Some checks failed
CI / PHP 8.3 (push) Failing after 2s
CI / PHP 8.4 (push) Failing after 2s
Aligns composer package name with forge repo path
(forge.lthn.ai/core/php-agentic).

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-03 10:37:04 +00:00
Snider
c697a6657f chore(brain): use HTTPS for Qdrant via Traefik
Some checks failed
CI / PHP 8.3 (push) Failing after 3s
CI / PHP 8.4 (push) Failing after 2s
Qdrant is also behind Traefik at qdrant.lan alongside ollama.lan.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-03 10:13:06 +00:00
Snider
f17e1a0b6c chore(brain): use HTTPS for Ollama via Traefik
Some checks are pending
CI / PHP 8.3 (push) Waiting to run
CI / PHP 8.4 (push) Waiting to run
Ollama is behind Traefik reverse proxy on ollama.lan, so no port
needed and TLS should be enforced by default.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-03 10:12:34 +00:00
Snider
43b470257b feat(brain): configurable embedding model, default to EmbeddingGemma
Some checks failed
CI / PHP 8.3 (push) Failing after 3s
CI / PHP 8.4 (push) Failing after 3s
Make embedding model swappable via BRAIN_EMBEDDING_MODEL env var.
Switch default from nomic-embed-text to embeddinggemma (Gemma 3
based, 2x better cluster separation in benchmarks).

Default Ollama URL now points to ollama.lan (Linux homelab GPU).

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-03 10:10:02 +00:00
Snider
dfd3dde7b1 feat(brain): add brain:seed-memory artisan command
Some checks failed
CI / PHP 8.3 (push) Failing after 2s
CI / PHP 8.4 (push) Failing after 2s
Scans ~/.claude/projects/*/memory/ for MEMORY.md and topic markdown
files, parses sections, infers memory types, and imports into
OpenBrain via BrainService::remember().

Supports --dry-run, --workspace, --agent, and --path options.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-03 09:53:28 +00:00
Snider
d82ad2b9b1 fix(brain): address code quality review findings
- Move BrainMemory::create() inside BrainService::remember() for
  full atomicity (DB + Qdrant in single transaction)
- Add forWorkspace() scope to recall() MariaDB query (tenant isolation)
- Wrap forget() in DB::transaction (MariaDB first, then Qdrant)
- Check qdrantDelete() response and log warnings on failure
- Validate embed() response is a non-empty array

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-03 09:47:42 +00:00
Snider
2c6a095a0e fix(brain): address spec review findings
- Move BrainForget DB lookup inside circuit breaker for consistency
- Check ensureCollection() PUT response for Qdrant errors
- Wrap remember() in DB::transaction for atomicity
- Align migration confidence default to 0.8 (matches PHP default)

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-03 09:45:05 +00:00
Snider
eeb6927d8f feat(brain): add BrainService, MCP tools, and registration
- BrainService: Ollama embeddings + Qdrant vector upsert/search/delete
- brain_remember: store knowledge with type, tags, confidence, supersession
- brain_recall: semantic search with filter by project/type/agent/confidence
- brain_forget: workspace-scoped deletion from both stores
- brain_list: MariaDB query with model scopes, no vector search
- Config: brain.ollama_url, brain.qdrant_url, brain.collection
- Boot: BrainService singleton + tool registration via AgentToolRegistry

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 09:39:19 +00:00
Snider
627813cc4d feat(brain): add BrainMemory model and migration
UUID-keyed brain_memories table with workspace scoping, self-referential
supersession chain, TTL expiry, and confidence scoring. Eloquent model
includes all scopes and helpers needed by the MCP tool layer.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 09:33:46 +00:00
Snider
daa11bab39 docs: OpenBrain implementation plan — 8 tasks, TDD
Some checks failed
CI / PHP 8.3 (push) Failing after 2s
CI / PHP 8.4 (push) Failing after 2s
8-task plan: migration, BrainService (Ollama+Qdrant), 4 MCP tools
(remember/recall/forget/list), Go bridge subsystem, MEMORY.md seed
command. 18 files across php-agentic and go-ai.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-03 09:28:31 +00:00
Snider
a6e4f865e4 docs: OpenBrain design — shared agent knowledge graph
Some checks failed
CI / PHP 8.3 (push) Failing after 3s
CI / PHP 8.4 (push) Failing after 2s
Shared vector-indexed knowledge store accessible by all agents via MCP.
MariaDB for relational metadata, Qdrant for semantic search, Ollama for
embeddings. Four MCP tools: brain_remember, brain_recall, brain_forget,
brain_list. Replaces scattered MEMORY.md files with singular state.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-03 09:22:56 +00:00
Snider
1ead364afe fix(ci): install zip in release workflow
Some checks failed
CI / PHP 8.3 (push) Failing after 3s
CI / PHP 8.4 (push) Failing after 4s
Forgejo Composer API requires zip format.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 17:43:52 +00:00
Snider
647635fc6d fix(ci): simplify release workflow, use FORGEJO_REF_NAME
Some checks failed
CI / PHP 8.3 (push) Failing after 4s
CI / PHP 8.4 (push) Failing after 3s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 17:36:18 +00:00
Snider
522433c019 fix(ci): use Forgejo-native variables in release workflow
Some checks failed
CI / PHP 8.3 (push) Failing after 5s
CI / PHP 8.4 (push) Failing after 3s
Replace github.server_url/GITHUB_REF_NAME with explicit forge URL
and GITEA_REF_NAME/GITEA_OUTPUT.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 17:13:12 +00:00
Snider
29656e3b92 feat: add Forgejo release workflow for Composer registry
Some checks failed
CI / PHP 8.3 (push) Failing after 2s
CI / PHP 8.4 (push) Failing after 2s
On tag push (v*), zips the package and publishes to the
forge.lthn.ai Composer package registry.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 17:00:19 +00:00
1bbc1336b7 Merge pull request 'refactor: consolidate duplicate state models (#18)' (#48) from refactor/consolidate-workspace-state-models into main
Some checks failed
CI / PHP 8.3 (push) Failing after 2s
CI / PHP 8.4 (push) Failing after 2s
2026-02-24 13:26:37 +00:00
darbs-claude
7fadbcb96c
refactor: consolidate duplicate state models into WorkspaceState (#18)
Some checks failed
CI / PHP 8.3 (pull_request) Failing after 1s
CI / PHP 8.4 (pull_request) Failing after 1s
- Delete Models/AgentWorkspaceState.php (legacy port, no backing table)
- Rewrite Models/WorkspaceState.php as the single canonical state model
  backed by agent_workspace_states table with array value cast,
  type helpers, scopeForPlan/scopeOfType, static getValue/setValue,
  and toMcpContext() for MCP tool output
- Update AgentPlan::states() relation and setState() return type
- Update StateSet MCP tool import
- Update SecurityTest to use WorkspaceState
- Add WorkspaceStateTest covering table, casts, type helpers, scopes,
  static helpers, toMcpContext, and AgentPlan integration
- Mark CQ-001 done in TODO.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 13:26:23 +00:00
80c778cb08 Merge pull request 'feat: add template version management' (#63) from feat/template-version-management into main
Some checks failed
CI / PHP 8.3 (push) Failing after 2s
CI / PHP 8.4 (push) Failing after 2s
2026-02-24 13:25:33 +00:00
0e7b617551
feat: add template version management (#35)
Some checks failed
CI / PHP 8.3 (pull_request) Failing after 2s
CI / PHP 8.4 (pull_request) Failing after 1s
Snapshots YAML template content in a new `plan_template_versions` table
whenever a plan is created from a template. Plans reference their version
via `template_version_id` so existing plans are unaffected by future
template file edits.

Key changes:
- Migration 0006: create `plan_template_versions` table (slug, version,
  name, content JSON, content_hash SHA-256); add nullable FK
  `template_version_id` to `agent_plans`
- Model `PlanTemplateVersion`: `findOrCreateFromTemplate()` deduplicates
  identical content by hash; `historyFor()` returns versions newest-first
- `AgentPlan`: add `template_version_id` fillable and `templateVersion()`
  relationship
- `PlanTemplateService::createPlan()`: snapshot raw template before
  variable substitution; store version id and version number in metadata;
  add `getVersionHistory()` and `getVersion()` public methods
- Tests: `TemplateVersionManagementTest` covering model behaviour, plan
  creation snapshotting, deduplication, history ordering, and service
  methods

Closes #35

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 13:25:17 +00:00
ffc441f22a Merge pull request 'fix: improve template variable error messages' (#58) from fix/template-variable-error-messages into main
Some checks failed
CI / PHP 8.3 (push) Failing after 3s
CI / PHP 8.4 (push) Failing after 2s
2026-02-24 13:21:14 +00:00
a9a6e258e1 Merge pull request 'feat: add plan archival with retention policy' (#62) from feat/plan-retention-policy into main
Some checks failed
CI / PHP 8.3 (push) Failing after 3s
CI / PHP 8.4 (push) Failing after 2s
2026-02-24 13:20:38 +00:00
d26250fc12 Merge pull request 'docs: document MCP tool dependency system' (#60) from docs/doc-002-mcp-tool-dependency-system into main
Some checks failed
CI / PHP 8.3 (push) Has been cancelled
CI / PHP 8.4 (push) Has been cancelled
2026-02-24 13:20:33 +00:00
6a1709fca9 Merge pull request 'fix: audit UK/US spelling consistency (#36)' (#64) from fix/uk-spelling-consistency into main
Some checks failed
CI / PHP 8.3 (push) Failing after 4s
CI / PHP 8.4 (push) Failing after 3s
2026-02-24 13:20:24 +00:00
darbs-claude
7d6081bdd7 fix: use UK English spelling in MCP server docstring (#36)
Some checks failed
CI / PHP 8.3 (pull_request) Failing after 3s
CI / PHP 8.4 (pull_request) Failing after 2s
Audited all PHP files for US English spellings per CLAUDE.md convention.
Fixed "Organize" → "Organise" in Mcp/Servers/Marketing.php docstring.

CSS/JS identifiers (borderColor, backgroundColor, transition-colors) and
array keys that form interface contracts with the host-uk/core package are
unchanged as they are not prose.

Closes #36

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 15:41:16 +00:00
cc1c4c1adc feat: add plan archival with retention policy (#34)
Some checks failed
CI / PHP 8.3 (pull_request) Failing after 21s
CI / PHP 8.4 (pull_request) Failing after 19s
- Add `agentic.plan_retention_days` config (default 90 days via AGENTIC_PLAN_RETENTION_DAYS env)
- Add SoftDeletes and `archived_at` timestamp to AgentPlan model
- Add migration for `deleted_at` and `archived_at` columns on agent_plans
- Create `agentic:plan-cleanup` command with --dry-run and --days options
- Schedule retention cleanup to run daily via service provider
- Register PlanRetentionCommand in ConsoleBooting handler
- Add PlanRetentionTest feature test suite covering all retention scenarios
- Fix archive() to store archived_at as dedicated column (not metadata string)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 15:11:55 +00:00
b0e2be2633 fix(ci): correct container image expression
Some checks failed
CI / PHP 8.3 (push) Failing after 1s
CI / PHP 8.4 (push) Failing after 1s
2026-02-23 13:47:09 +00:00
a5da40a202 feat(ci): use lthn/build:php container image
Some checks failed
CI / PHP 8.3 (push) Failing after 0s
CI / PHP 8.4 (push) Failing after 0s
Replace setup-php action with pre-built container.
Eliminates ~50s setup overhead per matrix job.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 13:46:48 +00:00
Claude
be820fead8
fix: use Mockery mocks for ApiKey and fix named arg matching
All checks were successful
CI / PHP 8.4 (push) Successful in 1m41s
CI / PHP 8.3 (push) Successful in 1m44s
- Replace anonymous class extending ApiKey with Mockery mock to avoid
  requiring php-api package at load time
- Replace with() named args with withSomeOfArgs() for Mockery compat

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 12:20:01 +00:00
Claude
5f016c6275
style: fix Pint violations in ProcessContentTask and AgentDetection
Some checks failed
CI / PHP 8.3 (push) Failing after 1m49s
CI / PHP 8.4 (push) Failing after 1m48s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 12:08:49 +00:00
Claude
ae4188c063
fix: template interpolation and Cache::fake() in tests
Some checks failed
CI / PHP 8.4 (push) Failing after 1m26s
CI / PHP 8.3 (push) Failing after 1m55s
- interpolateVariables: use string concatenation for triple-brace
  placeholders instead of PHP string interpolation which only
  produces single braces
- AgentToolRegistryTest: replace Cache::fake() (not available) with
  Cache::flush() since array driver is already in-memory

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 12:05:54 +00:00
darbs-claude
eb6bc27a4e docs: document MCP tool dependency system
Some checks failed
CI / PHP 8.3 (pull_request) Failing after 1m44s
CI / PHP 8.4 (pull_request) Failing after 1m44s
Add README to Mcp/Tools/Agent/ explaining:
- How ToolDependency works (contextExists, sessionState, entityExists)
- Context requirements (workspace_id, session_id) and multi-tenant safety
- Step-by-step guide for creating new tools
- AgentTool base class property and method reference
- Dependency resolution order and recommended declaration sequence
- Troubleshooting guide for common dependency errors

Closes #32

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 12:05:35 +00:00
b86714db6e Merge pull request 'docs: add PHPDoc to AgentDetection patterns' (#59) from docs/agent-detection-phpdoc into main
Some checks failed
CI / PHP 8.3 (push) Failing after 1m19s
CI / PHP 8.4 (push) Failing after 1m20s
Reviewed-on: #59
2026-02-23 12:04:17 +00:00
Claude
6cd9ca09d7
style: fix pint issues in ContentService and AgentToolRegistryTest
Some checks failed
CI / PHP 8.3 (push) Failing after 1m50s
CI / PHP 8.4 (push) Failing after 1m50s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 11:58:52 +00:00
darbs-claude
e47998bc15 docs: add PHPDoc to AgentDetection patterns
Some checks failed
CI / PHP 8.4 (pull_request) Failing after 1m21s
CI / PHP 8.3 (pull_request) Failing after 1m23s
Document each PROVIDER_PATTERNS entry with real User-Agent examples,
add inline comments to BROWSER_INDICATORS and NON_AGENT_BOTS with
categorised UA examples, document MCP_TOKEN_HEADER with token format
details, and add class-level usage examples and detection priority
ordering.

Closes #31
Refs: DOC-001

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 11:56:57 +00:00
Claude
938081f2f5
fix: resolve 14 test failures across 3 test files
Some checks failed
CI / PHP 8.4 (push) Failing after 1m33s
CI / PHP 8.3 (push) Failing after 1m35s
ProcessContentTaskTest: set mock properties directly instead of
shouldReceive('__get') which doesn't reliably intercept property
access on Mockery mocks of non-existent classes.

HasStreamParsing: fix parseJSONStream chunked read bug where the
inner parse loop restarted at position 0 with stale state from
a previous partial parse. Track scan position across chunks.

AgentDetection: fix Postman regex \bPostman\b → \bPostman/ so it
matches PostmanRuntime (no word boundary between n and R).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 11:48:29 +00:00
darbs-claude
91ee71b8a1 fix: improve template variable error messages (#30)
Some checks failed
CI / PHP 8.3 (pull_request) Failing after 1m37s
CI / PHP 8.4 (pull_request) Failing after 1m36s
Enhance `validateVariables()` in `PlanTemplateService` to produce
actionable errors instead of the generic "Required variable '...' is missing".

Changes:
- Extracted `buildVariableError()` helper that composes the message from
  the variable's `description`, `format`, `example`, and `examples` fields
- Added `naming_convention` key to the returned array so callers have
  a constant reminder that variable names use snake_case
- Added a `NAMING_CONVENTION` private const to avoid string duplication

Tests (6 new cases in `PlanTemplateServiceTest`):
- description included in error message
- single `example` value included
- `examples` list (first two) included
- `format` hint included alongside example
- `naming_convention` present in both valid and invalid results
- bare variable (no description) still produces useful "missing" message

Closes #30

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 11:48:27 +00:00
5fa46104f4 Merge pull request 'fix: validate API keys on AgenticManager init' (#57) from fix/validate-api-keys-on-init into main
Some checks failed
CI / PHP 8.3 (push) Failing after 1m25s
CI / PHP 8.4 (push) Failing after 1m28s
Reviewed-on: #57
2026-02-23 11:41:12 +00:00
6b7a7ade15 Merge pull request 'fix: improve workspace context error messages' (#56) from fix/workspace-context-errors into main
Some checks failed
CI / PHP 8.3 (push) Failing after 1m29s
CI / PHP 8.4 (push) Failing after 1m27s
Reviewed-on: #56
2026-02-23 11:39:41 +00:00
968cbcdd63 Merge pull request 'fix: add batch failure recovery to ContentService' (#55) from fix/batch-failure-recovery into main
Some checks failed
CI / PHP 8.4 (push) Has been cancelled
CI / PHP 8.3 (push) Has been cancelled
Reviewed-on: #55
2026-02-23 11:39:31 +00:00