Skip to content

feat(curtailment): add proto/SDK contracts and capability flags#118

Merged
rongxin-liu merged 31 commits intomainfrom
feat/issue-116-curtailment-foundation
May 1, 2026
Merged

feat(curtailment): add proto/SDK contracts and capability flags#118
rongxin-liu merged 31 commits intomainfrom
feat/issue-116-curtailment-foundation

Conversation

@rongxin-liu
Copy link
Copy Markdown
Contributor

@rongxin-liu rongxin-liu commented Apr 28, 2026

Closes #116.

Summary

Adds the kW-only curtailment foundation across proto/API contracts, SDK and plugin bridge support, capability flags, route registration, generated outputs, and initial plugin implementations. Fleet-level curtailment domain routes are intentionally stubbed, but direct plugin dispatch now supports explicit FULL curtailment and Proto efficiency curtailment.

This builds on command preflight filtering work from #110 so command handlers can reject unsafe requests before dispatch. The follow-up work will wire persistence, selection, reconciliation, and UI flows on top of these contracts.

What changed

Proto and API contracts

  • Added proto/curtailment/v1 with Fleet-level PreviewCurtailmentPlan, StartCurtailment, UpdateCurtailmentEvent, StopCurtailment, GetActiveCurtailment, and ListCurtailmentEvents RPC definitions.
  • v1 exposes one operator-selectable event mode: CURTAILMENT_MODE_FIXED_KW with FixedKwParams.
  • Removed the earlier proportional event-mode and device-level proportional curtailment shapes from the pre-release contract.
  • CurtailmentLevel now has EFFICIENCY=1 and FULL=2; Fleet event validation accepts FULL/default only for v1, while Proto direct device dispatch can advertise and use efficiency mode.
  • Preview/start validation now accepts only FIXED_KW with fixed_kw, least-efficient/default strategy, and normal/default/emergency priority.
  • Maintenance override flags must be paired: callers cannot force maintenance inclusion unless maintenance targets are also included.
  • CommandCapabilities now exposes only curtail_full_supported and curtail_efficiency_supported.

SDK and gRPC bridge

  • Added CurtailLevel, optional DeviceCurtailment, and curtail request payload plumbing to the Go SDK and generated gRPC driver bridge.
  • Python and Rust SDK surfaces expose full and efficiency curtailment capability constants only.
  • Server plugin command conversion validates curtail levels and maps only FULL and EFFICIENCY support.
  • Plugin interfaces now include Curtail and Uncurtail request-shaped APIs with safe unsupported defaults.

Server wiring

  • Registered the curtailment gRPC service in fleetd behind a stub domain handler so clients can compile against the new API while persistence and reconciliation are still being built.
  • Added route-level validation for the v1 kW-only contract, including unsupported future modes, unsupported non-FULL Fleet levels, unsupported HIGH priority, unbalanced maintenance override flags, and non-terminal overlap rejection paths.
  • Added capability conversion coverage so server metadata reports full and efficiency support consistently across plugin types.

Plugin implementations

  • Virtual and Antminer plugins advertise and implement FULL curtailment only.
  • Proto plugin advertises and implements FULL plus EFFICIENCY curtailment.
  • Full curtailment restore snapshots now refresh live device state before recording whether mining should be restored:
    • Antminer and Proto invalidate their status caches before snapshotting wasMining.
    • ASIC-RS invalidates telemetry before snapshotting was_mining.
  • ASIC-RS default capabilities and tests now match the kW-only/full-or-efficiency contract.

Generated outputs

  • Regenerated Go and TypeScript protobuf outputs after the contract changes.
  • Regenerated SDK bridge protobuf outputs after removing the pre-release proportional device level.

Behavior changes

  • Fleet event creation is kW-targeted only in v1.
  • Device-level direct curtailment supports FULL everywhere that advertises it, and EFFICIENCY only where the plugin advertises it.
  • Pre-release proportional request fields, enum values, SDK constants, and capability flags are no longer part of the branch contract.
  • Stop/uncurtail behavior avoids restarting miners that were already inactive immediately before Fleet dispatched FULL curtailment.

Reviewer guide

  • Contract shape: proto/curtailment/v1/curtailment.proto, proto/capabilities/v1/capabilities.proto, and server/sdk/v1/pb/driver.proto.
  • Route validation: server/internal/handlers/curtailment.
  • SDK and plugin bridge: server/sdk/v1, server/internal/domain/plugins.
  • Direct plugin behavior: plugin/virtual, plugin/antminer, plugin/proto, and plugin/asicrs.
  • Generated files were updated after the source proto changes; review source protos first.

Out of scope

  • Durable curtailment event persistence, target selection, target state machines, restore batching, and reconciliation loops.
  • UI and API client workflows for preview/start/stop.
  • Fleet-level efficiency events.
  • Closed-loop dispatch modes such as demand response, site power caps, thermal limits, price breakeven, and power trajectories.
  • Smart PDU/PDU-outlet, rack, site, group, webhook, and emergency shutdown execution beyond the contract placeholders.

Test coverage

  • Curtailment handler validation and stub route behavior for the kW-only v1 API.
  • Server plugin capability conversion and SDK enum/request conversion.
  • Virtual, Antminer, Proto, and ASIC-RS curtail/uncurtail behavior.
  • Cached-active then externally-stopped restore snapshot cases for Antminer, Proto, and ASIC-RS.
  • Generated Go and TypeScript protobuf compile coverage via targeted Go tests.

Foundational scaffolding for curtailment v1 (#116). Defines the public API
surface and SDK/plugin contract that every later piece depends on, and
wires empty (Unimplemented) RPC handlers so the proto types and route
registrations exist before persistence, selection, dispatch, reconciliation,
and restore land in follow-ups.

- New proto/curtailment/v1/curtailment.proto with CurtailmentService, mode/
  strategy/level/priority/state enums (with explicit reservations for v2/v3/v4
  values), oneof scope/mode_params requests, FixedKw/FixedPercent params, the
  include_maintenance / force_include_maintenance safety pair, and
  cursor-paginated ListCurtailmentEvents.
- proto/capabilities/v1/capabilities.proto: CommandCapabilities gains
  curtail_supported (v1) plus reserved curtail_efficiency_supported and
  curtail_partial_supported (v4).
- server/sdk/v1: new CurtailLevel enum and Curtail/Uncurtail methods on
  DeviceControl, ErrCurtailCapabilityNotSupported / ErrCurtailTransient
  sentinels, gRPC server+client bridge wiring, and matching capability
  string constants for plugin Capabilities maps.
- server/sdk/v1/pb/driver.proto: Curtail/Uncurtail RPCs and CurtailLevel
  message so plugins implement curtailment over the existing gRPC bridge.
- server/internal/domain/miner/interfaces.Miner: Curtail/Uncurtail; PluginMiner
  adapter wraps sdk.Device via wrapPluginError. Mocks regenerated.
- server/internal/domain/plugins/capabilities.go maps the new SDK capability
  strings into the proto CommandCapabilities flags.
- server/internal/handlers/curtailment: empty handler returns
  CodeUnimplemented for every v1 RPC; registered in cmd/fleetd/main.go next
  to the existing Connect handlers. Test asserts every RPC is reachable and
  returns Unimplemented.
- Plugin implementations:
  - virtual: full Curtail(FULL)/Uncurtail with state tracking and tests
    covering FULL, unsupported levels (efficiency/partial → permanent
    capability error), and idempotent uncurtail.
  - proto / antminer: wrap StopMining/StartMining for FULL; unsupported
    levels return ErrCurtailCapabilityNotSupported.
  - asicrs (Rust): wraps stop_mining/start_mining for Full level via
    tonic-generated trait; unsupported levels return Unimplemented.

Acceptance:
- buf lint and buf generate produce clean Go/TypeScript/Python outputs.
- Existing command and plugin capability behavior is unchanged for
  non-curtailment commands.
- Virtual plugin reports curtail_supported and executes Curtail(FULL) /
  Uncurtail against the device test fixture.
- All v1 RPC stubs are reachable via the Connect router but return
  Unimplemented.

Made-with: Cursor
@github-actions github-actions Bot added javascript Pull requests that update javascript code client server shared labels Apr 28, 2026
@rongxin-liu rongxin-liu changed the title feat(curtailment): v1 foundation — proto/SDK contracts and capability flags feat(curtailment): foundation — proto/SDK contracts and capability flags Apr 28, 2026
@rongxin-liu rongxin-liu changed the title feat(curtailment): foundation — proto/SDK contracts and capability flags feat(curtailment): add proto/SDK contracts and capability flags Apr 28, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 28, 2026

🔐 Codex Security Review

Note: This is an automated security-focused code review generated by Codex.
It should be used as a supplementary check alongside human review.
False positives are possible - use your judgment.

Scope summary

  • Reviewed pull request diff only (dfa84db4e0dc6806113ca0d16e3d19bbc2ee5524...fda29a334d39024b092bf2ba1a166e29a6c1fb3f, exact PR three-dot diff)
  • Model: gpt-5.4

💡 Click "edited" above to see previous reviews for this PR.


Review Summary

Overall Risk: MEDIUM

Findings

[HIGH] Proto “efficiency” curtailment can raise wattage instead of reducing it

  • Category: Reliability
  • Location: plugin/proto/internal/device/device.go:603
  • Description: curtailEfficiency() always switches the miner to target.DefaultW in efficiency mode. If a Proto miner is already manually underclocked below DefaultW, this “curtail” path increases the configured power target instead of clamping it downward.
  • Impact: A load-shed action can move site power in the wrong direction on already-throttled rigs, breaking curtailment guarantees and making any future planner overestimate delivered reduction.
  • Recommendation: Never raise watts during curtailment. Clamp to min(CurrentW, DefaultW) or explicitly reject cases where DefaultW > CurrentW, and add a regression test for CurrentW < DefaultW.

[MEDIUM] Uncurtail defaults to “start mining” when its snapshot is missing

  • Category: Reliability
  • Location: plugin/proto/internal/device/device.go:639, plugin/antminer/internal/device/device.go:530, plugin/asicrs/src/device.rs:613
  • Description: The new curtail/uncurtail flow keeps pre-curtail mining state only on the in-process device object, then treats “no snapshot” as permission to start/resume mining (shouldStartMining = true, !ok || restoreMining, unwrap_or(true)). A restarted plugin, recreated miner handle, or any other state loss turns Uncurtail into a generic start-mining action.
  • Impact: Previously stopped miners can come back online unexpectedly during restore, violating maintenance windows and causing unintended load spikes.
  • Recommendation: Persist pre-curtail state with the event/device record, or make missing snapshot state a safe no-op/error instead of defaulting to resume.

[MEDIUM] ASIC-RS now advertises full-curtail support before it has been proven

  • Category: Plugin
  • Location: plugin/asicrs/src/capabilities.rs:101, server/internal/domain/plugins/capabilities.go:69
  • Description: driver_base_capabilities() now hard-codes CAP_CURTAIL_FULL=true, and the server immediately exports that bit as curtail_full_supported. That means ASIC-RS devices can be reported as curtailable before a live probe has confirmed pause/resume support for the actual model/firmware.
  • Impact: Current capability APIs can overstate available shed capacity and present curtail controls that fail at dispatch time on unsupported miners.
  • Recommendation: Keep the base curtail flag false and only set it from proven per-model/per-device capability data, or introduce an explicit “unknown” state instead of treating unknown as supported.

Notes

  • The new curtailment RPC surface is registered but still returns Unimplemented, so the first two issues are latent in landed code rather than user-reachable through the public API today.
  • I did not find new auth bypasses, raw-SQL/SQLi patterns, shell-out injection paths, or pool/wallet rewriting logic in the reviewed hunks.

Generated by Codex Security Review |
Triggered by: @rongxin-liu |
Review workflow run

Two staleness checks failed against the foundation commit:

- cargo fmt rejected the asicrs Curtail::Unspecified match arm because
  the single-line Err wrapped its argument; rustfmt prefers a block-form
  arm there. Reformatted accordingly.
- driver_pb2_grpc.py was committed against grpcio-tools 1.76.0, but
  packages/proto-python-gen/requirements.txt pins 1.80.0 and CI generates
  with that version. Regenerated the stub against the pinned toolchain so
  GRPC_GENERATED_VERSION matches what CI produces.

Made-with: Cursor
@rongxin-liu rongxin-liu force-pushed the feat/issue-116-curtailment-foundation branch from ec4e78a to 56ac56c Compare April 28, 2026 22:31
rongxin-liu and others added 7 commits April 28, 2026 19:24
…emantics

Address review feedback on PR #118:
- Add buf.validate CEL rules to PreviewCurtailmentPlanRequest and
  StartCurtailmentRequest requiring exactly one scope and pairing
  mode_params with the chosen mode (FIXED_KW <-> fixed_kw,
  FIXED_PERCENT <-> fixed_percent).
- Drop redundant not_in: [0] mode constraint and document
  UNSPECIFIED-as-default semantics for strategy and level.
- Correct misleading page_size comment on ListCurtailmentEventsRequest.
- Remove unnecessary safeIntToInt32 cast on CurtailLevel (already int32).
- Align asicrs Curtail behavior with the Go plugins: any non-FULL level
  (including Unspecified) returns Unimplemented as a permanent
  capability error.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- asicrs: probe supports_pause && supports_resume to gate CAP_CURTAIL,
  and advertise the FULL level optimistically at the driver level via
  PROBED_CAPS. Add unit tests for the AND-gate and the driver-level
  advertisement.
- proto and virtual drivers: advertise CapabilityCurtail at DescribeDriver
  alongside their existing per-device advertisement. Add a virtual
  driver_test.go locking in the contract.
- Rust and Python SDKs: export CAP_CURTAIL / CAP_CURTAIL_EFFICIENCY /
  CAP_CURTAIL_PARTIAL constants so plugin authors can reference the
  capability strings without hardcoding.
- Extend the proto plugin's required-caps test to assert
  CapabilityCurtail is advertised.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
DriverGRPCServer methods that returned device-call errors directly
(StartMining, StopMining, BlinkLED, Reboot, SetCoolingMode,
SetPowerTarget, UpdateMiningPools, UpdateMinerPassword, FirmwareUpdate,
Close, Unpair, Curtail, Uncurtail) now route those errors through
sdkErrorToGRPCStatus so SDKError codes (Unauthenticated, Unavailable,
Unimplemented, etc.) propagate to wrapPluginError as the correct gRPC
status instead of being read as Internal.

Add a parameterized TestDriverGRPCServer_ControlRPCsMapSDKErrorStatus
covering one method per category against three distinct codes
(StartMining/Unauthenticated, SetCoolingMode/Unavailable,
UpdateMinerPassword/Unimplemented). Drop the redundant errors.Unwrap
from the existing Curtail/Uncurtail bridge tests since
status.FromError walks the wrap chain via errors.As.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
golangci-lint flagged 'new' as shadowing the predeclared identifier
in the updateMinerPasswordFunc type. Use the same parameter names as
the underlying interface (currentPassword, newPassword) for clarity.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Apr 29, 2026
@rongxin-liu rongxin-liu marked this pull request as ready for review April 29, 2026 05:19
Copilot AI review requested due to automatic review settings April 29, 2026 05:19
@rongxin-liu rongxin-liu requested a review from a team as a code owner April 29, 2026 05:19
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds the v1 “curtailment foundation” across the public API (proto/Connect), SDK/plugin bridge, and plugin implementations, including capability flags and stub server wiring so downstream work can build against stable contracts.

Changes:

  • Introduces curtailment.v1 proto service/types and registers stub Connect handlers returning Unimplemented after validation.
  • Extends the SDK driver bridge with Curtail/Uncurtail, curtailment capability flags, and new SDK error codes with gRPC/Connect error mapping updates.
  • Updates plugins (virtual, proto, antminer, asicrs) to advertise and/or implement FULL curtailment via stop/start or pause/resume, plus adds targeted tests.

Reviewed changes

Copilot reviewed 42 out of 52 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
server/sdk/v1/python/tests/test_utils.py Adds Python test ensuring curtailment capability constants are exported.
server/sdk/v1/python/proto_fleet_sdk/generated/pb/driver_pb2_grpc.py Generated Python gRPC stubs updated for Curtail/Uncurtail.
server/sdk/v1/python/proto_fleet_sdk/generated/pb/driver_pb2.pyi Generated Python type stubs updated with curtailment types.
server/sdk/v1/python/proto_fleet_sdk/generated/pb/driver_pb2.py Generated Python protobuf updated with curtailment enum/message/service.
server/sdk/v1/python/proto_fleet_sdk/capabilities.py Exports Python capability constants for curtailment.
server/sdk/v1/python/proto_fleet_sdk/init.py Re-exports new Python capability constants at package root.
server/sdk/v1/plugin_test.go Adds Go tests for SDKError→gRPC mapping, including curtailment and control RPCs.
server/sdk/v1/plugin.go Adds driver bridge Curtail/Uncurtail and centralizes SDKError→gRPC mapping for control RPCs.
server/sdk/v1/pb/generated/driver_grpc.pb.go Generated Go gRPC bindings updated with Curtail/Uncurtail.
server/sdk/v1/pb/driver.proto Adds curtailment level enum, request, and driver RPCs to SDK bridge proto.
server/sdk/v1/mocks/mock_driver.go Generated Go mocks updated to include DeviceCurtailment.
server/sdk/v1/mocks/generate.go Updates mockgen target list to include DeviceCurtailment.
server/sdk/v1/interface.go Adds CurtailLevel, DeviceCurtailment, and curtail capability strings to SDK interfaces.
server/sdk/v1/errors.go Adds curtailment-specific SDK error codes and constructors.
server/sdk/v1/README.md Documents optional DeviceCurtailment interface.
server/internal/handlers/interceptors/error_mapping_test.go Adds tests ensuring Connect errors are preserved and other errors mapped.
server/internal/handlers/interceptors/error_mapping.go Preserves *connect.Error values in the error mapping interceptor.
server/internal/handlers/curtailment/handler_test.go Adds tests for stub curtailment routes and validation behavior.
server/internal/handlers/curtailment/handler.go Adds stub curtailment handler returning Unimplemented for all RPCs.
server/internal/domain/plugins/plugin_miner_test.go Adds tests for PluginMiner curtailment dispatch and error taxonomy.
server/internal/domain/plugins/plugin_miner.go Adds Curtail/Uncurtail dispatch through optional SDK support with specialized error wrapping.
server/internal/domain/plugins/plugin_factory.go Extends new-device error classification switch to recognize curtailment SDK error codes.
server/internal/domain/plugins/capabilities.go Maps SDK curtail flags into proto CommandCapabilities.
server/internal/domain/miner/interfaces/mocks/mock_miner.go Generated miner mocks updated with Curtail/Uncurtail.
server/internal/domain/miner/interfaces/miner.go Extends miner interface with curtailment methods.
server/internal/domain/fleeterror/error_test.go Adds unit tests for IsUnavailableError.
server/internal/domain/fleeterror/error.go Adds NewUnavailableErrorf and IsUnavailableError.
server/internal/domain/command/execution_service_credentials_test.go Updates a test mock miner to satisfy the expanded interface.
server/generated/grpc/curtailment/v1/curtailmentv1connect/curtailment.connect.go Generated Connect bindings for the new curtailment service.
server/generated/grpc/capabilities/v1/capabilities.pb.go Generated Go proto updated with new curtailment capability fields.
server/cmd/fleetd/main.go Registers the new curtailment Connect handler routes in fleetd.
sdk/rust/proto-fleet-plugin/src/capabilities.rs Adds Rust plugin SDK capability constants for curtailment.
proto/curtailment/v1/curtailment.proto Introduces the curtailment v1 public API contract (enums/messages/service + validation).
proto/capabilities/v1/capabilities.proto Adds curtailment capability flags to CommandCapabilities.
plugin/virtual/internal/driver/driver_test.go Tests virtual driver advertises FULL curtail capability.
plugin/virtual/internal/driver/driver.go Virtual driver advertises curtailment capability.
plugin/virtual/internal/device/device_test.go Tests virtual device FULL curtail/uncurtail behavior and reserved level rejection.
plugin/virtual/internal/device/device.go Implements FULL curtail/uncurtail in virtual device and invalidates cached status.
plugin/proto/tests/unit/plugin_test.go Extends proto plugin tests to require curtail capability.
plugin/proto/internal/driver/driver.go Proto driver advertises FULL curtail capability.
plugin/proto/internal/device/device_test.go Adds tests for curtail/uncurtail error wrapping and auth/unsupported behavior.
plugin/proto/internal/device/device.go Implements FULL curtail/uncurtail via stop/start and wraps dispatch failures as transient.
plugin/asicrs/src/driver.rs Adds driver-side curtail/uncurtail RPC handling (FULL-only) with tests.
plugin/asicrs/src/device.rs Implements FULL curtail/uncurtail via pause/resume with capability gating + tests.
plugin/asicrs/src/capabilities.rs Adds curtail caps, probes gating, and tests for pause+resume requirement.
plugin/antminer/internal/driver/driver.go Antminer driver advertises FULL curtail capability.
plugin/antminer/internal/device/device_test.go Adds tests for cache invalidation and transient/unsupported curtailment errors.
plugin/antminer/internal/device/device.go Implements FULL curtail/uncurtail via stop/start and invalidates cached status.
client/src/protoFleet/api/generated/capabilities/v1/capabilities_pb.ts Generated TS updated with new curtailment capability fields.

Comment thread server/sdk/v1/pb/driver.proto Outdated
Comment thread proto/curtailment/v1/curtailment.proto
Comment thread proto/curtailment/v1/curtailment.proto
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 517ad5273e

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread plugin/asicrs/src/capabilities.rs
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9d12186185

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread server/internal/domain/plugins/plugin_miner.go
Copy link
Copy Markdown
Collaborator

@mcharles-square mcharles-square left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally looks good

Comment thread plugin/antminer/internal/device/device.go Outdated
Comment thread plugin/proto/internal/device/device.go Outdated
Comment thread plugin/proto/internal/device/device.go Outdated
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d5335ea0f9

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread server/internal/domain/miner/service.go Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 48 out of 59 changed files in this pull request and generated 1 comment.

Comment thread proto/curtailment/v1/curtailment.proto
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a7c7e22995

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread plugin/antminer/internal/device/device.go
@rongxin-liu rongxin-liu merged commit a57c322 into main May 1, 2026
70 checks passed
@rongxin-liu rongxin-liu deleted the feat/issue-116-curtailment-foundation branch May 1, 2026 13:06
rongxin-liu added a commit that referenced this pull request May 6, 2026
…-2 foundation)

Lays the foundation for the BE-2 ticket (#140): the curtailment persistence
layer, sqlc-backed store, domain models, the FIXED_KW mode implementation,
and the enum-stability guard for the AdminTerminateEvent validator pinned
in BE-1.x (#173).

Migration 000040:
- curtailment_event with full lifecycle columns plus the BE-1.x admin-only
  fields (allow_unbounded BOOLEAN, effective_batch_size INT). CHECK
  constraints enforce maintenance-pair consistency, non-empty external
  source/reference/idempotency_key, and non-empty reason. Partial UNIQUE
  indexes cover idempotency, webhook dedupe, and active-event lookup.
- curtailment_target with composite PK (event_id, device_identifier),
  partial indexes for pending work and active-by-device schedule lookup.
- curtailment_reconciler_heartbeat singleton seeded at migration time so
  the staleness alert always has a row to read.
- curtailment_org_config with per-org tunables seeded one row per existing
  org in the same migration transaction; down migration drops the table.

Domain layer:
- server/internal/domain/curtailment/models defines the boundary shapes
  (Event, Target, OrgConfig, Heartbeat, EventState/TargetState typed
  wrappers) so selector/handler/modes do not import sqlc-generated code.
- server/internal/domain/curtailment/modes ships the Mode interface and
  the FixedKw implementation. Pure logic — no I/O, no time, no shared
  state. Covers the three design-doc outcomes: target reached (overshoot
  bounded by last-added candidate), undershoot tolerated (only with
  explicit positive tolerance_kw), and insufficient curtailable load
  (with a structured InsufficientLoadDetail the handler can echo back).

Store layer:
- interfaces/curtailment.go defines the org-scoped CurtailmentStore;
  v1 surface is the minimum needed to support Preview plus the basic
  event/target CRUD primitives so store tests can verify the schema
  constraints round-trip.
- sqlstores/curtailment.go implements the interface using the sqlc-
  generated queries (GetCurtailmentOrgConfig, ListActiveCurtailedDevicesByOrg,
  ListRecentlyResolvedCurtailedDevicesByOrg, InsertCurtailmentEvent,
  GetCurtailmentEventByUUID, InsertCurtailmentTarget,
  ListCurtailmentTargetsByEvent, GetCurtailmentReconcilerHeartbeat).

BE-1.x guard:
- TestCurtailmentEventStateNumericPins asserts CANCELLED == 6 and
  FAILED == 7 at build time. The AdminTerminateEventRequest validator
  pins on (buf.validate.field).enum.in: [6, 7]; this test fails CI before
  any future enum reorder can silently desynchronize the validator.

Selector + handler implementation lands in follow-up commits on this branch.

Refs #140
Refs #118
Refs #173

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

client documentation Improvements or additions to documentation javascript Pull requests that update javascript code server shared

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add curtailment foundation

3 participants