Skip to main content

nousergon-groomer

The deterministic control plane for the autonomous backlog-and-PR maintenance loop — the fixture-runnable core specified by the groom-sweep policy.

It computes, for every carried issue and PR, exactly one disposition — act, blocked, terminal, or undecidable — from declared dependencies evaluated against an observed snapshot of the world. The core is pure logic: no network, no credentials, no model. The operational harness (dispatch, merge execution, PAT) lives in the private layer.

What this repo IS

  • Public (MIT). Framework, contracts, and eval logic — the "how rigorously we measure belief" tier — not strategy edge or secrets.
  • Fixture-runnable (§8.1). The entire test suite runs over recorded JSON snapshots with no network, no credentials, no model.
  • Pure logic over recorded state. Every function is a pure over its inputs; the same (items, world, store) always yields the same result.

What this repo is NOT

  • No GitHub client. No PAT, no gh calls, no API writes.
  • No model calls. The control loop is deterministic (§5.4); the model is at the leaf, invoked by the private harness, not here.
  • No dispatch / scheduler. When to run, which credentials to use, and how to perform a merge are the private harness's concern.
  • No fleet-specific configuration. Gate labels, lane definitions, and issue-body formats are adapter data passed in, not hardcoded.

Architecture

┌─────────────────────────────────────────────────────────┐
│  nousergon-groomer (PUBLIC — this repo)                  │
│                                                          │
│  ┌──────────────────────────────────────────────────┐   │
│  │  Deterministic core                               │   │
│  │                                                   │   │
│  │  models.py          Item, Dependency, Disposition │   │
│  │  dependency_        (Dependency, ObservedWorld)   │   │
│  │    evaluator.py      → DependencyEvaluation (§3)  │   │
│  │  dependency_        transitive blocked-ness (§3.4)│   │
│  │    graph.py                                       │   │
│  │  admission.py       WIP ceiling + unblocked (§4)  │   │
│  │  lane_classifier    Gate A + Gate B pure fn       │   │
│  │  disposition.py     §5.1 total over ItemState     │   │
│  │  observed_gen.py    §5.5 skip optimization        │   │
│  │  reconciler.py      the loop — ties it together   │   │
│  └──────────────────────────────────────────────────┘   │
│                                                          │
│  ┌──────────────────────────────────────────────────┐   │
│  │  Fixture harness (§8.1)                           │   │
│  │  fixtures/           recorded PR/issue snapshots  │   │
│  │  tests/              contract + property tests    │   │
│  └──────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────┘
                          │
                    published as a
                    versioned package
                          │
                          ▼
┌─────────────────────────────────────────────────────────┐
│  alpha-engine-config/scripts (PRIVATE — not this repo)  │
│                                                          │
│  ┌──────────────────────────────────────────────────┐   │
│  │  Operational harness                             │   │
│  │  groom_driver.py    dispatch + schedule           │   │
│  │  *_merge_sweep.py   merge execution (PAT)         │   │
│  │  record_agent_      attribution recording        │   │
│  │    merge.py                                       │   │
│  │  model leaf         generative candidate changes  │   │
│  │  GitHub client      fetch live state → snapshot   │   │
│  └──────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────┘

Policy alignment

Policy § Property Implementation
§3 Blocked-ness derived, never asserted dependency_evaluator.py — pure fn over (Dependency, ObservedWorld)
§3.1 Declaration validated at write boundary models.Dependency — rejects empty/unevaluable at construction
§3.3 Spec/status stored separately models.Dependency (spec) vs DependencyEvaluation (status) — separate types
§3.4 Dependencies compose transitively dependency_graph.py — walks the closure, names the chain
§4.1 WIP ceiling admission.AdmissionController — bounds the queue, not the rate
§4.2 Every carried item charged admission.current_wip — counts drafts, blocked, in-review
§4.3 PR opened only for unblocked admission.can_admit — rejects blocked issues
§5.1 One total reconciler disposition.py — every ItemState maps to exactly one disposition
§5.3 Idempotent and resumable reconciler.py — re-running over identical state = same output
§5.4 Deterministic core, model at leaf the core has no model import; the leaf is a strategy interface
§5.5 Observed-generation skip observed_gen.py — records last-evaluated generation, skips unchanged
§8.1 Core runs against fixtures fixtures/ + tests/ — no credentials needed
F5 No advanceable-but-unadvanced PRs disposition.py — a PR the reconciler could advance but didn't is ACT

Quick start

git clone https://github.com/nousergon/nousergon-groomer.git
cd nousergon-groomer
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest

That's it — 94 tests run over 8 fixture scenarios with no network, no credentials, and no model. If the last command exits 0, the core is correct against its recorded fixtures.

Using the core

from nousergon_groomer import (
    Reconciler, ReconcilerConfig, Item, Dependency, DependencyKind,
    ItemKind, ItemState,
)
from nousergon_groomer.dependency_evaluator import ObservedWorld
from nousergon_groomer.observed_gen import GenerationStore

# Record an issue with a declared dependency
issue = Item(
    id="i1", kind=ItemKind.ISSUE, state=ItemState.OPEN_ISSUE_ACTIONABLE,
    declared_dependencies=[
        Dependency(kind=DependencyKind.S3_OBJECT, target="s3://bucket/key"),
    ],
)

# Observe the world (s3_objects is empty → the dep is unsatisfied)
world = ObservedWorld(s3_objects=set())

# Run one reconciliation pass
config = ReconcilerConfig(wip_ceiling=5, generation=1)
reconciler = Reconciler(config)
result = reconciler.reconcile([issue], world, GenerationStore())

# The issue is BLOCKED on the S3 object
assert result.items[0].disposition.kind.value == "blocked"

Fixtures

The fixtures/ directory holds JSON scenarios that record item populations, observed worlds, and expected dispositions. Each is a self-contained proof that the core produces the documented outcome for that scenario:

Fixture Scenario Expected
clean_green_lane_pr green PR with a lane label ACT automerge
red_ci_pr PR with failing CI ACT fix_ci
blocked_issue issue blocked on an S3 object BLOCKED
transitive_blocked A blocked on B blocked on C BLOCKED (chain)
at_wip_ceiling WIP saturated, new issue BLOCKED (admission)
gate_labeled_pr green PR with a gate:* label TERMINAL
do_not_groom item marked do-not-groom TERMINAL
undecidable unobservable dependency UNDECIDABLE

Roadmap — v0.2.0 (usable tool) and beyond

The v0.1.0 core is a library — pure logic over recorded state. To make it a usable tool that external users can point at their GitHub repo and run, v0.2.0 adds the operational adapters (public, credentials injected at runtime):

  • GitHub snapshot adaptergh/API → Item[] + ObservedWorld (PAT via env var)
  • GitHub executor adapterReconcilerResult → merge, comment, create PR (PAT via env var)
  • Pluggable model interface — a ModelProvider protocol (see below)
  • CLIgroomer run --repo foo/bar --config config.yaml --dry-run
  • Configuration system — YAML for lanes, gates, WIP ceiling, model tiers

The private layer (fleet-specific config, krepis router adapter, spot bootstrap, prompt templates) stays in alpha-engine-config.

Model provider — provider-agnostic, never Anthropic

The model interface is a provider-agnostic protocol. The default implementation uses direct API calls to OpenAI-compatible endpoints — the common denominator across xAI (Grok), Moonshot (Kimi), Zhipu (GLM), DeepSeek, and other providers. Anthropic is never a default and never a dependency.

# pyproject.toml — optional dependencies, none required for the pure core
[project.optional-dependencies]
github = ["httpx>=0.24"]       # snapshot + executor adapters
config = ["pyyaml>=6.0"]       # config system
model  = ["openai>=1.0"]       # default OpenAI-compatible provider
cli    = ["typer>=0.9"]        # CLI entry point
# NOTE: "anthropic" is NEVER a dependency of this package.

The ModelProvider protocol:

class ModelProvider(Protocol):
    def complete(self, prompt: str, *, model: str, temperature: float = 0.0) -> str:
        """Generate a completion via the provider's API."""
        ...

The default OpenAICompatibleProvider works against any provider that exposes an OpenAI-compatible /v1/chat/completions endpoint (xAI, Moonshot, Zhipu, DeepSeek, local models via vLLM/Ollama). Configuration is by base_url

  • api_key + model_name — no vendor lock-in:
# config.yaml
model:
  provider: openai-compatible
  base_url: https://api.deepseek.com/v1
  api_key_env: DEEPSEEK_API_KEY
  model: deepseek-chat

The nousergon private layer implements this protocol with a krepis router adapter (proprietary). External users plug in any provider they choose.

Normative spec

The groom-sweep policy is the normative source. Every invariant in this code references its section number (e.g. §3, §5.1, §5.5) so the code and policy can be audited against each other.

Epic and issue breakdown

The v0.1.0 epic lives on alpha-engine-config as the single source of truth: alpha-engine-config#5853. Implementation issues are filed on this repo (#2–#13).

License

MIT. See LICENSE and NOTICE.

Relicensed from AGPL-3.0-only in 0.3.0 (2026-08-03): this tool is not monetised and nothing paid depends on operating it, so network-use copyleft protected revenue nobody intends to earn while taxing the only outcome the repo exists for — being installed and tried. Releases up to 0.2.1 remain available under AGPL-3.0-only.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nousergon_groomer-0.4.0.tar.gz (69.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nousergon_groomer-0.4.0-py3-none-any.whl (49.3 kB view details)

Uploaded Python 3

File details

Details for the file nousergon_groomer-0.4.0.tar.gz.

File metadata

  • Download URL: nousergon_groomer-0.4.0.tar.gz
  • Upload date:
  • Size: 69.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for nousergon_groomer-0.4.0.tar.gz
Algorithm Hash digest
SHA256 951d953024bfaf12a217ac0ede3034146e3902a665133a9978c9df6371e38fd8
MD5 79a9b9674652a2d032e9ca7b74b0f9b0
BLAKE2b-256 4f1ad3c408c7107922cc0f9332b6a70e170fdcd7087304d1faaebbbca886abbf

See more details on using hashes here.

Provenance

The following attestation bundles were made for nousergon_groomer-0.4.0.tar.gz:

Publisher: publish.yml on nousergon/nousergon-groomer

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file nousergon_groomer-0.4.0-py3-none-any.whl.

File metadata

File hashes

Hashes for nousergon_groomer-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3a1d2963e799031a37a6e1ac1e2dbfa64f5425eff602fe0cbd523232631f15fc
MD5 243825a96a834abc9b5d4b81e4278d7a
BLAKE2b-256 b81b11c33148d61ea8edcad0fffb6211a8519ab61dbb4a9f95701469c84b2e19

See more details on using hashes here.

Provenance

The following attestation bundles were made for nousergon_groomer-0.4.0-py3-none-any.whl:

Publisher: publish.yml on nousergon/nousergon-groomer

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.11.1

2 files

0.11.0

2 files

0.10.0

2 files

0.9.0

2 files

0.8.0

2 files

0.7.0

2 files

0.6.0

2 files

0.5.0

2 files

This release

0.4.0 This release

2 files

0.3.0

2 files

0.2.1

2 files

0.2.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page