Skip to content

Bugfix/188/correct period for rulesets without timebound constraints#42

Merged
boscar merged 6 commits intomainfrom
bugfix/188/correct-period-for-rulesets-without-timebound-constraints
Feb 3, 2026
Merged

Bugfix/188/correct period for rulesets without timebound constraints#42
boscar merged 6 commits intomainfrom
bugfix/188/correct-period-for-rulesets-without-timebound-constraints

Conversation

@boscar
Copy link
Copy Markdown
Contributor

@boscar boscar commented Jan 29, 2026

Description

For rulesets with:

  • time enabled
  • no timebound assume

It was possible to provide any from, such as after or before the period of the ruleset. This resulted in a solution with the given from.

After this change:

  • If from is before the ruleset's period, the solution's time is the beginning of the ruleset's period.
  • If from is after the ruleset's period, an error is returned.

Also improved error messages related to invalid time, and when the solver can't find a solution.

How Has This Been Tested?

  • Unit tests
  • Integration tests

Checklist:

  • My code follows the style guidelines
  • I have performed a self-review of my code
  • I have commented my code (where needed)
  • I have made corresponding changes to the documentation/README
  • My changes generate no new warnings
  • Tests are added for relevant functions
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published
  • Observability is added (where needed)

@boscar boscar requested a review from Copilot January 29, 2026 07:26
@boscar boscar requested a review from oursimon as a code owner January 29, 2026 07:26
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

This PR fixes a bug where rulesets with time enabled but no timebound constraints incorrectly accepted any from timestamp, even those outside the ruleset's valid period. The fix ensures that from timestamps before the ruleset period are adjusted to the period start, while timestamps after the period return an error.

Changes:

  • Added validation to reject from timestamps that fall after the ruleset's period
  • Improved error messages when the solver fails to find a solution, providing context about invalid timestamps
  • Updated period constraint creation to handle rulesets with time enabled but no timebound assumptions

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/integration_tests/solve/period_test.go Added three integration tests validating behavior with time-enabled rulesets without timebound constraints
puanerror/errors.go Added new NoSolutionFound error type for solver failures
puan/solution_creator.go Added error enhancement logic to provide better feedback when solve fails due to invalid timestamps
puan/ruleset_test.go Added unit tests for the new isValidTime method
puan/ruleset_creator.go Refactored period variable and constraint creation to support time-enabled rulesets without timebound assumptions
puan/ruleset.go Added isValidTime method to validate timestamps against ruleset periods
internal/gateway/glpk/convert.go Enhanced error handling to detect "mipfailed" status and return NoSolutionFound error

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Contributor

@oursimon oursimon left a comment

Choose a reason for hiding this comment

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

Looks good 👍 just some thoughts about error handling when validating solutionResponse.

status := strings.ToLower(solution.Solutions[0].Status)
if _, ok := VALID_STATUSES[status]; !ok {
if status == "mipfailed" {
return puanerror.NoSolutionFound
Copy link
Copy Markdown
Contributor

@oursimon oursimon Jan 29, 2026

Choose a reason for hiding this comment

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

Perhaps we also want to return the error msg here so we can check why it failed?
From the glpk-rust lib:

if mip_ret != 0 {
                solution.status = Status::MIPFailed;
                solution.error = Some(format!("GLPK MIP solver failed with code: {}", mip_ret));
                solutions.push(solution);
                continue;
            }

let status = glpk::glp_mip_status(lp);
match status {
                1 => {
                    solution.status = Status::Undefined;
                    solution.error = Some("Solution is undefined".to_string());
                },
...

WDYT about doing something like this?

if len(solution.Solutions) != 1 {
		return errors.Errorf("got %d solutions, expected 1", len(solution.Solutions))
}

if solution.Solutions[0].Error != nil {
		return errors.Errorf("%w: %s", puanerror.NoSolutionFound , *solution.Solutions[0].Error)
}

status := strings.ToLower(solution.Solutions[0].Status)
if _, ok := VALID_STATUSES[status]; !ok {
		return errors.Errorf("got invalid status: %s, expected one of %v", status, VALID_STATUSES)
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks for pointing this out. I adjusted the error handling a bit. NoSolutionFound is only correct if the glpk status code is 10, and don't get that explicetly in the response. Adjusted accordingly.


func (c *RulesetCreator) createPeriodConstraints(periodVariables TimeBoundVariables) error {
if len(c.timeBoundAssumedVariables) == 0 {
if c.timeDisabled() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nice!

solution,
)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nice tests!

@boscar boscar merged commit 05d6f7f into main Feb 3, 2026
3 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.

3 participants