diff --git a/contract/registry.yaml b/contract/registry.yaml index dfb20e2..c7fe978 100644 --- a/contract/registry.yaml +++ b/contract/registry.yaml @@ -147,8 +147,6 @@ modes: - { key: charge, tier: advanced } - { key: planner_self, tier: hidden } - { key: planner_cheap, tier: hidden } - - { key: priority, tier: hidden } - - { key: weighted, tier: hidden } # --------------------------------------------------------------------------- # Error codes the box sends. The box sends the code and args; this app owns all diff --git a/src/lib/format/plan.test.ts b/src/lib/format/plan.test.ts index b62084c..6568e94 100644 --- a/src/lib/format/plan.test.ts +++ b/src/lib/format/plan.test.ts @@ -26,8 +26,6 @@ const FTW_MODES = [ 'charge', 'planner_self', 'planner_cheap', - 'priority', - 'weighted', ] as const const T0 = Date.UTC(2026, 6, 15, 12, 0, 0) @@ -205,7 +203,7 @@ describe('mode wording comes from the box', () => { // divergence in the contract file fails a test. expect(FTW_MODES).toContain('planner_passive_arbitrage') expect(FTW_MODES).toContain('planner_arbitrage') - expect(FTW_MODES).toHaveLength(10) + expect(FTW_MODES).toHaveLength(8) }) it('has words for every reason the box can send', () => { diff --git a/src/lib/sim/box.ts b/src/lib/sim/box.ts index 37a7914..9cc0fe7 100644 --- a/src/lib/sim/box.ts +++ b/src/lib/sim/box.ts @@ -209,18 +209,6 @@ const MODE_CATALOG: ModeInfo[] = [ tooltip: 'Forecast-driven \u2014 grid-charges during cheap hours, never exports.', tier: 'hidden', }, - { - key: 'priority', - label: 'Priority', - tooltip: 'Fill the highest-priority battery first.', - tier: 'hidden', - }, - { - key: 'weighted', - label: 'Weighted', - tooltip: 'Distribute dispatch across batteries by configured weights.', - tier: 'hidden', - }, ] const MODE_KEYS = MODE_CATALOG.map((m) => m.key) @@ -780,10 +768,9 @@ export class SimBox { }) return } + // A hold sets what the car draws; a running boost keeps letting the + // house battery cover it, as the box does. this.#evHold = { powerW: Math.round(w) } - // The box's own tick withdraws a boost the moment an operator hold - // appears, and remembers why. - this.#stopBoost('operator_hold') } this.#cmdResult(cmd.cmdId, 'applied', undefined, { value: this.#evHold?.powerW ?? 0, @@ -830,7 +817,7 @@ export class SimBox { }) return } - if (this.#evHold || this.#evSurplusOnly) { + if (this.#evSurplusOnly) { this.#cmdResult(cmd.cmdId, 'rejected', { code: 'E_UNAVAILABLE', args: { op: cmd.op }, diff --git a/src/lib/state/loadpoints.test.ts b/src/lib/state/loadpoints.test.ts index a2db69f..d03ba6f 100644 --- a/src/lib/state/loadpoints.test.ts +++ b/src/lib/state/loadpoints.test.ts @@ -236,7 +236,7 @@ describe('the charger over the wire', () => { expect(lp.boostStopReason).toBe('cancelled') }) - it('reports a hold ending a boost, and the box refusing a boost under a hold', async () => { + it('keeps a boost beside Charge now, and takes one asked for under it', async () => { vi.useFakeTimers() vi.setSystemTime(CHARGING_EVENING) const { store } = await loadedStore() @@ -244,20 +244,21 @@ describe('the charger over the wire', () => { await settled(store.boost(store.points[0]!, 30, 3600)) expect(store.points[0]!.boostActive).toBe(true) - // A hold takes priority: the box withdraws the boost and keeps the why. + // Charge now sets what the car draws; the boost keeps letting the house + // battery cover it. await settled(store.chargeNow(store.points[0]!, 16)) let lp = store.points[0]! expect(lp.manualActive).toBe(true) + expect(lp.boostActive).toBe(true) + + await settled(store.stopBoost(lp)) + lp = store.points[0]! expect(lp.boostActive).toBe(false) - expect(lp.boostStopReason).toBe('operator_hold') - // And a boost asked for under that hold is refused, in a sentence about - // the boost rather than about the charger being out of reach. + // A boost asked for while Charge now runs is taken. await settled(store.boost(lp, 30, 3600)) - expect(store.command.kind).toBe('failed') - expect(store.command.kind === 'failed' && store.command.help).toMatch(/won't boost/) lp = store.points[0]! - expect(lp.boostActive).toBe(false) + expect(lp.boostActive).toBe(true) expect(lp.manualActive).toBe(true) })