<feed xmlns='http://www.w3.org/2005/Atom'>
<title>phorge-elasticsearch-modern.git/src/engine, branch production</title>
<subtitle>Phorge full-text search extension for Elasticsearch 7.x / 8.x / OpenSearch via the typeless API (mirror of https://github.com/vyos/phorge-elasticsearch-modern.git)
</subtitle>
<id>https://git.amelek.net/vyos/phorge-elasticsearch-modern.git/atom?h=production</id>
<link rel='self' href='https://git.amelek.net/vyos/phorge-elasticsearch-modern.git/atom?h=production'/>
<link rel='alternate' type='text/html' href='https://git.amelek.net/vyos/phorge-elasticsearch-modern.git/'/>
<updated>2026-05-23T14:16:51+00:00</updated>
<entry>
<title>Engine: fix negative pagination bounds and _ts accumulation</title>
<updated>2026-05-23T14:16:51+00:00</updated>
<author>
<name>Yuriy Andamasov</name>
<email>yuriy@vyos.io</email>
</author>
<published>2026-05-23T14:16:51+00:00</published>
<link rel='alternate' type='text/html' href='https://git.amelek.net/vyos/phorge-elasticsearch-modern.git/commit/?id=e5fbde8bb9b21d264f71f25881b44db1af3d03c6'/>
<id>urn:sha1:e5fbde8bb9b21d264f71f25881b44db1af3d03c6</id>
<content type='text'>
Two correctness bugs surfaced by CR / Phase 0, both inherited from
the bundled PhabricatorElasticFulltextStorageEngine pattern:

1. executeSearch() did not validate that $offset / $limit were
   non-negative. PHP's (int) cast on a negative string yields a
   negative int, which would be passed unchecked to Elasticsearch.
   Add an explicit non-negative check before the existing upper-
   bound (offset + limit &gt; 10000) check.

2. reindexAbstractDocument()'s relationship-data loop assigned the
   '_ts' field unconditionally instead of accumulating. For multi-
   valued relationships (multiple authorPHID, projectPHID, etc.
   entries on the same doc), only the last timestamp survived.
   Change to array-append, parallel to the PHID array.

Both fixes diverge from the bundled engine; bundled retains the
faithful-but-broken behavior. Worth fixing in this extension since
we control the wire format.

🤖 Generated by [robots](https://vyos.io)
</content>
</entry>
<entry>
<title>Engine: revert abstract → plain class for production dispatch</title>
<updated>2026-05-23T12:48:18+00:00</updated>
<author>
<name>Yuriy Andamasov</name>
<email>yuriy@vyos.io</email>
</author>
<published>2026-05-23T12:48:18+00:00</published>
<link rel='alternate' type='text/html' href='https://git.amelek.net/vyos/phorge-elasticsearch-modern.git/commit/?id=011eec12f9176a6a8b8d8b9133bc00107a7855c0'/>
<id>urn:sha1:011eec12f9176a6a8b8d8b9133bc00107a7855c0</id>
<content type='text'>
A recent fixup cascade declared VyOSElasticModernFulltextStorageEngine
as 'abstract class' to enable test-doubling subclasses
(VyOSElasticModernHostTestEngine, VyOSElasticModernFulltextStorageEngineTestDouble).

That change broke production dispatch: Phorge's PhutilClassMapQuery
selects engines via PhutilSymbolLoader::loadObjects(), which calls
setConcreteOnly(true) and unsets any class where reflection reports
isAbstract(). The engine class would no longer be discoverable by
cluster.search[].type:elasticsearch-modern, and the test-fixture
subclasses live in src/__tests__/ which libphutil excludes from
runtime class-map loading.

Revert to plain 'class' (not 'final', not 'abstract'). Plain
'class' is discoverable by PhutilClassMapQuery; existing test
fixtures keep working without rewrite; operators can subclass if
they need to (harmless).

Also update VyOSElasticModernHostTestCase::testPlaceholderEngineIsAbstract
→ testEngineIsConcreteForDispatch, inverting the assertion to document
the correct invariant and its rationale (setConcreteOnly reference).

All 22 tests pass.

🤖 Generated by [robots](https://vyos.io)
</content>
</entry>
<entry>
<title>Address PR 16 CR findings</title>
<updated>2026-05-23T12:35:17+00:00</updated>
<author>
<name>Yuriy Andamasov</name>
<email>yuriy@vyos.io</email>
</author>
<published>2026-05-23T12:35:17+00:00</published>
<link rel='alternate' type='text/html' href='https://git.amelek.net/vyos/phorge-elasticsearch-modern.git/commit/?id=a19f5c077c0ed8ee4d88c0c8d704995b155d3dc2'/>
<id>urn:sha1:a19f5c077c0ed8ee4d88c0c8d704995b155d3dc2</id>
<content type='text'>
- README cluster.search example uses 'phabricator/' to match host
  default (was '/phabricator'; both work for index name but the
  host uses path verbatim for the base URI).
- Add VERIFICATION.md placeholder with the planned smoke-test matrix
  template; status notes v0.1.0 is pending IS-474.
- Engine reindex: replace 'if ($time)' falsy check with 'if ($time
  !== null)' so a Unix epoch timestamp (0) is not silently dropped.

🤖 Generated by [robots](https://vyos.io)
</content>
</entry>
<entry>
<title>fix: correct Phase-0 findings from promotion PR review (3 issues)</title>
<updated>2026-05-23T12:14:02+00:00</updated>
<author>
<name>Yuriy Andamasov</name>
<email>yuriy@vyos.io</email>
</author>
<published>2026-05-23T12:14:02+00:00</published>
<link rel='alternate' type='text/html' href='https://git.amelek.net/vyos/phorge-elasticsearch-modern.git/commit/?id=4d27ac74aff1ca47db53510bf07e9d083635175b'/>
<id>urn:sha1:4d27ac74aff1ca47db53510bf07e9d083635175b</id>
<content type='text'>
1. TestDouble signatures: indexExists() and getIndexStats() in
   VyOSElasticModernFulltextStorageEngineTestDouble were missing the
   nullable host parameter added in E7/E8, causing a PHP fatal on
   'arc unit --everything'.

2. buildSpec() zero-query sort bug: the falsy check
   (!$query-&gt;getParameter('query')) incorrectly treats the string '0'
   as empty (PHP falsy), enabling date-sorted path for a real search
   term. Changed to explicit null/empty-string checks to match the
   existing must-clause guard on line 316.

All 22 unit tests now pass.

🤖 Generated by [robots](https://vyos.io)
</content>
</entry>
<entry>
<title>fix: use correct HTTPFutureResponseStatus in indexExists() catch clause</title>
<updated>2026-05-23T12:08:18+00:00</updated>
<author>
<name>Yuriy Andamasov</name>
<email>yuriy@vyos.io</email>
</author>
<published>2026-05-23T12:08:18+00:00</published>
<link rel='alternate' type='text/html' href='https://git.amelek.net/vyos/phorge-elasticsearch-modern.git/commit/?id=e36bce7dee75eccf67ae4737452275a6a01d7bb9'/>
<id>urn:sha1:e36bce7dee75eccf67ae4737452275a6a01d7bb9</id>
<content type='text'>
Phase 0 CodeRabbit caught a typo — HTTPFutureHTTPResponseStatus does not
exist in the libphutil type hierarchy; the correct class is
HTTPFutureResponseStatus (consistent with line 257 in executeRequest()).

🤖 Generated by [robots](https://vyos.io)
</content>
</entry>
<entry>
<title>E8 fixup: four correctness fixes from Phase 0 review</title>
<updated>2026-05-23T10:55:22+00:00</updated>
<author>
<name>Yuriy Andamasov</name>
<email>yuriy@vyos.io</email>
</author>
<published>2026-05-23T10:54:24+00:00</published>
<link rel='alternate' type='text/html' href='https://git.amelek.net/vyos/phorge-elasticsearch-modern.git/commit/?id=4d836601cb2f602c0089134eda1604f605fb39da'/>
<id>urn:sha1:4d836601cb2f602c0089134eda1604f605fb39da</id>
<content type='text'>
1. strlen(\$query_string) → null-safe check
   \$query-&gt;getParameter('query') may return null; strlen(null) is
   deprecated in PHP 8.1+. Switch to explicit !== null &amp;&amp; !== '' check.

2. ids.values nesting fix in buildSpec()
   'values' =&gt; array(\$exclude) wraps an already-array \$exclude in a
   nested array, producing invalid Elasticsearch ids syntax. Cast to
   (array) then re-index with array_values() so both scalar PHID and
   array-of-PHIDs produce a flat list.

3. initIndex() host consistency: pass write host to indexExists()
   initIndex() called indexExists() without a host argument, which
   fell back to the read host, then deleted via getHostForWrite().
   A read/write split lag window could cause false-positive
   "index doesn't exist" results. Pass the already-resolved write host.

4. getIndexStats() unchecked array access
   \$res['indices'][\$this-&gt;index] was accessed without verifying the
   key exists. If the index was just deleted or the name drifted, this
   throws an undefined-offset PHP notice. Add an explicit isset() guard
   with a clear exception message naming the index.

🤖 Generated by [robots](https://vyos.io)
</content>
</entry>
<entry>
<title>E6-E8: Apply code-quality fixes from review</title>
<updated>2026-05-23T10:48:42+00:00</updated>
<author>
<name>Yuriy Andamasov</name>
<email>yuriy@vyos.io</email>
</author>
<published>2026-05-22T09:00:28+00:00</published>
<link rel='alternate' type='text/html' href='https://git.amelek.net/vyos/phorge-elasticsearch-modern.git/commit/?id=51ceab16c95efc5527445788cb83f0289675d0bf'/>
<id>urn:sha1:51ceab16c95efc5527445788cb83f0289675d0bf</id>
<content type='text'>
- Remove dead $fields assignment in buildSpec() (copy-paste artifact
  from bundled engine; never actually used).
- Revert executeSearch catch from Throwable back to Exception. The
  earlier lint-driven widening was over-broad: Error subclasses
  (TypeError, etc.) should propagate immediately, not be aggregated
  into the per-host failover loop. Comment added explaining the
  intent.

🤖 Generated by [robots](https://vyos.io)
</content>
</entry>
<entry>
<title>Engine: Wire executeSearch, index lifecycle, buildSpec</title>
<updated>2026-05-23T10:48:42+00:00</updated>
<author>
<name>Yuriy Andamasov</name>
<email>yuriy@vyos.io</email>
</author>
<published>2026-05-22T08:52:43+00:00</published>
<link rel='alternate' type='text/html' href='https://git.amelek.net/vyos/phorge-elasticsearch-modern.git/commit/?id=58ab9f6cacf8735adec2413e7404b34fc129f8a5'/>
<id>urn:sha1:58ab9f6cacf8735adec2413e7404b34fc129f8a5</id>
<content type='text'>
Full public surface lands. executeSearch normalizes \$types before
both URL building and body filter construction. buildSpec uses the
bundled PhabricatorElasticsearchQueryBuilder (final, by composition)
and emits bool.must_not.ids for the exclude parameter from day one.
buildSpec also injects the body-level documentType filter via
buildTypeFilter. indexExists/indexIsSane/initIndex/getIndexStats
mirror the bundled engine but without the version-branching dead
code. The private check() and normalizeConfigValue() helpers are
copied verbatim from the bundled engine (no behavior difference).

catch (Exception) widened to catch (Throwable) per XHP132.
normalizeConfigValue() one-liners braced per XHP24. Lint clean.

🤖 Generated by [robots](https://vyos.io)
</content>
</entry>
<entry>
<title>E7 fixup: move didHealthCheck(true) to HTTP-success boundary in executeRequest()</title>
<updated>2026-05-23T10:47:45+00:00</updated>
<author>
<name>Yuriy Andamasov</name>
<email>yuriy@vyos.io</email>
</author>
<published>2026-05-23T10:47:45+00:00</published>
<link rel='alternate' type='text/html' href='https://git.amelek.net/vyos/phorge-elasticsearch-modern.git/commit/?id=3a4580e35a0c7a6480aeee9ec4acf90d0fa85326'/>
<id>urn:sha1:3a4580e35a0c7a6480aeee9ec4acf90d0fa85326</id>
<content type='text'>
Previously the success health-check was called inside the JSON-decode
try block. This was wrong: a valid HTTP 200 response with non-JSON
body (e.g. ES maintenance page) would mark the host as *unhealthy*
because JSON parse failure triggered didHealthCheck(false). The host
is demonstrably reachable and responding; only the application layer
failed.

Move didHealthCheck(true) to immediately after list($body) = resolvex()
succeeds, before the GET/non-GET branch. Remove the now-unnecessary
didHealthCheck(false) from the JSON catch block — only network/timeout
failures belong in the health-check false path.

🤖 Generated by [robots](https://vyos.io)
</content>
</entry>
<entry>
<title>Engine: Add reindexAbstractDocument() + executeRequest()</title>
<updated>2026-05-23T10:44:49+00:00</updated>
<author>
<name>Yuriy Andamasov</name>
<email>yuriy@vyos.io</email>
</author>
<published>2026-05-22T08:48:30+00:00</published>
<link rel='alternate' type='text/html' href='https://git.amelek.net/vyos/phorge-elasticsearch-modern.git/commit/?id=e0eb6dd6b21c129c775d8bb2bec203ae0d756d0f'/>
<id>urn:sha1:e0eb6dd6b21c129c775d8bb2bec203ae0d756d0f</id>
<content type='text'>
reindexAbstractDocument builds the document body with documentType
as a real field (replacing the bundled engine's per-type URL
segment), then PUTs to the typeless /_doc/{phid} URL via the new
helper. executeRequest mirrors the bundled engine's HTTPSFuture
pattern; it's private and not test-targeted directly (smoke tests
in V1-V3 exercise the full wire path).

🤖 Generated by [robots](https://vyos.io)
</content>
</entry>
</feed>
