Replace 0x27F971cb582BF9E50F397e4d29a5C7A34f11faA2 (Base Sepolia
NonfungiblePositionManager) with the correct Base mainnet address
0x03a520B32c04bf3beef7BEb72E919cF822Ed34F3 in all four files that
referenced it, and add an inline comment citing the chain and source.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add fitness_flags="token_value_inflation" to evo_run004_champion in
manifest.jsonl so callers can detect the inflated value without
discarding the entry entirely.
- Add effective_fitness() helper in evolve.sh pool admission (step 5)
that returns 0 for any entry with a token_value_inflation flag,
preventing inflated scores from biasing the top-100 evolved pool
ranking or eviction decisions.
- Document in evolve.sh that raw fitness values are only comparable
within the same evaluation run.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fixes#667
## Changes
## Summary
Implemented persistent top-100 candidate pool in `tools/push3-evolution/evolve.sh`:
### Changes
**`--run-id <N>` flag** (line 96)
- Optional integer; auto-increments from highest `run` field in `manifest.jsonl` when omitted
- Zero-padded to 3 digits (`001`, `002`, …)
**Seeds pool constants** (after path canonicalization)
- `SEEDS_DIR` → `$SCRIPT_DIR/seeds/`
- `POOL_MANIFEST` → `seeds/manifest.jsonl`
- `ADMISSION_THRESHOLD` → `6000000000000000000000` (6e21 wei)
**`--diverse-seeds` mode** now has two paths:
1. **Pool mode** (pool non-empty): random-shuffles the pool and takes up to `POPULATION` candidates — real evolved diversity, not parametric clones
2. **Fallback** (pool empty): original `seed-gen-cli` parametric variant behavior
- Both paths fall back to mutating `--seed` to fill any shortfall
**Step 5 — End-of-run admission** (after the diff step):
1. Scans all `generation_*.jsonl` in `OUTPUT_DIR` for candidates with `fitness ≥ 6e21`
2. Maps `candidate_id` (e.g. `gen2_c005`) back to `.push3` files in `WORK_DIR` (still exists since cleanup fires on EXIT)
3. Deduplicates by SHA-256 content hash against existing pool
4. Names new files `run{RUN_ID}_gen{N}_c{MMM}.push3`
5. Merges with existing pool, sorts by fitness descending, keeps top 100
6. Copies admitted files to `seeds/`, removes evicted evolved files (never hand-written), rewrites `manifest.jsonl`
Co-authored-by: openhands <openhands@all-hands.dev>
Reviewed-on: https://codeberg.org/johba/harb/pulls/683
Reviewed-by: review_bot <review_bot@noreply.codeberg.org>
- Extract magic number into named constant MAX_ANCHOR_WIDTH = 100 in LiquidityManager.sol
- Document effective ceiling in IOptimizer.sol natspec for anchorWidth return value
- Add testAnchorWidthAbove100IsClamped in LiquidityManager.t.sol asserting that
optimizer-returned anchorWidth=150 is silently clamped to 100 (not rejected)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Seeded with current reality. Dev-agent appends one line per merge (before merge, on the PR branch — goes through review). Planner will collapse into compact snapshot periodically.
Ref: dark-factory#5
Co-authored-by: openhands <openhands@all-hands.dev>
Reviewed-on: https://codeberg.org/johba/harb/pulls/680
Track CLAUDE_PID before launching the claude subprocess so cleanup()
can kill it before reverting Anvil state. Running claude via `&` +
`wait` lets the trap fire immediately on INT/TERM, killing the
subprocess and preventing it from making calls against an
already-reverted chain.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace _positionEthValue() with _positionEthOnly() in FitnessEvaluator.t.sol.
The new function returns only the WETH component of each position (amount0 if
token0isWeth, else amount1), ignoring KRK token value entirely. This prevents
evolution from gaming the fitness metric by inflating KRK price through position
placement — the score now reflects actual ETH reserves only.
Also removes the now-unused FullMath import.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
## Three bugs in evolve.sh
1. **Heredoc stdin conflict** — `py_stats()` used `<<PYEOF` heredoc which stole stdin from the pipe, so python never received score values → stats always `min=0 max=0 mean=0`
2. **Bash integer overflow** — global best comparison used `[ $MAX -gt $GLOBAL_BEST_FITNESS ]` which overflows on uint256 wei values (>9.2e18) → best always tracked as 0
3. **candidate_id mismatch** — evolve.sh looked up `gen0_c000` but batch-eval produces `candidate_000` (derived from filename) → score lookup always returned default 0
All 3 previous evolution runs (150+ candidates) reported all zeros despite batch-eval correctly scoring them at ~8.26e21 wei.
## Fix
- `py_stats`: heredoc → `python3 -c` inline
- Global best: bash `[ -gt ]` → `python3` big number comparison
- Score lookup: use `basename $CAND_FILE` instead of synthetic CID
Co-authored-by: root <root@debian-g-2vcpu-8gb-ams3-01>
Reviewed-on: https://codeberg.org/johba/harb/pulls/665
Reviewed-by: review_bot <review_bot@noreply.codeberg.org>
Add IOptimizer interface with getLiquidityParams() signature to IOptimizer.sol
so upgrade-compatibility is explicit and static analysis can catch ABI mismatches.
Update LiquidityManager to hold optimizer as IOptimizer instead of concrete Optimizer.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fixes#635
## Changes
The implementation is complete and committed. All 211 tests pass.
## Summary of changes
### `onchain/src/Optimizer.sol`
- **Replaced raw slot inputs** with normalized indicators in `getLiquidityParams()`:
- Slot 2 `pricePosition`: where current price sits within VWAP ± 11 000 ticks (0 = lower bound, 0.5e18 = at VWAP, 1e18 = upper bound)
- Slot 3 `volatility`: `|shortTwap − longTwap| / 1000 ticks`, capped at 1e18
- Slot 4 `momentum`: 0 = falling, 0.5e18 = flat, 1e18 = rising (5-min vs 30-min TWAP delta)
- Slot 5 `timeSinceRecenter`: `elapsed / 86400s`, capped at 1e18
- Slot 6 `utilizationRate`: 1e18 if current tick is within anchor position range, else 0
- **Extended `setDataSources()`** to accept `liquidityManager` + `token0isWeth` (needed for correct tick direction in momentum/utilizationRate)
- **Added `_vwapToTick()`** helper: converts `vwapX96 = price × 2⁹⁶` to tick via `sqrt(vwapX96) << 48`, with TickMath bounds clamping
- All slots gracefully default to 0 when data sources are unconfigured or TWAP history is insufficient (try/catch on `pool.observe()`)
### `onchain/src/OptimizerV3Push3.sol`
- Updated NatSpec to document the new `[0, 1e18]` slot semantics
### New tests (`onchain/test/`)
- `OptimizerNormalizedInputsTest`: 18 tests covering all new slots, token ordering, TWAP fallback, and a bounded fuzz test
- `mocks/MockPool.sol`: configurable `slot0()` + `observe()` with TWAP tick math
- `mocks/MockLiquidityManagerPositions.sol`: configurable anchor position bounds
Co-authored-by: openhands <openhands@all-hands.dev>
Reviewed-on: https://codeberg.org/johba/harb/pulls/649
Reviewed-by: review_bot <review_bot@noreply.codeberg.org>
- evolve.sh: fix fail-in-subshell bug — run seed-gen-cli as a direct
command so its exit code is checked by the parent shell and fail()
aborts the script correctly; redirect stderr to log file instead of
discarding it with 2>/dev/null
- seed-generator.ts: reorder enumerateVariants() to put
STAKED_THRESHOLDS outermost (192 entries/block) so that
selectVariants(6) with stride=192 covers all 6 staked% thresholds;
remove false doc claim about "first variant is current seed config";
add comments explaining CI=0n is intentional in all presets
- seed-gen-cli.ts: emit a stderr diagnostic when count exceeds the
1152-variant cap so the cap is visible rather than silently producing
fewer files than requested
- test: strengthen n=6 test to assert all STAKED_THRESHOLDS values are
represented in the selected variants
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add seed-generator.ts module and seed-gen-cli.ts CLI that produce
parametric Push3 variants for initial population seeding.
Variants systematically cover:
- Staked% thresholds: 80, 85, 88, 91, 94, 97
- Penalty thresholds: 30, 50, 70, 100
- Bull params: 4 presets (aggressive → mild)
- Bear params: 4 presets (standard → very mild)
- Tax distributions: exponential (seed), linear, sqrt
Total combination space: 6×4×4×4×3 = 1152 variants.
selectVariants(n) samples evenly so every axis is represented.
evolve.sh gains --diverse-seeds flag: when set, gen_0 is seeded with
parametric variants instead of N copies of the same mutated seed.
Remaining slots (if population > generated variants) fall back to
mutations of the base seed.
All generated programs pass transpiler stack validation (33 new tests).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three defensive layers so every Push3 program runs without reverting:
Layer A (transpiler/index.ts): assign bear defaults (CI=0, AS=0.3e18,
AW=100, DD=0.3e18) to all four outputs at the top of calculateParams.
Any output the evolved program does not overwrite keeps the safe default.
Layer B (transpiler/transpiler.ts): graceful stack underflow — dpop/bpop
return '0'/'false' instead of throwing, and the final output-pop falls
back to bear-default literals when fewer than 4 values remain on the
stack. Wrong output count no longer aborts transpilation.
Layer C (transpiler/transpiler.ts + index.ts): wrap the entire function
body in `unchecked {}` so integer overflow wraps (matching Push3), and
emit `(b == 0 ? 0 : a / b)` for every DYADIC./ (div-by-zero → 0,
matching Push3 no-op semantics).
Layer 2 (Optimizer.sol getLiquidityParams): clamp the three fraction
outputs (capitalInefficiency, anchorShare, discoveryDepth) to [0, 1e18]
after abi.decode so a buggy evolved program cannot produce out-of-range
values even if it runs without reverting.
Regenerated OptimizerV3Push3.sol with the updated transpiler; all 193
tests pass (34 Optimizer/OptimizerV3Push3 tests explicitly).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>