This is the multi-page printable view of this section. .
Design decisions
- 1: Warnings and safe fallbacks
- 2: Configuration model
- 3: Markdown-first authoring
- 4: Generated configuration schema
- 5: Optional CLI and result contract
- 6: Paper and Slate visual presets
A decision explains why OINK chose one compatible design over another. The five contracts above it remain the normative description of current behaviour; implementation and owning checkers remain the executable facts.
OINK used to keep reviews, PRDs, and execution notes in a local plan/
directory. That made useful reasoning hard to discover and allowed abandoned
designs to look authoritative. Accepted reasoning now lives here, in the same
bilingual, versioned site as the contracts it supports.
Decision map
| Decision | What it settles |
|---|---|
| Warnings and safe fallbacks | Why ordinary preview survives invalid input while publication remains strict |
| Configuration model | Where configuration belongs, how pages override it, and why OINK has no parallel configuration namespace |
| Markdown-first authoring | Why native Markdown is preferred and Docs, Blog, Book, and Landing extend shared systems |
| Generated configuration schema | Why the editor schemas are a generated projection, and how the drift gate keeps a third configuration authority from appearing |
| Optional CLI and result contract | Independent Go executable, versioned diagnostics, coverage, and explicit write boundaries for the local CLI candidate |
| Visual presets | Paper default, Slate compatibility, opt-in Appearance menu, independent mode and font boundaries |
Record format
An accepted decision records context, the choice, consequences, and the proof that makes the choice current. It does not reproduce a parameter reference or a tutorial. Every decision links to its owning contract and verification surface, and its English and Simplified Chinese pages change together.
When a decision changes, update the implementation, checker, affected contract, and decision record in one delivery. Preserve the old answer in Git history and the release changelog instead of leaving two active answers in the navigation tree.
Related
- Design contracts — current normative behaviour
- Research — dated evidence that informs decisions
- Proposals — ideas that have not been accepted
1 - Warnings and safe fallbacks
OINK does not call Hugo’s errorf. Invalid author or site input emits a
warning and either uses a documented safe fallback or omits the invalid
fragment. Release and deployment builds use --panicOnWarning, so the same
warning remains a hard publishing failure.
Context
Hugo builds the whole site as one transaction. An errorf raised while one
page is being edited makes every URL served by that rebuild return an error,
including unrelated pages and the home page. The server process survives and
recovers after the input is fixed, but collaborative preview is unavailable in
the meantime.
A warning has a different development cost. The affected value can fall back,
the rest of the site remains inspectable, and the author receives a precise
message. A publication build still fails because OINK’s CI and integration
gates add --panicOnWarning.
Decision
Validation follows four rules:
- Name the invalid key and value, the allowed shape, and the fallback.
- Include a page position when the value came from page front matter; avoid repeating one site-wide warning for every page.
- Never pass an invalid value into a later operation. Validate first, then render from the normalized value.
- Where no honest fallback exists, warn and render nothing. Do not invent content, make a network request, or emit an unsafe URL merely to keep going.
The shared enum, boolean, CSS-length, and number shapes live in
layouts/_partials/validate.html. Domain resolvers may add narrower checks,
but they preserve the same warning/fallback contract.
Safety boundary
Continuing a build never means continuing with unsafe output. A rejected CSS length falls back before it reaches a style attribute. An incomplete remote service configuration omits the component before the browser can make a request. An unsafe action URL is dropped. The protection is the absence of the bad output, not the act of terminating Hugo.
This also separates editing from publication cleanly:
| Stage | Invalid input |
|---|---|
hugo server or an ordinary local build |
Warn, fall back or omit, keep other pages available |
| CI, release validation, deployment | The same warning becomes a non-zero build under --panicOnWarning |
Consequences
- Every fallback is part of the public contract and must match the default declared by the theme.
- A change from failure to fallback also changes its tests. A negative test proves ordinary build survival, the warning text, the rendered fallback, and strict-build failure.
- Checkers must test the rejected output directly. A URL security test, for example, asserts that the unsafe URL is absent instead of treating any build failure as sufficient proof.
- Rendered markup owns DOM, attribute, ordering, and emitted-token assertions; the browser suite owns computed color, size, spacing, breakpoint, and interaction results. A checker does not freeze a Sass spelling when the public result can be observed directly.
- Source-level checks remain for forbidden constructs such as
errorfand for narrow topology invariants that output cannot prove, such as one authority, one resolver, or an intentionally restricted caller set.
Verification
The owning references are the
architecture contract,
bin/check-params.py, and strict builds of both the theme fixture and this
integration site.
2 - Configuration model
OINK keeps Hugo’s native keys and useful Docsy-compatible keys in place,
places theme presentation and behaviour under params.ui.*, and exposes a
matching top-level front-matter key for a page override. It does not add a
params.oink.* tree or a registry that shadows Hugo’s configuration model.
Context
OINK inherits a mature configuration surface and adds shells, content output, and local interaction. Earlier designs attempted to move every theme-owned key under a new namespace and resolve a complete configuration dictionary once per page. That produced a second language beside Hugo’s own keys, complicated section cascades, and made migration larger than the behaviour it was meant to control.
The current model keeps ownership visible instead:
| Layer | Responsibility | Examples |
|---|---|---|
| Hugo | Site identity, languages, menus, outputs, taxonomies, markup, modules | baseURL, languages, outputs |
| Site facts and integrations | Repository, version, author, local search, comments, external services | params.github_repo, params.version, params.comments |
| OINK interface | Shell, navigation, presentation, and local interaction | params.ui.sidebar_*, params.ui.typography, params.ui.share |
| Page or section | A narrow override of an eligible site default | sidebar_enabled, featured_image, share |
| Data files | Structured facts and ordered content that are not switches | data/landing, data/download, data/docs_nav.json |
Decision
The configuration API follows these rules:
- Site facts remain at the established top level. Interface choices belong
under
params.ui.*. - A page override drops the
ui.prefix and otherwise keeps the same name. A sectioncascadecan apply that top-level key to its descendants. - Boolean features use a scalar where that is the complete policy. A map is reserved for features with real subordinate settings; an established map may accept a boolean shorthand.
- Names are positive, snake_case, and grouped by function. Closely related settings share a prefix instead of growing another nested resolver.
- Theme defaults are declared in the theme’s
hugo.yaml. Templates may add a derived default only when one static value would erase a deliberate shell-specific distinction. - Each feature family owns its normalization and validation. A shared helper supplies common shapes, but there is no global compatibility registry that silently rewrites arbitrary old keys.
The complete current key list, types, and defaults live in the configuration reference. This decision records the placement rules; it is not a second parameter catalogue.
Compatibility
Public renames receive a targeted warning from the owning resolver, a migration note, and a negative test. Removed or misspelled keys do not justify a permanent alias layer. Hugo and third-party camelCase keys remain camelCase where changing them would break their native API; OINK-owned additions use snake_case.
Page values resolve through Hugo’s ordinary front-matter and cascade model.
OINK does not ask authors to put a nested ui: tree in front matter and does
not promise to merge arbitrary nested page maps.
Consequences
- Adding a public setting requires a declared default or an explicitly derived default, an owning resolver, documentation, and a positive and negative test.
- Configuration guides link to the one reference table instead of repeating types and defaults.
- A new data structure is justified by ordered or repeated facts, not merely by a desire to avoid adding a parameter.
- Invalid scalar values follow the warning and fallback decision.
Verification
bin/check-params.py audits declared defaults, page aliases, warning
behaviour, and the no-errorf invariant. The public reference and its Chinese
peer are checked in the integration site’s bilingual and rendered-link suites.
3 - Markdown-first authoring
Prefer a native Markdown form when Goldmark can preserve the intended semantics. Keep a shortcode only when it provides a capability the native form cannot express. Add a content scenario by extending an existing shell and data model, not by creating a parallel rendering system.
Context
OINK serves short manuals, large references, release archives, landing pages, and books. A survey of eleven consumer sites covered more than five thousand Markdown files and exposed both extremes: pages with almost no theme syntax and pages assembled from many nested shortcodes and local layout overrides.
A component API optimized only for the second group becomes a private DSL. An API optimized only for plain Markdown leaves books, rich figures, tab groups, and structured releases to site-local HTML. The useful boundary is capability, not novelty.
Decision
OINK applies the following order:
- Native Markdown first. Lists become Steps, Cards, or FileTree markers; tables become Fields or matrices; blockquotes become callouts; fenced code, images, and passthrough blocks carry attributes through render hooks.
- Shortcodes for missing capability. A full-form shortcode remains where CommonMark indentation, nested containers, processing options, or cross-page registration cannot express the same result safely.
- One semantic implementation. Native and full forms normalize into the same partials and output contract. They are not two components that merely look alike.
- One extension line. A new Landing section joins the section registry; a new Blog presentation remains a Blog variant; Book numbering joins the content primitive and navigation systems. OINK does not add a second card, landing, navigation, or article shell for one feature.
- Facts stay outside presentation strings. Versions, repositories, dates, and ordered records come from front matter, site parameters, or data files. A shortcode argument is not a second source of truth.
Output contract
An authoring form is complete only when its semantic content has a deliberate result in every enabled output:
| Output | Requirement |
|---|---|
| HTML | Semantic server-rendered content; JavaScript only enhances it |
| Static, expanded, and free of controls that require interaction | |
| Markdown / LLMS | Source-shaped prose, links, lists, tables, and fences; no component HTML |
| RSS | Safe static content or an explicit omission |
This requirement prevents an attractive HTML-only component from silently damaging agent output, feeds, or a printable book.
Trust and presentation
Render hooks and shortcodes consume explicit allowlists. Unsafe URL schemes, inline event handlers, and arbitrary style input are dropped. Author-provided classes are accepted only on the documented surfaces where downstream site CSS is part of the established authoring contract. Icons use one Font Awesome class pair; OINK does not invent a second icon-ID language.
Consequences
- A proposed component must first show why Markdown plus an existing hook is insufficient.
- Keeping a full-form shortcode requires a named capability and tests for both forms reaching the same normalized output.
- Shell variants use independent presentation keys so opting into a hero or a flow outline does not change taxonomies, feeds, pager order, or content type.
- Consumer evidence is dated research, not a permanent excuse to freeze an accidental syntax. The current public surface remains defined by the component contract and shell contract.
Verification
The authoring contract is exercised by theme component, Book, output, and golden checkers, then by this site’s bilingual examples and browser suites. The Goldmark facts behind the native forms are recorded in block-attribute research.
4 - Generated configuration schema
The two JSON Schemas under schema/ are projected by
bin/generate-config-schema.py from the theme’s hugo.yaml and the
template read-point scan; hand edits cannot survive CI. The schema is a
read-only projection of the existing authorities, never a third one.
Context
The theme already has two configuration authorities: hugo.yaml, which
declares every default beside a comment explaining it, and
check-params.py, whose read-point scan knows every key the templates
actually consume. Editors know neither, so authors type params.ui.* keys
and front matter from memory.
A JSON Schema gives editors completion and hover documentation. The danger is the schema quietly becoming a third authority that drifts from the other two. A hand-maintained schema always ends up out of step with the implementation, and stale completion is worse than none.
Decision
bin/generate-config-schema.py generates two files under schema/:
site-params.schema.json validates a site’s hugo.yaml (types and defaults
from the theme’s own hugo.yaml, descriptions from its comment blocks), and
front-matter.schema.json validates page front matter (every key the
templates read as authoring surface, descriptions inherited from the matching
site key). Keys read only to warn that they were renamed or removed are
excluded by name.
Two deliberate restraints are part of the decision:
- The front-matter schema carries no type constraints. Several keys
accept a bare-boolean opt-out beside their site type (
share: false,theme_color: false); a wrong red squiggle under valid input would be worse than no squiggle at all. - The
hugo.yamlreader is a small parser for exactly the shapes that file uses – nested maps, scalars, inline lists. Anything it cannot read is a hard error, so outgrowing it breaks the drift gate loudly instead of mis-generating.
Consequences
The only way to change a schema is to change hugo.yaml or the templates the
scan reads: when the public configuration surface moves, the schemas
regenerate in the same commit, and there is no second inventory anyone must
remember to maintain. The cost is that the generator and the read-point scan
become an implicit gate on the public surface – a new parameter key must be
something they can understand, or CI fails outright.
Verification
python3 bin/generate-config-schema.py --check regenerates in memory and
fails when schema/ is stale or missing; the theme’s CI runs it beside the
parameter contract checker. Editor wiring and the behaviour itself are
documented normatively in Configuration.
Visual preset enum values are also derived from preset-config.html. The
preset_menu union accepts a boolean or a list of those resolver-owned values;
the schema does not maintain its own list.
5 - Optional CLI and result contract
This contract describes the reduced local 0.1.0-dev command surface on
2026-10-04. Keep site diagnosis, real Hugo checks, initialization, builds,
upgrades, and guarded maintenance plans. Cobra provides command help;
colored English text is the default, with JSON/YAML results available.
Studio, general editing, context, snippets, editor setup, and CI generation
are retired. Historical R1–R8 acceptance applies only to its recorded source
and binaries; it does not replace validation of the current implementation.
No public CLI release, Homebrew distribution, or deployment is established.
Context and ownership
The theme is a Hugo module; consumer tooling is an optional executable with a
different installation and release lifecycle. pgsty/oink-cli owns that
executable, named oink, and its Go tests. It invokes an external Hugo binary
without importing Hugo’s private runtime or depending on a sibling checkout,
Python, Node.js, or unpublished theme scripts at runtime.
Hugo owns configuration resolution, rendering, routes, and anchors. The CLI inspects Hugo’s effective configuration, module graph, mounts, and rendered files. It does not create a second route resolver, navigation authority, or configuration namespace. Theme-only regression scripts remain maintainer tools. Configuration preprocessing relocates workspace, replacement, and cache paths only in the temporary copy. Hugo still owns defaults, configuration merging, language selection, validation, and rendering semantics. The public architecture contract continues to own theme behavior; this page owns the initial CLI boundary and result envelope.
The usage guide contains installation and command examples. The dated acceptance record separates executed checks from open limitations and release states. The maintenance acceptance record preserves the earlier R1–R8 program and its source-bound evidence. The roadmap retains future proposals and adoption hypotheses rather than duplicating the current command reference.
Command and mutation boundary
Help groups commands by daily, maintenance, and release work. Run
oink COMMAND --help for the exact options.
| Command | Behavior and mutation boundary |
|---|---|
doctor |
Read-only toolchain, configuration, module source, workspace/replacement/vendor diagnosis |
check [links|translations|style] |
Actual Hugo output and declared source/translation policy in isolated copies |
init DIRECTORY |
Validate the fixed Starter before creating a new or empty site |
dev, build |
Ordinary Hugo processes; Hugo owns normal output/cache writes |
upgrade --to TAG |
Preview by default; only --write applies verified module changes |
translations status, translations diff PAGE |
Read-only relationships, hash-review states, and differences |
translations review SOURCE TARGET, baseline capture |
Explicit reviewer/reason; preview with optional new --plan |
new BUNDLE --title TEXT, move SOURCE TARGET |
Validate a candidate and preview its full diff; optional new --plan |
plans apply FILE |
Revalidate supported saved plans; write only selected files in the selected site |
inspect PAGE, impact --since REF |
Read-only actual page and historical/current impact facts |
workspace list, workspace check [GROUP] |
Select only explicitly registered sites |
build --check |
Check, seal, and export the same isolated production render |
artifacts verify |
Compare local artifacts with a manifest offline |
verify |
Compare deployed HTTP responses with a manifest after explicit --network |
Network access is off by default. All commands are non-interactive. Only
dev and build accept Hugo arguments after --. Removed commands and their
old plans cannot apply. Supported plan kinds are authoring.new,
translations.review, baseline.capture, and content.move.
Versioned result envelope
| Option | Output |
|---|---|
| Default | Concise colored English text |
--json, -J |
One JSON oink.result/v1 object |
--yaml, -Y |
One YAML document with the same result fields and types |
--verbose, -v |
All findings, coverage details, and tool logs |
--no-color |
Plain English text |
Choose one structured format. Nonempty NO_COLOR or TERM=dumb also disables
text colors. Structured output adds no terminal colors. Tool logs go to
stderr. --format json|yaml and --non-interactive remain hidden compatibility
options. Every command is non-interactive.
Default text shows status, counts, up to eight active findings, and explicit coverage omissions. Detailed facts and reviewed findings remain available in structured results. Plan and upgrade previews retain their complete diffs. Cobra owns command dispatch and focused help. CLI messages use short, active English sentences inspired by ASD-STE100; this does not assert certification. User content and external tool evidence retain their original language.
| Field | Type and meaning |
|---|---|
schema_version |
String; oink.result/v1 for this contract |
version |
String; CLI build version, including a development suffix when applicable |
command |
String; requested command, or help / version |
site |
Optional string; selected source or generated target directory when available |
exit_code |
Integer; the CLI result code defined below |
diagnostics |
Array of findings; an empty array means no recorded findings |
coverage |
Array of scoped coverage statements; callers must inspect these alongside findings |
evidence |
Array of subprocess records; empty when no subprocess ran |
data |
Optional command-specific JSON value; current commands return objects such as inspected facts, initialization provenance, or an upgrade plan |
The JSON Schema describes this envelope.
The first version permits additive fields and new rule IDs. Consumers should ignore unknown fields and treat IDs as opaque strings, not parse their spelling. A change to the meaning or type of an existing envelope field requires a new schema version. Command-specific facts and raw tool output are evidence, not an SDK for importing internal Go packages.
The machine-readable schema is shipped in the CLI repository as
schema/result.v1.schema.json. Its identifier is not evidence that a schema
endpoint or public CLI release has been deployed.
Each evidence record has command (an argument array), optional directory,
stdout, stderr, and the subprocess’s exit_code. Captured inspection and
build output remains available in the result. Directly streamed dev/build
output is sent to the logging stream rather than buffered again in evidence.
The subprocess status remains distinct from the CLI’s 0/1/2 result; a
negative subprocess status can indicate that no normal exit code was obtained.
Findings, severity, and locations
Every diagnostic has rule_id, severity, message, and action, plus an
optional location. Stable rule IDs identify the condition. Raw Hugo wording,
translated messages, paths, and build-specific details are not stable IDs.
Existing IDs must not be reassigned to a different condition.
The optional incomplete: true identifies a required-work failure that policy
cannot downgrade. Reviewed exclusions and baseline acknowledgements retain the
finding in diagnostics with disposition: "excluded" or "baseline" and
review containing reason,
reviewed_by, and RFC 3339 reviewed_at. An excluded finding remains visible
with its recorded severity; it does not block the completed policy check.
Any uncompleted required coverage, including not_checked, determines exit
2; only complete or not_applicable satisfies required coverage.
The severity vocabulary is info, warning, and error. error is blocking.
info and warning are advisory unless the underlying required Hugo
build fails under --panicOnWarning, in which case required work is incomplete.
Informational completion and scope explanations belong in coverage details.
Automation must use the result exit code and coverage, not only count severities.
When present, location contains file, with optional kind, line, and
pointer. kind distinguishes source from output. A rendered finding
points to the actual artifact and may include an element, attribute, or JSON
location hint in pointer; that field does not universally claim RFC 6901
syntax. A line number is included only when known. A rendered link failure
does not justify inventing a Markdown source line.
The CLI does not reject unknown valid front matter merely because a generated editor schema omits it. Configuration validity continues to follow Hugo and the owning theme resolver/checker; see the generated schema decision.
Coverage and exit semantics
Each coverage entry contains id, status, required (boolean), and detail.
Entries describe the scope that actually ran. A report may contain several
statements about the same broad area; inspect all of them.
| Status | Meaning |
|---|---|
complete |
The stated operation, inspection, or artifact-check scope completed |
not_checked |
This run did not inspect the stated scope |
not_applicable |
The stated check is unnecessary for these inputs |
unsupported |
The stated contract or required input shape is unsupported |
incomplete |
The stated work was required but could not finish |
| CLI exit code | Meaning |
|---|---|
0 |
Requested required work completed without blocking findings |
1 |
Completed checks identified a policy violation, such as a broken local link or an unsafe requested write |
2 |
Required work is incomplete, including argument, tool, build, I/O, cancellation, or required unsupported-contract failures |
Incomplete work takes precedence over policy findings. Required coverage that
has not completed cannot produce success; only complete or not_applicable
can satisfy it. Hugo build failure preserves the
raw evidence and stops output acceptance; the CLI does not report stale or
partial output as a passing check. Disabled optional machine outputs do not
become missing-output errors.
The rendered-reference scope includes supported HTML URLs and anchors and supported emitted machine contracts. Coverage explicitly excludes browser interaction, accessibility, visual presentation, external URL availability, hosting redirects, and production deployment. It also identifies uninspected dynamic resources and content semantics. Static output evidence does not prove undeclared translation coverage, semantic translation equivalence or browser execution.
Hugo’s public Page.OutputFormats supplies expected artifact names and URLs
per page and per enabled language. The isolated copy adds an unlisted probe
with a unique per-run identifier; each enabled language must emit its own
verified manifest. Identified probe files are removed before artifact checks.
Existing authored pages retain their output selections. Static content hidden
from ordinary page lists is resolved through Hugo’s GetPage, without
deriving its route or output filename from source syntax. Effective per-language
base URLs and local-search settings accompany that enumeration. Enabled
supported machine artifacts are checked against these exact expectations;
optional disabled outputs remain optional.
The same Hugo probe supplies data.pages through public Page.Path,
Page.File, Page.Translations, Page.Aliases, and Page.OutputFormats.
Facts retain language, actual URLs, publication settings, translation
relationships and declared outputs. sourceKnown: false and
sourceScope: "unknown" identify pages without proven source provenance;
generated sections do not receive invented source files. Site-owned known
paths are relative to the selected site. Known copied dependency inputs have
explicit dependency scope. These facts describe the production view; a separate
internal analysis view can include drafts, future and expired pages without
changing production artifacts or claiming they are published.
data.references records observed HTML and machine-output references, their
actual resolved URL, output file/pointer, local target when present, and anchor
status when checked. It does not infer a Markdown source line. Page and
reference data remain additive command evidence, not a public Go SDK.
Project check policy
An optional regular oink.yaml at the selected site’s root uses
schema_version: oink.policy/v1. It owns check selection, severity overrides,
reviewed finding exclusions, reviewed external URL scopes, translation scopes,
protected prose declarations and the optional baseline file path. Hugo inputs
continue to own languages, titles, URLs, menus and configuration; module files
own dependency versions. A symlink, unknown key/group, unsupported version,
invalid review metadata or multiple YAML documents is required input failure
(exit 2). Diagnosis and checks only read this policy.
Without a policy, links, translations and style are enabled and required.
check links, check translations, or check style explicitly selects one
required group, regardless of its policy selection. Unselected or disabled
optional groups report not_checked. Every check invocation retains the
required strict Hugo build and output-enumeration prerequisites. Standalone
translation/source checks also use an explicit nonpublishable draft/future/expired
analysis view; it never replaces production artifacts. Managed build --check
renders only the production view and returns 2 for unknown required scope identities.
The rules mapping assigns error, warning or info to exact opaque rule
IDs. A reviewed exclusions entry requires rule_id, a clean relative file
glob, reason, reviewed_by and RFC 3339 reviewed_at; ** patterns and path
escape forms are unsupported. Findings remain visible with review metadata.
Site-local source locations match against their relative site path; source
paths outside the selected site cannot match an exclusion.
Required build, input, tool or coverage failure cannot become success through
severity changes or exclusions.
Same-origin HTML references outside the configured base path are policy
findings unless a reviewed external_scopes URL declares a separately deployed
path scope. Each scope needs the same review metadata and an absolute HTTP(S)
URL without credentials, query or fragment. Matching uses complete path
segments. The scope cannot exempt missing targets inside the project or required
local machine-output targets. Different-origin references remain explicitly
unverified by offline static checks.
Translation policy and review evidence
translations.scopes selects source pages using clean absolute Hugo
Page.Path prefixes, then finds targets through Hugo’s translation identity.
It does not infer a public route or language from a filename. Each scope has
path, source_language, required_languages, mode and drafts.
mode defaults to localized; strict is also supported. drafts defaults
to include; ignore excludes draft sources/targets, and require-published
requires the selected source and required targets to exist in production.
Known disabled Hugo languages are not_applicable; unknown languages are
invalid policy. The most specific matching path owns a source page.
Without scopes, existing pairs rooted in Hugo’s enabled default language and
duplicate relationships are inspected. Universal localization is not required;
translations.coverage records the unconfigured language scope as optional
not_checked. Missing required targets and duplicate selected relationships
are policy findings. Draft/publication state is independent of review state.
Constraints are opt-in: explicit_ids compares the full recognized explicit-ID
map in strict mode; localized mode requires a selected ids list. ids requires
each named ID in both files, placeholders compares exact declared prose-literal
counts, and code_labels protects fenced blocks with each named language/info
token. required_fields requires nonempty dotted front matter fields in both
files; equal_fields compares their actual values. No rule requires matching
heading counts, translated prose or all code blocks by default.
.oink/translations.json uses oink.translations/v1. Explicit review records
bind Hugo source/target IDs, source language, complete source/translation byte
SHA-256 values, reviewer, reason and RFC 3339 time. An absent record is unknown;
equal hashes are current; source-only, target-only or both changes are
source_changed, translation_changed or both_changed. These are evidence
of changes since review, not semantic judgments. File modification time never
establishes review. Unproven sources stay unknown; a recorded review or protected
constraint that cannot be verified produces required incompletion.
Native content rules and coverage
Source rules extract evidence from Markdown structure and independent enabled Hugo attributes, preserving original UTF-8 bytes, CRLF/BOM, source offsets and YAML/TOML/JSON front matter with unknown fields. Actual effective Hugo attribute switches and configured math passthrough delimiters control recognition. Fenced/inline code, shortcode bodies, raw HTML and passthrough contents do not become prose or invented headings. Unsupported body syntax remains visible; required source coverage cannot silently pass.
Generic rules detect duplicate recognized explicit IDs and evaluate declared
style.protected entries with file, exact prose literal and expected count.
The bounded OINK v1.1.0 catalog adds advisory code/table attribute, deprecated
front matter and dropped unsafe-attribute findings. Each rule records module,
version, immutable revision, module sum, license and exact source-file SHA-256
provenance in data.native_rule_provenance.
The catalog runs only when the actual mounted public v1.1.0 module-cache inputs
match those hashes. A replacement, vendor copy, other version or unknown source
does not select a latest-theme fallback: native-theme-rules is optional
not_checked, while generic syntax checks still run. This catalog does not
certify every custom component or theme feature.
Baselines and reviewed file plans
baseline selects a clean relative file, default .oink/baseline.json, using
oink.baseline/v1. Capture requires completed work and explicit review metadata.
The fingerprint binds exact rule ID, normalized location/pointer and condition
message; it excludes severity. Acknowledged findings stay visible with
disposition: "baseline" and original severity; new conditions still block
according to policy. Required incomplete findings/coverage cannot be acknowledged.
Review and capture preview oink.plan/v1 with selected edits, readable diffs,
base existence/bytes/modes, after bytes and read guards. The plan ID excludes
mutable validated/applied/recovery state. --plan FILE exclusively saves the
plan; these commands do not accept --write. plans apply FILE --site DIR
requires that exact selected site, fresh isolated candidate validation through
the same checks and rechecked source guards before any write. Escapes, .git,
symlinks and nonregular files are protected; overlapping candidate/source trees
are refused. Stale plans fail safely. Optional external_inputs_hash binds
captured non-site input bytes, full modes and inventory into the plan ID. This
opaque SHA-256 grants no external paths or permission to read them. The owning
validator compares fresh proven inputs; trusted original external guards are
rechecked around selected writes.
Exclusive installation preserves a file created during commit. Partial failures restore owned unchanged writes; subsequent editor bytes, modes or deletions remain intact. The reported recovery directory keeps original bytes/modes and actual concurrent captured evidence. Unrelated files and new editor children are preserved. No file content authorizes shell execution or publication.
Captured page inspection and impact
inspect PAGE selects an actual Hugo page by exact language:path ID, Hugo
Path, permalink or proven site-owned source file. A known default language
resolves a multilingual Path; remaining ambiguity or an unknown selector is
required incomplete (2). data.inspection exposes source byte hash/full
mode, actual output identities, observed inbound/outbound references,
translations and physical bundle attachments. Physical attachments are
distinguished from observed published resources.
impact --since REF compares captured current inputs and an isolated committed
Git tree rendered by the same Hugo engine. It retains deleted prior pages and
their inbound edges, unchanged referring pages, translation peers, attachments
and actual derived outputs. Global or uncertain inputs expand causal scope;
an unowned actual HTML output such as an alias also forces conservative full
scope. Alias declarations never create guessed route ownership. Historical
inputs in proven Git mode scopes compare only Git’s executable bit; other
module/foreign inputs and current facts retain full modes.
Historical materialization reads bounded Git objects without checkout hooks, filters, smudge execution or document execution. The limits are 10,000 files, 16 MiB per file and 128 MiB per tree; required private history is bounded at 256 MiB. Symlinks, submodules, capped or missing objects, required incomplete history and unsupported monorepo GitInfo are explicit incomplete states. Committed site-owned dependencies can be proven. Current external local replacement/workspace bytes cannot substitute for historical evidence.
data.impact.baseline_state is complete, incomplete or unavailable.
When the required baseline is unavailable, the result returns 2, retains all
known current pages/attachments/references/outputs and expands full scope. It
creates no prior pages or invented changes; only an actually resolved commit
is recorded. check [GROUP] --since REF deliberately performs the full
current check and declares data.check_scope: full; no incremental speed or
partial validation claim is made. data.impact.full_scope describes causal
uncertainty separately from that validation scope.
Completed inspect and impact fact queries return 0 even when
their separately reported data.current_check has completed quality findings
(1). Required capture failures remain top-level 2. check --since keeps
the current policy’s quality exit code and required completion precedence.
context is removed. Read page facts through the JSON/YAML report from inspect.
Content move plans
move SOURCE TARGET [--plan FILE] previews a physical site-relative file or
bundle relocation. Actual Hugo identities determine translation peers and
old/new outputs. The plan includes byte/full-mode-preserved files and binary
attachments, readable diffs, proven Markdown destination rewrites, observed
route changes and alias advice. Front matter is not rewritten to install
aliases. Raw HTML, shortcode output, transformed destinations and ambiguous
source/output ownership stay visible manual actions; opaque source spans are
not changed. Repeated ordinary Markdown destinations can also lack a unique
source/output occurrence proof, including aggregate/print appearances. Matching
URLs alone do not authorize rewriting those occurrences. A relocated physical
attachment does not prove its new published URL. Resource URL changes require
paired actual rendered edges and equal emitted bytes; a proven processed image
URL does not prove an absolute original-resource URL. Unproven original URLs
stay manual and are not constructed from the directory move.
The original before check and provisional route_probe are separate from
the final candidate check. The provisional relocation may expose findings
1 from stale inbound links. A plan is validated or saved only after the
final isolated candidate and reference proof pass. Unsupported identity or
required capture failure returns 2; an actual final quality failure remains
1 and cannot save an applicable plan.
Content move plans must be saved outside the selected site. Their additive
oink.plan/v1 move selectors and source_inputs_hash bind the complete raw
source inventory, bytes and full modes; external-input and fresh-directory
guards also apply. Saved apply regenerates the original/relocated Hugo proof
and requires exact expected plan ID and files before final reference
verification. It rechecks current guards before writes, restores raw original
modes rather than private-copy modes, and preserves later editor bytes/modes
on refusal or guarded recovery. Stale inputs or an occupied fresh target lack
the required proof and return 2. Only explicit plans apply writes selected
files; previews never stage or commit Git changes.
Supported input boundary
Initial full validation supports materialized files in a normal checkout or
without Git metadata, including supported local module replacements copied
into the isolated tree. It does not follow mounted symlinks or external mounts
back into the user’s workspace. Auxiliary symlinks outside effective mounts
are omitted rather than validated. Linked Git worktrees with a .git file
need a materialized review copy; Git-dependent behavior needs a copy with its
own Git metadata.
The snapshot excludes top-level public, resources, node_modules, tmp,
and the Hugo build lock. A mount that needs excluded input cannot silently
pass. Root configuration and the standard config tree are supported;
explicit configuration files must be inside the selected site, and custom
HUGO_CONFIGDIR locations are refused. Supported configuration relocation is
not a second implementation of Hugo validation.
Content adapters (_content.gotmpl) can create unlisted pages that cannot be
enumerated completely through the supported public Hugo APIs. Full output
validation therefore reports incomplete work for those inputs. A disabled
page kind or render segment that omits an enabled language’s probe is also
incomplete. Multihost language configuration is outside the first full-check
scope and returns incomplete work; multilingual paths on a single host remain
supported. These cases must not be presented as a successful partial check.
Ordinary content plans
new BUNDLE --title TEXT [--language LANG] [--translations LANGS]
[--kind page|docs|blog|book] [--plan FILE] previews an ordinary Hugo leaf
bundle through captured site-owned content mounts. The primary language
falls back to the effective default; selected peers must be distinct enabled
languages. Shared filename and language-directory layouts follow actual Hugo
mounts, including observed sites.matrix.languages selection rather than an
assumed legacy lang field. Ambiguous, filtered or unsupported mappings require
manual authoring.
Existing bundles or sibling files owning that page are refused.
The primary index has draft: false; selected peer indexes have draft: true.
The title is a supplied literal, not translated text. No review record is
created. Full quality analysis and isolated candidate validation precede the
shared guarded plan. Every proposed new file must map to exactly one actual
site-owned Hugo source page with actual rendered outputs, including translation
drafts in the explicit analysis view. Link-only/no-output, ignored, hidden or
build-never content cannot
pass merely because the existing site renders cleanly; required identity is
checked even when ordinary source-check groups are disabled. Saving --plan creates only a new plan file; explicit
plans apply FILE --site DIR revalidates and checks source bytes/modes,
existence, fresh directories and later attachment conflicts before applying.
Fresh-directory state is bound into the plan identity and checked before/after
validation, between writes and at completion. Rollback preserves later editor
attachments and reports recovery; it does not delete unrelated directory entries.
Editor settings and Markdown snippets belong to the site editor.
editor and snippets are removed.
Initialization profiles
init DIR [--profile project|docs|blog|book] [--languages en|en,zh|all]
composes one embedded MIT-licensed Starter archive. The default project
retains the previous complete language projection byte-for-byte. The language
selection is independent of the content profile: all means English,
Chinese and French.
Explicit docs, blog, and book retain their corresponding archived content
section and shared home, assets, examples, workflows and license. Native
section front matter defines their documentation, blog or sequential book
model and navigation. Each language’s site title/description comes from its
archived section; existing localized home cards/actions/CTA are projected to
that section. Only their generated hugo.yaml and data/home YAML files are
serialized; retained content/license bytes stay unchanged. There are no four
copied Starter trees or runtime template downloads.
Unknown profiles are policy refusals (1) before candidate validation or
writes. Missing/failed required Hugo validation remains incomplete (2). New
and empty-target, candidate-before-publication, exclusive creation and
concurrent-edit recovery protections apply to every profile. Ordinary Hugo
builds each generated site with provisioned dependencies. Archived workflows
remain source examples; init does not generate or execute the checksum-bound
R3 CI templates.
Bounded upgrade comparison
upgrade --to TAG now captures baseline and candidate views from the same
original site inputs and returns a readable module diff with full mode changes.
Its comparison records actual Hugo pages/outputs/language settings, emitted
file hashes/sizes/modes, raw alias declarations and separately observed alias
redirect files. It reports removed/added URLs, proven redirects, alias target/
byte changes and enabled language/output/search changes. A clean candidate
build alone does not prove route or capability preservation.
A previous URL is preserved only when an observed redirect at its exact old
output file targets the corresponding actual candidate page. Unknown/relative
custom alias identity remains required incomplete (2). Removed previously
emitted routes or outputs are blocking findings (1). Both resolved theme
versions must match the explicitly selected pins; unknown/substituted pins,
unknown/different normalized Hugo versions or environments, or unexpected
other input changes remain incomplete. Comparison supports one HTTP(S) base
origin/path; multihost inputs stay incomplete. No configuration migration
transform or universal browser/theme compatibility is claimed; manual review
remains explicit optional unchecked coverage. Observed aliases that retarget a different
unique Hugo page are blocking independently of same-page URL moves.
The v2 upgrade plan ID binds the selected module plan, copied source bytes/full
modes/file inventory and normalized actual comparison. --expect-plan ID
checks a fresh capture/comparison, not a previously saved successful build.
Because emitted file hashes are bound, nondeterministic templates can require
a refreshed preview even when source files appear unchanged. Guards are rechecked before returning a preview, before each write and after
writes, including proven local dependency/workspace inputs read-only. Only
selected module files are written; rollback restores only unchanged files
owned by the operation and preserves later editor bytes. Existing dirty target,
replacement and vendor safeguards remain in force.
File preservation and release checks
Initialization embeds the complete Starter commit and preserves its license. The provenance records its hash and every projection: independent content/language selection, the exact public theme pin/checksums, and disabling Git metadata for a fresh directory. The default project retains prior bytes; selected content profiles share the same archived source and license. Candidate validation precedes target writes. Exclusive creation refuses existing files; rollback removes only unchanged files created by that invocation and preserves concurrent user edits with recovery evidence.
Upgrade owns only a single site’s selected go.mod and go.sum changes. It
preserves unrelated dependencies and directives, refuses dirty target files
for --write, checks the reviewed plan ID when provided, rechecks targets
before writing, and records backups/recovery. Unrelated dirty source files do
not block read-only diagnosis or justify overwriting them.
check --release disables GOWORK and HUGO_MODULE_WORKSPACE and removes
the environment replacement for the subprocess. It preserves go.mod
replacements, reports conflicting local OINK replacement policy, and keeps
vendor evidence separate from the public requirement. Upgrade refuses an OINK
replacement and refuses _vendor; vendor refresh remains a separate explicit
workflow. No module pin change is described as updating vendor bytes. If Hugo
actually selects vendored OINK, --release reports required public-source
verification as incomplete (exit 2); matching version metadata is not proof
that the vendor bytes match the public tag. Ordinary check still validates
the actual vendor build.
Configured Hugo module replacements are also disabled only in the release snapshot. If Hugo changes module files in that snapshot during resolution or build, the CLI reports that dependency inputs need explicit preparation and review. It preserves the original bytes rather than silently accepting a build that depended on an unreviewed generated module-file change.
Checked builds and artifact identity
build --check --destination DIR --manifest FILE uses an isolated,
warning-strict production build. Hugo renders once; the check engines inspect
that output, then seal and export those same bytes. It never invokes a second
renderer to create the publication tree. The source checkout remains unchanged.
Only an outcome of 0 with complete or inapplicable required coverage can be
sealed. Blocked or incomplete checks do not create a verified export.
This production view does not include the separate nonpublishable maintenance
render. An explicit scoped translation policy whose required Hugo identities
are unknown because publication excludes their sources returns 2. It does
not infer missing translations from filenames or silently skip the scope.
Standalone check and translations retain the full maintenance view.
The destination must be new or empty, with an existing parent; the local manifest must be a new file outside that tree. Export uses exclusive creation, preserves exact bytes and full regular-file modes regardless of umask, and rechecks source and destination against the manifest. Existing entries, symlinks and overlapping trees are refused. Failed partial exports remain unverified evidence and are preserved. Empty directories and directory modes are outside the published file inventory.
--marker is optional and off by default. It adds
.well-known/oink-build.json with only oink.build-marker/v1 and the artifact
ID. That file’s entry is excluded from artifact-ID calculation to avoid a
circular hash, then its exact digest is included in the final inventory.
An existing marker path is refused. The manifest is never copied into the
public tree automatically.
The separately saved oink.artifact/v1 manifest records the source-input hash,
known source Git revision and dirty state, actual resolved theme identity,
CLI/Hugo versions, effective environment/base URL/release settings, required
coverage, actual Hugo route contexts and each file’s relative path, size, full
mode and SHA-256. Canonical URLs and HTML language values come from emitted
HTML; Hugo language keys remain separate. Unknown Git state stays unknown.
Original input bytes and modes are captured before temporary probe overlays
or workspace/replacement path rebasing. The public manifest omits absolute
local paths, arbitrary arguments, process logs and free-form coverage details.
Hashes prove byte identity, not a signature or publication of a local checkout.
Managed builds accept only the boolean Hugo flags --minify, --gc,
--ignoreCache and --noTimes after --, including =true/=false forms.
Other passthrough flags are unsupported inputs. Ordinary build keeps its
existing transparent passthrough behavior. Effective example/local addresses
are warnings during ordinary diagnosis and errors for checked release builds.
--release still requires separate evidence for actual public theme resolution;
a local Git revision or declared pin does not attest to vendor/replacement bytes.
Local and deployed verification
artifacts verify --artifact DIR --manifest FILE is read-only and offline.
It compares the exact file set, bytes, sizes and full modes. Changed, missing,
additional, unsafe or mode-changed files invalidate identity (1). Invalid
manifests, unreadable inputs and unsupported or interrupted inspection return
2. Verify the export again immediately before an uploader consumes it; later
edits cannot inherit a previous successful result.
verify --site URL --manifest FILE --network explicitly authorizes HTTP reads.
It checks every declared file and distinct actual Hugo route URL, including
all language/subpath contexts, against the manifest’s bounded response size
and decoded-byte digest. Recorded HTML canonical/language values and an enabled marker are
checked when available. Shared URL/file requests may be coalesced without
removing their recorded contexts. HTTP cannot verify local file-mode bits.
A wrong body, soft-404, wrong route, changed captured canonical/language value
or wrong marker is a conclusive finding (1). Timeouts, authentication failures,
rate limiting, server unavailability and an absent required marker leave work
incomplete (2). Redirects outside the selected origin/base path are blocked;
the command does not discover or send credentials. Static build checks do not
perform this deployment check. Network permission for a build does not authorize
a later verification request or an upload.
Retired CI generation
ci init is removed. Keep CI configuration in the site or Starter.
Local CLI validation does not execute hosted CI or deploy a site. Previously
saved CI plans are rejected by plans apply.
Explicit workspace registry
The registry and optional-tool boundaries passed frozen owning/runtime, actual protocol, four-consumer parity/preservation and canonical source/render gates. A07 adapter and A15 workspace supported scope is accepted locally in the maintenance record. The recorded R1–R8 and A18 scope passed for its historical source and binaries; changes to the current CLI require new evidence.
A workspace is one explicitly supplied YAML registry, independently versioned
as oink.workspace/v1. It contains only site names and directories:
The registry must be a regular nonsymlink file containing exactly one YAML
document with known fields, 1–64 entries and at most 256 KiB. Names match
[A-Za-z][A-Za-z0-9_-]{0,63} and are case sensitive. Directories are literal
relative paths from the registry’s actual parent, or absolute paths; variables,
globs and sibling discovery are not evaluated. Explicit directory symlinks and
operating-system aliases resolve to their canonical identity. Duplicate names,
duplicate or overlapping actual roots, filesystem roots, dangling symlinks
and nondirectory ancestors are rejected. Missing directories with a proven
existing ancestor remain listed; checking one returns that site’s 2 without
preventing later selected sites from being checked.
workspace list|check [GROUP] --workspace FILE [--sites NAME,NAME] selects all
registered sites when --sites is omitted. Explicit selections require exact,
nonempty, distinct registered names and retain registry order, including when
the names were supplied in another order. list needs no Hugo renderer.
check reuses the single-site engine and each site’s own Hugo inputs and
oink.policy/v1 policy. It never duplicates Hugo configuration in the registry.
The existing oink.result/v1 envelope contains data.registry,
selected_sites, sites: [{name, path, result}], completed_sites,
finding_sites and incomplete_sites. Each child is a full single-site result.
Completed sites include exits 0 and 1; finding sites are the 1 subset.
The aggregate exit is 2 if any selected site is incomplete, otherwise 1 if
any has blocking findings, otherwise 0. Human output includes each site’s
result and findings. This aggregation does not infer completion for unselected
sites.
Supported single-site commands accept --workspace FILE --site NAME, with one
explicit registered name and no default site. init, artifacts and verify
do not accept this selection. A saved plans apply FILE must bind to
the selected canonical directory; selecting another registered site returns
2 before source writes. There is no automatic multi-site apply or upgrade.
Existing candidate validation and source/dependency byte and mode guards still
apply. Registry listing/checking neither provisions missing sites nor installs
tools, commits or writes consumer configuration.
Optional check adapters
Explicit tools entries in each site’s oink.yaml select already provisioned
executables. These entries extend oink.policy/v1; they are not a second Hugo
configuration or an installer. Each kind has enabled (default true),
required (default false), command (default the kind’s name), config
(a clean site-relative regular file when supplied) and timeout_seconds
(default 60 seconds; bounded nondefault values 1–300). Commands are one
executable name or absolute path, not shell expressions.
| Kind | Owning check group | Current supported protocol | Configuration boundary |
|---|---|---|---|
markdownlint |
style |
markdownlint-cli 0.49.1 |
Optional declarative JSON/YAML/TOML; no JS, JSONC, custom rules or extends |
vale |
style |
Vale 3.24.0 |
Explicit INI plus captured styles from the supported declarative subset |
lychee |
links |
lychee 0.24.2 |
Optional bounded request settings; explicit network consent |
Unconfigured tools are not discovered. A tool outside the selected check group
is visibly not_checked. A configured optional tool that is unavailable,
unsupported or cannot complete leaves an omission; required incompletion
returns 2 and cannot be downgraded by rule severity, exclusions or a problem
baseline. Required and disabled cannot be combined. Completed typed findings
still follow policy: blocking findings return 1. An unrecognized tool version
or invalid protocol output does not count as a completed check.
data.adapters records each kind, requirement, status, typed diagnostics,
adapter.KIND coverage, raw process evidence, omissions and provenance.
Provenance includes the observed supported version, executable SHA-256,
captured configuration/style paths with SHA-256 and full mode, and pinned
public protocol sources. Tool logs stay in stderr and evidence; JSON stdout
remains one result. Per-process time and output are bounded, and changed
executables, captured configurations or tool-modified private inputs invalidate
their evidence.
Prose tools receive private masked copies of proven site-owned Markdown. Front matter, BOM/CRLF and UTF-8 offsets, code, shortcodes, raw HTML, configured math and attributes retain their source boundaries. Code prose is outside source attribution; Markdown structure and fence/inline code boundaries remain available to markdownlint, while Vale receives the prose-only mask. Findings touching excluded or synthetic mask text are not attributed to original source. Source locations are emitted only for proven original lines/ranges; unsupported syntax and suppressed findings remain visible omissions. These adapters do not format or rewrite original content.
Markdownlint uses an unpredictable generated JSON pointer to isolate the
captured rule object after upstream rc merging. Executable configs, custom
rule loaders and recursive extends are refused. Vale uses an explicit
captured INI, --no-global and copied declarative styles; sync, packages,
actions, scripts, conversions and style pipelines are unsupported. Lychee
accepts bounded timeout, max_retries and max_concurrency settings, plus
the literal cache = false; caching remains disabled and cache = true is
refused. Preprocessors and arbitrary command options are refused.
Offline is the default. Lychee is not invoked, including its version probe,
unless --network is explicit: optional coverage is not_checked, required
coverage is incomplete 2. It receives only observed external HTTP(S)
references from actual Hugo output; local links remain the native check’s
responsibility. Definitive failed 4xx responses are policy findings, except
401, 403, 408, 425 and 429; those, 5xx, DNS/TLS failures and timeouts
are inconclusive, returning 2 when required and an omission when optional.
External fragments, browser behavior and remote content identity are not
verified. Findings retain the rendered output file and DOM pointer; no Markdown
line is invented from an external URL.
Child processes do not receive caller proxy-URL/credential settings or Node
preload variables. Literal NO_PROXY/no_proxy host-list data may be forwarded
for the qualified runtime. This is not a promise that every operating-system
proxy route is disabled, or an OS network sandbox. Tool preparation and any
network operation remain separate explicit actions; no tool is installed by
these commands.
Offline and compatibility boundary
Offline is the default for managed subprocesses. Dependency misses are
incomplete work. --network explicitly enables network use for the current
operation; it conflicts with --offline. Isolated checks may seed disposable
caches from already provisioned local modules. Downloading into a disposable
cache does not promise a persistent cache for the next invocation.
Only module download artifacts are seeded; isolated resource caches start
fresh. The CLI does not reuse a global GetRemote cache to promise offline
remote-resource builds. Required resources must be available as local inputs,
or that operation must explicitly enable network access.
The CLI does not download a Go toolchain, install packages, alter global configuration, or enable telemetry. Its process policy is not an operating system network sandbox. Qualification records distinguish ordinary offline execution from tests that actually deny outbound access at the OS boundary.
Compatibility is declared from executed evidence, not inferred from a successful cross compilation. The local candidate has an exercised macOS arm64 path with Hugo Extended 0.166.0 and public OINK v1.1.0; the version gate accepts Hugo Extended 0.160.1 or newer without claiming all such versions were tested. Initialized sites retain normal Hugo inputs and require only their documented dependencies after removing the CLI.
Retired local Studio
studio is removed from the CLI. Use an ordinary editor and oink dev for
a site preview. Read maintenance facts through inspect and structured reports.
The dated R7 acceptance remains historical evidence for its identified inputs.
Retired management API
The CLI no longer serves a management API. Earlier Studio API acceptance does not describe the current executable.
Historical capture limits
Historical R7 limits belong to the dated acceptance record. Current command coverage and supported inputs are defined in this contract.
Retired general editing
edit and Studio editing are removed. Edit source with an ordinary editor,
then run check. new, move, review records, and baseline plans retain
candidate validation and byte/mode guards. Previously saved editing plans
are rejected. The dated R8 record remains historical evidence.
Retired text and field editing
The CLI no longer owns general text or front matter editing forms.
Retired snippets and attachment editing
Write Markdown and add attachments with the site editor. The CLI no longer provides a snippet catalog or general attachment editing command.
Retired Studio editing
The CLI does not serve an editor or accept browser Apply requests.
Verification and remaining scope
Use make test for offline Go tests and vet. Use make test-hugo for actual
Hugo integration and make test-tools for configured optional tools.
Skipped integration cases are not passing runtime evidence. Current changes
need new source/binary-bound evidence; an older record does not qualify them.
On 2026-10-04, make test passed on macOS arm64 with Go 1.27.1 and Hugo
Extended 0.166.0. The make test-hugo run failed
TestPublicR5CachedPublicModuleMovePreviewApplyAndOrdinaryHugo: module-collection
text preceded the configuration JSON, and the move returned 2 with
Hugo config did not return JSON. Candidate validation refused the operation
and reported the source unchanged. An immediate targeted rerun passed.
The intermittent failure remains unexplained; the rerun does not establish
a passing full integration gate for the current candidate.
The dated maintenance acceptance record preserves earlier R1–R8 and A18 evidence. Declared targets are macOS arm64 and Linux arm64/amd64. Darwin amd64 is experimental and unqualified; Windows is unsupported. Archive creation, signing, distribution, consumer adoption, and deployment are separate states. This contract authorizes no automatic commit, push, publication, or deployment.
6 - Paper and Slate visual presets
Paper and Slate ship in 1.2.0. Ink and Terminal are included as explicit opt-ins; their remaining design work is recorded below.
Decision
Paper is the default, with warm paper/ink colors, blue links, IBM Plex Sans,
heading hairlines and framed tables. Slate retains the v1.1.0 palette,
Inter/Chakra/Plex Mono roles and Landing grid/glow. This gives reading sites a
quieter default while preserving an explicit compatibility choice. The cost is
a visible default change: existing sites can set params.ui.preset: slate.
The 1.2.0 release notes and upgrade guide call out this default change.
The reader menu is opt-in (preset_menu: false). The docs site enables it.
One Appearance disclosure combines native Style and Light radio groups;
mobile uses a modal dialog in the browser top layer. It is reachable by touch
and keyboard without relying on hover. The cost is replacing the old one-click
mode toggle with a selection panel; the t shortcut still toggles mode.
Style and mode use separate attributes and storage keys. Choosing the site default preset clears the style key. Hugo renders the default without JavaScript; an allowlisted inline script restores reader state before CSS. This prevents the common initial preset mismatch, while keeping blocked storage usable. Presets ship in one stylesheet, at the cost of additional CSS bytes.
brand separates the wordmark from display headings. Paper adds the local
OFL IBM Plex Sans variable font, including normal/italic and the six supported
small writing-system subsets. Font files download on use; system typography
and explicit role overrides retain priority. Chinese uses the system stack.
Phase 1 includes no serif face and no external font request.
Page task determines density: Landing keeps display scale, long articles keep their reading measure, and navigation/configuration tables remain compact. No global spacing increase, new shell, or geometry abstraction is introduced. Giscus and print follow the preset; API vendors and charts retain their current mode-only behavior. This keeps the first implementation bounded.
Later work
A subsequent October 5 experiment implements Ink and Terminal behind explicit
configuration; see the experiment record.
They are not stable defaults. preset_menu: true offers Paper/Slate and the site
default; an explicit list can expose either experiment. The menu uses the same
compact icon-and-name buttons for all four, without experiment badges.
This gives reviewers actual theme output without changing ordinary menu choices.
The cost is additional scoped CSS and a larger menu when experiments are enabled.
The experiment uses owned component rules for square/2 px geometry and compact desktop navigation instead of introducing a global density framework. It reuses existing local fonts, state handling and accessibility controls. Charts and API vendors stay mode-only; comment palettes and print follow the experiments. Remaining work is visual acceptance, wider device review and any decision to promote them into the stable set.
Evidence
The architecture contract and
shell contract own behavior.
check-presets.py owns token parity, AA palette checks, the frozen Slate
v1.1.0 palette and strict configuration output. Font, parameter, vendor,
namespace, action and runtime checkers retain their existing ownership.
The documentation site’s appearance.spec.mjs tests real output; the
dated acceptance record
distinguishes executed checks from remaining experiments.