Skip to content

feat(miner-governor): fail-closed chokepoint composing every write calculator (#2340) - #5018

Merged
JSONbored merged 1 commit into
feat/miner-governor-dry-run-default-2342from
feat/miner-governor-chokepoint-2340
Jul 11, 2026
Merged

feat(miner-governor): fail-closed chokepoint composing every write calculator (#2340)#5018
JSONbored merged 1 commit into
feat/miner-governor-dry-run-default-2342from
feat/miner-governor-chokepoint-2340

Conversation

@JSONbored

@JSONbored JSONbored commented Jul 11, 2026

Copy link
Copy Markdown
Owner

Summary

  • Stacked on feat(miner-governor): dry-run-by-default enforcement + fail-closed chokepoint (#2342, #2340) #5014 (maintainer: dry-run-by-default enforcement #2342), which is itself stacked on feat(miner-governor): global + per-repo kill-switch (#2341) #5012 (maintainer: global + per-repo kill-switch #2341) — this PR's base is feat/miner-governor-dry-run-default-2342. The diff below is additive-only once the chain merges to main in order.
  • This is THE safety-critical core of Phase 5: packages/gittensory-engine/src/governor/chokepoint.ts exports evaluateGovernorChokepoint, the single decision point every miner write action must pass through before executing a LocalWriteActionSpec (src/mcp/local-write-tools.ts: open_pr, file_issue, apply_labels, post_eligibility_comment, create_branch, delete_branch, generate_tests).
  • Composes into ONE fail-closed precedence ladder ("safest wins", mirroring resolveAgentActionMode):
    global kill-switch (#2341) > per-repo pause (#2341) > dry-run (#2342) > rate-limit (#2344) > budget/turn/termination cap > non-convergence > self-reputation throttle > self-plagiarism > allow.
  • Scope note, flagging for review: the issue's own deliverable names three calculators explicitly ("rate-limit, budget caps, non-convergence detection"). This PR also composes self-reputation-throttle and self-plagiarism — not scope creep, but because both of those modules' own doc comments already forward-reference this exact chokepoint (self-plagiarism.ts: "the Governor open_pr chokepoint (maintainer: wire the fail-closed Governor chokepoint before every write action #2340) composes this verdict with rate-limit, budget caps, and non-convergence"; reputation-throttle.ts: "the chokepoint can record WHY a submission cadence was scaled"), and both already ship a *LedgerEvent builder keyed on their own boolean throttled/allowed field — reusing an existing, already-reviewed gate semantic rather than me inventing new policy inside a safety-critical module. Both stages apply only when actionClass === "open_pr" (their own ledger builders are submission-scoped; a label-apply or branch-delete has no diff fingerprint or "submission cadence" to throttle) and are entirely skippable — omitting their inputs (reputationHistory/selfPlagiarismCandidate left undefined) cleanly skips the stage rather than erroring.
  • Fail-closed, explicitly tested: any calculator stage that throws (a malformed value escaping this module's typed boundary) denies immediately with stage: "internal_error" and never falls through to allow. Caught and fixed a real bug during testing (see Notes).
  • Pure engine module — no IO, no bucket/ledger mutation. packages/gittensory-miner/lib/governor-chokepoint.js is the stateful wrapper: it persists the returned ledger event and advances/backs-off the rate-limit bucket state, but ONLY when the rate-limit stage actually ran (kill-switch and dry-run short-circuit before rate-limit is ever evaluated, so bucket state is provably untouched in those cases — tested explicitly).

Scope

Validation

  • git diff --check
  • npm run typecheck — clean.
  • npm run build + npm run test in packages/gittensory-engine — 399/399 passing. New chokepoint.test.ts (21 tests) covers: full allow path; kill-switch (both scopes) short-circuiting before any later stage runs; dry-run short-circuiting before rate-limit; each resource stage (rate-limit, budget-cap incl. the termination/kill_switch eventType, non-convergence) denying and halting the ladder; reputation-throttle denying (and its own fail-open-on-insufficient-history behavior reaching allow); self-plagiarism denying a losing near-duplicate claim; both submission-scoped stages being skipped entirely for a non-open_pr action even with inputs that would otherwise deny; and one fail-closed test per calculator (5 total) proving a thrown error denies with stage: "internal_error".
  • npm run build in packages/gittensory-miner (node --check, including the new governor-chokepoint.js) — clean.
  • vitest run test/unit/miner-governor-chokepoint.test.ts — 4/4 passing (ledger persistence + bucket-mutation-only-when-rate-limit-ran, verified for the allow path, a kill-switch denial, a dry-run shadow, and a rate-limit denial).
  • npm run test:coverage, test:workers, build:mcp, test:mcp-pack, ui:*, npm audit — not re-run locally; this PR touches only packages/gittensory-engine/**, packages/gittensory-miner/**, and test/unit/**, and per CONTRIBUTING.md only src/** is Codecov-measured. CI runs them authoritatively.

Safety

  • No secrets, wallet/hotkey/trust-score/reward data anywhere.
  • Fail-closed is not just asserted, it's tested: 5 dedicated tests force a runtime exception (via a type-violating null cast, the same technique a genuinely malformed upstream caller could produce) into each of the 5 composable calculators and confirm the chokepoint denies with stage: "internal_error" rather than silently skipping to the next stage or allowing.
  • This PR itself performs no real write execution and is not yet wired to any actual LocalWriteActionSpec call site — that live actuation wiring is separate, later, maintainer-owned work (maintainer: wire the submission-gate trigger into the harness driving loop #2337).
  • No UI changes. No docs/schema changes needed (composes only already-documented config surfaces).

Notes

@superagent-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 11, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
gittensory-ui 8d09504 Commit Preview URL

Branch Preview URL
Jul 11 2026, 09:17 AM

@codecov

codecov Bot commented Jul 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 44.64286% with 31 lines in your changes missing coverage. Please review.
✅ Project coverage is 94.29%. Comparing base (84b7f83) to head (8d09504).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...kages/gittensory-engine/src/governor/chokepoint.ts 44.64% 25 Missing and 6 partials ⚠️
Additional details and impacted files
@@                             Coverage Diff                              @@
##           feat/miner-governor-dry-run-default-2342    #5018      +/-   ##
============================================================================
- Coverage                                     94.36%   94.29%   -0.07%     
============================================================================
  Files                                           459      460       +1     
  Lines                                         39256    39312      +56     
  Branches                                      14317    14339      +22     
============================================================================
+ Hits                                          37042    37069      +27     
- Misses                                         1558     1582      +24     
- Partials                                        656      661       +5     
Flag Coverage Δ
shard-1 46.71% <1.78%> (-0.07%) ⬇️
shard-2 33.34% <1.78%> (-0.05%) ⬇️
shard-3 31.42% <1.78%> (-0.05%) ⬇️
shard-4 32.73% <1.78%> (-0.05%) ⬇️
shard-5 33.63% <44.64%> (+0.15%) ⬆️
shard-6 45.10% <1.78%> (-0.07%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...kages/gittensory-engine/src/governor/chokepoint.ts 44.64% <44.64%> (ø)

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@loopover-orb loopover-orb Bot added the gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. label Jul 11, 2026
@loopover-orb

loopover-orb Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Caution

🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥

🛑 Gittensory review result - fixes required

Review updated: 2026-07-11 09:12:51 UTC

7 files · 1 AI reviewer · 1 blocker · readiness 100/100 · CI failing · unstable

🛑 Suggested Action - Manual Review

  • Linked issue overlaps another open PR — Review the related PRs before spending reviewer time on duplicate work.

Review summary
This adds the pure `evaluateGovernorChokepoint` composer (packages/gittensory-engine/src/governor/chokepoint.ts) plus a stateful miner-lib wrapper that persists the ledger event and advances rate-limit bucket state. The precedence ladder in code (kill-switch → dry-run → rate-limit → budget/turn/termination cap → non-convergence → reputation-throttle → self-plagiarism → allow) matches the documented order exactly, every stage is wrapped in try/catch that denies with `internal_error` rather than falling through to allow, and the 18-case engine test suite plus the 4-case miner-lib test suite exercise the short-circuit ordering, both `open_pr`-only conditionals, and every fail-closed catch path. The scope addition of reputation-throttle/self-plagiarism beyond the issue's three named calculators is justified by those modules' own forward-referencing doc comments and is fenced to `actionClass === "open_pr"`, so it reads as a legitimate composition rather than scope creep.

Nits — 6 non-blocking
  • packages/gittensory-miner/lib/governor-chokepoint.js:29 — `options.append ?? appendGovernorEvent` is never exercised without `options.append` in test/unit/miner-governor-chokepoint.test.ts (every test supplies a fake `append`); add one case calling `evaluateGovernorChokepointGate(input)` with no options so the real `appendGovernorEvent` default path is covered — this is likely part of why codecov/patch landed at 44.64% against the 99% target.
  • packages/gittensory-engine/src/governor/chokepoint.ts:200-260 — the reputation-throttle and self-plagiarism stages are near-identical try/catch/if blocks that differ only in the calculator call and payload; consider extracting a small `runOptionalSubmissionStage(...)` helper to cut the nesting depth (flagged externally at line 210, depth 5) and the duplication.
  • packages/gittensory-engine/test/chokepoint.test.ts imports from `../dist/index.js` rather than `../src/index.js` — worth confirming this matches the sibling `*.test.ts` convention in this package so codecov's src-line mapping isn't silently degraded by testing against build output instead of source.
  • Add a no-options test for evaluateGovernorChokepointGate to close the real coverage gap noted above.
  • Factor the reputation-throttle/self-plagiarism stage bodies into one small helper to reduce chokepoint.ts's nesting and keep the ladder easy to extend when the next optional stage lands.
  • PR author also opened the linked issue — Link an issue that was opened by a different contributor, or provide a rationale for why this self-authored issue represents genuine discovery work.

Why this is blocked

  • Linked issue overlaps another open PR — Review the related PRs before spending reviewer time on duplicate work.

CI checks failing

  • codecov/patch — 44.64% of diff hit (target 99.00%)
Signal Result Evidence
Code review ❌ 1 blocker 1 reviewer
Linked issue ✅ Linked #2340
Related work ✅ No active overlap found No same-issue or scoped active PR overlap found.
Change scope ✅ 20/20 Low review scope from cached public metadata (1 linked issue).
Validation posture ✅ 25/25 PR body includes validation/test evidence.
Contributor workload ✅ 10/10 Author activity: 47 registered-repo PR(s), 39 merged, 428 issue(s).
Contributor context ✅ Confirmed Gittensor contributor JSONbored; Gittensor profile; 47 PR(s), 428 issue(s).
Gate result ❌ Blocking Repo-configured hard blocker found.
Improvement ✅ Minor risk: clean · value: minor — Code changes are accompanied by test evidence.
Linked issue satisfaction

Addressed
The PR adds `evaluateGovernorChokepoint` as the single fail-closed decision point over the exact local-write-tools action set, composes rate-limit/budget-cap/non-convergence (plus two extra, justified stages) in a documented 'safest wins' precedence ladder mirroring `agent-execution.ts`, wraps calculator calls in try/catch that deny on any error, and records every decision via a ledger event persi

Review context
  • Author: JSONbored
  • Role context: owner (maintainer lane)
  • Public audience mode: oss maintainer
  • Lane context: Repository is configured for direct PR review.
  • Public profile languages: not available
  • Official Gittensor activity: 47 PR(s), 428 issue(s).
  • PR-specific overlap: none found.
Contributor next steps
  • Treat this as maintainer-lane context rather than normal contributor-lane activity.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.

🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed


💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →.

Checked by Gittensory, a quiet PR intelligence layer for OSS maintainers.

  • Re-run Gittensory review

@JSONbored JSONbored added gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier. and removed gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. labels Jul 11, 2026
@JSONbored JSONbored self-assigned this Jul 11, 2026
@loopover-orb loopover-orb Bot added the manual-review Gittensor contributor context label Jul 11, 2026
@JSONbored JSONbored linked an issue Jul 11, 2026 that may be closed by this pull request
5 tasks
@loopover-orb loopover-orb Bot added gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. and removed gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier. labels Jul 11, 2026
@JSONbored
JSONbored force-pushed the feat/miner-governor-dry-run-default-2342 branch from 85f1e72 to 84b7f83 Compare July 11, 2026 09:15
…lculator (#2340)

The single decision point every miner write action (open_pr, file_issue,
apply_labels, post_eligibility_comment, create_branch, delete_branch,
generate_tests) must pass through. Composes the kill-switch (#2341) and
dry-run-default (#2342) primitives with the previously-shipped pure
calculators -- rate-limit (#2344), budget/turn/termination caps,
non-convergence detection, self-reputation throttle, and self-plagiarism --
into one precedence ladder: global kill-switch > per-repo pause > dry-run >
rate-limit > budget cap > non-convergence > reputation throttle >
self-plagiarism > allow.

Reputation-throttle and self-plagiarism extend beyond the issue's three
explicitly-named calculators, per those two modules' own doc comments
forward-referencing this exact chokepoint; both reuse their own
already-reviewed boolean gate semantic (throttled/allowed) rather than
inventing new policy. Both apply only to actionClass "open_pr" (their own
ledger builders are submission-scoped).

Any calculator that throws denies immediately with stage "internal_error",
never falls through to allow. Pure engine module (no IO); the miner-lib
wrapper owns persisting the ledger event and advancing rate-limit bucket
state only when the rate-limit stage actually ran.

Stacked on #2341 + #2342 (imports from both).
@JSONbored
JSONbored force-pushed the feat/miner-governor-chokepoint-2340 branch from d5af343 to 8d09504 Compare July 11, 2026 09:16
@JSONbored
JSONbored merged commit 8138bc9 into feat/miner-governor-dry-run-default-2342 Jul 11, 2026
15 of 16 checks passed
@JSONbored
JSONbored deleted the feat/miner-governor-chokepoint-2340 branch July 11, 2026 09:20
JSONbored added a commit that referenced this pull request Jul 11, 2026
…lculator (#2340) (#5018)

The single decision point every miner write action (open_pr, file_issue,
apply_labels, post_eligibility_comment, create_branch, delete_branch,
generate_tests) must pass through. Composes the kill-switch (#2341) and
dry-run-default (#2342) primitives with the previously-shipped pure
calculators -- rate-limit (#2344), budget/turn/termination caps,
non-convergence detection, self-reputation throttle, and self-plagiarism --
into one precedence ladder: global kill-switch > per-repo pause > dry-run >
rate-limit > budget cap > non-convergence > reputation throttle >
self-plagiarism > allow.

Reputation-throttle and self-plagiarism extend beyond the issue's three
explicitly-named calculators, per those two modules' own doc comments
forward-referencing this exact chokepoint; both reuse their own
already-reviewed boolean gate semantic (throttled/allowed) rather than
inventing new policy. Both apply only to actionClass "open_pr" (their own
ledger builders are submission-scoped).

Any calculator that throws denies immediately with stage "internal_error",
never falls through to allow. Pure engine module (no IO); the miner-lib
wrapper owns persisting the ledger event and advancing rate-limit bucket
state only when the rate-limit stage actually ran.

Stacked on #2341 + #2342 (imports from both).
JSONbored added a commit that referenced this pull request Jul 11, 2026
…lculator (#2340) (#5018)

The single decision point every miner write action (open_pr, file_issue,
apply_labels, post_eligibility_comment, create_branch, delete_branch,
generate_tests) must pass through. Composes the kill-switch (#2341) and
dry-run-default (#2342) primitives with the previously-shipped pure
calculators -- rate-limit (#2344), budget/turn/termination caps,
non-convergence detection, self-reputation throttle, and self-plagiarism --
into one precedence ladder: global kill-switch > per-repo pause > dry-run >
rate-limit > budget cap > non-convergence > reputation throttle >
self-plagiarism > allow.

Reputation-throttle and self-plagiarism extend beyond the issue's three
explicitly-named calculators, per those two modules' own doc comments
forward-referencing this exact chokepoint; both reuse their own
already-reviewed boolean gate semantic (throttled/allowed) rather than
inventing new policy. Both apply only to actionClass "open_pr" (their own
ledger builders are submission-scoped).

Any calculator that throws denies immediately with stage "internal_error",
never falls through to allow. Pure engine module (no IO); the miner-lib
wrapper owns persisting the ledger event and advancing rate-limit bucket
state only when the rate-limit stage actually ran.

Stacked on #2341 + #2342 (imports from both).
JSONbored added a commit that referenced this pull request Jul 11, 2026
…lculator (#2340) (#5018)

The single decision point every miner write action (open_pr, file_issue,
apply_labels, post_eligibility_comment, create_branch, delete_branch,
generate_tests) must pass through. Composes the kill-switch (#2341) and
dry-run-default (#2342) primitives with the previously-shipped pure
calculators -- rate-limit (#2344), budget/turn/termination caps,
non-convergence detection, self-reputation throttle, and self-plagiarism --
into one precedence ladder: global kill-switch > per-repo pause > dry-run >
rate-limit > budget cap > non-convergence > reputation throttle >
self-plagiarism > allow.

Reputation-throttle and self-plagiarism extend beyond the issue's three
explicitly-named calculators, per those two modules' own doc comments
forward-referencing this exact chokepoint; both reuse their own
already-reviewed boolean gate semantic (throttled/allowed) rather than
inventing new policy. Both apply only to actionClass "open_pr" (their own
ledger builders are submission-scoped).

Any calculator that throws denies immediately with stage "internal_error",
never falls through to allow. Pure engine module (no IO); the miner-lib
wrapper owns persisting the ledger event and advancing rate-limit bucket
state only when the rate-limit stage actually ran.

Stacked on #2341 + #2342 (imports from both).
JSONbored added a commit that referenced this pull request Jul 11, 2026
…okepoint (#2342, #2340) (#5014)

* feat(miner-governor): dry-run-by-default enforcement (#2342)

Resolves the miner's overall action mode with "safest wins" precedence
mirroring resolveAgentActionMode (src/settings/agent-execution.ts):
paused > dry_run > live. A freshly-configured miner (no opt-in anywhere)
always defaults to dry_run, never live. Live mode requires an explicit,
hard-to-fat-finger opt-in -- the exact string literal "live", never a
boolean -- from either the operator's own global env var
(GITTENSORY_MINER_LIVE_MODE) or the target repo's own .gittensory-miner.yml
(MinerGoalSpec.execution.liveModeOptIn). The #2341 kill-switch always wins
over any live-mode opt-in. Dry-run mode's would-be action is recorded to the
governor ledger with a distinct dry_run decision, without ever executing.

Stacked on #2341 (imports isMinerKillSwitchActive from kill-switch.ts).

* fix(miner-governor): close codecov/patch coverage gaps flagged on #2342

Two branches were untested: MinerGoalSpec's execution.liveModeOptIn
normalizer's "mapping present, key absent" path (distinct from the field
being omitted entirely), and buildMinerDryRunGovernorLedgerEvent's
repoFullName ?? null arm. Also narrows MinerExecutionPolicy.liveModeOptIn
from `string | null` to the literal `typeof MINER_LIVE_MODE_OPT_IN | null`,
so a typo in code constructing a MinerGoalSpec is caught at compile time
rather than relying solely on the runtime exact-match check.

* feat(miner-governor): fail-closed chokepoint composing every write calculator (#2340) (#5018)

The single decision point every miner write action (open_pr, file_issue,
apply_labels, post_eligibility_comment, create_branch, delete_branch,
generate_tests) must pass through. Composes the kill-switch (#2341) and
dry-run-default (#2342) primitives with the previously-shipped pure
calculators -- rate-limit (#2344), budget/turn/termination caps,
non-convergence detection, self-reputation throttle, and self-plagiarism --
into one precedence ladder: global kill-switch > per-repo pause > dry-run >
rate-limit > budget cap > non-convergence > reputation throttle >
self-plagiarism > allow.

Reputation-throttle and self-plagiarism extend beyond the issue's three
explicitly-named calculators, per those two modules' own doc comments
forward-referencing this exact chokepoint; both reuse their own
already-reviewed boolean gate semantic (throttled/allowed) rather than
inventing new policy. Both apply only to actionClass "open_pr" (their own
ledger builders are submission-scoped).

Any calculator that throws denies immediately with stage "internal_error",
never falls through to allow. Pure engine module (no IO); the miner-lib
wrapper owns persisting the ledger event and advancing rate-limit bucket
state only when the rate-limit stage actually ran.

Stacked on #2341 + #2342 (imports from both).

* test(miner-governor): close remaining branch-coverage gaps in chokepoint tests

Measured coverage showed chokepoint.js at 84.44% branch despite every
stage's fail-closed catch block already being exercised -- every
existing error test threw a genuine TypeError (a real Error instance)
via a null-cast input, so error instanceof Error was always true and
the String(error) fallback arm never ran. Add a throwingProxy helper
that throws a plain string instead, exercising that arm for all five
calculator stages. Also covers the rateLimitRandomFn conditional-spread
branch and the self-plagiarism similarity ?? null fallback (via a
whitespace-only fingerprint, which denies with no computed similarity).

chokepoint.js now measures 100/100/100 (lines/branch/funcs), up from
84.44% branch. No production code changes -- test-only.

* test(miner-governor): close chokepoint.ts patch-coverage gap (#2340)

test/unit/miner-governor-chokepoint.test.ts is the root-vitest file Codecov
actually measures (packages/gittensory-engine's own node:test suite is
invisible to it), and it only covered allow/kill_switch/dry_run/rate_limit
before this change -- budget_cap, non_convergence, reputation_throttle, and
self_plagiarism denies, their omitted-stage skips, the non-open_pr
actionClass skip, and every stage's fail-closed internal_error catch
(including the error-instanceof-Error vs String(error) fallback arms) were
all untested.

Add 21 tests covering: each deny path, each stage running-but-not-tripping,
both single-stage-omitted skips plus the combined non-open_pr skip, the
rateLimitRandomFn conditional-spread, the self-plagiarism similarity ?? null
fallback (whitespace-only fingerprint), and both throw shapes (null-deref
TypeError and a non-Error thrown value via a throwing Proxy) for all five
calculator call sites.

chokepoint.ts now measures 100/100/100/100 (statements/branches/functions/
lines), up from 44.64% patch coverage.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. manual-review Gittensor contributor context

Development

Successfully merging this pull request may close these issues.

maintainer: wire the fail-closed Governor chokepoint before every write action

1 participant