Skip to content

feat(miner-governor): closed-loop discovery re-entry trigger (#2338) - #5051

Merged
JSONbored merged 2 commits into
mainfrom
feat/miner-loop-reentry-2338
Jul 11, 2026
Merged

feat(miner-governor): closed-loop discovery re-entry trigger (#2338)#5051
JSONbored merged 2 commits into
mainfrom
feat/miner-loop-reentry-2338

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

Summary

Closes #2338.

Adds shouldReenter (packages/gittensory-engine/src/miner/loop-reentry-policy.ts) + attemptLoopReentry (packages/gittensory-miner/lib/loop-reentry.js): the final piece that turns single miner runs into an unattended repeat loop -- on a resolved outcome (merged, or rejected-and-disengaged), decide whether to re-enter discovery and, when allowed, actually dequeue the next candidate and transition run-state.

Per this issue's own "manual owner sign-off before enabling by default in any profile" deliverable, this is not wired into any scheduler/cron as part of this change -- it's a callable, fully-tested function ready for that sign-off.

Two independent hard ceilings, matching the issue's own framing of the top slop-at-scale risk (re-entering too fast, or looping on a permanently-rejected repo):

  • A per-repo circuit breaker: N consecutive disengaged (closed-without-merge) pr_outcome events for a repo pauses further re-entry for that repo specifically -- computed from real pr-outcome.js history via countConsecutiveDisengagements (walks backward from the most recent PR until a merged outcome breaks the streak).
  • A hard rate/session cap: independent of any repo's own history, a conservative ceiling (default 4/hour, 20/session) on re-entries -- computed from real event-ledger history via countReentriesSince.

Every call appends exactly one loop_reentry_decision event to the ledger, whether or not re-entry was allowed, with the full reason set -- and, when the caller supplies one, the just-completed cycle's loop-closure.js LoopClosureSummary threaded through verbatim into the payload for traceability (a LoopClosureSummary's own byType counts aren't detailed enough to compute the circuit-breaker tally from directly, so it's threaded as context, not a computational input).

On the file paths: the issue names packages/gittensory-miner/src/manage/loop-reentry.ts, but that src//TypeScript layout doesn't exist anywhere in packages/gittensory-miner (confirmed by search) -- the established, actual convention (already used by loop-closure.js, rejection-state-machine.js, pr-outcome.js, and every governor primitive this wave) is flat lib/*.js + hand-written .d.ts, validated via node --check. Followed that instead, plus this session's own engine(pure)/miner-lib(stateful) split for the decision vs. the IO.

Discovered and fixed while wiring this in: 10 other packages/gittensory-miner/lib/*.js files (governor-kill-switch.js, loop-closure.js, pr-outcome.js, rejection-state-machine.js, and others -- none from this session) were missing from the package's own node --check build script, an accumulating pre-existing gap unrelated to this issue. Verified all 10 pass node --check cleanly, then added all of them to the script alongside this issue's own loop-reentry.js.

Validation

Measured, not assumed -- across BOTH the engine's own node:test suite AND the actual codecov-visible root vitest path (see the local memory note from earlier in this batch about the engine package's Codecov blind spot):

# engine package
npx tsc -p tsconfig.json --incremental false && npx tsc -p tsconfig.test.json --incremental false && node --experimental-test-coverage --test "dist-test/**/*.test.js"
# root, the actual codecov-visible path
npx vitest run test/unit/miner-loop-reentry.test.ts --coverage --coverage.include="packages/gittensory-miner/lib/loop-reentry.js" --coverage.include="packages/gittensory-engine/src/miner/loop-reentry-policy.ts"
  • Engine: 437/437 tests pass; loop-reentry-policy.js 100.00% lines/branch/funcs.
  • Root vitest (codecov's real measurement path): 9/9 tests pass; both loop-reentry.js and loop-reentry-policy.ts at 100.00% stmts/branch/funcs/lines.
  • packages/gittensory-miner's node --check build script passes cleanly with all 11 newly-registered files.

Test plan

Per the issue's own explicit deliverable, named verbatim:

  • "merged outcome -> re-entry fires once" (dequeues, transitions run-state, records one audit event).
  • "rejected outcome with high repeated-blocker tally -> re-entry is suppressed and the repo is paused" (circuit breaker fires, candidate stays queued, run-state untouched).
  • A merged outcome resets the consecutive-disengagement streak to zero.
  • The hourly and session rate caps each fire independently of the other and of the per-repo circuit breaker.
  • Default thresholds apply when omitted; a caller-supplied override is honored.
  • Fails closed on every malformed-input/missing-dependency path.
  • loopSummary is threaded through verbatim when supplied, and recorded as null (not omitted) when not.
  • Works without the optional runState dependency.

@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 53b3f4f Commit Preview URL

Branch Preview URL
Jul 11 2026, 12:42 PM

@codecov

codecov Bot commented Jul 11, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.14%. Comparing base (2205cde) to head (53b3f4f).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #5051   +/-   ##
=======================================
  Coverage   94.13%   94.14%           
=======================================
  Files         466      467    +1     
  Lines       39573    39587   +14     
  Branches    14436    14442    +6     
=======================================
+ Hits        37254    37268   +14     
  Misses       1664     1664           
  Partials      655      655           
Flag Coverage Δ
shard-1 46.51% <21.42%> (-0.01%) ⬇️
shard-2 33.81% <21.42%> (+0.25%) ⬆️
shard-3 32.15% <21.42%> (+0.80%) ⬆️
shard-4 31.72% <21.42%> (-0.97%) ⬇️
shard-5 33.30% <100.00%> (-0.13%) ⬇️
shard-6 44.93% <21.42%> (-0.01%) ⬇️

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

Files with missing lines Coverage Δ
...gittensory-engine/src/miner/loop-reentry-policy.ts 100.00% <100.00%> (ø)
🚀 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

Tip

🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩

✅ Gittensory review result - approve/merge recommended

Review updated: 2026-07-11 12:45:25 UTC

9 files · 1 AI reviewer · no blockers · readiness 100/100 · CI green · unknown

✅ Suggested Action - Approve/Merge

  • safe to merge

Review summary
AI review could not be completed for this PR head. Gittensory is holding this PR for manual review instead of relying on deterministic signals alone.

Nits — 1 non-blocking
  • 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.
Signal Result Evidence
Code review ✅ No blockers 1 reviewer
Linked issue ✅ Linked #2338
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, 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

@JSONbored

Copy link
Copy Markdown
Owner Author

@gittensory chat what does this PR add?

@JSONbored

Copy link
Copy Markdown
Owner Author

@gittensory chat tell me about this PR

Adds shouldReenter (packages/gittensory-engine/src/miner/loop-reentry-
policy.ts) + attemptLoopReentry (packages/gittensory-miner/lib/loop-
reentry.js): the final piece that turns single miner runs into an
unattended repeat loop -- on a resolved outcome (merged, or rejected-
and-disengaged), decide whether to re-enter discovery and, when
allowed, actually dequeue the next candidate and transition run-state.
Not wired into any scheduler/cron as part of this change, per the
issue's own "manual owner sign-off before enabling by default in any
profile" deliverable.

Two independent hard ceilings, per the issue's own top-slop-at-scale-
risk framing (re-entering too fast, or looping on a permanently-
rejected repo):
- A per-repo circuit breaker: N consecutive disengaged (closed-
  without-merge) pr_outcome events for a repo pauses further re-entry
  for that repo specifically, computed from real pr-outcome.js history
  via countConsecutiveDisengagements (walks backward from the most
  recent PR until a merged outcome breaks the streak).
- A hard rate/session cap: independent of any repo's own history, a
  conservative ceiling (default 4/hour, 20/session) on re-entries,
  computed from real event-ledger history via countReentriesSince.

Every call appends exactly one loop_reentry_decision event to the
ledger, whether or not re-entry was allowed, with the full reason set
and (when the caller supplies one) the just-completed cycle's
loop-closure.js LoopClosureSummary threaded through verbatim for
traceability -- auditable regardless of this function's own return
value.

Discovered and fixed while wiring this in: 10 other packages/
gittensory-miner/lib/*.js files (governor-kill-switch, loop-closure,
pr-outcome, rejection-state-machine, and others -- none from this
session) were missing from the package's own `node --check` build
script, an accumulating pre-existing gap. Added all of them alongside
this issue's own loop-reentry.js.

Test-covered per the issue's own explicit deliverable: "merged outcome
-> re-entry fires once; rejected outcome with high repeated-blocker
tally -> re-entry is suppressed and the repo is paused" (both test
names verbatim), plus the independent rate/session caps, fail-closed
validation, and the optional loopSummary/runState threading.
…ift in pr-outcome.d.ts

pr-outcome.d.ts's RecordPrOutcomeOptions.eventLedger hand-declared a looser
{ appendEvent(event: { ...payload: unknown }): unknown } shape instead of reusing
EventLedger's real AppendEventInput/LedgerEntry contract, so a genuine EventLedger
(from initEventLedger) never type-checked against it -- only a same-shaped stub did.
This branch's own test was the first to pass a real EventLedger in, surfacing the gap.

Fixes it at the source (import + reuse the real types) and updates the one other
mock ledger (miner-pr-outcome.test.ts) that relied on the looser shape so it can't
silently drift from the real contract again. Also restores the two `as never` casts
missing from two deliberately-invalid-deps assertions in miner-loop-reentry.test.ts
that were inconsistent with the identical pattern on the four lines directly above them.

No behavior change -- purely type-declaration correctness; the runtime fail-closed
behavior these tests assert was always correct and remains covered.
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: closed-loop discovery re-entry trigger

1 participant