Skip to content

Support short-form effective-token limits (100M, 100K) across schema, parsers, and docs#36496

Merged
pelikhan merged 8 commits into
mainfrom
copilot/refactor-max-effective-tokens
Jun 2, 2026
Merged

Support short-form effective-token limits (100M, 100K) across schema, parsers, and docs#36496
pelikhan merged 8 commits into
mainfrom
copilot/refactor-max-effective-tokens

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Jun 2, 2026

This updates max-effective-tokens and max-daily-effective-tokens to consistently accept short-form limits (for example 100M) across frontmatter validation and runtime parsing paths. It also aligns reference docs to use short-form notation by default.

  • Schema refactor (single source of truth)

    • Introduced shared schema defs for effective-token limit types in main_workflow_schema.json.
    • Rewired both fields (max-effective-tokens, max-daily-effective-tokens) to reuse those defs, preserving existing -1 disable semantics and expression support.
  • Go compiler parser refactor

    • Added shared helpers in pkg/workflow/effective_token_limit_parser.go for normalization/parsing of positive values with optional K/M suffix.
    • Updated:
      • parseMaxEffectiveTokensValue (pkg/workflow/engine.go)
      • parseMaxDailyEffectiveTokensValue (pkg/workflow/daily_effective_workflow.go)
    • Result: both fields normalize to canonical base-10 consistently from one parsing path.
  • JavaScript parser helper refactor

    • Consolidated parsing flow in actions/setup/js/effective_token_limits.cjs around a shared bigint-based parser.
    • Keeps behavior consistent with Go normalization for suffix inputs and integer values.
  • Docs normalization to short-form

    • Updated reference examples/defaults to short-form values (5M, 15M, 25M) in:
      • frontmatter.md
      • glossary.md
      • cost-management.md
      • frontmatter-full.md
      • engines.md
      • compiler-enterprise-environment-controls.md
# now accepted consistently across validation + parsing
max-effective-tokens: 100M
max-daily-effective-tokens: 15M

Copilot AI and others added 3 commits June 2, 2026 17:15
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 Support short-form effective token limits across schema, parsers, and docs Support short-form effective-token limits (100M, 100K) across schema, parsers, and docs Jun 2, 2026
Copilot AI requested a review from pelikhan June 2, 2026 17:20
@pelikhan pelikhan marked this pull request as ready for review June 2, 2026 17:36
Copilot AI review requested due to automatic review settings June 2, 2026 17:37
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 standardizes support for short-form effective-token limits (e.g., 100M, 100K) across the frontmatter JSON schema, Go compiler parsing, the setup Action’s JS parsing helpers, and reference documentation—aiming to keep validation and runtime normalization aligned.

Changes:

  • Refactors the main workflow JSON schema to reuse shared $defs for max-effective-tokens / max-daily-effective-tokens and accept K/M suffix forms consistently.
  • Consolidates Go parsing/normalization logic into shared helpers for effective-token limits and wires both ET limit fields through them.
  • Updates JS parsing to a shared BigInt-based flow and updates docs/examples to prefer short-form notation (e.g., 5M, 15M, 25M).
Show a summary per file
File Description
pkg/workflow/engine.go Routes max-effective-tokens parsing through shared helper and improves invalid-value logging.
pkg/workflow/effective_token_limit_parser.go Adds shared Go helpers to normalize/parse positive limits and max-effective-tokens semantics.
pkg/workflow/daily_effective_workflow.go Reuses shared normalization for max-daily-effective-tokens runtime string emission.
pkg/parser/schemas/main_workflow_schema.json Introduces shared $defs for ET limit types and rewires both fields to reference them.
docs/src/content/docs/reference/glossary.md Updates examples/default wording to include short-form limits (e.g., 5M, 15M).
docs/src/content/docs/reference/frontmatter.md Updates effective-token limit docs/examples to prefer short-form notation.
docs/src/content/docs/reference/frontmatter-full.md Updates defaults/examples for max-effective-tokens (but one example now conflicts with its stated “integer format”).
docs/src/content/docs/reference/engines.md Updates default max-effective-tokens wording to 25M.
docs/src/content/docs/reference/cost-management.md Updates examples and defaults snippets to short-form K/M strings.
docs/src/content/docs/reference/compiler-enterprise-environment-controls.md Updates org env/variable examples to use short-form K/M values.
actions/setup/js/effective_token_limits.test.cjs Expands tests to cover whitespace trimming and numeric input.
actions/setup/js/effective_token_limits.cjs Refactors parsing around a BigInt helper and shared regex.

Copilot's findings

Tip

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

  • Files reviewed: 12/12 changed files
  • Comments generated: 2

Comment on lines 2537 to 2540
# Format 1: Maximum effective-token (ET) budget for AWF API proxy enforcement. Use
# a negative value to disable budget enforcement and token steering.
max-effective-tokens: 1
max-effective-tokens: 1M

Comment on lines +9 to 12
function parsePositiveEffectiveTokenLimitBigInt(value) {
if (typeof value === "number" && Number.isFinite(value) && Number.isInteger(value) && value > 0) {
return String(value);
return BigInt(value);
}
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 2, 2026

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 2, 2026

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 2, 2026

🧪 Test Quality Sentinel completed test quality analysis.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 2, 2026

PR Code Quality Reviewer completed the code quality review.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 2, 2026

🏗️ Design Decision Gate — ADR Required

This PR makes significant changes to core business logic (>100 new lines across pkg/workflow/ and the parser schema) but does not have a linked Architecture Decision Record (ADR).

📄 Draft ADR committed: docs/adr/36496-centralize-effective-token-limit-parsing.md — review and complete it before merging.

🔒 This PR cannot merge until an ADR is linked in the PR body.

📋 What to do next
  1. Review the draft ADR committed to your branch — it was generated from the PR diff (schema $defs consolidation, the new effective_token_limit_parser.go helpers, the JS bigint parser, and the short-form docs normalization).
  2. Complete the missing sections — confirm the driver, refine the decision rationale, and verify the alternatives reflect what you actually weighed.
  3. Commit the finalized ADR to docs/adr/ on your branch (change Status: Draft once accepted).
  4. Reference the ADR in this PR body by adding a line such as:

    ADR: ADR-36496: Centralize Effective-Token Limit Parsing

Once an ADR is linked in the PR body, this gate will re-run and verify the implementation matches the decision.

❓ Why ADRs Matter

"AI made me procrastinate on key design decisions. Because refactoring was cheap, I could always say 'I'll deal with this later.' Deferring decisions corroded my ability to think clearly."

ADRs create a searchable, permanent record of why the codebase looks the way it does. This PR introduces a new "single source of truth per layer" convention for limit parsing — exactly the kind of structural choice future contributors will want explained.

📋 Michael Nygard ADR Format Reference

An ADR must contain these four sections to be considered complete:

  • Context — What is the problem? What forces are at play?
  • Decision — What did you decide? Why?
  • Alternatives Considered — What else could have been done?
  • Consequences — What are the trade-offs (positive and negative)?

All ADRs are stored in docs/adr/ as Markdown files numbered by PR number (e.g., 36496-centralize-effective-token-limit-parsing.md for PR #36496).

🔒 Blocking notice: This gate will not clear until an ADR is linked in the PR body.

🏗️ ADR gate enforced by Design Decision Gate 🏗️ · opus48 615.7K ·

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 2, 2026

🧪 Test Quality Sentinel Report

Test Quality Score: 95/100 — Excellent

Analyzed 2 JavaScript test(s): 2 design, 0 implementation, 0 guideline violations.

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

Test Classification Details

Test File Classification Issues Detected
normalizes ET suffix strings actions/setup/js/effective_token_limits.test.cjs ✅ Design None — covers M/K suffixes, trimming, numeric input, zero, negative
parses safe integer ET suffix numbers actions/setup/js/effective_token_limits.test.cjs ✅ Design Minor: no test for null/undefined inputs

Language Support

Tests analyzed:

  • 🟨 JavaScript (*.test.cjs): 2 tests (vitest)
  • 🐹 Go (*_test.go): 0 tests

Verdict

Check passed. 0% of new tests are implementation tests (threshold: 30%). Both tests verify observable output of parsePositiveEffectiveTokenLimitString and parsePositiveEffectiveTokenLimitNumber against a range of inputs including edge cases (whitespace trimming, numeric 0, negative values, invalid strings like "abc").

📖 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.

🧪 Test quality analysis by Test Quality Sentinel · sonnet46 1.2M ·

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%). Both tests verify behavioral contracts with solid edge case coverage.

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, /grill-with-docs, and /improve-codebase-architecture — clean refactor overall, 3 minor observations (no blockers).

📋 Key Themes & Highlights

Key Themes

  • Missing Go unit tests: effective_token_limit_parser.go is the new single source of truth for K/M normalization across both enforcement paths, but ships without a test file. The JS side added tests; the Go side should too.
  • Schema metadata not fully updated: The default: 25000000 and its inline description in the max-effective-tokens field still reference the raw integer, while the PR updates all other occurrences to short-form.
  • $def name subtly misleads: positive_integer_with_km_suffix_or_expression sounds like the suffix is required, but the regex makes it optional.

Positive Highlights

  • ✅ Excellent DRY refactor — duplicated inline oneOf schema blocks extracted into shared $defs cleanly.
  • ✅ Parallel structure between Go and JS parsers (bigint inner + string outer) makes future updates easier.
  • ✅ Improved error log in engine.go now includes the value type, which is much more useful for debugging.
  • ✅ Negative-value / -1 disable semantics are preserved correctly through the refactor in both schema and Go helpers.
  • ✅ Whitespace trimming added to both the JS and Go paths — good defensive parsing.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 1.7M

return 0, false
}
return parsed, true
}
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 unit tests for the new Go parser functions — this is the single source of truth for K/M suffix normalization used by both engine.go and daily_effective_workflow.go, so a bug here silently corrupts both enforcement paths.

💡 Suggested test coverage

A effective_token_limit_parser_test.go file should cover at minimum:

(go/redacted):build !integration

package workflow

import "testing"

func TestNormalizePositiveEffectiveTokenLimit(t *testing.T) {
    tests := []struct{
        name string; input any; want string; ok bool
    }{
        {"plain int",    1000,    "1000",      true},
        {"K suffix",     "100K",  "100000",    true},
        {"M suffix",     "100M",  "100000000", true},
        {"lowercase m",  "5m",    "5000000",   true},
        {"whitespace",   " 5M ",  "5000000",   true},
        {"zero",         0,       "",          false},
        {"negative int", -1,      "",          false},
        {"zero string",  "0",     "",          false},
        {"expression",   "${{ inputs.limit }}", "", false},
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got, ok := normalizePositiveEffectiveTokenLimit(tt.input)
            if ok != tt.ok || got != tt.want {
                t.Errorf("got (%q, %v), want (%q, %v)", got, ok, tt.want, tt.ok)
            }
        })
    }
}

func TestParseMaxEffectiveTokenLimitValue(t *testing.T) {
    tests := []struct{ input any; want int64; ok bool }{
        {25000000,  25000000, true},
        {"100M",    100000000, true},
        {"-1",     -1, true},
        {-1,        -1, true},
        {"0",       0, false},
        {0,         0, false},
    }
    // ...
}

The negative-integer path (val != 0 in parseMaxEffectiveTokenLimitValue) also means values like -5 are accepted as valid — worth an explicit test to confirm whether that's intentional.

"description": "Maximum 24-hour per-workflow effective-token budget as a numeric string or GitHub Actions expression. Use -1 to disable."
}
],
"$ref": "#/$defs/max_daily_effective_tokens_limit",
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 default value and field-level description still reference the raw integer 25000000, while the PR's stated goal is to normalize examples to short-form notation.

💡 Suggested fix
"default": "25M",
"description": "Explicit ET budget control for firewall cost enforcement. Defaults to 25M when omitted. Set to a negative value to disable budget enforcement and token steering."

Note that changing default from integer to string may need a type check — if the schema validator or tooling treats default as a sample value, this is safe. If parsers actually use the schema default field to inject a value at runtime, keep it as 25000000 and just update the description.

"positive_integer_with_km_suffix_or_expression": {
"type": "string",
"oneOf": [
{
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] The $def name positive_integer_with_km_suffix_or_expression implies the K/M suffix is required, but the regex ^[1-9][0-9]*([kKmM])?$ makes the suffix optional — plain integers like "5000000" also match.

💡 Suggested rename

Consider positive_integer_with_optional_km_suffix_or_expression or simply positive_token_limit_string to avoid misleading readers of the schema. The current name could cause a future maintainer to think plain-integer strings are rejected by this def.

@pelikhan
Copy link
Copy Markdown
Collaborator

pelikhan commented Jun 2, 2026

@copilot review all comments, commit and apply copilot-review skill

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.

🔎 Code quality review by PR Code Quality Reviewer · sonnet46 616.4K

Comments that could not be inline-anchored

pkg/workflow/effective_token_limit_parser.go:51

Bug: any non-zero negative integer is accepted as a valid limit, not just -1. Any caller passing int(-2) or int(-999) will slip through, silently disabling token steering without matching the documented -1 sentinel.

<details>
<summary>💡 Suggested fix</summary>

Change the guard from val != 0 to val &gt; 0 || val == -1:

// Before
if val, ok := typeutil.ParseIntValue(raw); ok &amp;&amp; val != 0 {
    return int64(val), true
}

// After
if val, ok := typeutil.ParseIntValue(raw); ok &amp;&amp; (…

</details>

<details><summary>pkg/workflow/engine.go:144</summary>

**`GetMaxEffectiveTokens()` is now dead codeno callers remain after this refactor.** `awf_config.go` replaced the call with inline logic that also applies the enterprise env-var override (`ResolveDefaultMaxEffectiveTokens`). The exported method silently falls back to the compile-time constant, skipping the env override.

&lt;details&gt;
&lt;summary&gt;💡 Detail&lt;/summary&gt;

Before the refactor, `awf_config.go` presumably called `GetMaxEffectiveTokens()`. After the refactor it reads `EngineConfig.MaxEffect</details>

<details><summary>pkg/workflow/effective_token_limit_parser.go:23</summary>

**No direct unit tests for either new helper in this file.** The `val != 0` integer-path bug in `parseMaxEffectiveTokenLimitValue` (see other comment) would have been caught immediately by a table test.

&lt;details&gt;
&lt;summary&gt;💡 Suggested tests&lt;/summary&gt;

```go
// effective_token_limit_parser_test.go
func TestParseMaxEffectiveTokenLimitValue(t *testing.T) {
    tests := []struct{name string; input any; want int64; ok bool}{
        {&quot;int positive&quot;,    int(1000),    1000,  true},
        {&quot;int -1 s…

</details>

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

Copilot AI commented Jun 2, 2026

@copilot review all comments, commit and apply copilot-review skill

Addressed the in-scope review feedback and pushed commit 3d3a80c.

  • Enforced safe-integer-only numeric inputs before BigInt conversion in actions/setup/js/effective_token_limits.cjs.
  • Added regression coverage for unsafe numeric input in actions/setup/js/effective_token_limits.test.cjs.
  • Corrected frontmatter-full.md Format 1 example to an integer value (max-effective-tokens: 1).

@pelikhan pelikhan merged commit 02ca1f3 into main Jun 2, 2026
22 of 29 checks passed
@pelikhan pelikhan deleted the copilot/refactor-max-effective-tokens branch June 2, 2026 19:24
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