Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions .github/workflows/daily-max-ai-credits-test.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 14 additions & 12 deletions .github/workflows/daily-max-ai-credits-test.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
---
private: true
emoji: "🧪"
description: "⚠️ INTENTIONALLY FAILS — Tests that max-ai-credits: 1 is enforced by the AWF firewall and that the per-run budget guardrail cuts off the agent."
description: "Tests that max-ai-credits: 1 is enforced by the AWF firewall and that the per-run budget guardrail cuts off the agent. Concludes success when the credit limit is reached."
features:
intentional-failure: true
gh-aw-detection: true
Comment thread
Copilot marked this conversation as resolved.
on:
schedule: daily around 10:30
Expand All @@ -30,32 +29,35 @@ safe-outputs:
close-older-key: "daily-max-ai-credits-test"
labels: [automation, testing]
max: 1
report-failure-as-issue:
- "!ai_credits_rate_limit_error"
- "!max_ai_credits_exceeded"
messages:
run-started: "🧪 [{workflow_name}]({run_url}) — per-run AI credit limit test running (intentionally fails, limit: 1 AI credit/run)."
run-success: "⚠️ [{workflow_name}]({run_url}) completed without hitting the per-run limit of 1 AI credit — verify that max-ai-credits enforcement is working."
run-failure: "🚫 [{workflow_name}]({run_url}) {status} — expected: the per-run AI credit limit of 1 was reached and the AWF firewall cut off the agent."
run-started: "🧪 [{workflow_name}]({run_url}) — per-run AI credit limit test running (limit: 1 AI credit/run)."
run-success: "🧪 [{workflow_name}]({run_url}) — expected: the per-run AI credit limit of 1 was reached and the AWF firewall cut off the agent."
run-failure: "⚠️ [{workflow_name}]({run_url}) {status} — completed without hitting the per-run limit of 1 AI credit — verify that max-ai-credits enforcement is working."
---

### Daily Max AI Credits Test (Intentionally Fails)
### Daily Max AI Credits Test

**Report Formatting**: Use h3 (###) or lower for all headers in your report
to maintain proper document hierarchy. Wrap long sections in
`<details><summary>View Full Details</summary>` tags to improve readability.


> ⚠️ **This workflow is intentionally broken.** It exists solely to verify that
> 🧪 **This workflow tests the per-run AI credits guardrail.** It verifies that
> `max-ai-credits: 1` is enforced by the AWF firewall and that the agent is
> cut off when the per-run budget is exhausted.
> **Do not fix this workflow.**
> cut off when the per-run budget is exhausted. The run **concludes success**
> when the credit limit is reached as expected.

#### What This Tests

1. The AWF firewall enforces the `max-ai-credits` per-run budget.
2. Once the agent consumes more than 1 AI credit in a single run, the firewall cuts off the LLM API.
3. The prompt forces multiple turns and multiple large-file reads so the run reliably burns credits.
4. The run is expected to be cut off by the per-run budget before all jobs can complete.
4. The run is expected to be cut off by the per-run budget before all turns complete.

#### Task (broken by design)
#### Task

Use **at least four separate assistant turns**. Do not combine all work into one response.

Expand All @@ -70,7 +72,7 @@ Turn 4: Call `noop` with the message: "Completed max-ai-credits multi-turn guard
After each job, briefly summarize what was read, then continue to the next turn.

Since the per-run budget is `max-ai-credits: 1`, the AWF firewall should cut off the agent
before all turns complete. That failure is the expected and correct outcome.
before all turns complete. That is the expected and correct outcome — the run will conclude success.

If the workflow somehow completes without hitting the per-run limit, call `noop` with the message:
"Per-run credit limit not exceeded — verify that max-ai-credits: 1 is enforced by the AWF firewall."
10 changes: 7 additions & 3 deletions actions/setup/js/check_daily_aic_workflow_guardrail.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -703,12 +703,16 @@ async function main() {
} catch (summaryError) {
core.warning(`Failed to write daily AIC summary: ${getErrorMessage(summaryError)}`);
}
core.warning(`Daily workflow AIC guardrail exceeded for ${workflowName}: ${totalAIC}/${threshold}.`);
core.setFailed(`Daily workflow AIC guardrail exceeded for ${workflowName}: ${totalAIC}/${threshold}.`);
// Log as info so the activation job succeeds. The daily_ai_credits_exceeded output
// is already set to "true"; the agent job's condition (daily_ai_credits_exceeded != 'true')
// will skip the agent, and the conclusion job will handle reporting via the
// daily_ai_credits_exceeded flag. Failing the activation job here causes the overall
// workflow to fail even though hitting the daily limit is an expected, graceful outcome.
core.info(`Daily workflow AIC guardrail exceeded for ${workflowName}: ${totalAIC}/${threshold}.`);
} catch (error) {
// Treat unexpected guardrail execution errors as non-blocking skips so transient
// API/runtime issues do not fail activation. The output stays at the default "false",
// allowing the agent to run. Legitimate threshold exceedance still fails via setFailed.
// allowing the agent to run.
core.warning(`Daily workflow AI Credits guardrail encountered an unexpected error and will be skipped: ${getErrorMessage(error)}`);
}
}
Expand Down
5 changes: 2 additions & 3 deletions actions/setup/js/check_daily_aic_workflow_guardrail.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,7 @@ describe("check_daily_aic_workflow_guardrail", () => {
}
});

it("main() marks the step failed when the daily AI Credits guardrail is exceeded", async () => {
it("main() does not mark the step failed when the daily AI Credits guardrail is exceeded", async () => {
const getRunAICSpy = vi.spyOn(exports, "getRunAIC").mockResolvedValue(200);

const coreOutputs = {};
Expand Down Expand Up @@ -558,8 +558,7 @@ describe("check_daily_aic_workflow_guardrail", () => {
expect(coreOutputs["daily_ai_credits_exceeded"]).toBe("true");
expect(coreOutputs["daily_ai_credits_total_effective_tokens"]).toBe("200");
expect(coreOutputs["daily_ai_credits_threshold"]).toBe("100");
expect(setFailed).toHaveBeenCalledTimes(1);
expect(setFailed.mock.calls[0][0]).toMatch(/guardrail exceeded/i);
expect(setFailed).not.toHaveBeenCalled();
} finally {
delete global.core;
delete global.github;
Expand Down
19 changes: 17 additions & 2 deletions actions/setup/js/claude_harness.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const { countPermissionDeniedIssues, hasNumerousPermissionDeniedIssues, extractD
const { detectNonRetryableHarnessGuard, buildSoftTimeoutGuard, emitSoftTimeoutSignal, isAuthenticationFailedError } = require("./harness_retry_guard.cjs");
const { MODEL_NOT_SUPPORTED_PATTERN: INVALID_MODEL_ERROR_PATTERN } = require("./detect_agent_errors.cjs");
const { applyModelFallback } = require("./model_fallback.cjs");
const { parseMaxAICreditsExceededFromAuditLog } = require("./ai_credits_context.cjs");

// Pattern to detect Anthropic API overload errors (HTTP 529).
// Matches "overloaded_error" from the Anthropic error type field, and the
Expand Down Expand Up @@ -479,12 +480,26 @@ async function main() {
}

const nonRetryableGuard = detectNonRetryableHarnessGuard(result.output);
if (nonRetryableGuard.aiCreditsExceeded || nonRetryableGuard.awfAPIProxyBlockingRequests || nonRetryableGuard.maxRunsExceeded) {
const trustedAICreditsExceeded = nonRetryableGuard.aiCreditsExceeded && parseMaxAICreditsExceededFromAuditLog();
if (nonRetryableGuard.aiCreditsExceeded && !trustedAICreditsExceeded) {
log(`attempt ${attempt + 1}: AI credits marker found in CLI output without trusted firewall audit confirmation — preserving normal failure handling`);
}
const shouldTreatAICreditsExceededAsSuccess = trustedAICreditsExceeded && !isAuthenticationFailed;
if (shouldTreatAICreditsExceededAsSuccess || nonRetryableGuard.awfAPIProxyBlockingRequests || nonRetryableGuard.maxRunsExceeded) {
const reasons = [];
if (nonRetryableGuard.aiCreditsExceeded) reasons.push("AI credits budget exceeded");
if (shouldTreatAICreditsExceededAsSuccess) reasons.push("AI credits budget exceeded");
if (nonRetryableGuard.awfAPIProxyBlockingRequests) reasons.push("AWF API proxy is blocking requests");
if (nonRetryableGuard.maxRunsExceeded) reasons.push("maximum LLM invocations exceeded");
log(`attempt ${attempt + 1}: ${reasons.join(" and ")} — not retrying (non-retryable guard condition)`);
// When the per-run AI credits budget is exceeded the AWF firewall intentionally
// stopped the agent — this is controlled budget enforcement, not an unexpected
// error. Exit 0 so the agent step and job succeed; the ai_credits_rate_limit_error
// output surfaced by parse-mcp-gateway will inform downstream handlers (e.g.
// handle_agent_failure) of the budget exceedance.
if (shouldTreatAICreditsExceededAsSuccess) {
log(`attempt ${attempt + 1}: AI credits budget enforced — exiting 0 (budget control, not an error)`);
lastExitCode = 0;
}
break;
}

Expand Down
Loading
Loading