Skip to content

Add max-daily-ai-credits frontmatter guardrail and deprecate max-daily-effective-tokens#37101

Open
Copilot wants to merge 6 commits into
mainfrom
copilot/add-frontmatter-max-daily-ai-credits
Open

Add max-daily-ai-credits frontmatter guardrail and deprecate max-daily-effective-tokens#37101
Copilot wants to merge 6 commits into
mainfrom
copilot/add-frontmatter-max-daily-ai-credits

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Jun 5, 2026

This PR introduces max-daily-ai-credits as the new daily frontmatter guardrail field and deprecates max-daily-effective-tokens. Daily guardrail evaluation now tracks AI Credits (AIC) rather than effective tokens, while preserving backward compatibility for existing workflows.

  • Schema + deprecation path

    • Added max-daily-ai-credits to main workflow frontmatter schema with max_daily_ai_credits_limit.
    • Marked max-daily-effective-tokens as deprecated with explicit migration guidance.
    • Kept accepted value semantics aligned with existing daily limit behavior (integer, K/M suffix strings, expressions, -1 disable).
  • Compiler/import resolution behavior

    • Updated import extraction to prefer max-daily-ai-credits (first-wins), then fall back to deprecated max-daily-effective-tokens.
    • Updated frontmatter resolution to read new field first, then deprecated field.
    • Preserved explicit disable semantics and validation coverage for both fields during transition.
  • Runtime guardrail metric switch (ET ➜ AIC)

    • Switched daily activation guardrail aggregation from effective tokens to AI Credits when reading token-usage.jsonl.
    • Updated guardrail summary/stat rendering to report AIC totals and distribution metrics.
    • Kept existing output plumbing/guardrail control flow intact to minimize behavior drift outside the metric change.
  • Enterprise environment variable support

    • Added GH_AW_DEFAULT_MAX_DAILY_AI_CREDITS as the new enterprise env var override for the daily AIC guardrail default.
    • Built-in default is 500k AIC when neither frontmatter nor any env var override is configured.
    • ResolveDefaultMaxDailyAICredits checks GH_AW_DEFAULT_MAX_DAILY_AI_CREDITS first, then falls back to the deprecated GH_AW_DEFAULT_MAX_DAILY_EFFECTIVE_TOKENS with a deprecation warning log, then the 500k built-in default.
    • Wired default_max_daily_ai_credits into the gh aw env get/update YAML defaults manager alongside the deprecated field.
  • Docs + examples

    • Updated frontmatter, glossary, and cost-management references to use max-daily-ai-credits.
    • Updated user-facing failure guidance for daily guardrail exceeded context and examples.
    • Added/updated tests for schema acceptance, import precedence, deprecated fallback, guardrail runtime reporting, and env var resolution precedence.
# Preferred (new)
max-daily-ai-credits: 15M

# Deprecated (still accepted for compatibility)
max-daily-effective-tokens: 15M
# Preferred enterprise override
GH_AW_DEFAULT_MAX_DAILY_AI_CREDITS=2M

# Deprecated enterprise override (still accepted; logs a deprecation warning)
GH_AW_DEFAULT_MAX_DAILY_EFFECTIVE_TOKENS=2M

Copilot AI and others added 4 commits June 5, 2026 11:24
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title Add max-daily-ai-credits and deprecate max-daily-effective-tokens Add max-daily-ai-credits frontmatter guardrail and deprecate max-daily-effective-tokens Jun 5, 2026
Copilot AI requested a review from pelikhan June 5, 2026 11:31
@pelikhan pelikhan marked this pull request as ready for review June 5, 2026 11:32
Copilot AI review requested due to automatic review settings June 5, 2026 11:32
@pelikhan
Copy link
Copy Markdown
Collaborator

pelikhan commented Jun 5, 2026

@copilot

  • default value should be 500k
  • add support in environment variable manager for enterprise env var with default override
  • detect max daily effective token env var and raise warning as deprecated

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

Note

Copilot was unable to run its full agentic suite in this review.

Introduces max-daily-ai-credits as the new daily per-workflow guardrail field, deprecates max-daily-effective-tokens, and switches the runtime daily guardrail metric from Effective Tokens (ET) to AI Credits (AIC) while preserving compatibility paths.

Changes:

  • Added max-daily-ai-credits to frontmatter types + JSON schema and marked max-daily-effective-tokens as deprecated with migration guidance.
  • Updated compiler/import resolution to prefer the new field and fall back to the deprecated one.
  • Updated runtime guardrail reporting/aggregation to compute and display AI Credits (AIC), alongside docs and tests.
Show a summary per file
File Description
pkg/workflow/frontmatter_types.go Adds new frontmatter field for max-daily-ai-credits.
pkg/workflow/daily_effective_workflow_guardrail_test.go Updates guardrail resolution tests for new field + deprecated fallback.
pkg/workflow/daily_effective_workflow.go Prefers new frontmatter field, adds deprecated fallback, updates validation/disable detection.
pkg/workflow/compiler_validators.go Updates validator log message to reflect AIC guardrail.
pkg/parser/schemas/main_workflow_schema.json Adds new schema field/definition and deprecates old field with message.
pkg/parser/schema_test.go Updates schema validation tests to use max-daily-ai-credits.
pkg/parser/import_processor.go Updates import merge comment to reflect new/old field behavior.
pkg/parser/import_field_extractor_test.go Updates import precedence test to ensure new field wins with fallback.
pkg/parser/import_field_extractor.go Extracts max-daily-ai-credits first, then deprecated field into same accumulator slot.
docs/src/content/docs/reference/glossary.md Renames glossary entry and examples to AIC field.
docs/src/content/docs/reference/frontmatter.md Updates frontmatter docs and adds deprecation note.
docs/src/content/docs/reference/cost-management.md Updates cost-management guidance to AIC field + deprecation note.
actions/setup/md/daily_effective_workflow_exceeded.md Updates user-facing exceeded message and guidance to AIC terminology.
actions/setup/js/handle_agent_failure.cjs Updates guardrail terminology in logs/docs to AIC.
actions/setup/js/daily_effective_workflow_helpers.cjs Adds AIC summation/stat helpers and formatting.
actions/setup/js/check_daily_effective_workflow_guardrail.test.cjs Updates tests to validate AIC aggregation/stats/summary rendering.
actions/setup/js/check_daily_effective_workflow_guardrail.cjs Switches runtime aggregation and summary rendering from ET to AIC.

Copilot's findings

Tip

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

  • Files reviewed: 17/17 changed files
  • Comments generated: 6

Comment on lines +134 to +141
for _, field := range []string{maxDailyAICreditsField, maxDailyEffectiveTokensField} {
raw, ok := data.RawFrontmatter[field]
if !ok {
continue
}
if val, ok := typeutil.ParseIntValue(raw); ok && val < 0 {
return fmt.Errorf("%s must be at least 0, got %d", field, val)
}
Comment on lines 242 to +245
on:
workflow_dispatch:
stale-check: false
max-daily-effective-tokens: -1
max-daily-ai-credits: -1
err := compiler.CompileWorkflow(workflowFile)
if err == nil {
t.Fatal("expected compile to fail for negative max-daily-effective-tokens")
t.Fatal("expected compile to fail for negative max-daily-ai-credits")
}

core.setOutput("daily_effective_workflow_total_effective_tokens", String(totalEffectiveTokens));
core.setOutput("daily_effective_workflow_total_effective_tokens", String(totalAIC));
Comment on lines +10525 to +10530
"max_daily_ai_credits_limit": {
"description": "Maximum AI Credits budget allowed across the last 24 hours for runs of this workflow by the triggering user. Accepts positive integers (including K/M suffix strings and expressions). Set to -1 to disable.",
"oneOf": [
{
"$ref": "#/$defs/positive_effective_tokens_limit"
},
Comment on lines +1386 to +1389
* Build a context string when the 24-hour per-workflow AIC guardrail prevented the agent from
* starting in the activation job.
* @param {boolean} hasDailyEffectiveWorkflowExceeded - Whether the daily workflow quota was exceeded
* @param {string} totalEffectiveTokens - Aggregated ET usage across the last 24 hours
* @param {string} totalEffectiveTokens - Aggregated AIC usage across the last 24 hours
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

Design Decision Gate 🏗️ completed the design decision gate check.

No ADR enforcement needed: PR #37101 does not have the 'implementation' label (has_implementation_label=false) and has 94 new lines in business logic directories, below the 100-line threshold (requires_adr_by_default_volume=false). No custom config present.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

PR Code Quality Reviewer completed the code quality review.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

🧪 Test Quality Sentinel completed test quality analysis.

@github-actions github-actions Bot mentioned this pull request Jun 5, 2026
…deprecated var detection

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

🧪 Test Quality Sentinel Report

Test Quality Score: 95/100 — Excellent

Analyzed 12 changed test(s): 12 design, 0 implementation, 0 guideline violation(s). Changes faithfully track the max-daily-effective-tokensmax-daily-ai-credits rename across Go schema validation, Go guardrail integration, and JavaScript unit tests.

📊 Metrics & Test Classification (12 tests analyzed)
Metric Value
New/modified tests analyzed 12
✅ Design tests (behavioral contracts) 12 (100%)
⚠️ Implementation tests (low value) 0 (0%)
Tests with error/edge cases 10 (83%)
Duplicate test clusters 0
Test inflation detected No
🚨 Coding-guideline violations 0

Test Classification Details

Test File Classification Issues Detected
MaxDailyAICreditsZeroInvalid (renamed from MaxDailyEffectiveTokensZeroInvalid) pkg/parser/schema_test.go:582 ✅ Design Error contract
MaxDailyAICreditsStringZeroInvalid (renamed) pkg/parser/schema_test.go:596 ✅ Design Error contract
MaxDailyAICreditsNegativeAllowed (renamed) pkg/parser/schema_test.go:610 ✅ Design Boundary contract
MaxLimitsAllowExpressions (modified — added max-daily-ai-credits) pkg/parser/schema_test.go:522 ✅ Design Edge case: template expressions
MaxLimitsAllowSuffixStrings (modified) pkg/parser/schema_test.go:538 ✅ Design Edge case: suffix notation (100k)
MaxLimitsAllowSuffixStringsCaseVariants (modified) pkg/parser/schema_test.go:553 ✅ Design Edge case: case variants
TestResolveMaxDailyEffectiveTokens (modified — added deprecated fallback sub-test) pkg/workflow/daily_effective_workflow_guardrail_test.go:16 ✅ Design Backward-compat edge case; explicit-disable nil return
TestDailyEffectiveWorkflowGuardrailInCompiledWorkflow (modified — updated field name) pkg/workflow/daily_effective_workflow_guardrail_test.go:67 ✅ Design Integration happy path
TestDailyETGuardrailNegativeValueRejected (whitespace fix — tab stripped from YAML literal) pkg/workflow/daily_effective_workflow_guardrail_test.go:237 ✅ Design Error path; latent YAML tab defect corrected
"sums AI Credits from explicit token-usage entries" (renamed ET→AIC; unit value changed 125→1.25) check_daily_effective_workflow_guardrail.test.cjs:45 ✅ Design Real file I/O; no temp-dir cleanup
"computes aggregate AIC statistics for prior runs" (renamed; stats verified) check_daily_effective_workflow_guardrail.test.cjs:53 ✅ Design Statistical boundary: stddev, min, max
"renders a daily ET details summary" (updated markdown assertions ET→AIC) check_daily_effective_workflow_guardrail.test.cjs:85 ✅ Design Rate-limit truncation edge case

Language Support

Tests analyzed:

  • 🐹 Go (*_test.go): 9 tests — all unit (//go:build !integration)
  • 🟨 JavaScript (*.test.cjs): 3 tests (vitest)

Verdict

Check passed. 0% of new tests are implementation tests (threshold: 30%). All 12 changed tests enforce behavioral contracts. No guideline violations detected (build tags ✅, no mock libraries ✅, assertion messages ✅).

📎 Notable observations (non-blocking)

JavaScript unit-change precision (max-daily-ai-credits sum test)

The AIC fixture values changed from integers (125, 75, sum 200) to decimals (1.25, 0.75, sum 2.0). This is a correct, intentional unit change (credits are now fractional). The test assertion (toBe(2)) tracks this precisely ✅.

Pre-existing main() test in check_daily_effective_workflow_guardrail.test.cjs

This test was not modified by the PR. It remains functionally correct but uses GH_AW_MAX_DAILY_EFFECTIVE_TOKENS (old env var name) in global injection. Worth a future follow-up to align the env var name with the renamed field, but out of scope for this PR.

TestDailyEffectiveWorkflowGuardrailInCompiledWorkflow

The YAML field in the embedded test workflow was updated to max-daily-ai-credits: 100_000_000. The compiled output assertions remain comprehensive (12 assertions on env vars, step IDs, permissions, and conditional wiring) ✅.

📖 Understanding Test Classifications

Design Tests (High Value) verify what the system does:

  • Assert on observable outputs, return values, or state changes
  • Cover error paths and boundary conditions
  • Would catch a behavioral regression if deleted
  • Remain valid even after internal refactoring

Implementation Tests (Low Value) verify how the system does it:

  • Assert on internal function calls (mocking internals)
  • Only test the happy path with typical inputs
  • Break during legitimate refactoring even when behavior is correct
  • Give false assurance: they pass even when the system is wrong

Goal: Shift toward tests that describe the system's behavioral contract — the promises it makes to its users and collaborators.

References: §27012398711

🧪 Test quality analysis by Test Quality Sentinel · sonnet46 5.7M · 749 AIC ·

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

✅ Test Quality Sentinel: 95/100. Test quality is excellent — 0% of new tests are implementation tests (threshold: 30%). All 12 changed tests enforce behavioral contracts, covering schema validation error paths, backward-compatibility edge cases, suffix/expression boundary conditions, and statistical computation invariants. No guideline violations detected.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Jun 5, 2026

@copilot

  • default value should be 500k
  • add support in environment variable manager for enterprise env var with default override...

Done in commit 7a63bd1. Here's what was implemented:

  • Default value of 500k: resolveMaxDailyEffectiveTokens now calls ResolveDefaultMaxDailyAICredits("500000") so workflows with no frontmatter guardrail and no env var override compile with GH_AW_MAX_DAILY_EFFECTIVE_TOKENS: "500000".

  • New enterprise env var GH_AW_DEFAULT_MAX_DAILY_AI_CREDITS: Added DefaultMaxDailyAICredits constant and ResolveDefaultMaxDailyAICredits(fallback string) to compilerenv/manager.go. The resolver checks the new var first, falls back to the deprecated GH_AW_DEFAULT_MAX_DAILY_EFFECTIVE_TOKENS, then uses the built-in fallback. Wired into env_command.go (defaultsFile, defaultsBindings, validation) so it's manageable via gh aw env get/update.

  • Deprecation warning: When only GH_AW_DEFAULT_MAX_DAILY_EFFECTIVE_TOKENS is set, ResolveDefaultMaxDailyAICredits logs WARNING: GH_AW_DEFAULT_MAX_DAILY_EFFECTIVE_TOKENS is deprecated; use GH_AW_DEFAULT_MAX_DAILY_AI_CREDITS instead before applying the value.

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Skills-Based Review 🧠

Applied /tdd, /zoom-out, /grill-with-docs, and /improve-codebase-architecture — requesting changes on a correctness issue and test coverage gaps.

📋 Key Themes & Highlights

Blocking issues

  • Cross-import field priority bug (import_field_extractor.go:363): the new field only wins within a single frontmatter — if an earlier import uses max-daily-effective-tokens and a later import uses max-daily-ai-credits, the deprecated field silently wins. This contradicts the migration intent.
  • computeInferenceAIC fallback untested (daily_effective_workflow_helpers.cjs:124): every log entry written before this feature ships lacks an aic field and will hit the computed fallback during the 24-hour transition window. No test covers this path.
  • No backward-compat schema test (schema_test.go:527): all tests were migrated to the new field; there is nothing ensuring max-daily-effective-tokens still passes schema validation.

Important, non-blocking

  • Output key unit mismatch (check_daily_effective_workflow_guardrail.cjs:443): daily_effective_workflow_total_effective_tokens now carries AIC — downstream consumers receiving this output will silently get the wrong unit.
  • No compile-time deprecation warning (daily_effective_workflow.go:79): the deprecated: true schema annotation is IDE-only; gh aw compile never tells authors to migrate.
  • Template placeholder not renamed (daily_effective_workflow_exceeded.md:3): {total_effective_tokens} in a user-facing error message clashes with the surrounding AIC terminology.
  • Missing precedence contract tests: no test for both fields in the same frontmatter (daily_effective_workflow_guardrail_test.go) or across imports (import_field_extractor_test.go).

Positive highlights

  • ✅ Solid first-wins semantics within a single frontmatter (correct within-doc ordering).
  • ✅ The supports deprecated field fallback test is a great addition — exactly the kind of explicit contract test this feature needs.
  • resolveMaxDailyEffectiveTokens is clean and easy to follow; new-field-first then deprecated fallback is the right pattern.
  • ✅ Good coverage of the explicit aic field path in sumAICFromTokenUsageFile.
  • ✅ PR description is thorough and clearly explains the motivation.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 6.8M · 813 AIC

acc.extractFirstWinsJSONField(fm, fullPath, "max-turns", &acc.mergedMaxTurns)
acc.extractFirstWinsJSONField(fm, fullPath, "max-runs", &acc.mergedMaxRuns)
acc.extractFirstWinsJSONField(fm, fullPath, "max-effective-tokens", &acc.mergedMaxEffectiveTokens)
acc.extractFirstWinsJSONField(fm, fullPath, "max-daily-ai-credits", &acc.mergedMaxDailyEffectiveTokens)
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.

[/zoom-out] Field priority only holds within a single frontmatter; the new field does not win over the deprecated field when they come from different imports.

💡 Bug scenario

With the current implementation, extractFirstWinsJSONField is called for each frontmatter in import order. If import A (processed first) has max-daily-effective-tokens: 200 and import B has max-daily-ai-credits: 100, import A’s deprecated value wins — the accumulator is already set when import B is processed.

A safe fix is to track both fields in separate accumulators and merge with explicit priority at the end of import processing:

// Track separately:
acc.extractFirstWinsJSONField(fm, fullPath, "max-daily-ai-credits", &acc.mergedMaxDailyAICredits)
acc.extractFirstWinsJSONField(fm, fullPath, "max-daily-effective-tokens", &acc.mergedMaxDailyEffectiveTokens)
// In finalize: prefer new field over deprecated

total += explicit;
continue;
}
const computed = computeInferenceAIC({
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.

[/tdd] The computed-fallback path — triggered when a JSONL entry has no aic field — has no test. Every log entry written before this feature ships will hit this path during the 24-hour transition window.

💡 Suggested additional test case
it("falls back to computeInferenceAIC when aic field is absent", () => {
  const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "daily-guardrail-fallback-"));
  const filePath = path.join(tmpDir, "token-usage.jsonl");
  // Entry without aic — should compute AIC from token counts
  fs.writeFileSync(
    filePath,
    JSON.stringify({ model: "gpt-4o", input_tokens: 1000, output_tokens: 500 }),
    "utf8"
  );
  expect(exports.sumAICFromTokenUsageFile(filePath)).toBeGreaterThan(0);
});

Also worth a negative test: unknown model → 0 AIC, run is not counted.

}
if value, found := resolveMaxDailyEffectiveTokensFromRaw(frontmatter[maxDailyEffectiveTokensField]); found {
dailyEffectiveWorkflowLog.Print("Resolved max-daily-effective-tokens from workflow frontmatter")
dailyEffectiveWorkflowLog.Print("Resolved deprecated max-daily-effective-tokens from workflow frontmatter")
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.

[/grill-with-docs] The deprecated field is silently accepted — users who compile a workflow with max-daily-effective-tokens get no actionable feedback. The PR description says "explicit migration guidance" is added, but this log line is internal-only and not surfaced in gh aw compile output.

💡 Suggestion

Emit a compiler-level warning here so authors see the message at compile time:

// After logging the internal debug message:
compilerWarnings.Add(fmt.Sprintf(
    "%s is deprecated and will be removed in a future version; use %s instead",
    maxDailyEffectiveTokensField, maxDailyAICreditsField,
))

The deprecated: true annotation in the JSON schema only helps IDE tooling; the CLI itself never reads it.

@@ -443,7 +443,7 @@ async function main() {
}
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.

[/zoom-out] The output key daily_effective_workflow_total_effective_tokens now carries an AIC value, not an effective-token count. Any downstream step that reads this output and interprets it as effective tokens will silently receive a different unit.

💡 Options
  • Breaking (clean): Rename to daily_effective_workflow_total_aic; update all .lock.yml consumers.
  • Non-breaking: Emit both the legacy key (for backward compat) and a new daily_effective_workflow_total_aic key, then deprecate the old one.
  • At minimum, add a comment on this line and on the agentic_commands.yml step that reads it so the unit change is documented in-place.


- **24h ET usage:** `{total_effective_tokens}` effective tokens
- **Configured threshold:** `{threshold}` effective tokens
- **24h AIC usage:** `{total_effective_tokens}` AI Credits
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.

[/grill-with-docs] The label now says "AIC usage" but the template placeholder {total_effective_tokens} still reads "effective_tokens" — inconsistent terminology in a user-facing message.

💡 Suggestion
-**24h AIC usage:** `{total_effective_tokens}` AI Credits
+**24h AIC usage:** `{total_aic}` AI Credits

Update the interpolation call site (in handle_agent_failure.cjs) to pass total_aic instead of total_effective_tokens.

@@ -678,7 +680,7 @@ func TestExtractConfigFields_FirstWinsAndAccumulates(t *testing.T) {
assert.Equal(t, "10", acc.mergedMaxTurns, "max-turns should be first-wins")
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.

[/tdd] This test only validates same-frontmatter precedence (both fields in the same fm map). The cross-import case — import A has max-daily-effective-tokens, import B has max-daily-ai-credits — is not covered and currently produces the wrong result (see the ordering issue on import_field_extractor.go:363).

💡 Suggested additional sub-test
acc2 := &importAccumulator{}
acc2.extractConfigFields(map[string]any{"max-daily-effective-tokens": 200}, "/import-a.md")
acc2.extractConfigFields(map[string]any{"max-daily-ai-credits": 100}, "/import-b.md")
assert.Equal(t, "100", acc2.mergedMaxDailyEffectiveTokens,
    "new field from a later import should win over deprecated field from an earlier import")

* @param {Array<{aic:number}>} runs
* @returns {{count:number,total:number,average:number,min:number,max:number,stddev:number}}
*/
function calculateDailyAICStats(runs) {
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.

[/improve-codebase-architecture] calculateDailyAICStats is structurally identical to calculateDailyEffectiveWorkflowStats — the only difference is the field name (aic vs effective_tokens). Duplicating the stats logic means any future fix (e.g. handling NaN differently) must be applied in two places.

💡 Refactor suggestion
/**
 * `@param` {number[]} values
 * `@returns` {{count:number,total:number,average:number,min:number,max:number,stddev:number}}
 */
function calculateStats(values) {
  const filtered = values.filter(v => Number.isFinite(v) && v > 0);
  if (filtered.length === 0) return { count: 0, total: 0, average: 0, min: 0, max: 0, stddev: 0 };
  const total = filtered.reduce((s, v) => s + v, 0);
  const average = total / filtered.length;
  const variance = filtered.length > 1
    ? filtered.reduce((s, v) => s + (v - average) ** 2, 0) / (filtered.length - 1)
    : 0;
  return { count: filtered.length, total, average,
    min: Math.min(...filtered), max: Math.max(...filtered), stddev: Math.sqrt(variance) };
}

const calculateDailyAICStats = runs => calculateStats(runs.map(r => Number(r?.aic || 0)));
const calculateDailyEffectiveWorkflowStats = runs => calculateStats(runs.map(r => Number(r?.effective_tokens || 0)));

* starting in the activation job.
* @param {boolean} hasDailyEffectiveWorkflowExceeded - Whether the daily workflow quota was exceeded
* @param {string} totalEffectiveTokens - Aggregated ET usage across the last 24 hours
* @param {string} totalEffectiveTokens - Aggregated AIC usage across the last 24 hours
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.

[/grill-with-docs] The @param name totalEffectiveTokens is stale — this parameter now carries an AIC value. The description was updated but the name was not, so callers and readers must track the discrepancy mentally.

💡 Suggestion
- * `@param` {string} totalEffectiveTokens - Aggregated AIC usage across the last 24 hours
+ * `@param` {string} totalAIC - Aggregated AIC usage across the last 24 hours

Also rename the parameter in the function signature and all call sites to remove the misleading EffectiveTokens name.

Comment thread pkg/parser/schema_test.go
"max-effective-tokens": "${{ inputs.max-effective-tokens }}",
"max-daily-effective-tokens": "${{ inputs.max-daily-effective-tokens }}",
"on": "push",
"max-runs": "${{ inputs.max-runs }}",
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.

[/tdd] All schema tests were migrated to max-daily-ai-credits. There are no tests verifying that max-daily-effective-tokens is still accepted by the schema after being marked deprecated: true. If the deprecated definition is ever accidentally removed or broken, no test would catch it.

💡 Suggested backward-compat test
func TestValidateMainWorkflowFrontmatterWithSchemaAndLocation_DeprecatedMaxDailyEffectiveTokensStillAccepted(t *testing.T) {
    t.Parallel()
    fm := map[string]any{
        "on":                         "push",
        "max-daily-effective-tokens": 1000,
    }
    err := ValidateMainWorkflowFrontmatterWithSchemaAndLocation(fm, "/tmp/deprecated-field-compat-test.md")
    if err != nil {
        t.Fatalf("expected deprecated field to still pass schema validation, got: %v", err)
    }
}

t.Run("prefers top-level literal value", func(t *testing.T) {
t.Parallel()
got := resolveMaxDailyEffectiveTokens(map[string]any{"max-daily-effective-tokens": 1234}, `"999"`)
got := resolveMaxDailyEffectiveTokens(map[string]any{"max-daily-ai-credits": 1234}, `"999"`)
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.

[/tdd] No test covers the case where both max-daily-ai-credits and max-daily-effective-tokens are present in the same frontmatter — i.e., that the new field is preferred and the deprecated one ignored.

💡 Suggested sub-test
t.Run("new field wins over deprecated when both present", func(t *testing.T) {
    t.Parallel()
    got := resolveMaxDailyEffectiveTokens(
        map[string]any{
            "max-daily-ai-credits":       5000,
            "max-daily-effective-tokens": 9999, // should be ignored
        },
        "",
    )
    if got == nil || *got != "5000" {
        t.Fatalf("expected new field to win, got %v", got)
    }
})

This also documents the intended precedence contract for future readers.

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