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 non-convergence code exists yet anywhere in packages/gittensory-miner or packages/gittensory-engine — a recursive grep for "convergence" across both packages returns zero hits. The only "convergence" concept in this codebase today is an unrelated, hosted-review-side notion: src/selfhost/backlog-convergence.ts (a self-host sweep that republishes a stale gate surface) and src/review/sweep-watchdog.ts (a cron-liveness watchdog for that same sweep). Both are about the hosted review gate's own publishing freshness, not a miner's own attempt loop — useful prior art for "pure detector, structured signal, no side effects" as a style, but not the domain. This issue is genuinely new work.
The domain here: the miner's local portfolio-queue entries (packages/gittensory-miner/lib/portfolio-queue.js, status queued → in_progress → done, schema at portfolio-queue.js:89-98) can be re-enqueued — the ON CONFLICT clause at portfolio-queue.js:108-115 re-activates an already-tracked (repoFullName, identifier) back to queued in place. An item that keeps cycling queued → in_progress → queued without ever reaching done is "not converging" — worth a pure signal so a later stage can decide to stop spending budget on it. The table has no attempt-history columns today (only current status/priority/enqueued_at), so this issue does NOT invent new persistence — it defines a pure classifier over a typed attempt/outcome-count input the caller already has or will track, mirroring how classifyContributorFit in packages/gittensory-engine/src/contributor-fit.ts takes an already-computed profile rather than fetching one itself.
This is a pure calculator, not enforcement: it takes typed counts, returns a typed verdict, and touches nothing else. It produces one input signal for the fail-closed Governor chokepoint that composes it later — that composition (rate-limit + budget caps + this detector, combined into one allow/deny decision) is explicitly separate, maintainer-owned work tracked in #2340 ("wire the fail-closed Governor chokepoint before every write action," milestone 13). This issue is not that, and does not gate or block any action by itself.
Deliverables
New packages/gittensory-engine/src/portfolio/non-convergence.ts (sibling to queue.ts in the same directory — matches this item's own miner-portfolio component tag) exporting a typed input describing one queue item's attempt/outcome history (e.g. attempt count, consecutive-failure count since the last improvement) and a pure classifyPortfolioConvergence(input, thresholds)-style function returning a structured verdict (e.g. status: "converging" | "stalled" | "non_convergent" plus reasons: string[], matching the { verdict, reasons } shape classifyContributorFit already uses)
No IO, no Date.now(), no randomness — every count/threshold is a plain input argument, deterministic given the same input, matching the header discipline of packages/gittensory-engine/src/governor/rate-limit.ts:1-8
Zero-attempts / not-yet-tried input reads as converging (or neutral), never non_convergent — a first attempt is not evidence of a stuck loop, the same non-judgment-on-absence rule contributor-fit.ts:24-26,36-41 already applies to a contributor's own first attempt on a repo
A single failure is not non-convergent; only a sustained streak (consecutive failures past a threshold, or repeated re-enqueue without ever reaching done) reads non_convergent
Unit tests: zero history, one failure, a converging/improving streak, and a genuinely stuck streak past threshold
Export the new type(s) and function from the package's public entrypoint (packages/gittensory-engine/src/index.ts, alongside the existing export * from "./portfolio/queue.js"; at line 165)
packages/gittensory-miner/lib/portfolio-queue.js:89-98 (queue schema: status enum), :108-115 (re-enqueue-in-place ON CONFLICT clause) — the observable "cycling without reaching done" this detector classifies
packages/gittensory-engine/src/contributor-fit.ts (whole file, esp. :24-26 and :36-41) — the pure-classifier-over-typed-input pattern to mirror, including its "absence of history isn't evidence of a problem" rule
packages/gittensory-engine/src/governor/rate-limit.ts:1-8 — the "computes numbers only, no IO, no enforcement" header discipline to match
packages/gittensory-engine/src/index.ts:165 — where the new module should be re-exported
src/selfhost/backlog-convergence.ts, src/review/sweep-watchdog.ts — unrelated hosted-review-side "convergence" concepts; cited only as a pure-detector style reference, not the domain
No non-convergence code exists yet anywhere in
packages/gittensory-minerorpackages/gittensory-engine— a recursive grep for "convergence" across both packages returns zero hits. The only "convergence" concept in this codebase today is an unrelated, hosted-review-side notion:src/selfhost/backlog-convergence.ts(a self-host sweep that republishes a stale gate surface) andsrc/review/sweep-watchdog.ts(a cron-liveness watchdog for that same sweep). Both are about the hosted review gate's own publishing freshness, not a miner's own attempt loop — useful prior art for "pure detector, structured signal, no side effects" as a style, but not the domain. This issue is genuinely new work.The domain here: the miner's local portfolio-queue entries (
packages/gittensory-miner/lib/portfolio-queue.js, statusqueued → in_progress → done, schema atportfolio-queue.js:89-98) can be re-enqueued — theON CONFLICTclause atportfolio-queue.js:108-115re-activates an already-tracked(repoFullName, identifier)back toqueuedin place. An item that keeps cyclingqueued → in_progress → queuedwithout ever reachingdoneis "not converging" — worth a pure signal so a later stage can decide to stop spending budget on it. The table has no attempt-history columns today (only currentstatus/priority/enqueued_at), so this issue does NOT invent new persistence — it defines a pure classifier over a typed attempt/outcome-count input the caller already has or will track, mirroring howclassifyContributorFitinpackages/gittensory-engine/src/contributor-fit.tstakes an already-computed profile rather than fetching one itself.This is a pure calculator, not enforcement: it takes typed counts, returns a typed verdict, and touches nothing else. It produces one input signal for the fail-closed Governor chokepoint that composes it later — that composition (rate-limit + budget caps + this detector, combined into one allow/deny decision) is explicitly separate, maintainer-owned work tracked in #2340 ("wire the fail-closed Governor chokepoint before every write action," milestone 13). This issue is not that, and does not gate or block any action by itself.
Deliverables
packages/gittensory-engine/src/portfolio/non-convergence.ts(sibling toqueue.tsin the same directory — matches this item's ownminer-portfoliocomponent tag) exporting a typed input describing one queue item's attempt/outcome history (e.g. attempt count, consecutive-failure count since the last improvement) and a pureclassifyPortfolioConvergence(input, thresholds)-style function returning a structured verdict (e.g.status: "converging" | "stalled" | "non_convergent"plusreasons: string[], matching the{ verdict, reasons }shapeclassifyContributorFitalready uses)Date.now(), no randomness — every count/threshold is a plain input argument, deterministic given the same input, matching the header discipline ofpackages/gittensory-engine/src/governor/rate-limit.ts:1-8converging(or neutral), nevernon_convergent— a first attempt is not evidence of a stuck loop, the same non-judgment-on-absence rulecontributor-fit.ts:24-26,36-41already applies to a contributor's own first attempt on a repodone) readsnon_convergentpackages/gittensory-engine/src/index.ts, alongside the existingexport * from "./portfolio/queue.js";at line 165)References
packages/gittensory-miner/lib/portfolio-queue.js:89-98(queue schema:statusenum),:108-115(re-enqueue-in-placeON CONFLICTclause) — the observable "cycling without reaching done" this detector classifiespackages/gittensory-engine/src/contributor-fit.ts(whole file, esp.:24-26and:36-41) — the pure-classifier-over-typed-input pattern to mirror, including its "absence of history isn't evidence of a problem" rulepackages/gittensory-engine/src/governor/rate-limit.ts:1-8— the "computes numbers only, no IO, no enforcement" header discipline to matchpackages/gittensory-engine/src/index.ts:165— where the new module should be re-exportedsrc/selfhost/backlog-convergence.ts,src/review/sweep-watchdog.ts— unrelated hosted-review-side "convergence" concepts; cited only as a pure-detector style reference, not the domain