Orientation for agents (AGENT.md)
Orientation for coding agents working in this repository. Read this before editing.
Orion is deterministic, replayable index operations for any full-text engine (Elasticsearch, Solr, OpenSearch,
Meilisearch), with a thin hybrid query layer, for any PHP application. It is built the way polaris-core is:
a framework-free core with ports and working defaults, host integrations as packages, one monorepo
split-published later. The Univeros module lives here too, as packages/univeros.
Where things are decided
Section titled “Where things are decided”docs/spec/orion-spec.md— the product specification (v1). What Orion is and is not.docs/spec/revision.md— the framework-agnostic layout (v2 delta): packages, ports, the same-connection invariant, the Univeros module, the work packages (§7). Where it disagrees with v1, it wins.docs/decisions.md— append-only log. Read it before proposing something it already settled.
Status
Section titled “Status”- WP0 complete (2026-10-25): repository, root QA,
bin/check-importswith tests,packages/coreandpackages/testingskeletons, docs, CI. - WP1 complete (2026-10-25): query and schema ASTs with their declarative forms, core RRF, total schema
change classification, write types, adapter contracts and
Capability, theStorageport withInMemoryStorageandStorageConformanceTestCase(same-connection test included),Orion::create(). - WP2 complete (2026-10-25): versioned deletes,
drop()and a scopableraw()in the contracts;EngineConformanceTestCaseand the devInMemoryengine;orion-search/pdo(host-transaction aware);orion-search/elasticsearch(8.19 and 9.x). - WP3 complete (2026-10-26):
Planner(plan and apply for one engine), the org key and native settings diffed against the registration, versioned physical indices behind an alias (IndexNamer,AliasManager,SchemaAdapter::alias()), the index registry (orion_index,orion_alias,IndexRegistration),Outcomeas the receipt seam, andLifecycleDeterminismTestCase, green on the in-memory engine and Elasticsearch 9 and 8.19. - WP4 complete (2026-10-26):
Storage::increment()and the counter table (outbox offsets dense and committed in order), the compacted transactional outbox, the drain with dead letters, theDispatcherandMetricsports, signed hash-chained receipts (ReceiptSigner,SodiumSigner,ReceiptStore,Verifier), theReindexer(replay, leased lock, resume, abort, prune),orion-search/cli(bin/orion), andFaultInjectionTestCase, green on the in-memory engine and Elasticsearch 9 and 8.19. - WP5 complete (2026-10-26):
SqlStorageover anExecutorport (PDO and Cycle),univeros/orion(storage on the host’s Cycle connection, migration,orion:*commands, the seven doctor checks with--fix, bus dispatcher, Meter metrics, agent manifest section), thesearch:manifest’s index declarations in core,SchemaAdapter::resolve(), the harnesses on Cycle storage, andexamples/univeroswith a walkthrough. - WP6 complete (2026-10-26): tenancy on a shared index.
Graph::searcher()injects the tenant’s filter beneath every query and the escape hatch; outbox writes are tenant-qualified and stamped with the org key; an index’s org key must fit the tenancy;TenancyIsolationTestCasegates every engine;orion-search/polarisresolves the tenant from the Polaris token, and the module wires it when univeros/polaris is configured. - WP7 complete (2026-10-26):
orion-search/solrfor SolrCloud 9.x and 10.x, passing engine conformance, tenancy isolation, determinism and fault injection on both; the neutral schema needed no Lucene-shaped change. - WP8 complete (2026-10-26): the query bound to the registered schema (
Binder: fields, flags, value types, query-time boosts) and fused in core (reciprocal rank across the scoped legs) by theSearcher; amatchover several weighted fields; the offset window refused asUnsupportedClause; the manifest’ssearchandsourcekeys;orion-search/psr15serving the declared endpoints with RFC 9457 problems; the module serving them behindRequestTokenwith the PolarisGate; the endpoint fixtures through both harnesses and on every engine; the property-based query equivalence suite green on Elasticsearch and Solr. - WP9 complete (2026-10-27): the agent surface in core (
Orion\Tools:compile_query,search,list_indices,describe_index,capabilities,plan;applyandreindexpreviewing untilconfirm),Searcher::compile()over the engines’QueryCompilingport (the requestsearch()would send, bound and scoped, never sent), and the module’sorion__*adapters on the framework’s MCP server (mcp:serve,--allow-writesfor a confirmed mutation). - WP10 complete (2026-10-27):
orion-search/opensearchfor OpenSearch 2.19 and 3.x on the official client, passing engine conformance, tenancy isolation, determinism, fault injection, query equivalence and the endpoint fixtures on both; vectors through the bundled k-NN plugin, hybrid fusion left to core;ORION_ENGINE=opensearchin the module. - WP11 complete (2026-10-27):
orion-search/meilisearchfor Meilisearch 1.13 and later, passing engine conformance, tenancy isolation, determinism, fault injection, query equivalence and the endpoint fixtures on 1.56 and 1.13: the schema as settings plus a declaration document, documents validated before they are sent, versions kept by Orion with tombstones, writes as awaited tasks, aliases emulated by core, lexical clauses as filters on shadow attributes, vectors as user-provided embeddings, hybrid fusion left to core; nothing leaked upward into the ports. - 1.0.0 (2026-10-27): every work package done, CI green on every engine since the repository went public, the
version line at
^1.0/1.x-dev. Next: the per-package read-only repositories and Packagist. - Local services for the conformance and determinism suites:
docker compose up -d, then export.env.testing.
The rules
Section titled “The rules”composer qamust pass (phpcs, phpstan level 8,check-imports, phpunit) before any commit is proposed. Do not lower the level or skip tests to get green.- Dependency isolation is mechanical.
bin/check-importsfails onAltair\,Cycle\orUniveros\outsidepackages/univeros;Polaris\outsidepackages/polarisandpackages/univeros; an engine client outside its adapter package; the Symfony console outsidepackages/cliand the host packages. Core depends on PSR interfaces,symfony/uid,symfony/yamlandext-sodiumonly. - Dumb adapters, smart neutral core. An engine adapter implements
SearchAdapter,SchemaAdapter,WriteAdapterand reportsCapability; diffing, reindex, alias swap, replay and fusion live once in core. If an adapter needs orchestration logic, the core is missing something. - Determinism on the lifecycle, never on ranking. Plan, apply, reindex and swap are pure and reproducible; result order is not, and nothing may claim otherwise.
- The outbox commits with the entity write, on the same connection.
Outbox::record()runs inside the caller’s transaction on the host’sStorage. Never open a second connection for it. - Tenancy is explicit.
Tenancy::single()or aTenantResolver;TenantScopeis injected beneath the AST into every compiled query, escape hatch included. No code path may skip it. - Fail closed. An unrecognised schema change is breaking; an unsupported clause is a typed
UnsupportedClause; a capability an engine lacks is routed around or refused, never silently wrong. - Proof is the product. Every engine adapter extends and passes the conformance test case before it ships; the determinism harness and fault injection gate the lifecycle WPs.
- House style.
declare(strict_types=1),final readonlyvalue objects, immutable updates (return a copy), small files, tests first, UUID v7 ids, no hard-coded values.
How to work a work package
Section titled “How to work a work package”- Read
docs/spec/revision.md§7 for the WP’s scope and acceptance criteria. - Work on
maindirectly, or on a localwpNbranch fast-forwarded intomain. No pull requests. - Write the failing test, then the smallest change that meets the criteria; run
composer qacontinuously. - Record decisions in
docs/decisions.md, append only. - Commit in coherent steps, conventional format (
feat:,fix:,refactor:,docs:,test:,chore:), authored by the owner only: noCo-Authored-Byor any other attribution trailer, ever. - Close the WP’s epic with a comment listing each acceptance criterion and how it was verified.
Layout
Section titled “Layout”packages/core/{src,tests} orion-search/core Orion\ the framework-free core (revision.md §6)packages/testing/src orion-search/testing Orion\Testing\ in-memory storage, conformance cases, fakespackages/<engine> orion-search/<engine> one engine adapter each, own client dependency (from WP2)packages/pdo, psr15, cli, polaris storage, the search: endpoints over PSR-15, bin/orion, Polaris tenancy bridgepackages/univeros univeros/orion Univeros\Orion\ the Univeros moduleexamples/univeros the demo host: bin/setup, bin/walkthrough.sh (needs Elasticsearch)docs/ spec, revision, decisions.ai/skills/orion/SKILL.md the agent skillbin/check-imports the dependency-isolation gateCommands
Section titled “Commands”composer qa # cs + stan + imports + test (service suites skip unless .env.testing is exported)composer test # phpunitcomposer stancomposer cs-fixbin/check-imports # alonepackages/cli/bin/orion --bootstrap=<file returning Orion or Config> listNamespaces you will see and must not confuse
Section titled “Namespaces you will see and must not confuse”Orion\— the agnostic core and its packages (this repository).Univeros\Orion\— the Univeros module,packages/univerosonly.Altair\*— the Univeros framework,packages/univerosonly.Polaris\*—polaris/core,packages/polarisandpackages/univerosonly.