Skip to content

[P3] feat(marl): Implement MARL Stability - #592

Merged
fallofpheonix merged 5 commits into
mainfrom
feature/issue-144-marl-stability
May 21, 2026
Merged

fallofpheonix merged 5 commits into
mainfrom
feature/issue-144-marl-stability

Conversation

@fallofpheonix

@fallofpheonix fallofpheonix commented May 21, 2026

Copy link
Copy Markdown
Owner

Resolves #144. Implemented StabilityController to enforce action debt, cooldown periods, and maximum containment rates.

Summary by Sourcery

Introduce a stability controller for MARL agents to enforce action cooldowns and containment limits.

New Features:

  • Add StabilityController to regulate MARL agent actions based on cooldown duration and maximum containment debt.

Tests:

  • Add unit tests validating cooldown enforcement and containment limit behavior of the StabilityController.

Copilot AI review requested due to automatic review settings May 21, 2026 13:10
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented May 21, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@fallofpheonix has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 47 minutes and 2 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f59dcfa9-7c7c-4407-b516-4b5b464c10c0

📥 Commits

Reviewing files that changed from the base of the PR and between 0efada7 and 22e68a2.

⛔ Files ignored due to path filters (1)
  • go.work is excluded by !**/*.work
📒 Files selected for processing (4)
  • PHOENIX_SOLUTIONS.md
  • phoenix_os/agents/internal/game/marl/go.mod
  • phoenix_os/agents/internal/game/marl/stability.go
  • phoenix_os/agents/internal/game/marl/stability_test.go
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/issue-144-marl-stability

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sourcery-ai

sourcery-ai Bot commented May 21, 2026

Copy link
Copy Markdown

Reviewer's Guide

Introduces a MARL StabilityController to enforce cooldown-based throttling and containment rate limits for agent actions, along with unit tests validating basic behavior.

File-Level Changes

Change Details Files
Add StabilityController to enforce cooldowns and containment limits for MARL agent actions.
  • Define StabilityController struct with mutex, action debt, last action timestamp, cooldown duration, and maximum containment threshold.
  • Implement constructor to configure cooldown and maximum containment parameters.
  • Implement CanAct method that checks cooldown time elapsed and action debt against maximum containment before allowing actions.
  • Implement RecordAction method to update action debt and last-action timestamp after successful actions.
phoenix_os/agents/internal/game/marl/stability.go
Add unit tests to validate StabilityController behavior under cooldown and containment constraints.
  • Create TestStability to cover allowed initial action, cooldown enforcement, post-cooldown allowance, and containment limit blocking.
  • Use short real-time sleeps to simulate cooldown expiration in tests.
phoenix_os/agents/internal/game/marl/stability_test.go

Assessment against linked issues

Issue Objective Addressed Explanation
#144 Implement MARL stability mechanics: action debt, cooldown timers, and maximum containment rates in the MARL system.
#144 Add stability tests that validate the behavior of the MARL stability mechanics (action debt, cooldown, containment limits).
#144 Implement or document Lyapunov-based stability analysis/proofs for the MARL system. The PR only introduces a StabilityController and unit tests. There is no code or documentation related to Lyapunov analysis or proofs.

Possibly linked issues

  • [P3] [Phoenix Arbiter] Implement MARL Stability #144: PR implements the MARL stability features (action debt, cooldown, containment limits) explicitly requested in the issue.
  • #N/A: PR implements the action debt and cooldown logic requested in the issue to stabilize MARL containment behavior.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • The separation of CanAct and RecordAction means the check and state mutation aren't atomic, so concurrent callers could all pass CanAct before any RecordAction runs; consider a single TryRecordAction(cost) bool that both checks and updates under one lock to enforce limits correctly under concurrency.
  • All StabilityController fields are exported but mutated only under an internal mutex; making them unexported or providing read-only accessors would better encapsulate state and prevent callers from bypassing the controller's synchronization and invariants.
  • The test relies on time.Sleep with real time, which can introduce flakiness and slowness; consider injecting a clock or using a configurable time source to simulate cooldown expiry deterministically.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The separation of `CanAct` and `RecordAction` means the check and state mutation aren't atomic, so concurrent callers could all pass `CanAct` before any `RecordAction` runs; consider a single `TryRecordAction(cost) bool` that both checks and updates under one lock to enforce limits correctly under concurrency.
- All `StabilityController` fields are exported but mutated only under an internal mutex; making them unexported or providing read-only accessors would better encapsulate state and prevent callers from bypassing the controller's synchronization and invariants.
- The test relies on `time.Sleep` with real time, which can introduce flakiness and slowness; consider injecting a clock or using a configurable time source to simulate cooldown expiry deterministically.

## Individual Comments

### Comment 1
<location path="phoenix_os/agents/internal/game/marl/stability.go" line_range="26-35" />
<code_context>
+}
+
+// CanAct checks if an agent is allowed to perform a containment action.
+func (sc *StabilityController) CanAct(cost float64) bool {
+	sc.mu.Lock()
+	defer sc.mu.Unlock()
+
+	// Check Cooldown
+	if time.Since(sc.LastAction) < sc.Cooldown {
+		return false
+	}
+
+	// Check Containment Rate (Action Debt)
+	if sc.ActionDebt+cost > sc.MaxContainment {
+		return false
+	}
</code_context>
<issue_to_address>
**question (bug_risk):** Consider whether ActionDebt should decay or reset over time to avoid permanent lockout.

Because ActionDebt only increases and is never reduced, once `ActionDebt + cost` exceeds `MaxContainment`, `CanAct` will always return false and the agent can never act again unless the controller state is externally reset. If the goal is a time‑bounded rate limit, consider adding decay, a reset policy, or a moving-window–based limit instead. If the permanent lockout is intentional, documenting or naming it accordingly would help set expectations for callers.
</issue_to_address>

### Comment 2
<location path="phoenix_os/agents/internal/game/marl/stability_test.go" line_range="22-23" />
<code_context>
+		t.Error("Expected action to be throttled due to cooldown")
+	}
+
+	// Wait for cooldown
+	time.Sleep(150 * time.Millisecond)
+
+	// Should pass if under containment limit
</code_context>
<issue_to_address>
**suggestion (testing):** Avoid real-time sleeps to reduce test flakiness and runtime.

This test’s reliance on a 150ms `time.Sleep` makes it brittle on slower or loaded CI machines. Instead, allow `StabilityController` to take an injectable time source so you can advance time in tests without sleeping, or restructure the test to set `LastAction` directly to cover the cooldown boundary deterministically.

Suggested implementation:

```golang
	// Simulate cooldown elapsing by moving LastAction back in time
	sc.LastAction = sc.LastAction.Add(-150 * time.Millisecond)

	// Should pass if under containment limit

```

This change assumes:
1. `StabilityController` has an exported `LastAction time.Time` field.
2. `CanAct` uses `LastAction` and the current time to enforce cooldowns.

If `LastAction` is unexported or named differently, or if cooldown is enforced via another internal field, you will need to:
- Adjust the field name in the test to match the actual struct field (e.g., `sc.lastAction` or `sc.lastActionTime`).
- Ensure the test file is in the same package as `StabilityController` if you need access to unexported fields.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread phoenix_os/agents/internal/game/marl/stability.go Outdated
Comment on lines +22 to +23
// Wait for cooldown
time.Sleep(150 * time.Millisecond)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (testing): Avoid real-time sleeps to reduce test flakiness and runtime.

This test’s reliance on a 150ms time.Sleep makes it brittle on slower or loaded CI machines. Instead, allow StabilityController to take an injectable time source so you can advance time in tests without sleeping, or restructure the test to set LastAction directly to cover the cooldown boundary deterministically.

Suggested implementation:

	// Simulate cooldown elapsing by moving LastAction back in time
	sc.LastAction = sc.LastAction.Add(-150 * time.Millisecond)

	// Should pass if under containment limit

This change assumes:

  1. StabilityController has an exported LastAction time.Time field.
  2. CanAct uses LastAction and the current time to enforce cooldowns.

If LastAction is unexported or named differently, or if cooldown is enforced via another internal field, you will need to:

  • Adjust the field name in the test to match the actual struct field (e.g., sc.lastAction or sc.lastActionTime).
  • Ensure the test file is in the same package as StabilityController if you need access to unexported fields.

@fallofpheonix
fallofpheonix merged commit 4198f95 into main May 21, 2026
3 of 7 checks passed
@fallofpheonix
fallofpheonix deleted the feature/issue-144-marl-stability branch May 21, 2026 14:28
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.

[P3] [Phoenix Arbiter] Implement MARL Stability

2 participants