Skip to content

docs: priceCurveAlgo gotchas section in CLAUDE.md#64

Merged
damonrand merged 1 commit into
mainfrom
docs/price-curve-algo-gotchas
May 8, 2026
Merged

docs: priceCurveAlgo gotchas section in CLAUDE.md#64
damonrand merged 1 commit into
mainfrom
docs/price-curve-algo-gotchas

Conversation

@damonrand
Copy link
Copy Markdown
Contributor

Summary

Adds a new "priceCurveAlgo gotchas" section to CLAUDE.md under Key Concepts. Captures algorithm-side pitfalls that emerged during multi-round cc tuning sweeps.

Five entries:

  1. Force-charge ignores nivChasePeriods.chargeCurve = [] gates. The peak-approach force curve sits parallel to niv-chase; it imports based purely on the SoE-vs-time curve. Use peak.approach.assumedChargePower and chargeCushionMins to size the approach so the force ramp ends before any premium-import window — not via niv chargeCurve gating.

  2. peak.dynamic HOLD-on-LONG only fires when there's slack. The check requires dischargeable_soe / bess_max_power_discharge < peak_window − time_step. Without slack, every HH falls through to "must full discharge" and the dynamic block silently no-ops. Use peak.dynamic.minEndOfPeakSoe (added v2.1.1) to manufacture slack.

  3. assumedChargePower should match realistic charge rate, not BESS nameplate. If gridConnection.importLimit < BESS.nameplatePower, setting assumedChargePower above the import limit makes the algo plan for power it cannot deliver — approach_duration shortens, force-curve starts too late, toSoe is never reached.

  4. Cutoffs should be aligned across niv-chase and microgrid-control. nivChasePeriods.niv.volumeCutoffForPrediction and microgrid.imbalanceControl.nivCutoffForSystemStateAssumption should match — mismatch creates a split-signal regime where niv-chase classifies UNKNOWN while microgrid-control classifies LONG/SHORT.

  5. Time-to-empty math uses a static snapshot of bess_max_power_discharge. Realised effective power varies HH-to-HH (solar contribution, residual load); very tight slack budgets (<20 min) are unreliable in the dynamic HOLD branch.

Why now

Companion to a new skypro-service/tuning/ skill (separate repo) which points at this section as the source of truth for these gotchas. The skill encodes the round-pattern and scoring rules; this CLAUDE.md section codifies the algorithm-side knowledge that any tuning round needs to respect.

No code change. v2.1.1 stays.

Test plan

  • Markdown renders correctly in CLAUDE.md preview — no broken links or stale refs
  • Section sits under Key Concepts where the algorithm-related concepts live

Surfaces algorithm-side pitfalls that emerged during multi-round cc tuning
sweeps. New section under Key Concepts. Five entries covering:

- Force-charge ignores nivChasePeriods.chargeCurve gates (workaround:
  size approach via assumedChargePower + chargeCushionMins)
- peak.dynamic HOLD-on-LONG slack constraint (use minEndOfPeakSoe to
  manufacture slack)
- assumedChargePower must reflect realistic charge rate (importLimit −
  houseLoad), not BESS nameplate
- Cutoff alignment: volumeCutoffForPrediction ==
  nivCutoffForSystemStateAssumption to avoid split-signal regime
- Time-to-empty math is a static snapshot of bess_max_power_discharge;
  very tight slack budgets are unreliable

Companion to the new skypro-service/tuning/ skill (separate repo) which
points at this section as the source of truth for these gotchas.

No code change. v2.1.1 stays.
@damonrand damonrand merged commit 2869274 into main May 8, 2026
2 checks passed
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