Skip to content

feat(miner-governor): dry-run-by-default enforcement + fail-closed chokepoint (#2342, #2340) - #5014

Merged
JSONbored merged 5 commits into
mainfrom
feat/miner-governor-dry-run-default-2342
Jul 11, 2026
Merged

feat(miner-governor): dry-run-by-default enforcement + fail-closed chokepoint (#2342, #2340)#5014
JSONbored merged 5 commits into
mainfrom
feat/miner-governor-dry-run-default-2342

Conversation

@JSONbored

@JSONbored JSONbored commented Jul 11, 2026

Copy link
Copy Markdown
Owner

Summary

Note on scope: this PR originally covered only #2342. #2340 (the Governor chokepoint) was stacked on top of it in a separate PR (#5018) and got merged into this branch (its base at the time) rather than into main directly — a side effect of reviewing/merging a PR stack bottom-up before every link in the chain has landed on main. #5018 is correctly recorded as merged; its commit now rides along in this PR's diff. I've rebased this branch onto current main (which already has #2341) and updated the title/body accordingly so this is no longer confusing to review. Once this merges, both #2342 and #2340 land on main together.

  • Adds packages/gittensory-engine/src/governor/action-mode.ts (maintainer: 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 always defaults to dry_run, never live. Live mode requires the exact string literal "live" (never a boolean or truthy coercion) from either the operator's global env var (GITTENSORY_MINER_LIVE_MODE=live) or the target repo's own .gittensory-miner.yml (MinerGoalSpec.execution.liveModeOptIn: live). The maintainer: global + per-repo kill-switch #2341 kill-switch always wins over any live-mode opt-in.
  • Adds packages/gittensory-engine/src/governor/chokepoint.ts (maintainer: wire the fail-closed Governor chokepoint before every write action #2340): the single fail-closed decision point every miner write action must pass through, composing the kill-switch + this action-mode resolver + the already-shipped pure calculators (rate-limit, budget/turn/termination caps, non-convergence, self-reputation throttle, self-plagiarism) into one precedence ladder. Any calculator that throws denies immediately (stage: "internal_error") rather than falling through to allow — this fail-closed behavior is directly tested (5 dedicated tests forcing a real exception into each composable calculator).
  • Same composition pattern throughout: pure engine module → barrel export → thin packages/gittensory-miner/lib/governor-*.js (+ hand-written .d.ts) wrapper. The chokepoint's wrapper persists the ledger event and advances rate-limit bucket state only when the rate-limit stage actually ran.
  • New MinerGoalSpec.execution.liveModeOptIn field, tolerantly parsed exactly like killSwitch.paused — doc/schema/example surface kept in sync.

Closes #2342, Closes #2340

Scope

  • Focused: two closely-related, sequentially-dependent maintainer issues that ended up in one PR due to the merge-topology note above, not unrelated bundling.
  • Follows CONTRIBUTING.md.
  • Owner PR — linked issue eligibility gate does not apply.

Validation

  • git diff --check
  • npm run typecheck — clean.
  • npm run build + npm run test in packages/gittensory-engine — 402/402 passing.
  • npm run build in packages/gittensory-miner (node --check, including governor-action-mode.js and governor-chokepoint.js) — clean.
  • Targeted vitest run across every touched/adjacent root-level suite — all passing.
  • 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.
  • The live-mode opt-in deliberately requires exact string-literal equality (=== "live"), never truthy coercion — verified by dedicated tests.
  • Fail-closed is tested, not just asserted: 5 dedicated chokepoint tests force a runtime exception into each composable calculator and confirm the chokepoint denies rather than silently skipping to the next stage.
  • Neither module performs real write execution yet — the chokepoint is not 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. Docs updated (miner-goal-spec.md, JSON Schema, .gittensory-miner.yml.example); no CHANGELOG.md change.

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 aa82231 Commit Preview URL

Branch Preview URL
Jul 11 2026, 01:17 PM

@JSONbored JSONbored self-assigned this Jul 11, 2026
@JSONbored JSONbored linked an issue Jul 11, 2026 that may be closed by this pull request
4 tasks
@codecov

codecov Bot commented Jul 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.70130% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 94.20%. Comparing base (99c2429) to head (aa82231).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...ages/gittensory-engine/src/governor/action-mode.ts 90.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #5014      +/-   ##
==========================================
+ Coverage   94.18%   94.20%   +0.01%     
==========================================
  Files         468      470       +2     
  Lines       39616    39693      +77     
  Branches    14456    14486      +30     
==========================================
+ Hits        37314    37392      +78     
+ Misses       1646     1645       -1     
  Partials      656      656              
Flag Coverage Δ
shard-1 46.41% <12.98%> (-0.07%) ⬇️
shard-2 33.55% <3.89%> (-0.32%) ⬇️
shard-3 31.33% <6.49%> (-0.87%) ⬇️
shard-4 32.77% <3.89%> (+0.81%) ⬆️
shard-5 33.57% <81.81%> (+0.27%) ⬆️
shard-6 44.92% <18.18%> (-0.04%) ⬇️

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 100.00% <100.00%> (ø)
packages/gittensory-engine/src/miner-goal-spec.ts 98.23% <100.00%> (+0.19%) ⬆️
...ages/gittensory-engine/src/governor/action-mode.ts 90.00% <90.00%> (ø)

... 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 13:20:36 UTC

20 files · 1 AI reviewer · no blockers · readiness 100/100 · CI failing · unstable

🛑 Suggested Action - Manual Review

Review summary
This adds a pure, side-effect-free action-mode resolver (paused > dry_run > live) and a new `execution.liveModeOptIn` field on MinerGoalSpec, correctly defaulting to dry_run when no opt-in is present and requiring the exact string literal "live" (no truthy coercion) from either the operator's env var or the repo's own config, with the kill-switch always taking precedence. It follows the exact same tolerant-parse/default/freeze conventions as the sibling killSwitch field, is additive-only (not yet wired into any real write call site, by design since the Governor chokepoint lands separately), and closes the linked issue #2342. No reachable-path defects were found in this diff.

Nits — 6 non-blocking
  • packages/gittensory-engine/src/miner-goal-spec.ts normalizeExecutionPolicy: the `execution: {}` (object present, `liveModeOptIn` key absent) branch returning `fallback.liveModeOptIn` doesn't appear to have a dedicated test, and packages/gittensory-engine/src/governor/action-mode.ts buildMinerDryRunGovernorLedgerEvent's `repoFullName ?? null` null arm is only exercised with a non-null value in the tests shown — likely the source of the codecov/patch shortfall (95.23% vs 99%).
  • MinerExecutionPolicy.liveModeOptIn is typed as `string | null` rather than the narrower `"live" | null`; since only the exact literal is ever meaningful, a stricter type would catch typos at compile time rather than relying solely on the runtime check.
  • Add a parser test for `execution: {}` (present but no `liveModeOptIn`) to hit the currently-uncovered null/undefined-inner-value branch in normalizeExecutionPolicy.
  • Add a test for buildMinerDryRunGovernorLedgerEvent with `repoFullName` omitted or explicitly null to cover the `?? null` arm and close the coverage gap.
  • Consider narrowing `MinerExecutionPolicy.liveModeOptIn` to `"live" | null` for stricter typing, matching the runtime exact-literal contract.
  • 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.

CI checks failing

  • codecov/patch — 98.70% of diff hit (target 99.00%)
Signal Result Evidence
Code review ✅ No blockers 1 reviewer
Linked issue ✅ Linked #2342, #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 (2 linked issues).
Validation posture ✅ 25/25 PR body includes validation/test evidence.
Contributor workload ✅ 10/10 Author activity: 47 registered-repo PR(s), 39 merged, 423 issue(s).
Contributor context ✅ Confirmed Gittensor contributor JSONbored; Gittensor profile; 47 PR(s), 423 issue(s).
Gate result ✅ Passing No configured blocker found.
Improvement ✅ Minor risk: clean · value: minor — Code changes are accompanied by test evidence.
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), 423 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

@loopover-orb loopover-orb Bot added the manual-review Gittensor contributor context label Jul 11, 2026
@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
@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-kill-switch-2341 branch from b418a79 to 820d62c Compare July 11, 2026 09:14
@JSONbored
JSONbored force-pushed the feat/miner-governor-dry-run-default-2342 branch from 85f1e72 to 84b7f83 Compare July 11, 2026 09:15
Base automatically changed from feat/miner-governor-kill-switch-2341 to main July 11, 2026 09:19
@JSONbored
JSONbored force-pushed the feat/miner-governor-dry-run-default-2342 branch from 8138bc9 to b0b2d69 Compare July 11, 2026 09:26
@JSONbored JSONbored changed the title feat(miner-governor): dry-run-by-default enforcement (#2342) feat(miner-governor): dry-run-by-default enforcement + fail-closed chokepoint (#2342, #2340) Jul 11, 2026
@JSONbored

Copy link
Copy Markdown
Owner Author

Pushed a coverage-only follow-up commit (00b20d6): measured coverage via `node --experimental-test-coverage` found `chokepoint.js` at 84.44% branch despite the fail-closed catch blocks already being exercised by the existing `null as unknown as X` tests -- those all throw a real `TypeError` (an `Error` instance), so `error instanceof Error ? error.message : String(error)` never took its `String(error)` arm. Added a `throwingProxy` helper that throws a plain string to exercise that arm for all five calculator stages, plus the `rateLimitRandomFn` conditional-spread branch and the self-plagiarism `similarity ?? null` fallback.

`chokepoint.js` now measures 100.00% lines / 100.00% branch / 100.00% funcs. 409/409 tests pass. Test-only, no production code changes.

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).
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.
…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).
…int 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/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.
@JSONbored
JSONbored force-pushed the feat/miner-governor-dry-run-default-2342 branch from 23599a3 to aa82231 Compare July 11, 2026 13:16
@JSONbored
JSONbored merged commit 4719f2c into main Jul 11, 2026
19 of 20 checks passed
@JSONbored
JSONbored deleted the feat/miner-governor-dry-run-default-2342 branch July 11, 2026 13:28
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: dry-run-by-default enforcement maintainer: wire the fail-closed Governor chokepoint before every write action

1 participant