Framework Convergence Proposal After Three Case Studies¶
Date: 2026-08-07
Status: implementation handoff. This proposal contains only changes with a defined implementation and acceptance path. Open research questions are listed separately in the deferred research register.
Task¶
Finish moving the remaining generic improvements learned from the three case studies into Loreley so future experiments need target adapters, not a second orchestration system. Preserve existing databases and one-shot evaluator plugins. Do not rerun a case study as part of this work.
Completion requires framework code, migrations, configuration, tests, and user
documentation. A feature present only in tools/*_experiment does not satisfy
completion.
Scope¶
The framework should own:
- restart-stable sampling and duplicate-recipe provenance;
- exact-source and evaluator-relevant candidate identity;
- safe reuse of expensive measurements;
- evaluator concurrency independent of search and model-worker concurrency;
- identity-aware progress and stopping;
- phase-specific model configuration, preflight, and usage reporting; and
- concise commit metadata derived from existing agent reports.
Target adapters should continue to own repository scope, seeds, build commands, correctness checks, corpora, benchmarks, metric definitions, precision rules, and promotion thresholds.
The implementation must not add a model-request proxy, a default request cap, a local price reconstruction path for Kilo, Zstandard-specific schema fields, or a generic hidden-data system.
Current implementation state¶
Already merged on main¶
- Per-job and per-run-token Kilo state isolation.
- Kilo workspace verification, headless tool policy, and optional
--pure. - POSIX process-group timeout cleanup.
- Root-plus-descendant Kilo session usage and catalog-cost aggregation.
- Preservation of unpriced Kilo costs without local reconstruction.
- Campaign constraints take precedence over generic agent validation advice.
Framework foundation delivered by this closeout¶
- Separate planning and coding Kilo model settings.
- Explicit trajectory-summary provider, model, API surface, thinking mode, and reasoning effort, with preflight validation and usage recording.
- Deterministic commit messages from
coding.summary, thenplan.summary, with no commit-summary LLM call. - Persistent per-island sampling ordinals, restart-stable RNG derivation, order-insensitive recipe hashes, bounded recipe cooldown, and explicit reuse provenance.
- Exact source-tree hashing and reuse of a passed result under the same evaluator and campaign contract.
- Evaluator-provided candidate identity, a contract-scoped identity key, identity persistence, and duplicate archive-admission prevention.
- Full change summaries and migrations 16-18.
These items are not remaining work packages. The implementation agent must preserve them and must not reimplement them from historical experiment scripts.
Foundation regression contract¶
Keep the three identities distinct while implementing the remaining work:
- commit hash for ancestry and reproducibility;
- Git tree hash for exact source equality before evaluation; and
- evaluator identity for target-defined behavioral or artifact equality.
Run these focused regressions before and after the new implementation:
uv run pytest \
tests/core/map_elites/test_sampler.py \
tests/core/worker/test_candidate_identity.py \
tests/core/worker/test_commit_summary.py \
tests/core/worker/test_evolution.py \
tests/core/worker/test_job_store.py \
tests/core/worker/test_trajectory.py \
tests/scheduler/test_ingestion_resilience.py \
tests/db/test_migration_registry.py \
tests/db/test_migration_v0016.py \
tests/db/test_migration_v0017.py \
tests/db/test_migration_v0018.py \
tests/test_cli_config_dump.py \
tests/test_preflight.py
These invariants must remain true:
- restarting a scheduler does not restart its sampling stream;
- the recent recipe cooldown survives a restart and records unavoidable reuse;
- identical Git trees reuse only a passed result with the same evaluator name, evaluator version, and campaign-program hash;
- failed results never populate a reusable result;
- evaluator-equivalent commits do not occupy duplicate archive entries;
- trajectory summarization fails preflight when enabled without a resolvable model route;
- planning, coding, trajectory, and embedding usage retain their phase and model; and
- old databases migrate forward without deleting jobs, candidates, metrics, or artifacts.
Work package 1: move measurement reuse into the evaluator contract¶
Problem¶
Loreley can reuse an exact Git tree before evaluation and can deduplicate an
evaluator identity after evaluation. It cannot avoid an expensive measurement
when two different source trees produce the same binary, because the current
one-shot evaluator reveals candidate_identity only after the complete plugin
call. The Zstandard harness therefore implemented its own binary lock, accepted
measurement index, and cache.
Required behavior¶
Add an optional phased evaluator protocol while retaining the existing one-shot plugin API:
- Prepare and identify performs source-specific gates required before reuse, builds the candidate when needed, and returns an evaluator identity plus preparation artifacts.
- Measure performs the expensive cacheable work when no accepted measurement exists.
- Finalize combines source-specific preparation evidence with either the
new or reused measurement into the normal
EvaluationOutcome.
The exact public names may follow existing evaluator conventions, but all three states must be explicit. Do not reuse an entire source evaluation merely because the binary matches: source-level scope, tests, or portability checks may still differ.
Persist accepted measurements in framework tables. The cache key must include:
- normalized evaluator-provided candidate identity;
- evaluator name and version;
- campaign-program hash; and
- a plugin-supplied measurement-contract fingerprint when the evaluator version does not already cover corpus, benchmark, build mode, and metric protocol.
Only a completed, passed, plugin-marked-cacheable measurement may be reused. Failures, timeouts, incomplete artifacts, precision rejections, and results from a different contract must miss the cache.
Serialize the first measurement of one key across workers. Prefer a PostgreSQL-backed lock whose ownership ends automatically with the database session. After acquiring the lock, check the cache again before measuring.
Every reuse must create a new evaluation-attempt record that points to the
original accepted measurement and records measurement_reused=true. Copying
metrics without provenance is not acceptable.
Acceptance tests¶
- Two different trees with one evaluator identity run source-specific prepare twice but expensive measure once.
- Two concurrent workers racing on one identity produce one measurement and one reuse.
- A failed, timed-out, or imprecise first measurement is never reused.
- Changing evaluator version, campaign program, or measurement fingerprint forces a new measurement.
- A one-shot legacy plugin behaves exactly as before.
- A reused result exposes original attempt, artifact hashes, and cache key in the API and database.
Work package 2: make evaluator concurrency a framework control¶
Problem¶
Loreley exposes the algorithm limit on unfinished jobs and the physical model worker count, but the case studies enforced evaluator lanes with target-specific file locks. The three controls have different meanings:
U: maximum unfinished jobs visible to the search algorithm;W: physical planning/coding worker processes; andE: simultaneous evaluator measurements allowed by the host or service.
Coupling them changes archive dynamics or measurement quality when only machine parallelism should change.
Required behavior¶
Add an optional framework setting such as
WORKER_EVALUATOR_MAX_CONCURRENCY. Enforce it across processes and hosts for one
experiment and evaluator contract. Use a database-backed slot or equivalent
lease that is released after normal exit, exception, timeout, or worker death.
Do not implement it as a process-local semaphore.
For a phased evaluator, the plugin must declare whether the limit covers the whole evaluator or only the measurement phase. For a legacy one-shot plugin, the limit covers the complete plugin call.
Record slot number, wait time, acquisition time, and release outcome in the
evaluation attempt. Surface effective U, known local W, configured E, and
current evaluator waiters in loreley status and loreley config dump without
pretending that a distributed global worker count is known when it is not.
Acceptance tests¶
W=4, E=1never overlaps evaluator calls while planning/coding remains concurrent.W=4, E=4permits four evaluator calls.- Changing
Udoes not silently changeE, and changingEdoes not alter the sampler's unfinished-job limit. - A killed evaluator releases or expires its slot and does not deadlock the campaign.
- Wait time is persisted and visible in status output.
Work package 3: identity-aware progress and stopping¶
Problem¶
Physical jobs overstated useful Zstandard coverage. The V19 scripts had to query the database directly to count source trees, binaries, real measurements, and cache reuse, and to stop after a target number of new binaries.
Required behavior¶
Extend the normal status surface with:
- terminal, succeeded, failed, running, and queued jobs;
- distinct passed Git trees;
- distinct passed evaluator identities;
- real measurements and measurement reuses;
- archive entries and unique evaluator identities represented in the archive;
- occupied coordinates; and
- failure counts by stable failure kind.
Add an optional at-least endpoint such as
SCHEDULER_MAX_UNIQUE_EVALUATION_IDENTITIES. Once the count reaches the target,
the scheduler must stop new dispatch and drain existing unfinished jobs. Report
the target and any bounded asynchronous overshoot. Do not manufacture an exact
physical-job count from an identity endpoint.
The feature is inactive when evaluators do not provide identities. Preflight must reject an identity endpoint that cannot be satisfied by the configured evaluator contract.
Acceptance tests¶
- repeated commits and trees with one evaluator identity increment physical job counts but not the unique-identity count;
- reaching the identity target stops new dispatch and drains existing work;
- status clearly distinguishes archive entries, occupied coordinates, Git trees, evaluator identities, and measurements; and
- scheduler restart preserves the endpoint decision.
Work package 4: finish model and embedding configuration DFX¶
Keep the current phase-specific settings and make the effective routing visible
before a campaign starts. loreley doctor --role worker and config dump must
show, without secrets:
- planning backend, provider mode, model, and variant/reasoning setting;
- coding backend, provider mode, model, and variant/reasoning setting;
- trajectory-summary provider, API surface, model, thinking mode, and reasoning effort;
- embedding provider route, model, and dimensions; and
- that commit summaries make no model call.
text-embedding-3-small remains the default. Keep deterministic local-hash
embeddings available for tests and offline examples, but emit a prominent
preflight warning when they are used for an optimization campaign. Require an
explicit acknowledgement setting if a campaign wants to continue with them.
Continue treating Kilo session-tree cost as authoritative for Kilo. Report provider-reported, catalog, locally estimated, unpriced, and unavailable costs as different categories. Never turn a zero or missing Kilo cost into a local price estimate.
Work package 5: retire generic harness responsibilities¶
After work packages 1-4 land, document the boundary for future case studies. A target adapter may contain:
- seed patches and scope rules;
- build, correctness, compatibility, and benchmark commands;
- corpus construction and hidden-data handling;
- target-specific precision and result thresholds; and
- report-specific selection rules.
It must not reimplement Kilo routing, usage aggregation, process cleanup, sampling restart logic, source-tree reuse, evaluator identity persistence, measurement caching, evaluator concurrency, or identity-aware status.
Do not delete historical experiment code or evidence in this work. Mark it as frozen and stop extending it after equivalent framework functionality exists.
Verification and exit gates¶
Run the foundation regressions above, all new tests for work packages 1-4, then:
uv run pytest
uv run mkdocs build --strict
Before completion, verify:
- database migration from the last released schema and idempotent migration on the current schema;
- no API key, base URL credential, machine-local absolute path, or experiment corpus entered tracked fixtures or documentation;
- legacy one-shot evaluators and campaigns without candidate identities retain their behavior;
- the Zstandard target adapter can express its binary identity and measurement cache through the new public contract without importing Loreley internals;
- all reuse records are hash-linked to the original accepted evidence; and
- no target-specific metric or promotion rule entered the core framework.
The work is complete only when a minimal zero-model integration test runs two source-distinct candidates with one evaluator identity, performs one expensive measurement, records two source attempts, admits one evaluator identity, and stops at an identity endpoint without target-harness database queries.