Skip to content

Metadata schema v3

v3 is the only supported version. v1/v2 documentation remains as historical reference. The authoritative JSON Schema is checked into the repository and loaded locally by the build tools; unknown fields are errors.

{
  "$schema": "../../site/docs/statics/schemas/v3.json",
  "build": {"type": "latex"},
  "static_site": {"description": "My notes", "document_status": "wip"},
  "authors": ["jacob_shing"]
}

The canonical URL https://hku.jacobshing.com/statics/schemas/v3.json is also accepted. A normal document defaults to <directory-name>.tex and <directory-name>.pdf.

Build types

  • latex: compile with latexmk. Optional root_file, output_file, engine (pdflatex, xelatex, lualatex), shell_escape (false), args (extra latexmk arguments), and config_file (latexmk configuration file).
  • custom: requires output_file and an ordered steps array.
  • alias: requires target, the name of a sibling document. Builds resolve the full alias chain and reject cycles or missing destinations.
  • page: website entry without a downloadable artifact. No sentinel file needed.

LaTeX/custom builds support prepare, finish, requires, source, and outputs. prepare runs before the main build, and finish runs before outputs are verified and published. output_file is the primary output, relative to the document root. For LaTeX, the runner copies the compiled PDF to this path without renaming the TeX job. outputs maps additional artifact names to document-relative paths, for example {"sources": "sources.zip"}. Scripts must produce these additional artifacts. The name primary is reserved. Output paths must be distinct literal filenames; metadata, Makefile, manifest, preview-state, and .build/ paths are reserved.

All paths are relative, use forward slashes, and must stay within their root. Build inputs and step working directories are relative to the selected source root; website content and published output paths are relative to the document root.

Steps

Steps run in array order; separate documents can build in parallel.

{
  "type": "latex",
  "inputs": ["figs/*.tex"],
  "engine": "pdflatex"
}

Each matching TeX file produces a neighboring PDF, using latexmk dependency tracking. Optional shell_escape and args override the figure compilation options.

{"type": "wordcount", "inputs": ["contents.tex"], "output": "wordcount.txt"}

Writes the sum of texcount's “Words in text” counts to the specified source-relative file.

{
  "type": "command",
  "argv": ["{python}", "scripts/generate.py"],
  "inputs": ["data/*.csv", "scripts/generate.py"],
  "outputs": ["generated/table.tex"]
}

argv is executed without a shell. {python} expands to the configured Python interpreter; {document} and {source} expand to absolute paths within arguments. Optional cwd selects a source-relative working directory. Input/output paths remain relative to the source root, even when cwd is specified. Commands with inputs and outputs can be skipped when their input/recipe hash and output contents match the last successful run. Commands lacking either run every time. Globs with no matches are errors. Output paths are literal, not globs.

{"type": "shell", "interpreter": "bash", "script": "./legacy.sh"}

Shell steps also support cwd, inputs, and outputs. They are an escape hatch; portability and correct failure handling inside the script are the author's responsibility. Metadata executes code and should be treated like any build script.

Dependencies and environments

"requires": {
  "executables": ["latexminted"],
  "python": ["latexminted", "Pygments"]
}

Python entries are installed distribution names, not import module names. These are presence checks; dependency version pins belong in environment requirements files. The runner infers latexmk, the selected engine, texcount, and command interpreters. It does not install packages. Choose any TeX distribution providing the required tools and document packages. Site-only and indexing dependencies are separate.

Custom builds can constrain environment using os (linux, darwin, windows), architecture (x86_64, arm64), distribution, and version. Distribution/version refer to Linux os-release values. This checks compatibility; it does not provision it. Use a matching environment or explicitly supply an existing artifact to configure.

Change detection and ignored inputs

Successful builds record a content fingerprint and artifact checksums in dist/artifacts/<target>/manifest.json (or the document's .build/artifacts). Unchanged inputs reuse verified artifacts without running preparation, compilation, or finishing commands. Missing or corrupted artifacts cause a cache miss. Filenames, contents, and executable bits are hashed; timestamps are not. New and deleted files therefore invalidate the cache, while touching a file does not.

"build": {
  "type": "latex",
  "hash_ignore": [
    "README.md",
    "scratch/**",
    "!scratch/required-data.csv"
  ]
}

hash_ignore uses gitignore-style patterns: *, **, directory paths, leading / anchors, and ! exceptions. Patterns are relative to the document root and, when supplied, the local source override root. They extend the defaults in build/hash-ignore-defaults.txt. The repository's .gitignore is not implicitly applied. Source PDFs are included by default.

The document's metadata.json is always hashed, even if a pattern matches it. Changes to build settings, profiles, ignore rules, and presentation metadata all invalidate its fingerprint. Build-tool implementation, schema, provisioning and Python requirements files are also included. Build caches, Git administrative data, generated configure Makefiles, and declared output paths are always excluded. TeX intermediate files are excluded by the default patterns. Custom steps should name their outputs or exclude their generated files explicitly.

For pinned external sources, the source URL, full commit SHA and subdirectory are part of the fingerprint. The internal .build/sources checkout is a cache; use --source-dir for editable external sources, whose contents are hashed. Commands that depend on mutable remote services or other undeclared inputs should set "cache": false, which also disables step reuse and forces TeX compilation.

Use make FORCE=1 to force a rebuild, including after updating a system TeX toolchain. make clean invalidates the local artifact manifest. CI restores artifact caches from earlier runs and checks every target's fingerprint; it never trusts a cache key alone. @force-rebuild in a push commit forces rebuilding in CI.

Explicit --artifact-dir imports are treated as supplied outputs, not as proof that those outputs were built from the current sources. They cannot seed automatic source cache hits. Website assembly still runs with current metadata, navigation and pages; PDF previews are reused only when their PDF and image checksums match.

External sources

"source": {
  "type": "git",
  "url": "https://github.com/owner/repository.git",
  "revision": "0123456789012345678901234567890123456789",
  "subdirectory": "report"
}

revision is a full commit SHA. Sources are fetched into the document's .build/ cache. Repeated builds reuse that checkout without fetching. A changed source specification creates a different cache entry. configure --source-dir=/local/path uses an existing source directory in place of the checkout/subdirectory; it does not reset or update that directory. Offline builds work once tools and sources are present.

Profiles

Top-level profiles maps names to shallow overrides of build settings:

"profiles": {"solutions": {"root_file": "solutions.tex", "output_file": "solutions.pdf"}}

Select with configure --profile=solutions in a document. Arrays and objects replace the corresponding build field rather than merging. The effective build is revalidated. Profiles cannot change build type or source. Unrecognized profile names are errors.

Website presentation

static_site retains v2's description, meta_description, custom_md_file, document_status, pdf_viewer, and buttons. See the historical v2 presentation reference for their detailed descriptions. alias_to is removed; use build.type: alias and build.target.

Default download buttons use relative URLs so they work on a local site or another host. Explicit buttons retain their supplied URLs. buttons: [] suppresses all buttons; omitted/null buttons use defaults. Page entries get no default Download button.

Authors

authors retains v2's author handles, leading ! primary-author marker, and @unknown / @do_not_sort pseudo-handles. The default is ["jacob_shing"].

Migration from v2

Former field/behavior v3 replacement
Implicit LaTeX target build.type: latex
build_command LaTeX settings or custom steps
prebuild_command / postbuild_command prepare / finish
no_latex custom or page type
NON_FILE_TARGET page type
static_site.alias_to build.type: alias, build.target
python-minted-pkgs requires.executables and requires.python
Embedded git clone Pinned source declaration
MiKTeX package hashes Local latexmk state; environment setup is separate