Skip to content

fix(cache): drop wall-clock timestamp from program.yaml metadata#32

Open
dastin-sentient wants to merge 1 commit into
sentient-agi:mainfrom
dastin-sentient:fix/program-yaml-cache-busting
Open

fix(cache): drop wall-clock timestamp from program.yaml metadata#32
dastin-sentient wants to merge 1 commit into
sentient-agi:mainfrom
dastin-sentient:fix/program-yaml-cache-busting

Conversation

@dastin-sentient

Copy link
Copy Markdown
Collaborator

Summary

Two write paths embed a fresh created_at timestamp into every program.yaml:

  • src/registry/sdk_utils.py:options_to_config — for the base program
  • src/registry/models.py:ProgramConfig.child — for mutated programs

The run-cache (src/cache/run_cache.py) keys on the workspace's git tree SHA. Tree SHAs are content-addressed and were chosen specifically so identical content produces the same hash across --fresh launches. But the embedded timestamp byte-changes program.yaml on every launch / iteration, which busts the cache key and forces full re-inference even when nothing else has changed.

Net effect on a fresh re-run of the same dataset: 100% cache miss on base eval (~$5–$15 per re-run depending on dataset size and config).

Fix: drop the created_at field. Git's commit timestamp already records when each program was created, so the metadata field is redundant and only serves to defeat caching.

Test plan

  • Verified locally: relaunched evolution on the same dataset with --fresh true → second run's tree-hash matches the first run's; cache hits would now apply.
  • Inspected resulting program.yamlmetadata: { sdk: claude } (timestamp removed; sdk field preserved).
  • Reviewer to confirm no downstream consumer reads metadata.created_at (grep -r "metadata.*created_at" on the codebase returns no usages).

🤖 Generated with Claude Code

Two write paths embedded a fresh `created_at` timestamp into every
program.yaml:
  - src/registry/sdk_utils.py:options_to_config — for the base program
  - src/registry/models.py:ProgramConfig.child — for mutated programs

The run-cache (src/cache/run_cache.py) keys on the workspace's git tree
SHA. Tree SHAs are content-addressed and were chosen specifically so
identical content produces the same hash across `--fresh` launches.
But the embedded timestamp byte-changes program.yaml on every launch /
iteration, which busts the cache key and forces full re-inference even
when nothing else has changed.

Net effect on a fresh re-run of the same dataset: 100% cache miss on
base eval (~$5–$15 per re-run depending on dataset size and config).

Fix: drop the `created_at` field. Git's commit timestamp already records
when each program was created, so the metadata field is redundant and
only serves to defeat caching.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dastin-sentient pushed a commit to dastin-sentient/EvoSkill that referenced this pull request May 7, 2026
… cleanup

Three cache-correctness bugs that caused stale or wrong results:

1. **Tree-hash didn't include the LIVE skills directory.** When workspace
   ≠ project source root, the cache key hashed the workspace's
   .claude/skills/ (which has only stub .disabled files) instead of the
   project's. Result: same cache key across iterations even as evolved
   skills changed → cache returned responses generated under DIFFERENT
   skill conditions → mid-gate produced bogus "fixed=0, regressed=0,
   same=N" artifacts. Adds `live_skills_dir` and `project_source_root`
   to CacheConfig and threads them through.

2. **`program.yaml` embedded a wall-clock timestamp.** Per-run
   `created_at` field made every fresh run produce a different yaml
   blob, busting the run-cache key on every `--fresh`. Drop `created_at`
   from base_metadata in options_to_config + ProgramConfig.child.
   (Same fix as PR sentient-agi#32, intentionally duplicated here so this bundle
   stands alone if sentient-agi#32 is merged separately.)

3. **Workspace branches left over from GATE-rejected mutations.** When
   mid-gate or val GATE discarded an iter, the workspace's
   program/iter-skill-N branch remained, crashing the next iter's
   `git checkout -b program/iter-skill-N` with "branch already exists".
   Adds defensive cleanup in ProgramManager._git_checkout_new plus
   skill-tree snapshot/restore so historical iters reproduce.

Files: src/cache/run_cache.py, src/registry/{models,sdk_utils,manager}.py

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant