Divine

The Theme Harness

View as Markdown

When a theme is a Divine theme, Divine generates and versions a small set of files inside it that together form the theme harness. The harness exists so that every agent and every human who opens the theme works against the same authoring contract, the same check command, and the same pre-commit gate, without anyone having to wire those up by hand. A theme becomes a Divine theme when it carries a divine.json or is registered as a Divine app, and from that point Divine owns these files and keeps them current as the plugin version moves.

The harness is deliberately thin. It does not replace your theme code, your build, or your design system; it adds the connective tissue an agent needs to validate, preview, and export the theme correctly. Because Divine manages these files, you do not edit them directly. The one exception is a marked Theme Notes region inside AGENTS.md, which is yours to write and which Divine preserves verbatim across every refresh.

What Divine Manages

Divine writes five files into the theme root. Four of them carry content hashes that Divine tracks; the fifth is the manifest that records those hashes and the provenance of the check. The table below lists each managed file and what it is for.

File Role
AGENTS.md The Divine-owned authoring contract. Tells an agent how to write pages, blocks, layout, Tailwind, and performance, and which dv commands to run. Contains the author-owned Theme Notes region.
CLAUDE.md A one-line pointer, @AGENTS.md, so Claude-based tools read the same contract. The source of truth is always AGENTS.md, never CLAUDE.md.
dv An executable PHP shim. Resolves the Divine runtime and dispatches dv check, dv show, and dv export from inside the theme.
.githooks/pre-commit A shell hook that runs ./dv check . before each commit. Divine also sets the repository’s core.hooksPath to .githooks so the hook is active.
.divine/manifest.json Records the Divine version, the hash of each managed file, and the check provenance. Divine reads this to decide whether the harness is stale.

Everything else in the theme is author-owned. You own divine.json, style.css, blocks/, pages/, patterns/, templates, assets, copy, media, and all product behavior. Divine may scaffold divine.json for a new theme, but refresh never overwrites its contents.

AGENTS.md And The Theme Notes Region

AGENTS.md is the contract Divine regenerates, so the bulk of it is fixed: it opens with a generated-file banner, describes which files Divine manages, and then lays out the rules for pages and blocks, layout, Tailwind, performance, accessibility, and the check workflow. Treating it as Divine-owned means an agent reading it always sees the current, version-correct guidance rather than a copy that has drifted.

Inside that file is one region you own. Divine marks it with HTML comment delimiters and preserves the exact bytes between them across every refresh:

<!-- divine:theme-notes:start -->
Your project-specific notes go here. Divine keeps this region verbatim.
<!-- divine:theme-notes:end -->

Use the Theme Notes region for theme-specific guidance an agent should follow: brand voice, naming conventions, a component inventory, or constraints unique to this project. The contents of this region are excluded from the managed hash, so editing your notes never marks AGENTS.md as stale and never triggers a rewrite of the rest of the file. If the markers are missing, Divine seeds the region with a default placeholder the next time it writes the file.

CLAUDE.md

CLAUDE.md exists only so that tools which look for CLAUDE.md find the same contract. Its entire body is the include directive @AGENTS.md, under the same generated-file banner. Do not write theme guidance into CLAUDE.md; put it in the Theme Notes region of AGENTS.md, where Divine preserves it. Keeping a single source of truth means an agent never has to reconcile two divergent contracts.

The dv Shim

The dv file is a self-contained PHP wrapper, marked executable, that an agent runs from the theme root. It accepts the same commands as the plugin entrypoint: ./dv check . validates the theme, ./dv show ... prints a capturable render URL, and ./dv export . builds the standalone zip. The shim normalizes its arguments so that ./dv check and ./dv both default to checking the current directory, then it locates the Divine runtime and dispatches the command.

The point of the shim is that the theme does not need the Divine plugin installed in any particular place. The shim discovers Divine wherever it actually lives, which is what lets the same theme run its checks inside the monorepo, inside a WordPress install with the plugin active, and as an exported standalone theme that bundles its own runtime.

How The Shim Resolves Divine

The shim tries a fixed, ordered list of candidate autoload paths and stops at the first one that makes Divine\Check\Cli\DivineCheckCommand loadable. The order is chosen so an explicit override wins, the theme’s own bundled runtime comes next, and a discovered plugin or monorepo checkout is the fallback.

Order Candidate When it applies
1 The path in the DIVINE_AUTOLOAD environment variable You set it explicitly to pin the runtime.
2 divine-runtime/bootstrap-common.php, then the runtime’s vendor/autoload.php and bin/divine-check, under the theme root The theme is an exported standalone theme that bundles its own runtime.
3 A bounded walk-up looking for plugins/divine, mono/apps/divine, or apps/divine at each ancestor directory The theme lives inside a WordPress install with the plugin, or inside the monorepo.

The walk-up climbs at most eight ancestor directories, checking each level for an installed plugins/divine/bootstrap-common.php, a mono/apps/divine/bootstrap-common.php, or an apps/divine/bootstrap-common.php. If none of the candidates resolve the runtime, the shim prints a short message telling you to set DIVINE_AUTOLOAD or install the Divine plugin, and exits with code 2. This means a theme that cannot find Divine fails loudly rather than silently skipping its checks.

The Pre-Commit Hook

The harness ships a .githooks/pre-commit hook that changes into the theme root and runs ./dv check .. Because dv check defaults to fast mode, the hook does not bootstrap WordPress, open network connections, or shell out, so it stays fast enough to run on every commit and safe enough to run in CI. A commit that introduces a finding fails the hook, which keeps invalid pages, blocks, and Tailwind out of history at the moment they would be committed.

For the hook to fire, git has to know where to find it. Divine sets core.hooksPath to .githooks in the repository’s git config as part of writing the harness, and it treats a missing or incorrect hooksPath as a stale condition so the setting is restored on the next refresh. If you run the theme outside a git repository, there is simply no hook to install and Divine skips this step.

The Manifest

.divine/manifest.json is how Divine knows whether the harness is current. It records the Divine version that generated the harness, the SHA-256 hash of each managed file, and a check provenance block. The provenance pins the third-party versions the checks are derived from, so a Blockstudio or TailwindPHP bump is treated as a real change to the contract.

Field Notes
divineVersion The Divine plugin version that generated the harness. A higher current version marks the harness stale.
generatedAt The UTC timestamp of the last write. Only updated when a managed file or the manifest actually changes.
check.blockstudioVersion The bundled Blockstudio ruleset version the checks target.
check.tailwindPhpVersion The bundled TailwindPHP version the Tailwind check targets.
check.phpstanRulesChecksum A hash of the vendored Blockstudio PHPStan rule files, so changes to those rules invalidate the harness.
files A map of each managed file’s relative path to its recorded hash.

The hash Divine records for AGENTS.md is computed with the Theme Notes region normalized out, which is exactly why editing your notes does not change the recorded hash and does not trigger a rewrite.

Versioning And Refresh

Divine refreshes the harness when it detects that the harness is stale. The detection is conservative: the harness is stale when there is no manifest, when the manifest’s divineVersion is older than the current Divine version, when the check provenance no longer matches, when any managed file’s on-disk hash differs from the recorded or desired hash, or when core.hooksPath is not set to .githooks. Any one of these conditions is enough.

A refresh regenerates only the files that actually changed. Divine compares each managed file’s current content against the desired content for the current version and rewrites a file only when it differs, re-marks the dv shim and pre-commit hook as executable, restores core.hooksPath if needed, and updates the manifest. The Theme Notes region in AGENTS.md survives a refresh intact, because Divine reads your existing notes out of the file before regenerating and writes them back into the new contract. The result is that a Divine version bump updates the contract, the shim, and the hook in place without ever touching your theme code or your notes.

For the command the harness installs and the rules it enforces, see dv check. For turning a finished theme into a self-contained zip that carries its own runtime, see Theme Export and Standalone Runtime.