pbakaus/impeccable · error · Error
concept-seed: every challenger tier needs at least one…
Error message
concept-seed: every challenger tier needs at least one approved concept
What it means
Thrown during concept-seed roll selection when any of the well tiers (graphic, interaction, atmosphere) has zero approved concepts in the pool. The roll draws challengers per tier so that every candidate set mixes all three registers; an empty tier cannot be sampled around, so the roll refuses rather than silently starving one tier and pretending the hand is balanced.
Solutions
- Review and approve at least one concept in each tier (graphic, interaction, atmosphere) so every tier has an approved entry.
- Re-run the review flow on the existing catalog to convert rejected/pending candidates into approved ones for the missing tier.
- Generate additional candidates for the thin tier first, then approve one, then re-run the roll.
- Check that aggressive --min-rating or mode filters are not emptying a tier upstream of the roll.
Example fix
# before: pool has approved graphic+interaction only node skill/scripts/concept-seed.mjs --scope direction --from <key> # throws # after: approve one atmosphere-tier concept via the review flow, then node skill/scripts/concept-seed.mjs --scope direction --from <key>
Defensive patterns
Strategy: validation
Validate before calling
import { WELL_TIERS } from './lib/roll-selection.mjs';
function poolCoversAllTiers(approved) {
const tiers = new Set(approved.map(c => c.wellTier));
return WELL_TIERS.every(t => tiers.has(t));
}
if (!poolCoversAllTiers(approvedConcepts)) {
throw new Error(`approve at least one concept per tier before rolling: missing ${
WELL_TIERS.filter(t => !approvedConcepts.some(c => c.wellTier === t)).join(', ')}`);
} Type guard
function isRollablePool(approved) {
return ['graphic', 'interaction', 'atmosphere'].every(tier =>
approved.some(c => c.wellTier === tier));
} Prevention
- After each review session, check that every well tier retains at least one approved concept.
- Keep min-rating and mode filters modest so they cannot empty a tier before the roll.
- When seeding a new project, approve at least one concept per tier before the first --from roll.
When it happens
Trigger: Running concept-seed with --from <key> where the reviewed catalog approved only graphic-tier concepts; a fresh project whose approved pool has one concept each for interaction and atmosphere but none for graphic; minRating or mode filters configured upstream that emptied one tier's approved set before this check runs.
Common situations: Early in a project when only a few concepts have been human-approved; after a review session that rejected all concepts of one tier; switching scope (direction vs surface) against a pool that was only populated for the other scope.
Related errors
AI-assisted analysis of pbakaus/impeccable@f88b2837a7 (2026-08-18).
Data as JSON: /api/errors/21cc1d99e761e1fd.
Report an issue: GitHub.
Appendix: source
Thrown at skill/scripts/lib/roll-selection.mjs:170
export function* selectApprovedChallengers({ scope, key, reroll = 0, minRating = null, mode = null, concepts }) {
const approved = concepts.filter(concept => concept.status === 'approved');
// Direction chooses a durable identity, so it draws worlds; surface designs
// one page inside a committed identity, so it draws compositions. Duals serve
// both. A tier with no matching-strength approvals falls back to its full
// approved pool rather than starving the roll.
const wanted = scope === 'direction'
? new Set(['world', 'dual'])
: new Set(['composition', 'dual']);
const approvedByTier = new Map();
for (const concept of approved) {
const tier = approvedByTier.get(concept.wellTier) || [];
tier.push(concept);
approvedByTier.set(concept.wellTier, tier);
}
if (WELL_TIERS.some(tier => !(approvedByTier.get(tier) || []).length)) {
throw new Error('concept-seed: every challenger tier needs at least one approved concept');
}
// Optional minimum-rating gate, applied per tier and skipped for any tier it
// would empty, so a thin tier degrades to its full approved pool.
if (minRating) {
for (const [tier, pool] of approvedByTier) {
const rated = pool.filter(concept => (concept.review?.rating || 0) >= minRating);
if (rated.length > 0) approvedByTier.set(tier, rated);
}
}
// Mode eligibility, per tier and skipped where it would empty a tier. Worlds
// used to be drawn with no mode awareness at all, so a build asking for an app
// UI could get six worlds that only make sense on a landing page. A world is an
// identity and identities transfer further than compositions do, so this is a
// ceiling the reviewer sets rather than a category assignment: eligible
// everywhere until someone says otherwise.
if (mode) {
for (const [tier, pool] of approvedByTier) {View on GitHub (pinned to f88b2837a7)