fix: address review findings for diverse seed population (#638)

- 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>
This commit is contained in:
openhands 2026-03-13 05:21:05 +00:00
parent 850131b74f
commit 89a2734bff
4 changed files with 51 additions and 15 deletions

View file

@ -279,15 +279,22 @@ if [ "$DIVERSE_SEEDS" = "true" ]; then
# --- Diverse-seeds mode: use seed-gen-cli to produce parametric variants ---
# Generate up to POPULATION variants; any shortfall is filled by mutating the seed.
SEED_VARIANTS_DIR="$WORK_DIR/seed_variants"
SEED_VARIANTS_LIST="$WORK_DIR/seed_variants_list.txt"
VARIANT_IDX=0
# Run seed-gen-cli as a direct command (not inside <(...)) so its exit code is
# checked by the parent shell and fail() aborts the entire script on error.
# Stderr goes to the log file for diagnostics rather than being discarded.
run_seed_gen_cli --count "$POPULATION" --output-dir "$SEED_VARIANTS_DIR" \
> "$SEED_VARIANTS_LIST" 2>>"$LOG" \
|| fail "seed-gen-cli.ts failed to generate variants"
while IFS= read -r VARIANT_FILE && [ "$VARIANT_IDX" -lt "$POPULATION" ]; do
CAND_FILE="$GEN_DIR/candidate_$(printf '%03d' $VARIANT_IDX).push3"
cp "$VARIANT_FILE" "$CAND_FILE"
printf '0\n' > "${CAND_FILE%.push3}.ops"
VARIANT_IDX=$((VARIANT_IDX + 1))
done < <(run_seed_gen_cli --count "$POPULATION" --output-dir "$SEED_VARIANTS_DIR" 2>/dev/null \
|| fail "seed-gen-cli.ts failed to generate variants")
done < "$SEED_VARIANTS_LIST"
# Fill any remaining slots with mutations of the seed (fallback)
while [ "$VARIANT_IDX" -lt "$POPULATION" ]; do

View file

@ -54,6 +54,14 @@ if (!outputDir) {
mkdirSync(outputDir, { recursive: true });
const variants = selectVariants(count);
if (variants.length < count) {
process.stderr.write(
`[seed-gen] Note: --count ${count} exceeds the 1152-variant parameter space;` +
` generating ${variants.length} variants. The remaining ${count - variants.length}` +
` slots in evolve.sh will be filled by mutating the seed.\n`,
);
}
for (let i = 0; i < variants.length; i++) {
const text = generateSeedVariant(variants[i]!);
const filename = `variant_${String(i).padStart(3, '0')}.push3`;

View file

@ -41,7 +41,13 @@ export const STAKED_THRESHOLDS: readonly number[] = [80, 85, 88, 91, 94, 97];
/** Penalty thresholds: penalty < N → bull; else → bear. */
export const PENALTY_THRESHOLDS: readonly number[] = [30, 50, 70, 100];
/** Bull output parameter variants (higher confidence / tighter ranges). */
/**
* Bull output parameter variants (higher confidence / tighter ranges).
*
* capitalInefficiency is intentionally fixed at 0 across all variants the
* current seed (optimizer_v3.push3) uses CI=0 in all branches, and varying it
* is left to the mutation operators once evolution starts.
*/
export const BULL_VARIANTS: readonly BullBearParams[] = [
// Aggressive bull (current seed): maximum confidence, tight anchor
{ capitalInefficiency: 0n, anchorShare: ONE_E18, anchorWidth: 20, discoveryDepth: ONE_E18 },
@ -53,7 +59,11 @@ export const BULL_VARIANTS: readonly BullBearParams[] = [
{ capitalInefficiency: 0n, anchorShare: 700_000_000_000_000_000n, anchorWidth: 50, discoveryDepth: 700_000_000_000_000_000n },
];
/** Bear output parameter variants (defensive / wide ranges). */
/**
* Bear output parameter variants (defensive / wide ranges).
*
* capitalInefficiency is intentionally fixed at 0 for the same reason as BULL_VARIANTS.
*/
export const BEAR_VARIANTS: readonly BullBearParams[] = [
// Standard bear (current seed)
{ capitalInefficiency: 0n, anchorShare: 300_000_000_000_000_000n, anchorWidth: 100, discoveryDepth: 300_000_000_000_000_000n },
@ -249,17 +259,21 @@ export function generateSeedVariant(params: SeedVariantParams): string {
/**
* Enumerate all parameter combinations in a deterministic order.
*
* STAKED_THRESHOLDS is the outermost loop so that even-stride sampling in
* selectVariants(n) naturally covers all staked% values: each block of
* (4 penalty × 4 bull × 4 bear × 3 tax) = 192 entries maps to one threshold.
*
* Total = |STAKED_THRESHOLDS| × |PENALTY_THRESHOLDS| × |BULL_VARIANTS|
* × |BEAR_VARIANTS| × |TAX_DISTRIBUTIONS|
* = 6 × 4 × 4 × 4 × 3 = 1152
*/
export function enumerateVariants(): SeedVariantParams[] {
const result: SeedVariantParams[] = [];
for (const taxDistribution of TAX_DISTRIBUTIONS) {
for (const stakedThreshold of STAKED_THRESHOLDS) {
for (const penaltyThreshold of PENALTY_THRESHOLDS) {
for (const bull of BULL_VARIANTS) {
for (const bear of BEAR_VARIANTS) {
for (const stakedThreshold of STAKED_THRESHOLDS) {
for (const penaltyThreshold of PENALTY_THRESHOLDS) {
for (const bull of BULL_VARIANTS) {
for (const bear of BEAR_VARIANTS) {
for (const taxDistribution of TAX_DISTRIBUTIONS) {
result.push({
stakedThreshold,
penaltyThreshold,
@ -278,11 +292,12 @@ export function enumerateVariants(): SeedVariantParams[] {
/**
* Select `n` diverse variants from the full parameter space.
*
* When n total combinations (1152), samples evenly so each axis of variation
* is represented. When n > total, returns all combinations.
* When n total combinations (1152), samples evenly across the enumeration so
* each axis of variation is represented. Because STAKED_THRESHOLDS is the
* outermost loop, selectVariants(6) picks one representative per staked%
* threshold (stride = 1152/6 = 192, one block per threshold value).
*
* The first variant is always the current seed configuration
* (stakedThreshold=91, penaltyThreshold=50, bull/bear=defaults, exponential).
* When n > total, returns all 1152 combinations.
*/
export function selectVariants(n: number): SeedVariantParams[] {
if (n < 1) throw new RangeError('n must be at least 1');

View file

@ -243,10 +243,16 @@ describe('selectVariants', () => {
});
it('n=6 covers all staked thresholds via even stride', () => {
// 1152 / 6 = stride of 192; the 6 selected entries span the full combination space
// enumerateVariants puts STAKED_THRESHOLDS outermost (192 entries per threshold).
// stride = 1152/6 = 192 → one representative per staked% value.
const variants = selectVariants(6);
expect(variants).toHaveLength(6);
// Each variant is valid
const stakedValues = new Set(variants.map(v => v.stakedThreshold));
expect(stakedValues.size).toBe(STAKED_THRESHOLDS.length);
for (const st of STAKED_THRESHOLDS) {
expect(stakedValues.has(st)).toBe(true);
}
// Each representative is a valid Push3 program
for (const v of variants) {
expect(parseAndValidate(generateSeedVariant(v))).toBe(true);
}