feat(pool): deterministic weighted tier model pools - #6
Draft
yury-procoders wants to merge 17 commits into
Draft
Conversation
…(PR 2 of 3) Lets one tier name several (backend, model) pairs and hands successive jobs to them in turn, so a run burns three providers' quotas evenly instead of draining one. Purely additive: a config with no pools key behaves exactly as today. Round-robin decides at FIRST dispatch and the choice is frozen into state.json, so /v:resume re-dispatches to the recorded backend rather than re-deriving it — otherwise an interrupted job could come back on a different backend with different isolation and a different worktree. The one exception is a quota failure, where moving the job is the point. Independent of the zai backend; zai just becomes an eligible pool member.
The pool spec is cut from main and linked the zai design doc, which lives only on feat/zai-backend. The gate is line-based and does not respect inline code spans, so even quoting the path in backticks trips it — the branch dependency is now stated in prose instead. Adds the archaeology and library audits.
Four blocking defects in the first draft, two of them mine and inverted:
* the justification for skipping quota-aware balancing named the wrong
backend. z.ai is the MOST measurable of the three; the genuinely blind one
is claude, which this repo already lists in UNMEASURED_BACKENDS. The real
reason is architectural — Compound V never speaks HTTP to a provider, it
reads a CLI's stdout, so no provider header ever reaches the dispatcher.
* the draft would have introduced the policy regression its own Non-goals
promised to avoid: the never-Haiku execution-layer gate fires on a job's
explicit model, and a pool job carries none, so the gate became unreachable.
It now runs on the RESOLVED model.
claude is excluded from pools by default: Anthropic shares usage limits between
Claude and Claude Code, so a claude pool member competes with the operator's own
session. Integer weights (default 1) ship now — they are the only way to express
a reduced share, and weighted rotation is what comparable routers actually do.
Assignment is computed from MANIFEST order, not dispatch order, and the member
list is frozen at run start; an unavailable member is skipped with the counter
still advancing, so a shrinking pool cannot shift later assignments.
Corrected: rate_limited retries the same backend and never reroutes; only
out_of_credits reroutes, and today always to claude — which under a pool would
dump an exhausted run onto the operator's subscription. Availability has no
mechanism in this repo at all (removed in v2.6.2), so the spec now defines it
narrowly instead of assuming it. PR 1 is a declared merge prerequisite.
Adds the domain audit and its knowledge-base file.
Six rounds between the archaeologist and the doc-validator; real errors found in both directions. Two practices came out of it and are now in the knowledge base: report a check's denominator and a negative control that can fail (a broken harness and a clean repo both print zero), and re-run a gate after editing any doc that describes it — explaining a construct requires reproducing it, which is how a dead link landed inside the paragraph warning against dead links.
Fixes every CONFIRMED finding from docs/superpowers/reviews/2026-08-01-tier-model-pool-review.md: - Two Blocking, provably-vacuous selftest guards (recorded-assignment idempotence in pool-state.py, the pool retry-budget halt in failure-policy.py) now have fixtures that reproduce the exact planted mutation that exposed them. - The zai/PR-5 merge-order landmine: freeze_pool_members now reports a clear, member/tier-scoped error instead of a bare resolver ValueError, and a configured pool collapsing to <=1 available backend surfaces a warning instead of silently defeating the feature's whole purpose. - A case-variant `backend: Pool` no longer bypasses every pool gate; pool-state.py now normalizes the same way the manifest validator does. - context_length tier escalation and breaker-clearing on a pool job no longer produce a state the validator permanently rejects. - liveness.py now sweeps `dispatched` jobs, not only `running` ones. - backend_max_parallel is actually shape-validated now (it never was); the pool-sensitive-job filter matches type+id+title like the reviewer filter already does. - One CI-tracked manifest now exercises `backend: pool` end to end via a new examples/compound-v.example.json, closing the "entirely untested in CI" gap for the whole pool validation path. - Assorted Minor doc/test-coverage gaps: weight-bound fixtures, a typo'd pool-member key now warns, contract-block labels now name all three copies, state-machine.md no longer documents `deep` as a legal pool_tier, the dashboard shows the resolved backend instead of the literal "pool". Every selftest is green with a higher assertion count than before; all 12 tracked manifests validate; jq/lint-frontmatter/shellcheck 0.11.0 clean.
Adds the review that drove the previous commit (CONFIRMED/PLAUSIBLE/REJECTED per finding, merge verdict, and the applied-fixes table) plus the original handoff brief it was run against.
…s/ dir
- compound-v-pool-state.py: validate_state() compared job.get("backend")
!= "pool" directly instead of via _normalized_backend(), the third spot
the prior review fix (199e794) missed alongside manifest_pool_ordinals
and freeze_assignments. A job with backend "Pool" (mixed case) now hits
the same resume-safety gate as the lowercase form.
- SKILL.md: list docs/superpowers/reviews/ in Output Directory
Conventions — introduced by this branch's own adversarial self-review
but never added to the canonical directory list.
yury-procoders
added a commit
to yury-procoders/superpowers-v
that referenced
this pull request
Aug 4, 2026
This branch (PR procoders#7) forked from tier-model-pool (PR procoders#6) two commits before PR procoders#6's own adversarial-review fixes landed, so PR procoders#7 was missing them. Reconciles both branches: - decide()'s earliest_reset_seconds now gates to the terminal halt action only (was leaking onto ordinary retry/reroute results), matching PR procoders#6's review fix ported onto the new pool-agnostic decide() signature (pool_routed bool instead of pool_members list). - context_length tier-escalation now sets clear_assignment for pool-routed jobs, so a stale pool_index isn't left behind when a job leaves the ring — same fix, ported the same way. - validate_state()'s "pool" backend comparison uses the shared _normalized_backend() helper (was comparing the raw string directly), the third spot the original review fix missed. - skills/compound-v/failure-policy.md: PR procoders#7 rewrote this doc's cooldown/network-pause model; folded back PR procoders#6's escalation/ non-pool-fallback facts and the anti-patterns section (including the pool retry-budget footgun) that the rewrite had dropped, since those aren't duplicated anywhere else. - compound-v-dashboard.py: merged both branches' fixture/assertion additions (PR procoders#7's cooldown-state rendering checks, PR procoders#6's pool backend-cell check) into one fixture; had to add a matching pool_members ring entry for the added pool job, which PR procoders#7's now-stricter validate_state() call requires. - compound-v-pool-state.py: updated a PR procoders#6 selftest ("an available member that fails concrete resolution") that used "zai" as a stand-in for an unresolvable backend — PR procoders#7 has since given zai a real resolver entry, so the scenario the test needs no longer reproduces with that name. Swapped in a synthetic backend name that stays genuinely unresolvable regardless of future backends. All affected selftests pass: compound-v-pool-state.py (128/0), compound-v-failure-policy.py (59/0), compound-v-dashboard.py (77/0), compound-v-validate-manifest.py (145/0), compound-v-classify-failure.py (114/0), compound-v-resolve-model.py (PASSED).
…eption An independent Codex spec-compliance review flagged that the code (from this branch's own adversarial-review fix) clears a pool job's frozen assignment on context_length tier-escalation, while §3/§5 only documented the quota-class exception. The code is correct — pools don't cover deep, so an escalating job is leaving the ring by construction, and retaining a stale pool_index would fail validation — the spec just never caught up. Document it as the second exception instead of reverting tested code. The example json's codex+cursor pairing (flagged as diverging from the spec's codex+zai illustration) was left unchanged: the review doc already records that choice as deliberate, to keep the CI-covered example free of a secret-dependent backend.
yury-procoders
marked this pull request as draft
August 4, 2026 15:59
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Merge prerequisite
Do not merge this PR before #5. The code paths are fixture-testable independently, but the shipped Codex + z.ai pool policy depends on the z.ai backend. PR #5 is currently open and green; this PR does not claim it has merged.
Verification
git diff --checkpassedPython 3.9 grammar parsing passed for changed scripts. A Python 3.9 runtime was not installed locally; GitHub CI is the authoritative Python 3.9 execution gate.
What this does not show
Weighted rotation distributes job counts, not tokens, credits, messages, wall-clock time, quota burn, savings, or provider capacity. No balance percentage or improvement metric is claimed.