Skip to content

feat(miner-portfolio): non-convergence DETECTOR (pure signal, no enforcement) #4286

Description

@JSONbored

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)
  • Doc comment stating explicitly this is a DETECTOR only — no enforcement, no write-blocking — and naming maintainer: wire the fail-closed Governor chokepoint before every write action #2340 as the future (separate, maintainer-only) consumer

References

  • 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
  • maintainer: wire the fail-closed Governor chokepoint before every write action #2340 (milestone 13, maintainer-only) — the fail-closed chokepoint that composes this detector's output later; explicitly not this issue

Metadata

Metadata

Assignees

No one assigned

    Labels

    gittensor:featureGittensor-scored feature linked to a feature issue — scores a 0.25x multiplier.help wantedExtra attention is needed

    Projects

    Status
    In Progress

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions