# How `.hallmark/log.json` Tracks Build Diversification in the Hallmark Project

> Discover how .hallmark/log.json tracks build diversification by recording build details and enforcing rotation rules for Hallmark project success.

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

---

**[`.hallmark/log.json`](https://github.com/Nutlope/hallmark/blob/main/.hallmark/log.json) is a JSON ledger that records every successful build's macrostructure, theme, diversification axes, enrichment, and brief summary, enabling Hallmark to enforce rotation rules across successive builds.**

The `Nutlope/hallmark` repository implements a build diversification system that prevents repetitive designs by consulting a project-level history file. This file—[`.hallmark/log.json`](https://github.com/Nutlope/hallmark/blob/main/.hallmark/log.json)—stores chronological records of previous choices and drives intelligent rotation across macrostructures, themes, and layout components.

## The Log File Structure

[`.hallmark/log.json`](https://github.com/Nutlope/hallmark/blob/main/.hallmark/log.json) exists at your project root and contains a **JSON array with newest entries first**. Each entry captures the essential parameters of a build:

```json
[
  {
    "date": "2026-04-30",
    "macrostructure": "Bento Grid",
    "theme": "Coral",
    "enrichment": "E1 clipped-edge",
    "brief": "Tracejam · SaaS observability"
  }
]

```

According to [`skills/hallmark/SKILL.md`](https://github.com/Nutlope/hallmark/blob/main/skills/hallmark/SKILL.md) §2.5, this schema enables Hallmark to compare incoming choices against recent history. The file is created automatically after your first build and updated after each successful run.

## How Diversification Logic Reads the Log

When Hallmark initiates a new build, it **reads [`.hallmark/log.json`](https://github.com/Nutlope/hallmark/blob/main/.hallmark/log.json)** (if present) and applies three rotation constraints:

1. **Macrostructure rotation** — The candidate macrostructure must not appear in any of the last 3–5 entries
2. **Theme rotation** — The new theme must differ on at least one of three axes from the previous entry (catalog vs. catalog, or custom vs. custom)
3. **Nav/Footer rotation** — Navigation and footer archetypes stored in the same stamp must be unique across successive builds

If [`.hallmark/log.json`](https://github.com/Nutlope/hallmark/blob/main/.hallmark/log.json) is missing, Hallmark bypasses these constraints, creates the `.hallmark/` directory, and initializes a fresh log after completion.

## Custom Theme Entries and Axis Tracking

For **custom themes**, [`.hallmark/log.json`](https://github.com/Nutlope/hallmark/blob/main/.hallmark/log.json) stores two additional fields so diversification checks can operate without parsing CSS token files:

- `theme_axes`: The three axis values as a slash-delimited string
- `vibe`: An optional descriptive field

```json
{
  "date": "2026-05-01",
  "macrostructure": "Stat-Led",
  "theme": "custom",
  "theme_axes": "light / italic-serif / chromatic-terracotta",
  "vibe": "archival warmth, hand-set, no varnish",
  "enrichment": "none",
  "brief": "Coffeebox · subscription"
}

```

As defined in [`skills/hallmark/references/custom-theme.md`](https://github.com/Nutlope/hallmark/blob/main/skills/hallmark/references/custom-theme.md) §F, this extended schema allows direct axis comparison during the diversification check.

## Implementing Log Operations in TypeScript

The following implementation shows how to read [`.hallmark/log.json`](https://github.com/Nutlope/hallmark/blob/main/.hallmark/log.json) and validate diversification constraints:

```ts
import { readFileSync, writeFileSync } from 'fs';
import { join } from 'path';

type LogEntry = {
  date: string;
  macrostructure: string;
  theme: string;
  theme_axes?: string;   // only for custom themes
  enrichment: string;
  brief: string;
};

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

/** Load the log, creating an empty array if the file does not exist */
function loadLog(): LogEntry[] {
  try {
    const raw = readFileSync(LOG_PATH, 'utf8');
    return JSON.parse(raw);
  } catch {
    return [];
  }
}

/** Return true if the candidate macrostructure is allowed */
function macrostructureAllowed(candidate: string, recent: LogEntry[]): boolean {
  return !recent.slice(0, 3).some(e => e.macrostructure === candidate);
}

/** Return true if the candidate theme differs on at least one axis */
function themeDiversified(candidate: { name: string; axes?: string }, recent: LogEntry[]): boolean {
  if (!candidate.axes) return true;
  const last = recent[0];
  if (!last || !last.theme_axes) return true;
  
  const candAxes = candidate.axes.split(' / ');
  const lastAxes = last.theme_axes!.split(' / ');
  
  return candAxes.some((a, i) => a !== lastAxes[i]);
}

```

## Appending New Build Records

After a successful build, prepend the entry and maintain a rolling window of history:

```ts
function appendLog(entry: LogEntry) {
  const log = loadLog();
  log.unshift(entry);
  const trimmed = log.slice(0, 20);  // retain last 20 builds
  writeFileSync(LOG_PATH, JSON.stringify(trimmed, null, 2), 'utf8');
}

/* Example entry for a catalog theme build */
appendLog({
  date: new Date().toISOString().split('T')[0],
  macrostructure: 'Stat-Led',
  theme: 'Garden',
  enrichment: 'none',
  brief: 'Maple Street Bread · bakery'
});

```

## Key Source Files

| File | Purpose |
|------|---------|
| [`skills/hallmark/SKILL.md`](https://github.com/Nutlope/hallmark/blob/main/skills/hallmark/SKILL.md) §2.5–2.6 | Documents the diversification algorithm and log schema |
| [`skills/hallmark/references/custom-theme.md`](https://github.com/Nutlope/hallmark/blob/main/skills/hallmark/references/custom-theme.md) §F | Defines custom theme entry structure with axis tracking |
| [`site/css/tokens.css`](https://github.com/Nutlope/hallmark/blob/main/site/css/tokens.css) | Stores catalog theme axis values for reference |
| `.gitignore` | Allows `.hallmark/` exclusion from version control |

## Summary

- **[`.hallmark/log.json`](https://github.com/Nutlope/hallmark/blob/main/.hallmark/log.json)** serves as the persistent record for build diversification in Hallmark projects
- Each entry captures **date, macrostructure, theme, enrichment, and brief**—plus `theme_axes` for custom themes
- The diversification engine checks the last **3–5 entries** for macrostructure conflicts and requires **at least one axis difference** for themes
- The JSON array format with **newest-first ordering** enables efficient recency-based constraint checking
- The **TypeScript `loadLog()` pattern** provides graceful handling of missing files during first runs

## Frequently Asked Questions

### What happens on the first build when [`.hallmark/log.json`](https://github.com/Nutlope/hallmark/blob/main/.hallmark/log.json) doesn't exist?

Hallmark skips diversification constraints, creates the `.hallmark/` directory automatically, and writes the initial [`log.json`](https://github.com/Nutlope/hallmark/blob/main/log.json) after the build completes. No manual setup is required.

### How many previous builds does Hallmark consider for rotation rules?

The macrostructure constraint examines the last **3–5 entries**, while theme diversification compares against the **immediate previous entry** for axis differences. The log retains **20 entries** by default.

### Can I manually edit [`.hallmark/log.json`](https://github.com/Nutlope/hallmark/blob/main/.hallmark/log.json) to influence future builds?

Yes. The file is plain JSON—modifying `macrostructure`, `theme`, or `theme_axes` values will affect how Hallmark applies diversification rules on the next run. However, manual edits may produce unexpected constraint violations if history appears inconsistent.

### What's the difference between catalog and custom theme entries in the log?

**Catalog themes** store only the theme name and reference [`site/css/tokens.css`](https://github.com/Nutlope/hallmark/blob/main/site/css/tokens.css) for axis values. **Custom themes** embed `theme_axes` directly in the log entry and may include an optional `vibe` field, enabling self-contained diversification checks without external file parsing.