Skip to content

v0.7.0 — the pre-1.0 hardening

Choose a tag to compare

@mrDlef mrDlef released this 18 Aug 17:55
· 85 commits to main since this release

the pre-1.0 hardening

Fingerprints: q3: unchanged.

Nothing here moves a hash. It is the work of deciding what a 1.0.0 would be
promising, before promising it.

What is public, and what is not

Every class in src/ is now marked @api or @internal, and ApiBoundaryTest
fails the suite if one is marked neither, marked both, or if a public method
hands back an internal type — a type reachable from a public signature is public
whatever its annotation claims.

Fourteen classes are the public surface. The parser, the tree, the
renderers, the canonicaliser and the hasher are not, and that is the point:
those are exactly the classes that change whenever a query type is promoted.
Frozen, every improvement to the rendering would be a major release.

Breaking: IndexNormalizer moves from Support\ to the root namespace,
beside Normalization, the sibling concept it is configured with.

use MrDlef\OsQueryDigest\Support\IndexNormalizer;   // before
use MrDlef\OsQueryDigest\IndexNormalizer;           // after

The elasticsearch keyword and mention are gone from composer.json. The DSL
overlaps enough that the library is useful against Elasticsearch; that is not
the same as promising it when no ES-specific type was ever classified or
certified.

Teaching it a query type it does not know

Options::withClauseRenderer() takes an Extension\ClauseRenderer for a type
the library leaves opaque — the Learning-to-Rank plugin's sltr, or a query
type private to your cluster.

before  q=(sltr(?))
after   q=(_score:sltr(model=ltr_model_v3))

Three properties make it safe: a renderer cannot reach a natively modelled type
(the hook sits in the parser's default branch, so term has already returned);
the hash version is marked q3x: as soon as one is registered, because the
rules are then no longer this library's alone; and explain() reports
extension_rendered.

Would the tests notice?

Mutation testing runs in CI — make mutation locally. Nothing in src/ is
uncovered and the covered score is 79%, guarded so it cannot quietly fall.

It found three real gaps on its first run: ABSORB_MATCH_NONE was recorded from
two branches and tested on one, UNWRAP's guard was unpinned, and Hasher
carried a second copy of the defaults that live in Options — unreachable, and
free to drift from the values actually used.

What it costs

make bench measures the request path against the committed fixtures. The
pitch — that you can afford a digest on every search — was an argument until
now: ~30 µs per request on PHP 8.5, ~37 µs on 7.4, against a search that
takes milliseconds. lazy() costs 0.2 µs, some hundred times less, so a debug
record your handler drops really does parse nothing.

No timing gate in CI: wall-clock on a shared runner is noise, and a threshold
tight enough to catch a regression would fail on a busy afternoon.

This file

CHANGELOG.md is new, and it is the source: release notes are extracted from
it, so they are reviewed in the pull request that ships the change rather than
written after the tag. tools/changelog.php check holds each entry to the
hashes pinned in tests/fixtures.