Skip to content

openapi: the compile's work and size bounds are written as a dozen separate counters #785

Open

Description

@OmarAlJarrah

Problem

Every bound in the compiler is a counter with a limit, and each is written out where it is used. At 210283f, among others:

Counter Where Direction Past the limit
budget{spent, limit} load/reach_tree.go up caller reads exhausted()
resumeWork{spent, limit} load/resume.go up BudgetExceeded error
mappings.work/limit load/mappings.go up stops reading
loops.work against maxLoopWork load/loops.go up CycleScanFailed warning
tally{steps, limit} load/treereads.go up stops counting, limit held below math.MaxInt
reached.spent (per document) load/reached.go up BudgetExceeded error; limit <= 0 means unbounded
*int budgets overlay/overlay.go (substituteGrafts, expandAlias), schema/schema.go (schemaNullVerdict and two callers) down return false / read as silent
weigher{ceiling} overlay/sequence.go saturating answers stop at limit+1
rawConv.nodes annotation/rawjson.go up error
View.cachedPairs nodeview/nodeview.go up declines to memoize, silently
sourceindex.Build(root, maxNodes) sourceindex/sourceindex.go up, int64 Truncated()

grep -rnE 'spent|budget|work +int' --include='*.go' compilers/ re-derives the list.

They differ in ways that look incidental: whether the count goes up or down, how "unbounded" is spelled (limit <= 0 in reached, a negative Limits value projected to zero by bounded, no spelling at all for a constant), whether the arithmetic can overflow (only weigher saturates), and what crossing the limit does. Some of the last are deliberate; the rest look like history. A fix in one has no way to know what the others do; #571, a charging error in one of them, is the kind of defect that results.

Proposed direction

One small type in compilers/openapi/internal with Spend(n) bool, Exhausted() bool, Remaining() int and a constructor that applies the Limits convention in one place (zero takes the default, negative is unbounded, saturating add). Each site keeps its own diagnostic and its own decision about what crossing means; only the counting moves.

It starts internal rather than in compilers/compile: promotion needs evidence from all three compilers (docs/micro-compiler-design.md §3).

No bound changes value. This is a refactor with unchanged output.

Acceptance

  • No private spent/limit pair remains where the type fits; the table above is reduced to the sites that genuinely differ, each with a comment saying why.
  • Every existing bound test still reddens when its limit is lowered by one (check by mutation, not by reading).
  • Goldens and the harness sweep are byte-identical.
  • The type has a test for saturation at math.MaxInt and for the negative-unbounded constructor.

Activity

  1. added
    priority:P4Refactors, tests, CI, hostile-input hardening, cosmetic diagnostics, other formats
    on Oct 19, 2026
  2. OmarAlJarrah commented on Oct 20, 2026

    @OmarAlJarrah
    MemberAuthor

    #1016 changes this list. At 83ab508:

    • Deleted, with the cycle scan and the reach check: budget in load/reach_tree.go, maxReachWork in load/reach.go, the mapping read's work and limit in load/mappings.go, loops.work and maxLoopWork in load/loops.go, and the work counter in scan/chains.go.
    • New: the schema binder's budget in compilers/openapi/internal/bind/table.go, one counter for all binding work, with DefaultLimit and no unbounded spelling, reported as openapi/budget-exceeded. bind/documents.go adds two caps, maxPointerTokens and maxPasses.
    • Moved, unchanged: resumeWork, treeReads.tally, reached.spent and View.cachedPairs.
    • The *int budget of the schema null verdicts is now a field of schema.nulls, and is still a down-counter.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    priority:P4Refactors, tests, CI, hostile-input hardening, cosmetic diagnostics, other formatstype:choreRefactor or cleanup, no behavior change

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions