How the Diversification Rule Prevents Repetitive AI Output in Hallmark
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.
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 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 (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. After each full-page generation, the system appends an entry recording:
{
"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 (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 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 (lines 308-317), this prevents the "same-theme-different-layout" drift that makes AI output feel repetitive.
The check follows this priority:
- Compare proposed theme axes against
history[0](most recent) - Reject if
paper-band,display-style, andaccent-hueall match - Accept and log if any single axis differs
- 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 (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 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 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 (lines 33 and 222).
The inversion works by:
- Ignoring
log.jsonrotation checks for theme selection - Locking macrostructure, theme, and axes to
design.mdspecifications - 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 and custom-theme.md:
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.mdinversion 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.jsonwith full generation metadata - New themes must differ on at least one axis from the most recent
log.jsonentry - Nav and footer archetypes are independently tracked to prevent component-level repetition
- The rule inverts when
design.mdexists, enforcing consistency for multi-page applications - All logic is centralized in
skills/hallmark/SKILL.mdwith theme-specific details incustom-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 (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 (lines 274-317) and implemented by the Hallmark generation engine at runtime. The engine reads /.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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →