feat(gardener): strategy-tick subcommand for ADR-0081 (#NEW6)#84
Merged
Conversation
Implements the orchestrator side of ADR-0081 unified experiment loop (closes parts of issue #81). New subcommand: tri-gardener strategy-tick # dry-run, print decisions JSON tri-gardener strategy-tick --apply # placeholder; live writes in PR-2 New module: bin/tri-gardener/src/strategy.rs Pure decision logic over a (now, experiments, workers, bpb_samples) snapshot. Three independent, idempotent steps: 1. ResetStaleClaim — workers whose heartbeat is older than stale_claim_threshold release their claims. 2. SpawnMirror — `done` champions whose final_bpb < gate2 get sibling rows enqueued for 3-seed quorum. 3. PriorityBoost — running rows whose own best BPB beats the next-best other row by >= 0.05 jump to top of the queue (priority=0). Decision action labels match the gardener_decisions.action CHECK constraint added in #NEW4 — drift trips the test `decision_action_labels_match_ddl_check`. Honest scope (R5): PR-1 ships pure logic + dry-run JSON CLI. The `--apply` flag is parsed and warns explicitly that live Neon writes (UPDATE experiment_queue / INSERT gardener_decisions in one transaction) land in a follow-up. Operator can pipe the JSON output into psql manually as an audit-friendly intermediate step. Tests: 173/173 GREEN (was 163, +10 in tri-gardener strategy): - empty_snapshot_yields_noop - stale_claim_is_recovered - live_worker_keeps_its_claim (heartbeat fresh -> no reset) - champion_spawns_three_mirrors - champion_above_gate_does_not_spawn_mirrors - priority_boost_when_running_beats_leader_by_threshold - priority_boost_does_not_fire_when_already_top - strip_suffix_handles_rng_and_seed_and_neither - decision_action_labels_match_ddl_check - decisions_are_serde_round_trippable Anchor: phi^2 + phi^-2 = 3 — TRINITY — SEED→NEON→GARDENER→LOOP
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements the orchestrator side of ADR-0081 — Unified Experiment Loop.
Adds
tri-gardener strategy-ticksubcommand. Pure decision logic over a(experiments, workers, bpb_samples)snapshot.Three decision steps
Independent and idempotent — each tick can run as many times as it wants.
ResetStaleClaimlast_heartbeat < now - stale_claim_threshold(default 2 min) → orphanedclaimed/runningrows return topendingSpawnMirrordonechampion withfinal_bpb < gate2_target→ enqueue sibling rows atseed,seed+1,seed+2for 3-seed quorumPriorityBoostrunningrow whose own best BPB beats the next-best other row by ≥ 0.05 →priority=0If none fire → emit
Noop.Decision/DDL contract
Decision::action_label()returns one ofnoop / reset_stale_claim / priority_boost / spawn_mirror / enqueue— exactly the values in thegardener_decisions.actionCHECK constraint added in #NEW4. Drift between code and DDL tripsdecision_action_labels_match_ddl_check.Tests
cargo test --workspace -- --test-threads=1→ 173/173 GREEN (was 163, +10).empty_snapshot_yields_noopstale_claim_is_recoveredlive_worker_keeps_its_claimchampion_spawns_three_mirrorschampion_above_gate_does_not_spawn_mirrorspriority_boost_when_running_beats_leader_by_thresholdpriority_boost_does_not_fire_when_already_topstrip_suffix_handles_rng_and_seed_and_neitherdecision_action_labels_match_ddl_checkdecisions_are_serde_round_trippableHonest admission (R5)
--applyflag is parsed and explicitly warns that live Neon writes (UPDATE experiment_queue / INSERT gardener_decisions in one transaction) land in a follow-up. The operator can pipe the dry-run JSON into psql manually as an audit-friendly intermediate.(model, h, lr, scheduler)lives in PR feat(gardener): Seed Hunter — power-law fit + leader-relative classifier #67 (feat/seed-hunter) and will be merged into the strategy module after the queue receives 6-12 hours of real samples — exactly per your earlier choice ("оба параллельно").Decision::Enqueue) is not yet wired. The variant exists and round-trips through serde, but the heuristic indecisions_for_snapshotdoes not yet emit it. Bootstrap of the 41-canon grid is currently a manualINSERTfrom the operator. Wiring lands together with the live--applywriteback in the next PR.Cooperative hold
Land after PR #NEW5 (
feat/seed-agent) — they share the workspace deps and #NEW5 introducestokio-postgres with-uuid-1which strategy uses indirectly viaWorkerRow.R-rules compliance
--applyis honest: prints a warning instead of silently doing nothing.DELETE FROM experiment_queue) are not part of strategy by design — only INSERT and UPDATE-status. Cleanup of stale data is a separate ledger concern.Anchor:
phi^2 + phi^-2 = 3 · TRINITY · SEED→NEON→GARDENER→LOOP.