You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
No budget/turn/termination cap calculator exists yet. A repo-wide search for budget-cap/turn-cap/termination-cap variants under packages/ and src/ turns up nothing on point — the one incidental hit, effectiveIssueCapForAccountAge in src/queue/account-age-throttle.ts:23-26, is an unrelated repo-side account-age issue-cap, not governor/miner code.
This is a SIBLING to packages/gittensory-engine/src/governor/rate-limit.ts, not something built on top of it. rate-limit.ts computes a different metric — a rolling-WINDOW request rate (evaluateLocalRateLimit(bucket, config, nowMs), rate-limit.ts:53) plus jittered backoff (jitteredBackoffMs, rate-limit.ts:84) — while this issue's caps are cumulative, monotonic counters across a whole run (total budget spent, total turns taken, elapsed session time vs. a termination ceiling): a different shape of math with no rolling window to reset. What they should share is placement (same packages/gittensory-engine/src/governor/ directory) and header discipline: pure, no IO, no internally-read clock (elapsed/usage values are caller-supplied, exactly like rate-limit.ts's injected nowMs/randomFn), and every numeric input normalized so a non-finite or negative value can never produce a NaN or negative verdict (mirror rate-limit.ts:41-45's finiteNonNegativeInt helper).
The audit ledger at packages/gittensory-miner/lib/governor-ledger.js (#2328) is the other neighboring piece, and it is explicitly NOT this issue either — its header says outright it "does not enforce governor policy; it only persists structured events other phases will emit" (governor-ledger.js:9). This issue's calculator is what would eventually PRODUCE one of the events that ledger records; it doesn't write to the ledger itself. For vocabulary consistency with that later wiring, this calculator's verdict should align with the immutable decision vocabulary already defined at packages/gittensory-engine/src/governor-ledger.ts:2-7 (GOVERNOR_LEDGER_EVENT_TYPES = ["allowed", "denied", "throttled", "kill_switch"]) rather than inventing a parallel one.
This is a pure calculator only — it computes a verdict from typed inputs and does nothing else. It does not store state, schedule anything, or gate a write action. The actual enforcement wiring — composing this calculator with rate-limit and the non-convergence detector into one real allow/deny chokepoint in front of every miner write action — is separate, maintainer-owned work tracked in #2340 ("wire the fail-closed Governor chokepoint before every write action," milestone 13), which names exactly this trio ("rate-limit, budget caps, non-convergence detector") as the calculators it composes. This issue builds one of those three pieces in isolation.
Deliverables
New packages/gittensory-engine/src/governor/budget-cap.ts exporting types for the three independent cap dimensions (budget/cost ceiling, turn/iteration-count ceiling, termination — e.g. max elapsed session time) plus a current-usage snapshot type, and a pure evaluateGovernorCaps(usage, limits)-style function
Each dimension evaluated independently and combined into one verdict; verdict vocabulary aligned with GOVERNOR_LEDGER_EVENT_TYPES (packages/gittensory-engine/src/governor-ledger.ts:2-7) rather than a new ad hoc set
No IO, no internal Date.now()/clock read — elapsed time and usage counts are caller-supplied inputs, matching rate-limit.ts's injected-clock pattern (rate-limit.ts:53,84)
Every numeric input normalized (non-finite/negative → safe default) so the function can never return NaN or a negative remaining-budget/turns value, mirroring finiteNonNegativeInt (rate-limit.ts:41-45)
Unit tests per dimension (under cap, at cap, over cap, non-finite/negative input) plus combined-verdict tests
Export from the package's public entrypoint (packages/gittensory-engine/src/index.ts, alongside the existing export * from "./governor/rate-limit.js"; at line 131)
packages/gittensory-engine/src/governor/rate-limit.ts (whole file, esp. :1-8 header, :41-45 normalization helper, :53 and :84 injected-clock/random pattern) — the sibling pure calculator whose placement and discipline this mirrors, without inheriting from it
packages/gittensory-miner/lib/governor-ledger.js:9 — the audit ledger's own "does not enforce policy" disclaimer, the pattern this issue's disclaimer should match
packages/gittensory-engine/src/governor-ledger.ts:2-7 (GOVERNOR_LEDGER_EVENT_TYPES) — the decision vocabulary this calculator's verdict should align with
packages/gittensory-engine/src/index.ts:131 — where the new module should be re-exported, alongside rate-limit.js
src/queue/account-age-throttle.ts:23-26 — the one incidental "cap" hit in the repo, confirmed unrelated (repo-side account-age issue cap, not governor/miner)
No budget/turn/termination cap calculator exists yet. A repo-wide search for budget-cap/turn-cap/termination-cap variants under
packages/andsrc/turns up nothing on point — the one incidental hit,effectiveIssueCapForAccountAgeinsrc/queue/account-age-throttle.ts:23-26, is an unrelated repo-side account-age issue-cap, not governor/miner code.This is a SIBLING to
packages/gittensory-engine/src/governor/rate-limit.ts, not something built on top of it.rate-limit.tscomputes a different metric — a rolling-WINDOW request rate (evaluateLocalRateLimit(bucket, config, nowMs),rate-limit.ts:53) plus jittered backoff (jitteredBackoffMs,rate-limit.ts:84) — while this issue's caps are cumulative, monotonic counters across a whole run (total budget spent, total turns taken, elapsed session time vs. a termination ceiling): a different shape of math with no rolling window to reset. What they should share is placement (samepackages/gittensory-engine/src/governor/directory) and header discipline: pure, no IO, no internally-read clock (elapsed/usage values are caller-supplied, exactly likerate-limit.ts's injectednowMs/randomFn), and every numeric input normalized so a non-finite or negative value can never produce a NaN or negative verdict (mirrorrate-limit.ts:41-45'sfiniteNonNegativeInthelper).The audit ledger at
packages/gittensory-miner/lib/governor-ledger.js(#2328) is the other neighboring piece, and it is explicitly NOT this issue either — its header says outright it "does not enforce governor policy; it only persists structured events other phases will emit" (governor-ledger.js:9). This issue's calculator is what would eventually PRODUCE one of the events that ledger records; it doesn't write to the ledger itself. For vocabulary consistency with that later wiring, this calculator's verdict should align with the immutable decision vocabulary already defined atpackages/gittensory-engine/src/governor-ledger.ts:2-7(GOVERNOR_LEDGER_EVENT_TYPES = ["allowed", "denied", "throttled", "kill_switch"]) rather than inventing a parallel one.This is a pure calculator only — it computes a verdict from typed inputs and does nothing else. It does not store state, schedule anything, or gate a write action. The actual enforcement wiring — composing this calculator with rate-limit and the non-convergence detector into one real allow/deny chokepoint in front of every miner write action — is separate, maintainer-owned work tracked in #2340 ("wire the fail-closed Governor chokepoint before every write action," milestone 13), which names exactly this trio ("rate-limit, budget caps, non-convergence detector") as the calculators it composes. This issue builds one of those three pieces in isolation.
Deliverables
packages/gittensory-engine/src/governor/budget-cap.tsexporting types for the three independent cap dimensions (budget/cost ceiling, turn/iteration-count ceiling, termination — e.g. max elapsed session time) plus a current-usage snapshot type, and a pureevaluateGovernorCaps(usage, limits)-style functionGOVERNOR_LEDGER_EVENT_TYPES(packages/gittensory-engine/src/governor-ledger.ts:2-7) rather than a new ad hoc setDate.now()/clock read — elapsed time and usage counts are caller-supplied inputs, matchingrate-limit.ts's injected-clock pattern (rate-limit.ts:53,84)finiteNonNegativeInt(rate-limit.ts:41-45)packages/gittensory-engine/src/index.ts, alongside the existingexport * from "./governor/rate-limit.js";at line 131)rate-limit.ts:5-6andgovernor-ledger.js:9References
packages/gittensory-engine/src/governor/rate-limit.ts(whole file, esp.:1-8header,:41-45normalization helper,:53and:84injected-clock/random pattern) — the sibling pure calculator whose placement and discipline this mirrors, without inheriting from itpackages/gittensory-miner/lib/governor-ledger.js:9— the audit ledger's own "does not enforce policy" disclaimer, the pattern this issue's disclaimer should matchpackages/gittensory-engine/src/governor-ledger.ts:2-7(GOVERNOR_LEDGER_EVENT_TYPES) — the decision vocabulary this calculator's verdict should align withpackages/gittensory-engine/src/index.ts:131— where the new module should be re-exported, alongsiderate-limit.jssrc/queue/account-age-throttle.ts:23-26— the one incidental "cap" hit in the repo, confirmed unrelated (repo-side account-age issue cap, not governor/miner)