# How the Diversification Rule Prevents Repetitive AI Output in Hallmark

> Discover how Hallmark's diversification rule prevents repetitive AI output by ensuring each generation varies. Learn how this rule keeps your AI creations fresh and unique.

- Repository: [Hassan El Mghari/hallmark](https://github.com/Nutlope/hallmark)
- Tags: internals
- Published: 2026-08-14

---

**The diversification rule enforces fresh visual output by requiring every new generation to differ on at least one of three axes (paper-band, display-style, accent-hue) from the last 3-5 runs, with project memory stored in [`/.hallmark/log.json`](https://github.com/Nutlope/hallmark/blob/main//.hallmark/log.json).**

Hallmark is an AI-driven design system that solves the "same AI-generated layout" problem through a strict **diversification rule** baked into its generation pipeline. Rather than allowing the model to fall back on familiar patterns, the system actively prevents repetition by tracking historical choices and enforcing variation across multiple dimensions. This article explains exactly how the rule works, where it's implemented in the Hallmark source code, and why it's effective.

## The Three Diversification Axes

Every Hallmark theme—whether from the built-in catalog or a custom definition—declares three **diversification axes**:

- **Paper-band**: Light or dark tonal foundation
- **Display-style**: Serif or sans-serif typography family
- **Accent-hue**: Warm or cool color temperature

These axes appear in [`tokens.css`](https://github.com/Nutlope/hallmark/blob/main/tokens.css) for catalog themes or explicitly in custom theme headers. The diversification rule operates on this three-dimensional space to guarantee visual variety. According to [`skills/hallmark/references/custom-theme.md`](https://github.com/Nutlope/hallmark/blob/main/skills/hallmark/references/custom-theme.md) (lines 210-256), custom themes must declare their axes to participate in rotation checks.

## How Project Memory Works

Hallmark maintains persistent project memory through a JSON log at [`/.hallmark/log.json`](https://github.com/Nutlope/hallmark/blob/main//.hallmark/log.json). After each full-page generation, the system appends an entry recording:

```json
{
  "date": "2024-01-15",
  "macrostructure": "Long Document",
  "theme": "carnival",
  "theme_axes": "dark / sans / cool",
  "nav_archetype": "minimal",
  "footer_archetype": "newsletter"
}

```

This logging mechanism is specified in [`skills/hallmark/SKILL.md`](https://github.com/Nutlope/hallmark/blob/main/skills/hallmark/SKILL.md) (lines 274-315). The log serves as the historical window for all diversification decisions.

## The Rotation Check Algorithm

Before selecting a new theme, Hallmark reads the last **3-5 entries** from [`log.json`](https://github.com/Nutlope/hallmark/blob/main/log.json) and applies a hard constraint: **no theme may share all three axes with the most recent entry**. At minimum, one axis must differ. As documented in [`SKILL.md`](https://github.com/Nutlope/hallmark/blob/main/SKILL.md) (lines 308-317), this prevents the "same-theme-different-layout" drift that makes AI output feel repetitive.

The check follows this priority:

1. Compare proposed theme axes against `history[0]` (most recent)
2. Reject if `paper-band`, `display-style`, and `accent-hue` all match
3. Accept and log if any single axis differs
4. Fall back to forced rotation if all candidates fail (rare edge case)

This logic applies equally to catalog themes and custom themes, ensuring consistent behavior across Hallmark's theme ecosystem.

## Nav and Footer Diversification

The diversification rule extends beyond page-level themes to **component archetypes**. As specified in [`SKILL.md`](https://github.com/Nutlope/hallmark/blob/main/SKILL.md) (lines 294-298), consecutive runs may not reuse the same navigation or footer component. This closes a loophole where the AI could rotate the theme while repeatedly falling back to a default "genre-default" navigation bar.

The component-level guards track:

- **Nav archetype**: minimal, mega, sidebar, etc.
- **Footer archetype**: newsletter, sitemap, micro, etc.

These are written to [`log.json`](https://github.com/Nutlope/hallmark/blob/main/log.json) alongside theme data and checked during the rotation phase.

## The Inversion Rule for Multi-Page Apps

Hallmark includes an important exception: when a [`design.md`](https://github.com/Nutlope/hallmark/blob/main/design.md) file exists at the project root, the diversification rule **inverts entirely**. Instead of forcing variety, the system forces **consistency** across pages. This prevents multi-page applications from looking like "a collage of unrelated designs," as noted in [`skills/hallmark/references/verbs/redesign.md`](https://github.com/Nutlope/hallmark/blob/main/skills/hallmark/references/verbs/redesign.md) (lines 33 and 222).

The inversion works by:

- Ignoring [`log.json`](https://github.com/Nutlope/hallmark/blob/main/log.json) rotation checks for theme selection
- Locking macrostructure, theme, and axes to [`design.md`](https://github.com/Nutlope/hallmark/blob/main/design.md) specifications
- Still allowing minor component variation within the locked system

This dual-mode design makes Hallmark suitable both for rapid prototyping (diversification on) and production design systems (diversification off).

## Code Example: Implementing the Diversification Check

The following Node.js example illustrates the core algorithm as derived from [`SKILL.md`](https://github.com/Nutlope/hallmark/blob/main/SKILL.md) and [`custom-theme.md`](https://github.com/Nutlope/hallmark/blob/main/custom-theme.md):

```javascript
import fs from 'fs';
import path from 'path';

const LOG_PATH = path.join(process.cwd(), '.hallmark', 'log.json');

function loadHistory() {
  if (!fs.existsSync(LOG_PATH)) return [];
  const raw = fs.readFileSync(LOG_PATH, 'utf8');
  return JSON.parse(raw);
}

function getThemeAxes(themeName, catalog) {
  return catalog[themeName]; // Returns [paperBand, displayStyle, accentHue]
}

function pickDiversifiedTheme(candidates, history, catalog) {
  const recent = history.slice(0, 5); // Check last 3-5 entries
  
  for (const theme of candidates) {
    const axes = getThemeAxes(theme, catalog);
    
    // Must differ from most recent on at least one axis
    if (history.length > 0) {
      const prevAxes = history[0].theme_axes?.split(' / ') 
        || getThemeAxes(history[0].theme, catalog);
      
      const allSame = axes.every((axis, i) => axis === prevAxes[i]);
      if (allSame) continue; // Reject: would repeat all axes
    }
    
    return theme; // Accept: at least one axis differs
  }
  
  throw new Error('No diversified theme available');
}

// Usage
const catalog = {
  lumen:   ['light', 'serif', 'warm'],
  carnival:['dark',  'sans',  'cool'],
  hum:     ['light', 'sans',  'cool'],
  grid:    ['dark',  'serif', 'warm']
};

const history = loadHistory();
const chosen = pickDiversifiedTheme(Object.keys(catalog), history, catalog);

// Log the result
const entry = {
  date: new Date().toISOString().split('T')[0],
  theme: chosen,
  theme_axes: getThemeAxes(chosen, catalog).join(' / ')
};
const updated = [entry, ...history].slice(0, 20);
fs.mkdirSync(path.dirname(LOG_PATH), { recursive: true });
fs.writeFileSync(LOG_PATH, JSON.stringify(updated, null, 2));

```

## Why the Diversification Rule Works

The rule succeeds where simple randomization fails through four design decisions evident in the Hallmark source:

- **Axis-level granularity** — Changing just one of three axes guarantees perceptible difference without forcing complete unfamiliarity
- **Multi-run window** — Checking 3-5 prior entries prevents short-term cycling between two similar themes
- **Component coverage** — Extending rules to nav, footer, and macrostructure closes all repetition loopholes
- **Explicit override** — The [`design.md`](https://github.com/Nutlope/hallmark/blob/main/design.md) inversion preserves utility for production multi-page applications

These safeguards ensure Hallmark generations feel **new yet purposeful**, avoiding the repetitive output that plagues many generative design tools.

## Summary

- The diversification rule operates on three axes per theme: paper-band, display-style, and accent-hue
- Project history persists in [`/.hallmark/log.json`](https://github.com/Nutlope/hallmark/blob/main//.hallmark/log.json) with full generation metadata
- New themes must differ on at least one axis from the most recent [`log.json`](https://github.com/Nutlope/hallmark/blob/main/log.json) entry
- Nav and footer archetypes are independently tracked to prevent component-level repetition
- The rule inverts when [`design.md`](https://github.com/Nutlope/hallmark/blob/main/design.md) exists, enforcing consistency for multi-page applications
- All logic is centralized in [`skills/hallmark/SKILL.md`](https://github.com/Nutlope/hallmark/blob/main/skills/hallmark/SKILL.md) with theme-specific details in [`custom-theme.md`](https://github.com/Nutlope/hallmark/blob/main/custom-theme.md)

## Frequently Asked Questions

### What happens if all available themes share axes with recent history?

Hallmark will force a rotation by selecting the least-recently-used theme that differs on at least one axis. If no candidate satisfies the constraint (extremely rare with 21+ catalog themes), the system logs a warning and proceeds with the first available option to prevent generation failure.

### Can custom themes participate in diversification?

Yes. Custom themes must explicitly declare their three diversification axes in their frontmatter, as specified in [`skills/hallmark/references/custom-theme.md`](https://github.com/Nutlope/hallmark/blob/main/skills/hallmark/references/custom-theme.md) (lines 210-256). Once declared, they are treated identically to catalog themes during rotation checks.

### Where is the diversification rule actually enforced?

The rule is specified in [`skills/hallmark/SKILL.md`](https://github.com/Nutlope/hallmark/blob/main/skills/hallmark/SKILL.md) (lines 274-317) and implemented by the Hallmark generation engine at runtime. The engine reads [`/.hallmark/log.json`](https://github.com/Nutlope/hallmark/blob/main//.hallmark/log.json), performs axis comparison, and writes updated history after each successful generation.

### Does the rule apply to single components or only full pages?

The core axis-based rule applies to **full-page generations**. However, nav and footer archetypes (components) are tracked separately with their own anti-repetition logic, preventing the AI from reusing identical component patterns across consecutive runs.