Skip to content

This is the multi-page printable view of this section. .

Return to the regular view of this page.

Design and development

OINK maintainer contracts, accepted decisions, dated research, and proposals in one canonical bilingual section.
OINK 1.2.0 contract

This contract describes the v1.2.0 release. Its canonical bilingual sources are in content/docs/design/. Hugo Extended 0.160.1 remains the compatibility floor. CI uses 0.165.0; the floor is not a second full CI matrix.

This section is the durable design record for OINK. It complements the task-oriented guides elsewhere on the site: use those guides to build a site, and use this section to understand current invariants, the reasons behind them, the evidence used to evaluate alternatives, and work that is still only a proposal.

Reading this section

Layer Meaning
Contracts Normative behavior that compatible implementations must preserve
Decisions Accepted rationale and boundaries that explain current behavior
Research Dated, non-normative evidence that may need to be refreshed
Proposals Draft PRDs and RFCs; publication here is not proof of implementation

Contract map

Contract Authority
Architecture Build, configuration, diagnostics, localization, featured images, output, security, accessibility, and performance
Components Component API, Book and release primitives, validation, and output degradation
Shell and navigation Navigation, search, blog presentation, actions, taxonomies, and page-end composition
Landing pages Landing data, the 22-section registry, runtime, accessibility, and outputs
Migration boundary Supported 0.4-to-current content and configuration migrations

Design records

Collection Contents
Decisions Accepted diagnostic, configuration, and authoring rationale
Research Goldmark probes and evidence from real OINK consumers
Proposals Active PRDs for knowledge graphs, media convergence, and machine-readable indexes

Create every new OINK PRD or RFC as an English and Chinese page pair under content/docs/design/proposals/. Do not create another repository-local plan/, plans/, or proposal/ tree. Once a proposal is accepted, update the implementation, owning checker, and relevant contract; preserve the stable rationale under Decisions and retire the draft through Git history and the changelog.

Authority and maintenance

This directory owns the maintainer design prose in English and Chinese. The theme repository owns executable facts: hugo.yaml owns published defaults; owning resolvers and checkers define optional shapes; layouts/ and assets/ own rendered behavior; check scripts and tests/goldens/ own validation; and VENDOR.json owns bundled versions, licenses, files, and checksums.

Whenever public behavior changes, update the implementation, its owning checker, and both language versions of the relevant contract in the same delivery. Tests should exercise behavior and output rather than pinning prose.

1 - Architecture contract

Repository assembly, configuration, diagnostics, localization, output, performance, security, CSS, accessibility, and release-state boundaries.
OINK 1.2.0 contract

This contract describes the v1.2.0 release. Its canonical bilingual sources are in content/docs/design/.

Repository and assembly

The repository root is a Hugo Module and complete theme, not a site or npm workspace. Hugo Extended compiles SCSS and templates. Browser runtimes and third-party assets are committed, so a normal build performs no network fetch. Public bilingual documentation, examples, and browser tests live in the sibling oink.pgsty.com repository; the theme repository keeps only narrow internal regression fixtures under tests/site/ and has no separate public example surface.

Generated public/ and resources/ trees are never source. Vendored runtimes, font families, and Font Awesome glyph definitions are supported distributions, not dead-code candidates; VENDOR.json and bin/check-vendor.py pin their integrity. OINK ships the complete supported Font Awesome distribution because consumer-authored content may use icons that theme templates do not.

The official compiled Font Awesome CSS is one stable, fingerprinted vendor stylesheet loaded before the fingerprinted main.css produced from theme and consumer SCSS. A site-style edit therefore does not invalidate the icon distribution, while ordinary cascade order still lets the site override it. Capability styles such as KaTeX, DocSearch, Swagger, and Asciinema remain separate and load only when used. Fingerprints make immutable URLs possible; the deployment host, not the Hugo theme, owns their HTTP cache headers.

Hugo types docs, book, blog, and swagger select the reading shells; params.ui.shell_types may add types. Landing is layout: landing. There is no article type or second blog shell: immersive pages are a blog presentation described in the shell contract.

layouts/_partials/shell/config.html resolves shared shell facts. Layouts must render through content/render.html before scripts.html, because render hooks and shortcodes register capability flags in the Page Store. Override the narrowest partial; superficially similar base templates remain separate where merging would change Hugo lookup precedence.

Configuration and diagnostics

Theme policy lives under params.ui.*; multi-setting integrations such as comments.giscus, plantuml, and drawio stay top-level. Boolean features use bare booleans unless they also have several settings. A page override drops the ui. prefix: params.ui.image_zoom becomes image_zoom, never a front-matter ui map. hugo.yaml declares published defaults; an owning resolver and its checker define any optional configuration shape or range.

Invalid input follows one rule: warn with the value, allowed shape, and safe fallback; then use that fallback or omit the unsafe feature. Ordinary hugo server therefore remains usable, while every publishing gate uses --panicOnWarning. The theme never calls errorf, and check-params.py enforces that boundary. Do not add speculative validation for unreachable states.

There is no generic renamed-key registry. A transition that still needs a migration diagnostic uses a targeted warning in its owning resolver plus a strict negative test; removed keys are never read as a compatibility path.

Network-capable features are explicit and degrade closed. PlantUML requires plantuml.svg_image_url, Draw.io requires drawio.drawio_server, and Algolia requires appId, apiKey, and indexName; incomplete configuration warns and emits no request. Draw.io loads only when rendered content contains PNG or SVG candidates, then inspects each distinct image URL once. Diagram endpoints must be strings containing an HTTP(S) URL with a host or a same-site path. Unsupported schemes, protocol-relative URLs, backslashes, whitespace, and pathless same-site references warn and disable the integration before its runtime is selected.

Interface localization

Available since OINK 1.1

OINK 1.1.0 expands the native interface catalogs to the complete locale set below. Consumer-authored content still needs its own translations.

OINK ships native interface catalogs for the 31 locale filenames present in google/docsy@64f51c5, plus generic zh as the Simplified Chinese default:

ar az bg bn de en es et fa fi fr he hi hu it ja ko nl no oc pl pt-br ro ru
sr-cyrl sr-latn sv tr uk zh-cn zh-tw

That is a compatibility scope, not a runtime dependency on Docsy and not a claim that a consumer’s authored content has been translated. A new Docsy locale does not enter OINK automatically: it needs a complete OINK catalog and the same review as every existing locale.

i18n/en.yaml owns the 194-message schema. Every one of the 32 OINK bundles has exactly that key set and native UI text; an English value may remain only when it is a reviewed product name, punctuation token, conventional abbreviation, or genuine word shared by the target language. There are no generated English fallback blocks. zh and zh-cn carry Simplified Chinese, while zh-tw carries Traditional Chinese.

On the Hugo 0.160.x compatibility floor, a non-default generic zh language key must set the concrete locale: zh-CN value when the regional Chinese catalogs are also present. Bare locale: zh resolves in that configuration from Hugo 0.161 onward. This affects language configuration, not the i18n/zh.yaml catalog name.

Runtime placeholders such as %s, {count}, and {{ .Count }} may move to a grammatically natural position but must remain byte-for-byte identical. Values are strings or Hugo plural-message maps. Plural maps use the locale’s supported categories (zero, one, two, few, many, other); other is required, and every form is a string with the same placeholders. Catalogs contain no hidden bidirectional controls; Arabic, Persian, and Hebrew direction still comes from the consumer language setting (direction: rtl), not from characters injected into translations. bin/check-i18n.py enforces the locale set, schema, value shape, placeholders, directional controls, and the small reviewed set of English-identical terms. Adding a visible string therefore means translating it in every bundle in the same change, not running a fallback generator.

Hugo’s images is the single authored API; params.images is only the site-wide social fallback.

Source Reader thumbnail Social card
Page images, or bundled **featured*, *feature*, {*cover*,*thumbnail*} yes yes
Section cascade.images yes yes
Site params.images no yes

images: [] clears an explicit or cascaded value but does not disable bundled resource discovery. Only the first resolved image is representative. Local processable rasters may be cropped; SVG, static, and remote resources remain valid without Hugo image operations.

featured-image-resolve.html owns source ranking and relative/absolute URLs. A page’s explicit images outranks its bundled resource, which outranks an inherited cascade image, even when explicit and inherited values are identical. For file-backed pages, authored presence is read from the source front matter; Hugo parses its YAML, TOML, or JSON. For generated pages without a source file, resolved images is treated as explicit. List thumbnails, Open Graph/Twitter/schema helpers, author avatars, Pinterest media, and blog presentation all consume that decision.

params.ui.featured_image is blog-only and defaults to none; front matter overrides it per page or cascade. banner renders a figure above a single-page title, wash colors its header, and hero paints the shell backdrop on single pages and section indexes. Missing images and non-HTML output render no image.

Outputs and runtime

Every base template sets Page.Store.tdOutputFormat:

Output Contract
HTML Complete semantic content; local runtime only for used capabilities
Print Expanded content; no shell navigation, search, or zoom runtime; the shared action layer supports explicit print controls
Markdown / LLMS Source-shaped Markdown without td- component markup
LLMSFULL Opt-in per top-level section: one llms-full.txt per enabled section per language, that same Markdown concatenated in reading order
RSS Safe static summary or explicit omission
NAVJSON Opt-in per site: one navigation.json per language, serializing the navigation authority the sidebar and pager already read
BookManifest Opt-in ordered JSON handoff for a publication packager; never presented as an EPUB or PDF

Output formats run in their defined order; the mutable-format concern is not a cross-format race. Within Print, however, Hugo may render a Book page and overlapping aggregates in parallel. One per-page cached coordinator therefore produces the plain and Book variants in a fixed order, and each caller selects the form it needs. Plain Print keeps page-local heading and routed xref URLs; Book aggregates keep namespaced headings and in-document xrefs.

Consumers opt into custom outputs; OINK does not force expensive Book aggregates. HTML gets the shared action and core layers plus stable first-party capability chunks selected by the page flags. Templated capabilities publish at most one chunk per language; flags choose script tags and never create a new combination bundle. Print keeps the action layer and only runtimes required by rendered print features. Large third-party UMD files stay separate; unused feature runtimes stay absent.

LLMSFULL is enabled by a top-level section listing it in its _index front matter outputs; the theme never adds it to a site’s output set. One shared renderer produces the per-page Markdown and the bundle, so a bundle is that same semantic Markdown – no td- component markup – concatenated in the sidebar and pager reading order. Enabling it below the top level warns and emits nothing, so an ordinary build stays usable while --panicOnWarning blocks publication.

NAVJSON is enabled by the site’s outputs.home and publishes one navigation.json per language at the language root. It serializes the same authority chain the sidebar and pager read: an explicit data/docs_nav.json tree when present, the weighted content tree otherwise. Array order is the contract and weight is never serialized, and the output is notAlternative. schema/nav.v1.schema.json versions the format as a hand-authored contract artifact, edited with its templates and checker rather than by the generated configuration schemas’ drift gate. Both outputs default off, so a site that enables neither builds byte-identically; bin/check-agent-indexes.py owns them.

BookManifest is disabled unless a Book root explicitly lists it in outputs. It references that Book’s existing per-page Markdown and records derived page order, headings, numbered targets, and xrefs. It contains no publication metadata guessed by the theme and is not a distributable ebook.

The theme repository ships bin/book-epub.py and bin/book-pdf.py as explicit publication steps, with bin/check-book-epub.py and bin/check-book-pdf.py as their artifact gates. The EPUB packager combines BookManifest with the same whole-Book Print HTML and accepts consumer metadata separately. The PDF runner serves that Print output only on a temporary loopback address, invokes an explicit Chrome/Chromium binary behind a script-src 'none' Content Security Policy, and emits A4 pages with CSS page numbers. The PDF server also applies a CSP sandbox, rejects meta-refresh navigation, and refuses symlinks escaping the build tree. Without the network opt-in, image and media requests are limited to the loopback origin and data URLs, including requests initiated by CSS or SVG. Both tools refuse missing or out-of-tree resources; network resources and output replacement each require a separate explicit flag. The network opt-in allows passive HTTP(S) media only; remote scripts and local-file schemes remain invalid. Relative assets in the EPUB metadata file resolve from that file’s directory, not from the caller’s working directory. No publication work runs during an ordinary Hugo build, and PDF remains Print-derived rather than another template output.

Performance rules:

  • do not walk .Site.Pages per page when a site-level resource or partialCached result can own the work;
  • render .Content once and read Page Store flags only after it;
  • emit correct markup instead of scanning the DOM to repair it;
  • group browser work by resource URL, not DOM instance;
  • keep ordinary outputs opt-in when their aggregate cost is material;
  • emit no Speculation Rules by default: a named production consumer must first measure Sec-Purpose: prefetch requests, useful navigations, transferred bytes, and CSP impact with a reversible moderate experiment;
  • validate reachable author input, not hypothetical internal states.

bin/measure-baseline.py measures build time, output weight, bundle count, and shortcode density.

Trust, CSS, and accessibility

Authors may enable Goldmark unsafe; configuration and component parameters are not raw HTML. The shared attribute policy consumes an allowlist, validates class tokens, passes data-* and aria-*, and warns while dropping style, srcdoc, on*, reserved, and unknown attributes. URL helpers reject dangerous schemes and protocol-relative URLs where local or explicit absolute URLs are required. Promised remote URLs remain supported but are never fetched at build time.

Theme output uses td- classes, data-td-* attributes, and --td-* custom properties; author markers such as .steps, .cards, and .full-width stay unprefixed. CSS supports RTL, print, forced colors, reduced motion, long tokens, and narrow viewports. Theme-owned decorative icons carry aria-hidden; pages with task lists or raw authored Font Awesome elements alone load the authored accessibility repair.

Reading-container focus distinguishes pointer origin from keyboard navigation. A pointer-focused main region, table viewport, or code pre does not acquire an outline merely because the reader presses another key. Tab, blur, or a new non-pointer focus clears that exemption. Controls retain their own focus styles, scrollable containers retain tabindex, and the skip-link destination shows a local outline around its title instead of the entire article. Forced colors preserve the keyboard indication; no global focus-outline reset is used.

Font roles are ui, body, heading, code, display, meta, brand, and print, exposed as --td-*-font-family. ui is the main face: body resolves through it, and heading through body, so one assignment moves chrome, prose, and headings together. params.ui.typography is technical or system; both compile into one stylesheet with no runtime. Legacy Bootstrap/Docsy Sass variables continue to seed these roles.

params.ui.fonts reaches the same roles from configuration, for a site that would rather not mount SCSS or add a stylesheet. It names faces and never loads them: a family must be one the reader has or one the site declared in an @font-face of its own, which keeps the key outside the network contract. Values are gated to plain font family syntax and the emitted :root block is rebuilt from the matched parts; an unknown role or an unsafe value warns and is dropped alone. The block renders after the stylesheet, which is what lets an authored face outrank the preset at equal specificity. A shell reads in the site’s faces and owns none of its own: a Book sets its numbers and captions in the prose face, not in a technical one.

The accent family splits by role. Accent text – links, external URLs, inline code – follows the Bootstrap link family and --bs-code-color, which a theme color never redeclares. Inline code follows the visual preset: Slate retains the crimson pair, while Paper uses ink text on a quiet chip. Accent grounds – selected rows, the greyed ground a navigation row takes under the pointer, hover washes, the outline pill, rail and dot, chip hovers, a card’s hovered edge, a share button’s hover fill, selection, focus rings – follow --td-accent, --td-accent-rgb and --td-accent-hover, which default to the link family and are the only properties params.ui.theme_color emits. Ink that belongs to the shell rather than to the prose follows them too: the outline anchors the viewport is standing over, and a Book chapter’s headings under the pointer or keyboard focus, light in the section’s color, not in the link blue. theme_color and theme_color_dark take #rgb/#rrggbb; front matter and section cascades override the site value. An unconfigured site emits nothing. An unparseable value warns and keeps the default palette. A resolved color below 4.5:1 against the site default preset canvas warns with a suppressible id and still ships: the check is advisory, and only a parse failure drops a color. The light color is the key: a theme_color_dark with no valid theme_color warns and is ignored, so a page is colored in both modes or in neither. An omitted dark half lightens toward white in 4% steps until it clears 4.5:1 on the dark canvas. Every emitted byte is formatted from parsed integer channels, never from author text. One resolver answers “what color is this page” for the head block and the sidebar root switcher alike.

Visual presets

OINK 1.2.0 defaults to Paper. params.ui.preset accepts paper, slate, and the explicit experimental presets ink, terminal. Invalid and reserved names (folio, canvas) warn and fall back to paper. params.ui.preset_menu defaults to false; true offers Paper, Slate and the site default. A list selects available choices, including experiments. A list must include the site default; missing it warns and adds it. There is no page-level preset override.

Hugo renders data-td-preset and data-td-site-preset on every document root, including 404 and print output. With reader choice enabled, an inline head script validates td-preset before CSS loads. Selecting the default removes that storage key. Invalid saved values are removed; blocked storage leaves in-page controls usable and shows a non-persistence note. The storage event synchronizes tabs. td-preset-change carries {preset, previous, stored}. Mode remains independent: data-bs-theme, td-color-theme, and td-theme-change retain their meaning. The browser chrome color follows the resolved mode and preset. Without JavaScript, the site default light palette renders; appearance controls require JavaScript.

All four presets compile into one stylesheet. Slate retains the v1.1.0 base palette selectors and values. Paper changes palette and selected component rules without changing shell columns, breakpoints, or global spacing. Dark Paper redeclares every light palette token, including nested dark islands. Font roles have equal selector specificity: preset, then typography: system, then head-emitted params.ui.fonts. Site _styles_project.scss remains last. brand controls the wordmark independently from display headings. Paper uses local IBM Plex Sans; Slate keeps Inter; both retain Chakra Petch for the wordmark and IBM Plex Mono for code. System typography requests no bundled text face unless the site explicitly overrides a role. No external font is introduced. Ink uses Inter throughout, with red markers, underlined prose links, square geometry and no shadows. Terminal uses mono chrome/headings, Plex Sans prose, teal links, amber accents, 2 px corners and no shadows. Its navigation density changes only on desktop; prose measure and mobile targets remain unchanged. CSS heading marks have empty accessible alternatives and are omitted in unsupported browsers. Explicit fonts.ui still supplies the main face unless a valid fonts.body overrides it. See the experiment record.

Giscus auto palettes follow both dimensions; explicit Giscus theme or light/dark stylesheet overrides remain authoritative. Print uses a light preset palette on white paper even when the screen is dark. Mermaid and ECharts continue to follow mode only; API widgets retain vendor palettes. See the accepted decision and local acceptance record.

Release states

Source complete, locally validated, committed, tagged, pushed, pinned by a consumer, deployed, and production-identical are distinct states. A local Hugo build proves only local validation.

2 - Component contract

The maintainer contract for OINK authoring primitives, validation, Book and release behavior, and output degradation.
OINK 1.2.0 contract

This contract describes the v1.2.0 release. Its canonical bilingual sources are in content/docs/design/.

Tutorials and exhaustive examples belong in the reader-facing Components section. This page defines the API and behavior that those guides rely on.

Authoring model

Use ordinary Markdown when one block plus attributes can express a component. Use shortcodes for compound bodies or facts Markdown cannot carry. There is no parallel component registry. Native forms require:

markup:
  goldmark:
    renderer: { unsafe: true }
    parser:
      wrapStandAloneImageWithinParagraph: false
      attribute: { block: true }

Only {{%/* steps */%}} uses percent delimiters because its body belongs to the page outline; every other shortcode uses angle delimiters. Compound bodies pass through content/render-block.html with a unique ID scope. Shortcode and component parameter captions, labels, titles, and names are plain text; Markdown belongs in bodies. Landing narrative fields follow their own contract. An icon is one Font Awesome class pair. Components expose safe classes and attributes, not arbitrary color or inline style.

Public API

OINK has 29 shortcodes:

  • core: tabs, tab, steps, cards, card, fields, field, include, kbd, badge, param, comment, contributors, asciinema;
  • Book: fig, tbl, eq, eg, xref, book-toc, book-figures, book-tables, book-equations, book-examples;
  • release: release-card, release-assets, download;
  • OpenAPI: swagger, redoc.
Component Native form Shortcode form HTML runtime
Callout > [!TYPE], fold, {icon=} none none
Tabs adjacent fences/tables with {tab= group= value=} tabs / tab tabs on used pages
Steps ordered list + {.steps} steps none
Cards link list + {.cards} cards / card none
Fields table + {.fields} fields / field none
FileTree filetree data fence none divider only with comments
Gallery gallery data fence none shared Image Zoom when eligible
Image Markdown image + block attributes none Image Zoom when eligible
Table attributes, caption, number, or tabs tbl for compound Book tables tabs when tabbed
Book target image/table/passthrough/fence + {num=} fig, tbl, eq, eg none
Release assets checksums data fence release-assets copy in HTML
Math and chemistry passthrough, math, chem fences eq none; build-time rendering and local styles
Diagram/data mermaid, plantuml, markmap, echarts, infographic fences none selected local runtime only

Validation

Invalid author input follows the architecture contract: warn, use the documented safe fallback or omit the component, and let --panicOnWarning make the same diagnostic fatal at publication gates. Named and positional forms are not mixed. Book target IDs match [A-Za-z][A-Za-z0-9_.:-]*; Book numbers match [0-9A-Za-z.-]+; classes are token-validated. Hook and shortcode targets share one page registry, so collisions cannot produce duplicate output IDs.

URLs use content/url.html; raw backslashes are invalid because browsers may interpret them as URL separators. Images resolve through page resources, section resources, global assets, then static or explicit remote URLs. Local rasters carry intrinsic dimensions; SVG, static, and remote sources remain valid but cannot use Hugo image operations. Resource metadata alt must be a string; an invalid value warns and is ignored, preserving the image’s authored alt text.

Component behavior

Callouts and tabs

Callout types are note, tip, important, warning, caution, success, danger, question, example, quote, and details; - starts folded and + expanded. Unknown types render as plain blockquotes with their markers preserved, without JS.

Adjacent tabs group only when consecutive and of the same block kind. group enables hash #<group>-<value> and storage td-tabs:v1:<group>; ungrouped tabs use neither. HTML exposes every panel before JS, print expands them, Markdown retains authored source, and RSS receives the rendered text summary. The full form supports arbitrary Markdown; tab.label is required, value is required exactly with a parent group, and an orphan tab warns and renders nothing.

Steps, cards, fields, and tables

Native steps accept ordinary block content. Use the shortcode only when a step must contain a percent-delimited container. Native cards are link lists; the full form adds bodies, badges, icons, and images. Native fields map the first column to the name, the last to the description, and middle columns through meta= or headings; the full form allows block descriptions. card and field are valid only inside their parents.

Field anchors are field-<name> with lowercase punctuation runs collapsed to hyphens, so params.ui.typography becomes field-params-ui-typography. Duplicate anchors receive positional suffixes.

The table hook owns responsive wrapping and captions. .matrix makes the first column row headers; .full-width widens normal or matrix tables. .fields cannot combine with matrix, full-width, numbering, or tabs; numbering and tabs are also mutually exclusive.

The Markdown image hook is the ordinary image API. Inline images stay inline; block images become figures with caption or num. Image processing belongs to this native form alone: the full fig source form is a numbered container whose parameter list deliberately excludes command/options, so a processed numbered image is written as a native block image with num. Allowed image attributes are id, num, caption, width, height, link, command, and options plus shared safe attributes. command and options appear together and use Hugo Fit, Resize, Fill, or Crop on processable local resources. A plain linked image uses Markdown syntax; the link attribute therefore requires a caption or number. Linked and decorative images do not load Zoom.

Zoom triggers keep the image’s alt text and localized preview action in their ARIA accessible name, without inserting helper text into the article. Copying content as plain text or rich HTML must not add preview instructions, even when an editor discards the theme’s styles. Authored images and captions are preserved. When Draw.io and Image Zoom share an image, Edit and Zoom remain separate sibling buttons. The editor entry supports keyboard access and stays visible on touch devices and in forced-colors mode.

Gallery accepts one Markdown image per line with optional description, link, and class. FileTree accepts indentation, - name, optional /, comments, and validated icon/tone/open/type attributes. Markdown preserves authored source; print renders expanded static figures and trees.

All code highlighting uses Chroma. Common fence attributes include title, copy, wrap, collapse, label, id, line options, tabs, and Book num/caption. Copy returns authored source. Mermaid palettes follow mode independently of visual presets. The default dark edge-label background is #404040 for AA text contrast; authored params.mermaid.themeVariables remain authoritative.

ECharts input is declarative JSON/YAML; callbacks use $fn:<name> from window.OinkEchartsFunctions, never embedded script execution.

Mathematics uses Hugo’s build-time KaTeX output and local CSS, without a browser math runtime. The shared renderer normalizes pre-0.18 KaTeX class names to the vendored stylesheet in HTML and Print, preserving the Hugo 0.160.1 floor, MathML, and authored TeX. Markmap uses the matching vendored KaTeX runtime. On narrow screens, numbered-equation captions wrap within the reading column; a long caption must not widen the page.

Swagger and Redoc accept an HTTP(S) specification URL or a path rooted under static/; neither resolves page resources. Redoc treats leading and non-leading slashes equivalently and joins local paths to baseURL. Only HTML is interactive; Print, Markdown, and RSS render a static specification link.

Book

The book type extends the docs shell and follows the content tree or data/docs_nav.json. book_number, book_part, book_kind, and book_status are presentation metadata; they do not change Hugo publication state.

Numbered kinds are fig, tbl, eq, and eg, with default ID <kind>-<num>. eg needs a caption; eq without num is an unnumbered display formula. xref names exactly one kind plus optional page/anchor, or an anchor with explicit text. A numbered example is one framed body and caption.

Footnotes belong to the page document. Native numbered tables and fences keep them there. A shortcode body is a separate Goldmark document, so footnote references in tbl, eg, fig, card, tab, field, or include warn and remain literal; code-shaped text is ignored by that check.

book-toc follows navigation order at depth 1–3; the four book-* indexes collect one target kind each. Single-page Print preserves the page’s ordinary heading and footnote IDs exactly as regular HTML renders them. Multi-page section Print and whole-Book Print rewrite cross-page links and namespace those page-local headings and footnotes to avoid aggregate collisions, while preserving explicit target IDs. Consumers opt into those potentially expensive aggregate outputs.

Release and download

Release front matter is one release_url in the form https://github.com/<owner>/<repo>/releases/tag/<tag>; owner, project, and tag come from the URL and date from the page. No remote release state is fetched. The removed release map, release_products, and release_group_by_product warn with their replacement and are not compatibility paths. The section index lists every page, using parsed project tag when available and the page title otherwise.

Checksums accept canonical lines or one source resource, never both; filenames cannot be paths. HTML adds local copy, while static outputs expose full hashes.

Downloads use data/download/<key>.yaml. Channels are rolling or pinned; only pinned URLs and commands interpolate ${version} and ${tag}. Before publication, rolling channels remain usable and pinned channels show pending. Markdown renders the complete channel list; RSS omits the component.

Verification

Shared output rules live in the architecture contract; exceptions are defined with their components above. Markdown and RSS set no browser runtime flags; Print retains only flags required by rendered print features. Source checks cover parameters, hook policy, runtime isolation, and migration; output checks compare HTML, print, Markdown, RSS, and LLMS goldens; browser tests cover interactive surfaces. Migration is documented in the migration contract.

3 - Shell and navigation contract

Navigation authorities, immersive blog presentation, search, actions, taxonomies, indexes, and page-end composition.
OINK 1.2.0 contract

This contract describes the v1.2.0 release. Its canonical bilingual sources are in content/docs/design/.

Authorities and navigation

Concern Authority
Global navigation Hugo menus.main
Docs / Book sidebar and pager content tree or data/docs_nav.json
Root switcher resolved top-level content roots
Discovery per-language local search index
Page and Palette actions shared action registry

No feature introduces another menu or page tree. One menu child level is interactive; deeper levels warn and flatten beneath linked group headings. External links use target="_blank" rel="noopener noreferrer"; internal links remain language- and subpath-aware.

Navbar desktop and drawer views project one tree, and every dropdown panel is one moderate column of icon-and-title rows — the mega panel and its columns menu parameter are retired, and a configured columns warns while keeping the single column. Menu descriptions are configuration data only. The link tree stays true-centered at every width: text links from lg, icon links below. Between lg and md the end edge keeps search, version, language, theme, and GitHub with no menu button. Below md appearance stays beside search and the drawer entry; version, language, and GitHub move to the footline dock. Home or explicit Landing pages add one drawer entry beside search that opens the full labelled tree. Shell pages with a sidebar open its drawer in that position instead; no drawer button is shown from md upward. Language links target the page translation or that language’s home, stay relative when languages share a host/base path, and become absolute only for language-specific baseURLs; hreflang stays absolute and lists only actual translations, never the language-home fallback offered by the switcher. Paginated blog indexes use their own canonical URL; later pages omit cross-language alternates because translated archives need not have matching page boundaries. navbar_autohide applies to fine pointers from 768px, never touch or drawer widths, and the hidden bar keeps its slot: the layout reserves the navbar band in both states, a pinned bar occupies exactly that band with its rule inside it, revealing fades the bar in place without covering resting content, and hero pages ignore the policy in favour of their overlay bar. The home page owns the same soft boundary a hero page does: its navbar carries no bottom rule and no scrolled shadow, resolving into a short wash below the bar instead.

Sidebar and pager share root and order. manual_link, build.render: link, dividers, hidden nodes, and placeholders retain their documented semantics. sidebar_icon_policy is all (default), groups, or none; icons are one Font Awesome class pair. Invalid policies follow the shared warning/fallback contract. At sidebar_cache_limit, the two walkers may reuse neutral markup only for the same language, navigation root, and output-affecting effective settings. That markup remains visible without JavaScript; the normal shell runtime adds the active path. A Book page that emits sidebar_headings stays page-specific and bypasses the shared tree cache.

Root candidates are linkable, non-divider top-level sections followed by sidebar_root_for: self sections, deduplicated by URL. Both sources honor explicit sidebar_root_menu: false; absent/true preserves inclusion. The current resolved root is appended even when excluded from global choices. Zero entries emit no control; one emits a static link. Language and deployment prefixes stay on every URL. A divider or a section with build.render: never cannot become a switcher link.

A sidebar_divider leaf retains its static heading. A divider section retains its children in both sidebar walkers, with a non-link label and a real disclosure button when folding is enabled. It never becomes a pager target; its children retain their positions. Pair it with build.render: never to omit the section’s own outputs without hiding its descendants. Breadcrumbs render its label without a link, search omits it, navigation JSON hoists its children, and Print keeps the child documents. Book TOCs retain the group label and child links, but omit headings from the unpublished group body. toc_hide still hides the whole subtree and is not a grouping option. Explicit navigation keys are language- and deployment-independent paths; rendered links retain both prefixes. An explicitly empty navigation sections array warns and falls back to the content tree in every navigation output. Both authorities prune toc_hide subtrees, and navigation JSON preserves manual_link_relref as an internal link to its resolved destination rather than a page identity.

Sidebar runtime

Available since OINK 1.1

OINK 1.1.0 provides this disclosure API and explicit hidden-content isolation. Version 1.0.0 does not provide the API.

window.OinkSidebar owns registered tree disclosures and movable TOC, backlink, and taxonomy groups, independent of their current DOM parent. setExpanded(id, boolean, {source}) returns true for a valid target and false for unknown IDs or non-boolean values. getState(id) returns a fresh {id, expanded} snapshot or null. IDs are the existing aria-controls region IDs; arbitrary elements outside the registered OINK groups cannot be changed.

Sources are user, active-path, responsive, and api (default). Every writer commits aria-expanded, td-is-open, the localized label, and the region’s inert state before one oink:sidebar-disclosure document event with detail: {id, expanded, source}. Repeated state writes emit no event. API restoration keeps current-path ancestors expanded; explicit user disclosure can still collapse them. Closing a region containing focus returns it to the toggle before isolation.

ready is a Promise resolving to the API after initial hydration and responsive placement; isReady and oink:sidebar-ready also expose completion to late consumers. Optional persistence belongs to the site: await ready, read storage inside a try/catch, and restore valid region IDs through the setter. OINK owns whole-column collapse, width, and scroll persistence, not a version/locale schema for reader-selected branches.

Desktop collapse and a closed mobile drawer make panel content inert and mark the panel aria-hidden. Focus leaves before isolation; opening clears it before focus enters. The panel itself remains the 16px pointer sensor, and the external restore control remains active. Hover, Escape, backdrop dismissal, breakpoint cleanup, and scroll unlocking retain their existing behavior. These runtime attributes are not emitted into the no-JavaScript fallback.

Whole-column TOC collapse also isolates its hidden panel. If the collapsing control held focus, focus moves to the visible floating restore button; restoring the column returns focus to its visible column control. When the aside moves into the mobile sidebar, its former column isolation is cleared before the drawer owns interaction. The drawer’s Tab trap includes only rendered, non-inert controls, excluding hidden or collapsed descendants.

Immersive blog presentation

There is no article type or second shell. Immersive reading is four independent keys on the ordinary blog shell, set on a page or section cascade; the section index repeats values it also needs:

featured_image: hero
toc_style: flow
toc_taxonomies: false
sidebar_enabled: false

The blog shell renders no breadcrumb by default—an article reads as a standalone piece—so the recipe needs no key for it. breadcrumb remains an ordinary key a page or cascade may still set either way, on any shell.

hero uses the shared featured image as a decorative full-bleed backdrop on single pages and section indexes. With no image it renders the normal opening; banner and wash remain single-page modes. The navbar overlays a hero on a contrast scrim and scrolls with it.

toc_style is fixed or flow; flow places a wider rail beside the article and pins it only after scrolling. Its resting place aligns with the article’s info line, or its description where a page has no info line. docs-shell.js measures the offset because a title wraps to an unknown number of lines; without JavaScript the rail starts where the article starts. toc_taxonomies: false removes term clouds; a rail with neither TOC nor clouds renders nothing. notoc remains the page-level TOC opt-out. These switches do not change bylines, tags, series, pager order, feeds, or page-end composition, and the rail disappears below the xl breakpoint.

Search, actions, and runtime

params.offline_search opts into a local per-language index. When enabled it also builds under hugo server by default; set offline_search_on_serve: false for large edit loops. HTML search appears on Home, shell pages, and Landing when landing_search is enabled. Other non-shell pages and Print omit the dialog, Lunr, and Palette.

Search metadata is search_keywords, search_boost (default 1), and search_exclude. The index carries URL, title, taxonomies, excerpt, headings, description, body/summary, root, section, type, keywords, boost, breadcrumb, and icon. Fixture budget is 2 MiB raw / 512 KiB gzip. Sites may return extra strings from hooks/search-keywords-extra.html. Keywords affect matching and ranking; CJK keyword-only matches display the page description or excerpt, not the keyword list. Body matches retain their surrounding text as context.

Built-in action IDs are copy_markdown, copy_link, open_chatgpt, open_claude, view_markdown, view_history, edit_page, create_child_page, create_issue, create_project_issue, print_section, print, switch_preset, switch_theme, switch_language, switch_version, and open_github. copy_link is Palette-only outside the share bar. Site commands under languages.<lang>.params.ui.command_palette.commands may open a safe URL or invoke a built-in ID, never inject JavaScript. The legacy clipboard fallback restores the previous focus and selection, including its direction, without taking focus back if another control acquired it during the copy operation.

Edit, history, and create-child actions require a repository-relative source file. Physical filenames and the site working directory use normalized / separators before containment checks. path_base_for_github_subdir matches that normalized path: relative to the working directory for local content, absolute for an external mount. A string regex removes its matches; a {from, to} mapping may replace it. External sources require an explicit match. After mapping and path cleanup, empty paths, ., absolute or drive-qualified paths, and paths starting with .. as a segment suppress these three actions. Docs and project issue actions remain available under their existing repository settings. Windows mappings must match / rather than \; normalization does not change filename case.

The Palette has empty, text-search, and > command modes; quick links derive from navigation. It has no history, semantic search, personalization, or remote fallback. Search queries stay in-browser and no default telemetry is sent.

OinkSurfaceCoordinator arbitrates Palette, drawer, root, language, and version menus. Surfaces own focus restoration and Escape. Keyboard navigation ignores editable controls and modals; Ctrl/Cmd+K also yields to another open dialog, including fixed-position ARIA dialogs. /, \, f, c open search/commands; j/k move headings; q/e move pages; h changes presentation; l/y, t, and r open language, theme, and root choices. Sidebar WASD/Arrow navigation uses real focus without rewriting Tab order. Non-link divider buttons participate in this tree navigation. Left/a from a child first focuses its parent group, then a second press folds it; Right/d opens a closed group or enters its first visible child when already open. Previous/next page navigation still considers links only, never group buttons.

The outline derives cursor and visible-heading range from one heading model and the scroller’s computed scroll-padding-top; its SVG line and dot share the same animated values so they cannot drift. URL fragments are decoded when valid; malformed percent sequences fall back to the literal heading ID, both when indexing links and when selecting a requested heading near the page end. No speculative DOM repair pass is allowed. This tracking is always owned by the normal shell runtime. params.ui.scroll_spy and the page key scroll_spy are quiet compatibility no-ops throughout 1.x, emit no separate runtime, and may be removed only in a future breaking release.

Search-tail extensions

Available since OINK 1.1

This API is included in OINK 1.1.0 and absent from version 1.0.0.

Trusted site JavaScript may call OinkCommandPalette.registerSearchTail({id, rows, activate}); YAML and the action manifest remain data-only. The bundle stays conditional on local search. Registration requires a unique ID matching [A-Za-z0-9][A-Za-z0-9_-]* and two functions; invalid or duplicate registrations throw. The returned unregister function is idempotent and cannot remove a later registration reusing the ID. Live changes schedule one owned render; removal cancels that provider’s pending action.

rows(context) synchronously returns descriptors. Context is a frozen snapshot {query, locale, phase, pageResultCount}: query is trimmed; locale is the HTML language tag; phase is results, empty, or error; count covers only local page results after the limit. Providers run only for settled, non-empty text search, never empty, command, choice, or loading states. Rows follow all native results and actions in the localized Actions group, in registration order. Native empty/error messages and input-triggered index retry remain available.

Each descriptor requires a unique per-provider id with the same ID syntax and a non-empty string title. Optional description, icon, and disabledReason are strings; available is boolean, default true. OINK copies and freezes these fields and renders display strings as text. Invalid descriptors, duplicate IDs, an asynchronous return, or a thrown callback discard that provider for the render without affecting other providers. No callback-count promise is made.

activate(row, context) runs only through ordinary row activation. It receives the copied descriptor and its original context plus an AbortSignal and a handoff() function. Pending activation blocks all other row activations, including entry into native choice menus. Synchronous throws and rejected promises release pending state, keep the Palette open, and announce the localized action-failed message. Fulfillment values are ignored; success closes the Palette without stealing focus from another surface. Closing, reopening, changing the rendered query, or unregistering cancels pending work; late settlement cannot modify a newer session.

Before opening another coordinated surface, call context.handoff(). It closes the Palette without returning focus or aborting that activation. The consumer then owns the new surface’s focus and error UI. A later Palette session or unregistration can still cancel unfinished work; successful completion does not abort a handed-off operation. OINK imposes no timeout.

rows() must remain pure. This is a trusted-code contract, not a sandbox. The default query remains local, with no remote provider or telemetry bundled. Any extension network behavior and provider consent belong to the site.

Share

params.ui.share is empty by default and accepts any ordered subset of 16 targets: x, bluesky, mastodon, facebook, linkedin, reddit, hackernews, telegram, whatsapp, line, pinterest, weibo, chatgpt, claude, email, and copy. A page list replaces its inherited list; share: false opts out. Unknown entries warn and are dropped. Only regular pages render the bar; print, Markdown, and RSS omit it.

Targets are plain intent links carrying the page permalink/title, plus the local copy_link button. Pinterest media comes from the shared featured-image resolver. ChatGPT and Claude receive build-time permalink prompts and are independent of page-menu assistant actions. Discord has no public intent target and is deliberately absent.

The bar loads no platform SDK, iframe, script, stylesheet, counter, or campaign parameter and makes no request until a reader activates a link. It is one accessible labeled glyph row. share/items.html resolves targets and share/bar.html renders them.

Annotation

Page annotation resolves descriptors in annotation-items.html and renders them through page-meta-lastmod.html; either may be overridden narrowly. Lines appear in this order:

Line Condition
Last modified Lastmod is set
Upstream front matter upstream_link is non-empty
Translation configured authoritative language has a translation and this page has authored text

upstream_link is per-page; a cascade counts, and upstream_link: "" opts out. Other upstream facts resolve site params → data/upstreams[upstream_source] → front matter: upstream_name, upstream_copyright, upstream_license, upstream_notice, optional upstream_ref, and upstream_modified. The first four are required with a link. Invalid or incomplete attribution warns and emits no legal notice; unsupported URLs are refused. Publication gates reject the warning with --panicOnWarning.

upstream_modified changes the credit verb and links commit history; it adds no line. The notice page carries full license/warranty text. Translation notice is opt-in through params.ui.translation_notice, cascades as the page key translation_notice, skips generated or bodyless pages, and can be disabled on a natively authored page with translation_notice: false.

Authors and series

A blog article head is title, info line, term badges, byline, then the series strip; the description leads the body below them. The info line (article-info.html) always carries the date; with reading_time on it adds the word count and the minutes. Front matter upstream_link—the same per-page fact the annotation attributes—adds a localized link to the original, gated by the shared URL policy. Term rows are bare badge runs whose taxonomy name lives on the group label, not as a visible prefix. At rest a term badge is a pale neutral chip with muted ink, led by the taxonomy’s term glyph; a linked badge picks up the current section’s accent wash, border, and ink on hover or focus. taxonomy-icon.html owns the vocabulary—each taxonomy pairs a whole-taxonomy glyph with a term glyph (folder-open/folder, tags/tag, cubes/cube, users/user-pen, book-bookmark/book for series, generic shapes)—and params.ui.taxonomy_icons overrides a pair with one string for both surfaces or a taxonomy/term map; unusable input warns and keeps the built-in. The right-rail cloud wears the whole-taxonomy glyph on its head alone: cloud chips stay text plus count, because repeating the glyph beside an announced taxonomy is noise. A standalone taxonomy directory card carries one term glyph; the byline carries the people alone—portrait, name, and the profile’s one-line bio—with no label and no date. List rows, cards, and term archives share one metadata line of the same shape: date, one localized author-and-section phrase, then word count and minutes behind the same reading_time switch. Under that sentence sits one wrapping badge line with every taxonomy’s terms, taxonomies in alphabetical order, each badge wearing its term glyph; cards leave out authors, whom their sentence already names.

Authors activate only through taxonomies: {author: authors}. The profile term page owns display name, summary, body, and featured-image avatar; an absent profile falls back to link title, initial, and archive. authors-resolve.html preserves front-matter order for article heads, list rows, and one RSS dc:creator per author. Legacy author remains unchanged when authors is absent; when both exist, authors wins without warning. Custom author taxonomy plurals behave as ordinary taxonomies.

Series activate only through taxonomies: {series: series}. Term pages own the introduction; no parameter, data file, cover model, or runtime is added. A page uses series: [name] and optional series_weight. series-pages.html orders weighted members first by weight, then unweighted members by ascending date, with Path tie-breaks; strip and term page share it. The first named series gets one HTML/print strip. The panel is translucent over a blur rather than an opaque card, because a hero article paints its featured image behind this band and an opaque ground would punch a hole through the picture; on a plain article the tint resolves to the page’s own ground, so one treatment serves both. Its summary owns the full bar and trailing caret, while the series name – its taxonomy icon included – remains a sibling link laid over a hidden width reservation so the summary never contains a nested interactive control. Opening the bar rules a hairline under it and places the reading order in one adaptive grid on the same surface, preserving DOM order. Every member link owns its ordinal, set at the end of a fixed square track so the titles hold one edge at any list length; equal cells stay one column when narrow and add columns only while each title retains a readable measure, so a desktop panel uses its width without stretching one selected row across it. Hover and the reader’s own place borrow the two grounds sidebar navigation already uses for those states, and the current member adds a filled ordinal and a heavier title, so the cue is never colour alone. Print shows the same list expanded in one column. Singleton series and non-HTML outputs omit it. Numbering, cross-references, and aggregate output remain Book concerns.

The default article taxonomy chips omit reserved authors and series because their dedicated surfaces already carry them. Explicit params.taxonomy.page_header restores either.

Blog indexes and page composition

Blog section indexes use params.ui.blog_index: list (default) and cards are one flat run, newest first, sharing blog_index_size pagination—the metadata line’s dates make year headings redundant; table shows the whole section as date/title/tag rows without pagination. Cards use the shared lead image, localized date/author/section metadata, tags, and a three-line summary.

A taxonomy page (/tags/, /authors/) and its term pages share one head, shell/taxonomy-head.html. The taxonomy page opens with the whole-taxonomy glyph in a tinted tile, the localized name, and a count of terms. A term page opens with the term’s title and its page count from ui_taxonomy_pages, using the current locale’s CLDR plural form; where no breadcrumb is rendered, a kicker above the title names the taxonomy and links back to it, since an enabled trail already does both one line higher: a crumb standing for a generated taxonomy page borrows the same localized label the head renders, not Hugo’s plural title. Under the head the taxonomy page lays its terms out as a grid of one-line cards, shell/taxonomy-cards.html, most-used first with alphabetical ties—the order the rail cloud already uses—filling equal columns by auto-fill so a short taxonomy never stretches two cards across the page. A card is the term glyph, the term, and its page count, and the whole card is the link; authors alone lead with the byline’s small portrait through the same avatar partial. No card carries a description or a newest page: a term has nothing to say that its title and count do not, and the extra line only blurred the grid. There is no filter chip row and no “All” chip: the section root already lives in the sidebar and the navbar. Term pages stay row lists, and author profiles keep their own head.

The rail on a taxonomy or term page leads with shell/taxonomy-switcher.html: one row per declared taxonomy—whole-taxonomy glyph, localized name, term count—linking to its index page, the current taxonomy on the selected ground. It is the way from one taxonomy’s pages to another’s, because cloud chips jump to terms and cloud heads only collapse; a site with one taxonomy renders no switcher. The group sits behind the same toc_taxonomies switch as the clouds. A taxonomy page scopes its clouds to the whole site (taxonomy-root.html returns no root for that kind) and omits its own cloud, whose terms are the cards beside it; term pages keep the section scope and the full set.

params.ui.blog_index_toggle renders all three forms for the current paginator slice and lets readers cycle them. The configured form controls first paint and hidden forms load no images. A reader’s stored choice is scoped to indexes that publish all three forms: a section whose toggle is off publishes one form and always shows it. A front-matter value or cascade overrides the site mode per section. A table published without the toggle remains a complete, unpaginated archive.

params.logo is always the brand mark; params.wordmark, or the site title, is the text half hidden at compact widths. Docs, Book, Blog, and Swagger share one shell model. Page-end order is Share, Feedback, Annotation, Pager, Comments. Docs/Book pager follows sidebar preorder; Blog uses weight then reverse date; pager: false opts out. Static outputs omit pager UI.

Every rendered footer style keeps an icon-only utility dock at the end of its bottom line: version, language, theme, then keyboard help. Its menus open upward; the version trigger never exposes the current branch or release label. The fat footer’s collapse chevron follows the dock. Below lg the bottom line gives up its copyright/center/dock columns and stacks them as three centered full-width rows, the dock last. These global controls do not render in the sidebar footer, and footer_style: none removes the whole bottom line.

There is no archive shell, arbitrary-depth flyout, second navigation authority, query upload, or browser compatibility shim for removed config. Feedback emits only docs_feedback through an existing gtag, stores the choice locally, and does not replace Giscus.

Verification

bin/check-navigation-contract.py, bin/check-shell.py, JS tests, output goldens, and the consumer browser suite cover navigation, language/subpath links, blog variants, page-end order, keyboard behavior, accessibility, and responsive layout.

Appearance control

The navbar and footer dock share one click/keyboard disclosure. The Landing mobile drawer also provides a labeled Appearance row. The panel offers native Style radios when preset_menu allows a choice and native Light/Dark/System radios when dark_mode.show_menu is enabled. Selection is immediate and keeps the panel open. Enter, Space, or ArrowDown opens it; arrow keys select within a group; Tab moves between groups; Escape closes and returns focus. Sun/moon trigger icons show the resolved current state: sun for light and moon for dark, including changes while following the system.

The English group labels are Style and Light. Style options are independent buttons in a two-column grid, with a colored page, layers, pen-nib or terminal icon and the preset name. There are no Aa previews or experiment badges. The site default is identified in its tooltip and accessible name; selecting it clears the saved preset. A tinted background and border show selection, and keyboard focus has a separate outline.

Desktop uses a non-modal dialog anchored to the trigger; outside press or focus leaving closes it. Below 768 px and inside the Landing drawer, showModal() opens a bottom sheet in the browser top layer with a close button and 44 px option targets. Closing the sheet preserves the underlying drawer. The surface coordinator closes unrelated popovers before opening. The t shortcut still toggles light/dark through switch_theme; switch_preset is the separate command-palette choice.

The local Ink/Terminal experiments require explicit configuration; preset_menu: true continues to offer Paper/Slate plus the site default. Both reuse the same state, keyboard, command-palette and bottom-sheet mechanisms. Terminal compacts desktop navigation rows, while prose and mobile touch targets retain their sizes.

4 - Landing contract

The maintainer contract for landing data, the built-in section registry, language resolution, runtime, accessibility, and outputs.
OINK 1.2.0 contract

This contract describes the v1.2.0 release. Its canonical bilingual sources are in content/docs/design/.

Shared rules live in the architecture and component contracts; migration belongs in the migration contract.

Shell and data

Any regular page may declare layout: landing. It renders navbar, full-width canvas, and footer without docs sidebars or TOC rails. The homepage keeps data/home/<lang>.yaml as a compatible authoring path through the same renderer.

A non-home page resolves sections from inline front matter, data/landing/<key>/<lang>.yaml, an exact-language entry in one data/landing/<key>.yaml, then English or unsuffixed local data. Landing never fetches mutable facts; stars, prices, screenshots, and avatars are committed or generated before Hugo runs.

params.ui.landing_search defaults to true and enables the existing local Palette only when offline_search is enabled. params.ui.github_stars and params.ui.alt_site are optional local chrome facts.

Section registry

The registry has exactly 22 built-ins:

  • hero, metrics, capabilities, principles, cards, logo-wall, gallery, testimonials, contributors, faq, markdown, cta;
  • pricing, pricing-compare, command-box, steps, timeline, code-plate, preview, case-study, download, bar-chart.

An entry is a type string or a map with type, key, id, enabled, inline data, or a deliberate local partial. Authors provide unique IDs; OINK normalizes them to anchor-safe values. Unknown types follow the shared warn-and-safe-fallback policy; they never vanish silently, and --panicOnWarning rejects them at publication. landing/ partials own built-ins; removed home/ partial names are not an API.

preview places Markdown source beside RenderString output through the site’s hooks, so its content registers the same runtimes as docs content. The source pane uses Chroma and a file name, default page.md. Markdown output uses a four-backtick markdown fence; RSS omits it. Pane labels are theme i18n.

hero.align is start or center. Center is text-only; combining it with an image warns and falls back to start, preserving the image. download consumes the same data/download/<key>.yaml schema as the shortcode and introduces no second channel, version, publication, or interpolation model.

Language, runtime, and accessibility

Narrative files may be language-specific. Shared fact fields resolve <field>_<exact language> with - normalized to _, then <field>_<primary language>, then the unsuffixed field. camelCase aliases are not accepted. Narrative fields render inline or block Markdown through the site’s hooks; values reused as accessible names are plainified. Section copy is site data; only theme controls use OINK i18n.

Interactive HTML sets hasLanding, which conditionally adds only landing.js. The runtime reuses OinkSurfaceCoordinator and owns reveal, count-up, copy, compact-menu, and theme-image enhancement. Server output remains complete without JavaScript or when the Landing script fails to load. Reveal candidates are visible by default; only an installed observer may mark one pending its entrance animation. Metrics render their configured number formatting, prefix, and suffix on the server, and the count-up’s final frame uses that same display.

Marquee duplication is CSS-only; the duplicate is aria-hidden and inert, and a localized checkbox persists pause without JS. Reduced motion disables motion, forced colors preserves controls, and theme images follow the shared theme event. The navbar mega panel and its columns parameter are retired: a menu that still sets columns warns and keeps the single column. The compact menu uses real links/buttons, traps no focus, and does not duplicate the desktop tree.

Outputs and compatibility

Output Contract
HTML Full static sections plus progressive enhancement
Print Static grids and content; controls removed
Markdown Headings, prose, lists, tables, and code without theme classes
RSS Landing sections omitted

Non-HTML output sets no Landing flag or runtime. Root-relative links and assets honor deployment subpaths; normal builds download no images.

Removed 0.4 component forms belong to the migration toolkit, not parallel Landing implementations. OINK adds no pricing-period toggle, remote-fact API, hotspot editor, visual builder, or second registry. Existing homepage data and explicit custom section partials remain valid.

Visual presets

Paper removes the hero grid and glow, uses warm shadows, Plex Sans display headings, and a link-colored primary action. Slate retains its technical grid, glow, Chakra Petch headings, and original primary-action colors. Shared section geometry and density remain unchanged. The mobile drawer includes the shared Appearance sheet; see the shell contract.

The explicit Ink/Terminal experiments also remove the grid, glow and shadows. Ink uses heavy Inter headings, square cards and a red primary action. Terminal uses mono headings, 2 px corners and an amber primary action with a static cursor-shaped decoration. Neither adds animation or changes section columns.

5 - OINK migration boundary

Supported source, configuration, and validation boundaries for OINK migration, including the 1.2.0 changes.
OINK 1.2.0 contract

This contract describes the v1.2.0 release. Its canonical bilingual sources are in content/docs/design/.

This is source and configuration guidance, not a release ledger. Local source, commit, tag, push, consumer pin, deployment, and production parity remain separate states. For the reader-facing upgrade procedure, see Upgrade.

Toolkit scope

bin/migrations/oink06.py only scans and automatically rewrites Markdown files under a site’s content directory, including supported YAML front matter. It does not rewrite Hugo configuration, data files, layouts, assets, modules, or generated output. TOML/JSON front matter and ambiguous Markdown are reported with positions for manual review.

Dry-run is the default and a completed migration is idempotent:

python3 bin/migrations/oink06.py report --sites <dir>... --md report.md --json report.json
python3 bin/migrations/oink06.py migrate --site <dir>
python3 bin/migrations/oink06.py migrate --site <dir> --write
python3 bin/migrations/oink06.py check --site <dir>

Code-fence contents are not rewritten, including fences that begin on the same line as an ordered or unordered list marker, with optional blockquotes. An extra literal quote prefix inside a code example does not close that fence. book_figures.py retains narrow TPME, DDIA v1/v2, and pg-internal profiles; it is not a generic parser.

The isolated validation tools bin/measure-baseline.py and bin/sites/build-all.py reject snapshot destinations that overlap any input site, the running tools checkout, the selected theme checkout, or another snapshot before deleting existing output. This includes --keep destinations reached through symlink aliases and an alternate --theme checkout.

Updating consumer repositories

After publishing a theme release, inventory maintained consumer checkouts and upgrade their exact pins. The theme’s bin/update-consumers.py scans immediate project directories under the supplied roots; it does not recurse into archives, generated sites, caches, or theme fixtures.

The tool ships with OINK 1.2.0. Run it from the theme checkout to inventory consumers and upgrade them to the published tag.

python3 bin/update-consumers.py v1.2.0 --roots ~/www ~/pgsty
python3 bin/update-consumers.py v1.2.0 --roots ~/www ~/pgsty --write --check

The first command only reports adoption. The second updates go.mod and OINK’s go.sum entries, verifies the exact module graph, and builds each selected site with warnings fatal. It disables GOWORK, Hugo’s module workspace, and environment module replacements. Logs and original module files go to a temporary report directory, or an explicit --report-dir. The tool restores module files if the update fails; a build failure leaves the new pin available for diagnosis and returns a failing status. An unreadable scan root or malformed consumer module is recorded as a failed entry; the inventory continues through the remaining sites and exits nonzero. An explicitly selected missing or non-consumer directory also fails visibly.

Linked worktrees, hidden copies, and non-default branches are skipped. Review all skipped and blocked entries: --sites <path>... explicitly selects a reviewed checkout, including one with existing module edits. OINK replacements in go.mod require manual resolution. Vendored themes require a separate review before --refresh-vendor, which backs up and regenerates _vendor/; changing a module pin alone does not update a vendored theme.

Preserve unrelated work, update current theme-version references in site READMEs and configuration, and run each site’s owning checks and visual review. The tool does not edit content, commit, push, or deploy. Record those completion states separately, including consumers already on the target tag.

0.4 content to current forms

Removed form Current form Toolkit key
alert, details, pageinfo, raw disclosure > [!TYPE] callout callout
tabpane, legacy tab, code-group, code-tab adjacent {tab=} blocks or tabs / tab tabs
FileTree shortcodes or {.filetree} list filetree fence filetree
Gallery shortcodes or {.gallery} list gallery fence gallery
ECharts / infographic shortcode same-named data fence datafence
Docsy card families .cards list or cards / card cards
imgproc, image Markdown image + attributes image
readfile include include
fence filename= title= fencetitle
badge outline= remove outline badge
leaf example, book-figures kind= eg, explicit book-* index eg
percent-delimited fields angle-delimited fields / field fieldsdelim
Docsy _param placeholders and card header= highlights Font Awesome / badge / param or callout param_placeholders
unsupported legacy shortcodes manual review with source position reportonly

Configuration and front matter

The following configuration changes are manual; the toolkit may report matching front-matter keys but never edits site configuration.

Old Current
offlineSearch* offline_search*
disable_click2copy_chroma ui.code_copy (inverted)
content_width `reading_width: slim
github_url github_repo
ui.no_left_sidebar ui.sidebar_enabled (inverted)
breadcrumb aliases ui.breadcrumb
ui.scrollSpy No behavioral replacement; ui.scroll_spy remains a quiet 1.x compatibility no-op
ui.showLightDarkModeMenu ui.dark_mode.show_menu
ui.readingtime ui.reading_time
ui.ul_show ui.sidebar_expand_levels
ui.docs_root ui.docs_sidebar_root
ui.pager ui.pager_types
{ enable: bool } annotation/zoom/keyboard/reading maps bare booleans
ui.typography.preset ui.typography
print.disable_toc print.toc (inverted)

Prism, rss_sections, and algolia_docsearch are removed. Chroma is the only highlighter; Algolia configuration is search.algolia. Page overrides drop the ui. prefix. Legacy hide_feedback, hide_readingtime, exclude_search, content_width, camelCase manual links, and nested front-matter ui maps are reported with replacements.

0.5 to 0.6

  • Replace upstream_attribution with upstream_link plus upstream_name, upstream_copyright, upstream_license, and upstream_notice; rename downstream_modified to upstream_modified.
  • Replace the release map with one GitHub release_url; remove release_products and release_group_by_product from release indexes.
  • Blog and default dates now default to ISO 2006-01-02; retain explicit time_format_blog or time_format_default for prose dates.

Removed names warn and take the documented safe fallback or render nothing; ordinary previews continue, while --panicOnWarning rejects them at a strict gate. blog_index_toggle, featured_image: hero, toc_style, and toc_taxonomies are additive opt-ins. They introduce no content type; immersive reading stays on the ordinary blog shell.

Prerequisites and validation

Enable Goldmark unsafe rendering, block attributes, and standalone block images as shown in the component contract. Enable passthrough explicitly for \(...\), \[...\], or $$...$$; Hugo does not merge theme markup config.

Run the smallest source and output checks for the changed contract with the pinned Hugo Extended 0.165.0 toolchain, JS tests when runtime changes, and strict root and subpath builds. For maintained sites, inspect representative EN/ZH Docs and Blog routes at desktop and narrow widths, then record pin, deployment, and hosted parity separately.

6 - Design decisions

Accepted choices that explain why OINK’s public contracts and implementation have their present shape.
Accepted rationale

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.

6.1 - Warnings and safe fallbacks

Invalid author input warns and degrades safely during preview; –panicOnWarning restores a hard publication gate.
Decision

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:

  1. Name the invalid key and value, the allowed shape, and the fallback.
  2. Include a page position when the value came from page front matter; avoid repeating one site-wide warning for every page.
  3. Never pass an invalid value into a later operation. Validate first, then render from the normalized value.
  4. 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 errorf and 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.

6.2 - Configuration model

OINK extends Hugo and Docsy-compatible configuration without creating a second namespace or a parallel global resolver.
Decision

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:

  1. Site facts remain at the established top level. Interface choices belong under params.ui.*.
  2. A page override drops the ui. prefix and otherwise keeps the same name. A section cascade can apply that top-level key to its descendants.
  3. 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.
  4. Names are positive, snake_case, and grouped by function. Closely related settings share a prefix instead of growing another nested resolver.
  5. 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.
  6. 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.

6.3 - Markdown-first authoring

Native Markdown carries common semantics; shortcodes fill real capability gaps, and content models extend shared shells instead of forking them.
Decision

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:

  1. 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.
  2. 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.
  3. One semantic implementation. Native and full forms normalize into the same partials and output contract. They are not two components that merely look alike.
  4. 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.
  5. 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
Print 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.

6.4 - Generated configuration schema

The editor schemas are projected from the existing configuration authorities; a CI drift gate keeps them from ever becoming a third one.
Decision

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.yaml reader 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.

6.5 - Optional CLI and result contract

The independent Go executable boundary, versioned diagnostics, isolated validation, and guarded maintenance plans for the current local CLI candidate.
Current scope; local candidate

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

R6 supported local scope accepted

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:

schema_version: oink.workspace/v1
sites:
  - name: docs
    directory: ../docs-site
  - name: blog
    directory: ../blog-site

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.

Current integration gate is incomplete

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.6 - Paper and Slate visual presets

Accepted phase-one visual identity and appearance controls, with separate reader style and mode state.
OINK 1.2.0

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.

7 - Design research

Dated experiments and consumer evidence used to make OINK design decisions, without normative force.
Evidence, not a contract

Research records what was measured, with which inputs and tool versions. Results may explain a decision, but they do not override the current contracts or implementation.

Research belongs in the public Design tree when another maintainer can inspect its method, understand its limits, and repeat the relevant check. Raw agent transcripts, temporary build logs, and local absolute paths do not meet that standard.

Research map

Record Evidence
Goldmark block attributes Render-hook visibility and CommonMark container limits on the supported Hugo floor
Consumer and migration evidence A dated corpus survey plus deterministic Book migration results
Comprehensive review, 2026-08-26 Implementation, configuration, output, security, test, performance, and doc audit
Community issue and PR review, 2026-09-19 Reproductions, PR acceptance advice, and remedies for sidebar, focus, and search feedback
OINK 1.1 release review, 2026-09-20 Five runtime repairs, documentation readiness, validation evidence and publication boundaries
CLI acceptance snapshot, 2026-09-29 Executed Starter, real-site, offline, upgrade, and reproducible-archive checks; final local acceptance and public release remain separate
Visual preset acceptance, 2026-10-05 Paper/Slate local implementation, actual output and bounded browser evidence
Ink and Terminal experiment, 2026-10-05 Explicit experimental presets, design tradeoffs and real-site verification
OINK 1.2 pre-release review, 2026-10-05 Final local candidate checks, cleanup, local resources, compatibility and publication boundaries

Publication rules

A research record states its date, inputs, relevant versions, method, result, and known limits. Volatile counts are labeled as snapshots. External framework comparisons are refreshed from primary sources before publication and distilled into OINK-relevant conclusions rather than copied as a competitor catalogue.

When a result becomes a stable product choice, link it from an accepted decision. When it proposes behaviour that does not exist, move the design question to Proposals.

7.1 - Goldmark block-attribute evidence

Reproducible findings for lists, images, tables, passthrough blocks, fences, callouts, and nested containers on Hugo 0.160.1 and 0.164.0.
Verified snapshot

These probes produced byte-identical relevant output on Hugo Extended 0.160.1 and 0.164.0. They explain OINK’s native component forms; the current component contract remains authoritative.

Method

The probe used a minimal Hugo site without OINK templates. Render hooks printed their context fields and .Attributes as visible markers. The site enabled Goldmark block attributes, passthrough delimiters for inline and block math, unsafe rendering for the deliberately inspected raw HTML, and wrapStandAloneImageWithinParagraph: false.

Each source shape was rendered with the compatibility-floor Hugo and the then current Hugo version. Relevant output was compared byte for byte. The findings below record platform behaviour, not visual styling.

Findings

Source shape Hook result Design consequence
Ordered list with paragraphs, fences, callouts, nested lists, and {.steps} The class attaches to the outer <ol> and rich list-item blocks survive A Markdown list is the native Steps form
Heading inside a list item The heading remains inside <li> and enters .TableOfContents Native Steps can carry navigable headings
Nested list with {.filetree} The class attaches to the outer <ul> FileTree needs no wrapper merely to preserve hierarchy
Standalone image plus {#id num= caption= .class} render-image receives IsBlock=true and all attributes A Book figure can have a native image form
Inline image inside a paragraph IsBlock=false; the image receives no block attributes Inline images cannot use the block-figure contract
Block math plus {#id num=} render-passthrough receives block type and attributes A numbered equation can use the native passthrough form
Table plus {.fields #id num= caption=} render-table receives the class and named attributes Field tables, matrix markers, captions, and Book numbering can share one hook
Fenced code plus {#id num= caption=} The code-block hook receives the attributes A numbered example can be the fence itself
Callout plus {icon= tab=} The blockquote hook receives callout metadata and attributes Folding, inline title markup, icon, and tab metadata can coexist
Attribute line separated from its block by a blank line The attribute silently disappears Source checks must reject orphan attribute lines
Adjacent tables with tab= Each table hook receives its own tab label Adjacent-block tabs can extend beyond code fences

Container boundary

Hugo’s % shortcode delimiter renders .Inner as Markdown, but its template must put a blank line before and after that inner Markdown. Without both blank lines, a following list may be treated as literal HTML-block content instead of Markdown.

A multi-line % container inside a CommonMark list item has a harder limit: the generated HTML is not indented as list content, so the list closes before the container and restarts afterwards. This is why OINK keeps a full Steps form for steps that must contain another full container. Ordinary rich blocks, fences, and < shortcodes do not have that limitation.

Nested % shortcodes also receive already rendered inner HTML in the relevant collector shape. A collector that requires the child’s original Markdown uses < delimiters and renders the captured body through the shared scoped block renderer.

Attribute ownership

An available attribute is not automatically a public attribute. Every hook owns a documented allowlist. style and inline on* handlers are rejected; URL-bearing values pass the shared URL policy. A site class is retained only on the surfaces where downstream CSS is an established extension mechanism.

The experiment also showed that gallery images inside list items can be block images while still receiving no knowledge of their parent list’s marker. A runtime may therefore need either a theme-emitted marker or a narrow structural fallback; it cannot assume the image hook sees arbitrary ancestors.

Limits and verification

These results cover Hugo 0.160.1 and 0.164.0 with the stated Goldmark settings. They do not promise identical behaviour for a site that changes those settings or for a later Hugo release. A Hugo-floor change reruns the focused component, Book, table, gallery, and Markdown-output checks before this snapshot is updated.

7.2 - Ink and Terminal experiment, 2026-10-05

Explicit experimental presets in real theme output, their design tradeoffs, checks and remaining work.
Local experiment, not a release

Ink and Terminal now compile into the actual theme stylesheet and use the existing Appearance control. These are not injected screenshot styles. They remain explicitly enabled experiments, pending design acceptance.

Inputs and method

This extends the Paper/Slate implementation on the October 5 working trees. Tools: Hugo Extended 0.166.0, Go 1.27.1, Node 26.9.0 and Playwright 1.62.1 on macOS ARM64. The documentation site’s local sibling-theme build is the integration surface; its published pin remains v1.1.0. This is not compatibility-floor, pinned-CI-toolchain or hosted acceptance.

The same Home, configuration, callout and tab content is compared across four presets, EN/ZH, 390/1440 px and light/dark mode. Checks inspect real rendered fonts, overflow, appearance controls and local font requests. Further component checks cover code, parameter fields, Blog, Book, API, Mermaid, ECharts, search and print. API vendor DOM is excluded from axe under the site’s existing policy.

Design choices

Choice Improvement Cost / limit
Ink: black/white canvas, Inter, red markers, underlined prose links, strong heading rules Clear hierarchy and link affordance with little decoration Heavier headings and repeated rules need long-page editorial review
Terminal: mono controls/headings, sans prose/tables, teal links and amber emphasis A recognizable technical interface while retaining paragraph readability Long Latin navigation labels wrap sooner; CJK uses platform fallback faces
Square Ink geometry, 2 px Terminal geometry, no component shadows Visibly different surfaces using the same content and layout Scoped component rules add CSS; this is not a global spacing/radius API
Compact Terminal desktop navigation only More useful navigation rows without shrinking article text Density is a preset decision, not a new reader preference
Existing local fonts and state handling No new font files, external font service, framework or persistence mechanism All preset CSS remains in one stylesheet
Explicit experimental menu entries Reviewers can switch immediately without changing ordinary menu choices Four cards make the enabled menu taller

Ink uses #ffffff / #0b0b0b canvases, #141414 / #ededed text and #c8102e / #ff5c4d accent. Terminal uses #f4f5f2 / #0c0f0e canvases, #1d211f / #d3dbd6 text, #0a6560 / #4cc9bd links and #935400 / #f0a73a accent. Site/section accent overrides still win. Terminal’s heading markers use empty accessible alternatives; unsupported engines omit them. Its hero cursor is a static shape, with no typing, blinking, scanlines or glow.

Try it

params:
  ui:
    preset: paper
    preset_menu: [paper, slate, ink, terminal]
    dark_mode: true

The local docs site enables this list. Select Ink or Terminal in Appearance, then choose light, dark or system independently. Following the October 5 menu revision, all four options use icon-and-name buttons without experiment badges. A site may set either as preset without enabling reader choice. preset_menu: true remains Paper/Slate plus the site default; it does not include every experiment. Selecting the site’s default preset clears the saved preset. Font overrides, system typography and pre-CSS initialization use the same contracts as Paper/Slate.

Verification

Executed check Result and scope
check-presets.py AA text/link/accent contrast on three surfaces, light/dark token parity, advisory canvas luminance, frozen Slate v1.1.0 palette; seven strict builds and 28 document roots
Theme checks Parameters, font roles, 32 catalogs with 205 keys, generated schemas, component/output contracts, runtime isolation and namespace passed; 52 existing output goldens unchanged
Runtime tests 49 Node tests passed
make check 57 non-browser tests passed; EN/ZH coverage 142/142, Markdown, rendered content and internal links checked
Standard browser suites Eight suites passed 170 tests; the appearance suite passed all 49 after fixing the default-Terminal build issue below. The nine suites total 219 checks; this records the initial run plus the focused rerun, not one uninterrupted successful make browser invocation
Appearance coverage Four presets × EN/ZH × 390/1440 px × light/dark on Home, configuration, callouts and tabs; local font requests and menu axe checks; state, keyboard, print and Giscus asset checks; four experiment/mode checks over 11 page types plus search, and shared Mermaid contrast
Font/configuration builds Actual docs site rebuilt with Terminal as default: system fonts with/without explicit overrides, plus explicit technical-font overrides; all three passed
Browser engines Six checks passed on Chromium, Firefox and WebKit at 390/1440 px, including pre-CSS state, keyboard selection through Ink/Terminal, persistence and focus return; desktop Chromium also used 4× CPU throttling
Visual review 96 actual-output viewport captures; representative Home, Docs and mobile menu images inspected. The local comparison gallery selects content, language, size and mode without injecting styles

The standard sitemap axe pass used 15 routes: EN/ZH Home, configuration, callouts, tabs and OpenAPI; English search, Mermaid, ECharts, Blog and /book/04-design/. The existing responsive axe matrix also ran. This was not an exhaustive sitemap scan. Standard browser checks used the established 4173 fixture server; the engine gate used the identified sibling-theme dev server at port 1313. No external font request was observed in the appearance matrix.

The first default-Terminal font build found an omitted entry in the advisory canvas-luminance map, causing false accent-contrast warnings. Both experimental canvases now participate, with checker assertions and authored-accent fixtures. Only the affected appearance suite was rerun after this correction. The added research index entry and updated proposal description were reviewed before refreshing the corresponding two changes in the LLMS golden.

The experiment exposed two new styling faults: the global underline suppression hid Ink’s links, and Terminal’s selected search row retained dim summary text. Both received scoped fixes. A separate inherited Mermaid dark-label pair (#cccccc on #585858, 4.43:1) reproduced in Paper and Slate. The shared mode-only default label background is now #404040; authored Mermaid values retain priority. This does not introduce preset-specific chart palettes.

Remaining work

Before stable promotion, review the look on real Windows and Android devices, including CJK fallback faces, underlines, mono heading wraps and long parameter tables. Manual screen-reader speech and first-paint filmstrips remain unverified. CSS initialization-order checks are not a guarantee about every painted frame.

Mermaid/ECharts keep mode-only palettes, API widgets keep vendor styling, and Giscus coverage checks generated palette assets rather than the remote iframe. The experiment does not introduce a complete geometry/density token framework. The decision to promote Ink/Terminal or redesign chart palettes remains open. No commit, push, release, consumer upgrade or deployment is part of this record.

7.3 - OINK 1.2 pre-release review, 2026-10-05

Local 1.2.0 candidate review, cleanup, compatibility, resource provenance and publication checks, with release boundaries.
Local candidate evidence

This review covers the October 5 working trees, including uncommitted changes. It does not certify an immutable release commit or a published 1.2.0 module. The public theme tag and the documentation site’s consumer pin remain v1.1.0.

Scope and inputs

The theme starts at a1979a4 and the documentation site at ed2d0e3. The reviewed working trees also contain the CJK keyword-summary, literal-percent outline and repository-source-path fixes, the site’s existing English editorial changes, and the cleanup below. The separate optional CLI is not part of this theme release. No tag, push, consumer upgrade or deployment was performed.

Most checks used Hugo Extended 0.166.0, Go 1.27.1, Node 26.9.0 and Playwright 1.62.1 on macOS ARM64. Official, checksum-verified Hugo Extended 0.160.1 and 0.165.0 binaries were used for selected compatibility checks. These are local results, not a replay of the full Linux CI toolchain.

Review findings and cleanup

Finding Correction Impact
The 1.2 release draft omitted the new default and appearance controls Update both release drafts and upgrade guidance with Paper, the Slate compatibility setting, independent persistence, current-state icons and experimental preset selection Readers can identify the visible upgrade change before adoption
Current proposal, decision, experiment and source comments still described preview letters, experimental badges, a separate Default card or an undecided release target Align current descriptions with compact icon/name buttons, site-default reset and 1.2 release preparation; preserve dated test evidence Guidance agrees with the accepted interface without rewriting historical results
A working-tree ignore rule hid all site tests/ except two files Remove that broad rule; retain existing generated-output exclusions New regression tests remain visible to Git; no test or build output was deleted
Development previews do not expose production-only analytics Inspect strict production output and distinguish core local resources from explicitly configured services The local-first claim has an observable boundary

These cleanup findings required no runtime change. Earlier working-tree runtime fixes are covered by their owning checks and the final integration run.

Executed verification

The local candidate passed the following technical pre-release checks. No release-blocking theme defect was found within this scope. Counts are dated snapshots of this working-tree review.

Check Result and scope
Preset checker Passed: seven warning-strict configuration builds, 28 document roots, light/dark token parity, AA text/link/accent checks on three surfaces and the frozen Slate v1.1.0 base palette
Runtime tests 49 Node tests passed, including current-state icons, search summaries, outline tracking, clipboard and dialog focus
Theme regression and tooling checks 40 checker/tool commands passed, including 90 migration tests, snapshot/consumer safety and current output goldens; the PDF browser case was then rerun with an explicit browser, with all three isolation tests passing
Documentation checks Final make check: 57 tests passed; 143/143 bilingual files, 1,197 source headings, 228 rendered content pages and internal links checked; only the new research index entry and changed release title/description required reviewed Markdown-golden updates
Standard browser suites One complete make browser run passed all 219 checks across nine suites, including the full 372-route sitemap axe scan; no failed, flaky or skipped cases
Documentation follow-up A fresh build passed a separate axe scan of 14 updated EN/ZH routes, including this new report pair; this supplements the original full sitemap scan
Browser engines Six appearance checks passed on Chromium, Firefox and WebKit at 390/1440 px; pre-CSS restoration, keyboard selection, persistence and focus return; desktop Chromium also used 4× CPU throttling
Visual spot checks Current-output captures inspected for the Chinese mobile Paper menu, English desktop dark Paper menu and Chinese Terminal reading on mobile/desktop; two-column icon/name options, current-state icons and reading layout confirmed
Hugo compatibility floor 0.160.1 passed preset and reading/math checkers plus a strict minified production build of the actual documentation site
CI Hugo version 0.165.0 passed a strict minified production build of the actual site, Hugo Module/include/static/print checks, system typography, legacy Sass font overrides and expected rejection of invalid typography
Production resources 28 page visits across four presets and seven routes; core fonts and scripts served from the site’s origin; configured external services recorded separately
Production output security Passed on 921 files in the final strict production build with the documented third-party integration policy
Book publication Root and subpath EPUBs passed the theme checker and EPUBCheck 5.3.0 with zero errors/warnings; both PDFs passed the 23-page, five-chapter structure checks; the root PDF script-isolation probe passed
Published consumer pin Existing v1.1.0 resolved and passed the site’s release-pin check with environment replacements and both workspaces disabled; this is not validation of a published v1.2.0

The full sitemap scan follows the site’s existing axe policy: OINK-maintained surfaces are checked, Giscus requests are blocked, and Swagger UI/Redoc vendor DOM is excluded. It does not establish accessibility of those widgets. Responsive checks cover 360, 768, 820, 1024, 1200 and 1440 px in English/Chinese and light/dark mode.

Appearance checks use the same Home, configuration, callout and tab content in four presets, both languages, 390/1440 px and light/dark mode. They also cover search, code, tables, input/focus states, Blog, Book, API, diagrams and print. Ink and Terminal remain explicitly selected experiments; passing these checks does not promote them to stable presets.

Publication used local Pandoc 3.11, Java 26 and Chrome headless-shell 151.0.7922.34. The first attempt with the full Chrome for Testing application timed out on this Mac. Selecting headless-shell explicitly, as CI does, produced the verified PDFs. This does not claim compatibility with every Chrome installation. CI pins Pandoc 3.10 and Java 21 on Linux.

Local-first resource boundary

IBM Plex Sans, Inter, IBM Plex Mono, Chakra Petch, icons, KaTeX fonts and core browser libraries are bundled locally. Preset switching introduces no runtime font-service or CDN-script dependency. System typography and explicit font-role overrides retain their documented precedence.

The production audit loaded Home, Chinese configuration, math, Mermaid, Markmap, ECharts and OpenAPI under each preset, then switched dark/light mode. The production base origin was preserved while built files were served locally. Request tracing recorded and blocked off-origin requests; local fonts and diagrams still loaded without uncaught JavaScript or local HTTP errors.

Two configured services requested external scripts: Giscus and Google Analytics. Giscus is an accepted optional comments integration; its OINK palette files are local. This documentation site already configures an analytics ID, so production output includes Google Tag Manager’s script. Neither service is required by the new presets. They were left configured; the documentation site therefore does not have a zero-external-request claim. Authored remote media and explicitly selected diagram services retain their existing opt-in boundaries.

Repeating the checks

Run owning theme checks before the real-site checks. These commands use the sibling theme through the documented Make targets; do not commit a filesystem module replacement:

python3 bin/check-presets.py
node --test 'tests/js/**/*.test.js'
make -C ../oink.pgsty.com check
env -u A11Y_PATHS -u PLAYWRIGHT_BASE_URL make -C ../oink.pgsty.com browser

The full theme checker set and publication commands are defined in .github/workflows/ci.yml. Run all owning checkers with fresh fixtures, including parameters/schema, vendored assets/fonts, navigation/search/actions, components, output/namespace/goldens, migrations, snapshot protection, consumer tooling and PDF isolation. The engine suite is npm run test:appearance:engines in the site repository.

For compatibility checks, put the selected Hugo binary on PATH, disable inherited Go/Hugo workspaces, and identify the sibling replacement explicitly. Build the real site with --environment production --minify --printPathWarnings --panicOnWarning into a separate output directory. For published-pin checks, disable the replacement as well. These are different validation targets.

Remaining release steps and limits

Local technical pre-release acceptance passed. A release still needs reviewed changes assembled into commits, CI on those exact commits, a published tag and module archive, consumer adoption and hosted verification. Changing a version label cannot complete those steps. Release notes remain drafts and existing consumer pins were not changed.

Real Windows/Android font rendering, manual screen-reader speech and first-paint filmstrips were not verified. Windows source-path behavior was checked through deterministic fixtures rather than a Windows host. Ink/Terminal design follow-up remains in the experiment record.

7.4 - Visual preset acceptance, 2026-10-05

Local Paper and Slate verification, real theme output, and bounded browser evidence.
Local evidence only

This record concerns sibling-checkout theme output, with no injected prototype styles. It is not a release, a consumer upgrade, or hosted-site acceptance.

Inputs

Theme and documentation working trees on 2026-10-05; Hugo Extended 0.166.0, Go 1.27.1, Node 26.9.0 and Playwright 1.62.1 on macOS ARM64. The ordinary browser suite uses Chromium; the additional engine gate uses Chromium, Firefox and WebKit. The site still pins v1.1.0; make check, make browser, and make dev select the local sibling theme. The published pin was not changed. This run is not a Hugo 0.160.1 compatibility-floor or pinned-CI-toolchain test.

Executed checks

Evidence Result and scope
check-presets.py Paper light/dark token parity and AA text/link/code/copper contrast; frozen v1.1.0 Slate base palette; four strict configuration builds and 16 HTML roots including 404 and print
Existing theme checkers Parameters, font roles, vendor inventory, 32 locale catalogs, actions, shell, output, namespace, Landing and runtime isolation passed; generated schemas match their sources
check-goldens.py 52 surfaces passed after reviewing and updating the 34 HTML/print expectations affected by root attributes, prepaint colors, the menu and its action/runtime; other output formats were unchanged
Strict site build Real sibling-theme EN/ZH site built with --panicOnWarning; translations, rendered Markdown and internal links passed
node --test 'tests/js/**/*.test.js' 49 runtime tests passed
appearance.spec.mjs 25 tests passed: Paper/Slate × EN/ZH × 390/1440 px × light/dark on Home, configuration, callouts and tabs; menu axe checks; keyboard, persistence, default reset, language navigation, cross-tab sync, blocked storage, invalid values, no JS, print, command palette, reading-anchor/breakpoint handling and generated comment stylesheets
appearance-engines.spec.mjs Six checks passed: Chromium, Firefox and WebKit at 390/1440 px; stored state and browser chrome color restored before CSS, native keyboard selection, focus return and language navigation. Desktop Chromium also used 4× CPU throttling
Font requests All observed fonts were local. Paper requested no Inter; Slate requested no Plex Sans. Two real-site overlay builds proved system typography requests no bundled text face and explicit font roles override both presets
make check Complete non-browser suite passed: 57 tests, 141/141 translated pages and the existing Markdown/rendered-content/internal-link checks
make browser 197 tests passed across all nine standard suites; sitemap axe scan scoped to the 15 routes below
Visual inspection Actual Paper desktop Home, mobile long-form Docs, English/Chinese light/dark Appearance panels and Slate dark Home reviewed; screenshots come from browser tests, not injected styles

The browser suite’s sitemap axe pass is deliberately scoped with A11Y_PATHS to 15 representative routes: EN/ZH Home, configuration, callouts, tabs and OpenAPI; English search, Mermaid, ECharts, Blog and a Book chapter. The existing responsive axe matrix runs in addition. Cross-origin Giscus and vendor API widget DOM retain the suite’s established exclusions. This is not an exhaustive sitemap scan.

The integration run found and fixed Paper dark highlighted-line gutter contrast and smooth-scroll interference with reading-anchor restoration. Theme-color checks now assert both presets: Paper’s opaque warm selection surface and Slate’s existing translucent selection surface.

Limits and next checks

Manual screen-reader speech and visual filmstrip/paint traces remain unverified. The blocked-stylesheet and throttled-CPU assertions verify initialization order, not every browser’s first painted frame. Slate comparison freezes base palette values and verifies rendered font behavior; it does not claim pixel identity for all 1.1.0 components after unrelated 1.2 work.

Mermaid/ECharts retain mode-only palettes. Ink and Terminal remain research; serif display headings, full geometry/density tokens and preset-colored charts remain later work. No release tag, push, cross-site upgrade or deployment was performed for this work.

7.5 - Consumer and migration evidence

A dated corpus snapshot that shaped OINK’s shells, authoring primitives, and deterministic Book migration policy.
Dated corpus snapshot

These counts describe the repositories inspected in August 2026. They are evidence for design choices, not live product metrics or compatibility promises.

Corpus

The authoring survey scanned the content/ trees of eleven OINK consumer sites: 5,325 Markdown files, of which 5,293 had YAML front matter. The set included single-language English and Chinese references, bilingual product sites, release archives, custom landing pages, and separate Book consumers.

The survey deliberately measured source Markdown rather than generated HTML. It counted shortcode calls, fenced-code attributes, callouts, table markers, raw HTML, front-matter keys, content types, and site-local layouts. A later Book-focused pass added five long-form consumers.

Findings that changed the design

Evidence Resulting choice
Content ranged from nearly plain Markdown to pages with many nested components Native Markdown is the default form; a full form survives only for a named capability gap
Documentation, Blog, Landing, releases, and books repeatedly reimplemented navigation or cards locally Extend the shared shell, registry, and primitive rather than adding a parallel system
Site-specific table classes were common, while canonical Field-table headings were rare Hook attributes use an allowlist but preserve documented site-class extension points; Fields cannot be inferred from arbitrary two-column tables
Book sites carried private figure, table, equation, example, and cross-reference conventions Numbered primitives and migration profiles need deterministic classification, stable IDs, and rendered-target verification
Sites mixed single-language, peer-file bilingual, and generated-language content Language authority and generation boundaries must be explicit; a migration never treats an untracked generated tree as source
Rich HTML pages still needed print, Markdown, feeds, and agent output Every component declares its output degradation before its interactive HTML is accepted

The evidence also rejected several attractive additions. Documentation sites did not justify a second Landing system; Book sites did not need a new cover component; a serial archive did not justify a new shell type; and remote API collection belonged to site-side CI rather than a Hugo theme that promises local builds.

Block and table evidence

A focused pass over eleven sites plus Book consumers found 11,484 pipe tables. Only eleven already matched the strict Field-table heading vocabulary, while roughly 874 were reference-style tables and about 1,300 were compatibility matrices. The result was explicit .fields and .matrix markers rather than shape guessing.

The same pass found eighteen Steps blocks in the eleven-site corpus. They all used the full form with headings and rich content. Platform probes showed that a native ordered list could carry most of that content, while another full % container inside a list item could not. OINK therefore keeps both forms for a technical capability boundary, not merely for stylistic preference.

Deterministic Book migration

Three dated dry-run profiles tested whether the migration rules could account for every recognized source without inventing semantics:

Profile snapshot Classified result Manual boundary
DDIA v2 106 figures, 3 tables, 22 code examples, and all 304 relevant links accounted for One caption link flattened to visible text; no unaccounted skip
DDIA v1 90 numbered figures and 203 matching references 14 decorative or unnumbered images deliberately left alone
TPME 31 figures, 10 tables, 44 numbered references, and 1,018 generic stable references No skipped recognized item
Private Book profile 119 figures, 5 tables, and 136 numbered references 3 ambiguous images retained for manual review

Each profile was dry-run first, wrote only after its ambiguity boundary was understood, produced zero changes on a second run, built with warnings fatal, and passed rendered kind/number/anchor checks. The public migration toolkit and current profile boundaries are documented in Writing a book and the migration contract.

Publication adoption snapshot

An isolated 2026-08-24 pass exercised the released generic Book publication path against two consumers:

Consumer Generic publication evidence Downstream status
DDIA 23 ordered pages, 131 typed targets, and 292 resolved cross-references; EPUBCheck, internal, and PDF checks passed At this snapshot it still retained a semantic preprocessor pending independent acceptance of the new gate
TPME 18 ordered pages, 41 typed targets, and 1,062 resolved cross-references; the same generic checks passed A second consumer confirmed portability; it created no upstream migration gate

This is downstream adoption evidence, not an open upstream design boundary.

Limits

These counts should not be copied into product marketing or used as a current site inventory. Repeating the research requires a fresh repository list and a new dated report. Paths, uncommitted content, private repository names, raw agent transcripts, and generated build artifacts are intentionally excluded from this public record.

7.6 - OINK comprehensive review, 2026-08-26

An evidence-based review of OINK’s post-v0.7.0 implementation, configuration, outputs, security, tests, performance, bilingual contracts, and real integration site.
Review snapshot, not a new contract

This page records evidence collected against github.com/pgsty/oink and its integration site on 2026-08-26. It changes no API and does not mean that any recommendation below is implemented. Current Design contracts, implementation, and owning checkers remain authoritative.

Superseded in part by OINK 0.7.1. The code findings F01–F06 were fixed in that release — see the 0.7.1 release notes. Read the findings below as the evidence that motivated the fix, not as the current state of the theme.

Review verdict

OINK’s main-line quality is substantially above that of a typical Hugo theme. The default path builds, bilingual coverage is strong, component tests are broad, and the project treats output and trust boundaries seriously. The real site showed no general breakage across desktop, mobile, light/dark, and the primary accessibility paths. Theme and site worktrees were clean, their current remote checks were green, and every locally rerun first-party suite passed.

Green checks do not prove that every published invariant holds. This review found 4 P1, 9 P2, and 5 P3 findings. The recurring pattern is that OINK has a strong modern contract, while several early or peripheral surfaces have not joined it; the current gates are excellent at preserving selected positive scenarios but do not systematically cover configuration space, static-output degradation, or the semantic accuracy of public documentation.

Before the next release tag, at minimum:

  1. disable Swagger UI’s default online validator and lock zero implicit egress with a non-localhost browser test;
  2. place all public configuration and Landing data behind common type, range, URL, and CSS-value validation;
  3. redesign Swagger, Redoc, and Asciinema output degradation and runtime gates; and
  4. repair generated schemas and bring the public configuration/front-matter references back to current behavior.

Baseline and method

Review baseline

Item Snapshot
Theme repository clean main at fe439fdb1d7c2df745088c9bfcbb8c350403ee63, equal to origin/main
Current stable tag v0.7.0 at cbb6f4e0bfe47e17ba7aa41d04b8651c943cf858
Documentation site clean main at fd5fcde, publicly pinned to github.com/pgsty/oink v0.7.0
Local tools Hugo Extended 0.164.0, Python 3.14.6, Node 26.4.0, npm 11.17.0
Remote CI theme HEAD GitHub Actions run 32792753866 succeeded

Validation executed

  • all 31 theme checkers passed;
  • all 85 migration unit tests passed;
  • all 38 theme browser-runtime unit tests passed;
  • all 40 HTML/Print/Markdown/RSS/LLMS golden surfaces passed;
  • the strict tests/site Hugo build passed;
  • the real bilingual site’s npm test passed: 121/121 page pairs, 886 heading IDs, 24,860 internal links, and 3,172 fragments;
  • the full real-site Playwright suite passed: sitemap-wide axe, 29 accessibility cases, 45 responsive/navigation cases, 16 keyboard cases, 10 content-component cases, 18 code-block cases, 4 PRD5 cases, and 5 theme-color cases;
  • extra visual review at 320 CSS px covered the EN home, ZH configuration, ZH Book, and OpenAPI/Redoc pages with no page-level horizontal overflow;
  • npm audit reported no advisory among the site’s 79 npm dependencies; an OSV Query API batch for the 26 exact versions in VENDOR.json returned no known advisory;
  • measure-baseline.py assets --fixture-site passed its isolated strict build.

Severity

Level Meaning
P1 Breaks a core product, security/privacy, or ordinary-editing invariant; fix before the next tag
P2 Material behavior, contract, or compatibility defect; fix soon with a behavior gate
P3 Maintainability, performance, process, or documentation-governance debt

Finding summary

ID Level Finding Default-site impact
F01 P1 Swagger UI enables its online validator on production URLs Pages using swagger only
F02 P1 Invalid configuration can crash ordinary Hugo or silently emit bad output Depends on authored configuration
F03 P1 Swagger/Redoc/Asciinema violate static-output and runtime-isolation contracts Pages using those shortcodes
F04 P1 Landing sends unvalidated data to safeCSS and lets other bad values pass silently Related Landing fields
F05 P2 Custom page-action and archived-version URLs bypass the shared URL policy Sites configuring those options
F06 P2 Generated JSON Schemas contain wrong defaults, types, descriptions, and candidate keys Authors using editor schemas
F07 P2 The supposedly complete configuration/front-matter references lag v0.7 behavior All maintainers and consumers
F08 P2 Design contracts and proposal lifecycle present conflicting authorities Maintainers
F09 P2 OpenAPI accessibility defects are excluded while Redoc is presented as an alternative OpenAPI readers
F10 P2 Strict-CSP guidance omits theme-owned inline script and style Strict-CSP consumers
F11 P2 No browser support baseline; automation is Chromium-only Firefox, Safari, RTL, forced-color users
F12 P2 Output-security and rendered-Markdown gates have systematic blind spots Consumers relying on those verdicts
F13 P2 Real cross-repository candidate integration is manual and non-atomic Every public behavior change
F14 P3 Checker duplication and source-string coupling are high Maintainers and isolated worktrees
F15 P3 Baseline CSS and fonts remain the main first-visit payload Every HTML page
F16 P3 Vendor integrity is strong, but vulnerability/SBOM and CI supply-chain gates are manual Release maintainers
F17 P3 Changelog, implemented proposals, and behaviorless metadata reduce signal Maintainers and upgraders
F18 P3 The Print isHTML FIXME no longer explains the real dependency Print-template maintainers

Detailed findings

F01 — Swagger UI implicitly contacts the online validator (P1)

layouts/_shortcodes/swagger.html initializes SwaggerUIBundle without validatorUrl: null. The vendored swagger-ui-bundle.js defaults that option to https://validator.swagger.io/validator and suppresses the badge only when the specification URL contains localhost or 127.0.0.1. On a deployed host it creates an online-validator badge whose request includes the specification URL.

This violates the promises that theme-owned network features are off by default, that same-origin specifications remain local, and that OINK is local-first. An intranet deployment can disclose its internal hostname/specification URL. The localhost exemption is also why every current local browser test misses the request.

Set validatorUrl: null explicitly. Any future online validator should be an explicit opt-in URL, pass the shared URL policy, and be documented as a privacy/CSP integration. Test a production-like non-localhost origin while intercepting every request and require same-origin specifications to fetch first-party resources only.

F02 — Invalid configuration does not consistently warn and fall back (P1)

ui-param.html says callers validate types; several do not. Minimal builds produced the following results:

Input Actual result
ui.blog_index_size: nope ordinary build fails because .Paginate requires a positive integer
ui.sidebar_expand_levels: nope ordinary build fails in add
ui.sidebar_menu_truncate: nope ordinary build fails while first casts the value
offline_search_summary_length: nope ordinary build fails while truncate casts the value
ui.sidebar_width_min: "1; color: red" warning-free build emits --td-shell-sidebar-min: ZgotmplZpx
ui.sidebar_width_min: -50 warning-free build emits -50px
blog_index_columns: 2.5 / section_index_columns: 2.5 warning-free build feeds 2.5 to CSS repeat()
ui.sidebar_item_overflow: clip warning-free build silently behaves as ellipsis
ui.sidebar_menu_foldable: definitely the non-boolean string is truthy and enables folding
ui.blog_index_size: 0 Hugo default silently converts it back to 12

Landing marquee.rows/capabilities.columns and Asciinema numeric parameters also call int/float directly. Other bad types, such as print.toc or offline_search_max_results, silently change behavior.

This directly contradicts the Diagnostics decision: ordinary hugo server may become unusable, while some bad input reaches a strict publishing gate without any warning. Add shared integer, positive-integer, range, paired-range, and grid-count validators. Normalize before arithmetic or output. Every public key needs legal site and page cases plus illegal ordinary (warn/fallback) and strict (failure) cases. Cross-field invariants such as min <= max, pager size >= 1, and integer grid counts belong in domain resolvers.

F03 — OpenAPI and Asciinema remain HTML-only islands (P1)

Architecture and Components require Markdown/LLMS without theme component markup, static Print, and safe static RSS or explicit omission. Current behavior disagrees:

  • Redoc emits <style>, <div class="td-redoc">, and <redoc spec-url=...> into generated .md;
  • Swagger places an executable inline initializer in its shortcode;
  • Asciinema .md contains the full td-asciinema tree and JSON script;
  • Asciinema Print loads about 185 KB of player JS/CSS and can print only an incidental frame;
  • Swagger/Redoc leave empty Print containers and can still select 1–2 MB runtimes; and
  • these shortcodes are absent from the Markdown/RSS/Print golden matrix.

Agent output contains theme HTML, paper/EPUB readers receive empty shells, Print carries useless runtime, and Swagger breaks a strict CSP. Reader-facing guides currently document these defects as output behavior, contradicting the normative contracts.

Make all three branch on tdOutputFormat: full behavior in interactive HTML; a titled static link and spec/cast address in Print/Markdown/RSS, or explicit omission. Only interactive HTML should set capability flags. Move Swagger initialization into a stable chunk and Redoc styles into a stylesheet; add four-output goldens and runtime-absence assertions.

F04 — Landing CSS, URL, and numeric inputs do not share one trust boundary (P1)

hero.html validates title_size but concatenates media.ratio and media.max_width verbatim before marking the complete string safeCSS. This input:

sections:
  - type: hero
    data:
      title: Probe
      image: /icons/logo.svg
      media:
        ratio: "1fr; background-image: url(https://example.invalid/x)"
        max_width: "240px; color: red"

builds strictly with no warning and emits:

style="--td-hero-columns: 1fr; background-image: url(https://example.invalid/x);
       --td-hero-media-max: 240px; color: red;"

Landing permits inline sections in page front matter, so this is not merely an internal repository constant. Other section columns, rules, dimensions, styles, icons, and URLs are handled ad hoc. A javascript: URL often becomes #ZgotmplZ without warning; bad columns become ZgotmplZ; some direct integer casts abort the build.

Add a section normalization layer with common class, icon, URL, CSS-length, grid-count, boolean, and enum handling. hero.media.ratio should be two constrained track values rather than arbitrary CSS; max_width should use the length validator. All Landing actions should reuse content/url.html, and each built-in section needs negative tests.

F05 — Two configuration URL surfaces bypass shared policy (P2)

params.ui.page_context_menu.links passes through url-template.html and directly into safeURL; url_latest_version is also treated as trusted configuration and marked safeURL. Neither path validates scheme, host, whitespace, or protocol-relative URLs. A warning-free build can produce:

<a class="td-page-actions__item"
   href="javascript:document.body.dataset.pwned=1;undefined">

Clicking executes JavaScript. Site configuration is high-trust input, so this is not a default remote exploit, but it violates the published safe-URL model and gives copied configuration unnecessary execution power.

Allow only HTTP(S) and explicitly supported first-party relative URLs, using the shared resolver. Validate archived-version URLs too. The browser action registry’s second check is good defense, but the progressive-enhancement anchor must not bypass it.

F06 — Generated schemas disagree with actual YAML (P2)

The small parser in generate-config-schema.py does not strip inline comments. At least 11 defaults become strings, including print.toc ("true # ..." instead of boolean), print.section_break_wordcount, both index column counts, and enum defaults such as footer_style, blog_index, and typography.

Comment association also drifts: breadcrumb commentary is attached to section_index; quick-link commentary to sidebar_icon_policy; taxonomy-icon commentary to pager_types; and local-chrome commentary to image_zoom.

The front-matter schema advertises removed detector keys (release, upstream_attribution, downstream_modified) and misclassifies navbar-menu Params.columns as page front matter. The drift check compares the same buggy generator with its committed output, so it reliably preserves the error.

Use a real comment-preserving parser or explicit machine metadata markers rather than extending the ad-hoc parser. The scanner must distinguish page, menu, shortcode, and legacy-detector contexts. Tests should compare each schema default to Hugo’s actual parsed value and keep removed keys out of completion.

F07 — Public configuration and front-matter references are not current (P2)

Both reference pages claim to list every key the theme reads. Material drift includes:

  • long English date defaults where hugo.yaml now uses ISO 2006-01-02;
  • Blog docs missing hero, table, toggle, size, toc_style, and toc_taxonomies;
  • the removed release map and release filters presented as current, while release_url is absent;
  • images: [] described as disabling featured images even though bundle discovery continues;
  • upstream_modified described as adding a line, while current behavior changes the attribution verb;
  • inconsistent claims that invalid input directly fails versus warns in ordinary preview and fails only at a strict gate;
  • the Book guide saying OINK stops at Print HTML after v0.7 shipped BookManifest/EPUB/PDF tooling;
  • Asciinema/OpenAPI guides turning static-output defects into product contracts; and
  • Features saying 28 vendor dependencies when the authoritative manifest has 26.

English and Chinese usually agree on the stale answer, so translation parity cannot detect the error. Treat the two references as a focused contract migration. Derive a comparable key inventory from implementation/schema, keep semantics reviewed by hand, and gate current-key coverage, removed-key placement, enums, and defaults.

F08 — The Design tree contains conflicting authorities and unretired proposals (P2)

The clearest contradiction is that Shell retires navbar columns/mega panels and promises a warning plus one column, while Landing still says navbar mega-menu columns accept 1–4. Implementation and tests follow Shell.

Lifecycle is also incomplete. config-schema is marked implemented but remains an Active proposal. Book publication has shipped manifest, EPUB, PDF, and most CI work while a Draft proposal duplicates the Architecture contract. Media convergence retains implemented milestones and the open M4 in one original design record.

Correct Landing, move stable config-schema facts into Architecture/Decision and retire the proposal, and reduce Book publication to the remaining consumer-migration question or replace it with a narrow follow-up. Active proposals should not contain a second current API.

F09 — OpenAPI accessibility claims conflict with test exclusions (P2)

The axe suite excludes both .td-swagger-ui and .td-redoc. Its comments name Swagger’s unnamed server selector and non-keyboard scrollable version stamp, plus Redoc operation-description contrast. The guide discloses only Swagger’s defects and presents the rendered Redoc as the alternative, implying that Redoc meets the site’s zero-violation gate.

Publish the real boundary in both languages. Fix Redoc contrast in theme CSS where possible; use a narrow post-render adapter for fixable Swagger DOM. Remaining upstream defects should have versioned waivers, upstream issue links, and a separate axe report instead of excluding the whole supported surface while claiming a site-wide zero.

F10 — The current theme does not directly support a strict CSP (P2)

Deployment guidance says strict CSP is workable but lists only author scripts, ECharts callbacks, analytics, remote specs or diagram services, and Giscus. A normal Docs page already emits two theme-owned executable inline scripts (theme first paint and shell prepaint) plus inline style. Markmap, Swagger, Algolia, and Google CSE add more theme-owned inline initializers. There is no nonce API, hash manifest, or complete sample policy.

script-src 'self' blocks theme first paint and shell-state restoration; style-src 'self' blocks theme color, font roles, Landing, and several inline custom properties. Consumers must add 'unsafe-inline', maintain hashes, or override templates, none of which the guide states.

Move stable initializers into same-origin chunks with data/JSON configuration. For unavoidable inline content, provide a generated hash manifest or one nonce hook. Publish minimal-core, Markmap/OpenAPI, and third-party-integration policies and state the style-src requirements.

F11 — Browser compatibility has no baseline or cross-engine proof (P2)

CI installs Chromium only, and product documentation names no minimum Chrome, Firefox, or Safari version. The implementation uses or enhances with :has(), dialog, inert, color-mix(), @property, logical properties, and discrete display transitions. Some paths have fallbacks, but there is no engine matrix.

RTL assurance is mostly source markers, small JS tests, and one element-level geometry mutation rather than a full RTL-language site. Most forced-color assurance only checks that strings exist in SCSS rather than computed behavior.

Publish a small support matrix and run core shell/navigation/content/dialog cases on Chromium, Firefox, and WebKit. Add a real languageDirection: rtl integration configuration plus forced-colors, reduced-motion, 320 px, and 200% zoom scenarios.

F12 — Output-security and Markdown gates do not inspect every claimed surface (P2)

For .md, check-output-security.py scans only Markdown-link syntax; it does not feed raw HTML through the HTML scanner, so Redoc/Asciinema scripts, spec-url, and raw href are invisible. It also ignores URLs in CSS and JSON configuration, while the fixture runs with a broad --third-party allowance.

check-rendered-markdown.mjs is also misleadingly named: it scans generated HTML text nodes for leftover Markdown syntax; it does not read generated .md. The actual Markdown golden set covers 15 pages and omits OpenAPI/Asciinema.

Separate HTML trust, machine-output purity, and rendered-text residue into clearly named gates. Give generated Markdown a very narrow raw-HTML allowlist; parse CSS URLs, form actions, JSON URLs, and non-executable JSON scripts deliberately. Every public shortcode should enter at least one Markdown/Print/RSS behavior case.

F13 — Candidate integration across the two repositories is manual (P2)

Theme CI tests only synthetic tests/site; documentation-site CI tests only the public tag pinned by go.mod. Real EN/ZH and Playwright validation of a theme PR depends on a maintainer’s local HUGO_MODULE_REPLACEMENTS, and changes in the two repositories cannot be committed atomically.

Both repositories can therefore be green while public references drift from implementation, as this review demonstrates. The written release-state separation is correct, but automation does not enforce the same-delivery rule for implementation, owning checker, and paired contract.

Add a read-only candidate workflow that checks out a theme PR SHA and a declared documentation-site SHA, applies a temporary module replacement, and runs npm test plus the critical browser suites. Allow a Design-contract PR to identify the candidate theme SHA too. Tag, pin, and deployment remain distinct, but the candidate pair gains one traceable joint verdict.

F14 — Checker maintenance cost and source coupling are high (P3)

The coverage is valuable, but 34 check-*.py files contain 546 read_text() calls. Many repeat require, temporary-site creation, file writes, Hugo invocation, and error aggregation. Numerous assertions freeze template/SCSS spelling, nearby comments, or whole-file equality instead of observable behavior.

Some helpers hard-code theme: oink with --themesDir <repo-parent>, making the checkout/worktree basename an implicit precondition. There is no unified Python lint/type gate. This makes checkers quick to add but encourages shared blind spots.

Create a common fixture builder and assertion library; move negative cases into table-driven data. Keep source checks for true topology invariants only and move the rest to parsed output or computed styles. Load the theme through an explicit symlink or module replacement rather than repository basename.

F15 — Runtime splitting succeeded, but baseline CSS/fonts dominate first visit (P3)

The isolated strict fixture baseline was:

Metric Value
Cold/warm build 1.256 s / 1.273 s
Pages 249
Stable JS chunks 18
Main + Font Awesome CSS 549.8 KB raw / 91.1 KB gzip
Fonts total (FA portion) 999.7 KB raw / 248.5 KB gzip
Median Docs-page JS 176.9 KB raw / 55.3 KB gzip
Generated public 26.2 MB
v0.7.0 Go module zip 7.8 MB (about 20.5 MB and 1,140 files expanded)

Stable first-party capability chunks correctly removed combinatorial bundles, and large third-party runtimes are page-local. The remaining common cost is Bootstrap/theme/Landing CSS and the complete Font Awesome distribution.

Do not prune Font Awesome by observed template usage; that would violate the authoring contract. Instead measure whether Landing, Book, or Swagger CSS can become independently cached/surface-local, inspect fonts actually requested on first visit, and maintain a trend report rather than an arbitrary hard threshold.

F16 — Vendor builds are reproducible, but advisory and CI supply-chain gates remain manual (P3)

Positive evidence: VENDOR.json pins 26 packages, 56 artifacts, 31 license files, and tree hashes; check-vendor.py passed; OSV and npm audit reported no known advisory in this snapshot.

The custom manifest is not part of a common SBOM/advisory gate, and npm audit cannot see vendored browser packages. Two documentation-site workflows download a Hugo .deb and immediately install it with sudo dpkg -i without a checksum. Actions use movable major tags, and theme CI floats Python at 3.x.

Generate CycloneDX/SPDX from VENDOR.json, schedule OSV scanning, pin Hugo archive/deb SHA-256, pin high-trust release actions to commit SHAs, and choose a specific Python version or matrix.

F17 — Design and release records have lost signal (P3)

CHANGELOG.md has 1,768 lines; the v0.7.0 section alone is about 300 lines, and Unreleased spends about 20 lines on one checker retry. The narratives are useful engineering history but make breaking changes, migrations, and observable behavior harder for upgraders to find.

book_kind and book_part are acknowledged by contract and repeated in content front matter while templates explicitly do not read them. They impose API-like authoring cost without behavior. Implemented proposals remaining active add another duplicate answer.

Keep the changelog to observable changes, breaking/migration notes, and concise fixes; move long design stories to Blog/Research and link them. Give behaviorless metadata a consumer/schema or demote it to site-owned fields.

F18 — The Print isHTML FIXME is no longer accurate (P3)

hugo.yaml says to leave isHTML unset until Hugo fixes issue #14381. Hugo closed that issue on 2026-01-17, and the fix shipped before OINK’s 0.160.1 floor. Simply enabling isHTML: true still produces missing page/section/landing Print-layout warnings in the current theme, causing a strict build to fail.

The actual dependency has shifted from “waiting for an alias fix” to “the current Print template names rely on non-HTML lookup rules.” Do not simply delete the workaround. First complete the HTML-classified Print lookup matrix and alias/subpath tests; if false remains intentional, update the comment to the real reason and add a test that prevents cleanup based on a closed issue.

Strengths

  • Source, local validation, commit, tag, public module, consumer pin, and deployment are explicitly separated.
  • The Hugo 0.160.1 floor plus 0.164/0.165 theme matrix is strong.
  • Most newer components follow warning/fallback, four-output, shared URL/attribute, and capability-flag contracts.
  • The 32 locale schemas match, with strong real EN/ZH page, heading-ID, link, and narrow-navigation gates.
  • Search, keyboard behavior, surface coordination, page actions, and theme color have both unit and browser behavior tests.
  • Vendor license/hash checks and EPUB/PDF path, loopback, CSP, and overwrite boundaries are thoughtfully designed.
  • Manual 320 px review found no page-level overflow; current core visual quality is good.
  • Builds are fast, and first-party JS now uses stable capability chunks.

Recommended remediation roadmap

Phase 0: before the next tag

  1. Set Swagger validatorUrl: null and add a production-origin no-network test.
  2. Build the public-parameter inventory and validate every F02/F04 field with negative cases.
  3. Redesign four-output behavior and runtime gates for Swagger, Redoc, and Asciinema.
  4. Validate custom action and archived-version URLs.
  5. Repair the schema parser/scanner and regenerate both schemas.
  6. Synchronize paired Config, Front matter, OpenAPI, Asciinema, Book, Features, and Landing-contract pages.

Phase 1: contract gates

  1. Create a minimum HTML/Print/Markdown/RSS coverage map for all 29 shortcodes.
  2. Split and strengthen output-trust and machine-output-purity gates.
  3. Normalize all Landing section input centrally.
  4. Externalize theme-owned inline initializers and publish CSP guidance.
  5. Add a cross-repository candidate workflow.

Phase 2: compatibility and structure

  1. Add Firefox/WebKit, real RTL, forced colors, and 200% zoom.
  2. Consolidate the Python checker harness and source-string assertions.
  3. Evaluate surface-specific CSS and actual font requests.
  4. Generate an SBOM, schedule OSV, and pin CI download digests.
  5. Retire implemented proposals and reduce changelog volume.

Acceptance criteria

  • A same-origin Swagger specification on a production-like origin makes no third-party request.
  • Every invalid public configuration warns and falls back/omits in ordinary builds, fails strictly, and emits no ZgotmplZ.
  • Generated .md contains no td-*, theme script/style, or empty interactive container.
  • Print loads no Swagger/Redoc/Asciinema runtime and provides an understandable static alternative.
  • Schema default types exactly match Hugo parsing, and removed keys are absent from completion.
  • EN/ZH configuration and front-matter key/enum/default inventories match implementation.
  • Core Playwright passes on Chromium, Firefox, and WebKit, with real RTL and forced-color behavior assertions.
  • Every candidate theme SHA has a traceable joint validation against the real documentation site.

Review limits

This pass did not individually audit every consumer repository, production response headers/CDN caches, real Firefox/Safari, or screen readers, and it did not manually reverse-engineer 13 MB of minified third-party source. Advisory checks are a 2026-08-26 snapshot and may change. Existing CI/contract evidence was used for DDIA/TPME EPUB/PDF consumers; no site was republished or deployed during this review.

7.7 - Community issue and PR review, 2026-09-19

Evidence, acceptance advice, and focused remedies for community issues 40, 41, 42, 44 and pull request 43.
Original review snapshot

This research records source inspection, live GitHub status, local builds, and targeted browser observations on 2026-09-19. At the initial review checkpoint, the recommendations were not yet accepted contracts or implemented features, and no PR had been merged, release published, or contributor reply posted. The implementation follow-up at the end records the subsequent changes.

The initial review is preserved below. The maintainer subsequently authorized merging PR #43 and implementing the remaining items directly on main; see the same-day implementation and acceptance follow-up.

Verdict

The reports identify useful problems, but they are not all defects of the same kind. Fix hidden navigation focus first. Accept the direction of PR #43 as a small correctness fix, after clarifying its boundary and adding coverage. Treat search-tail registration and public sidebar state as additive APIs with their own acceptance work.

Item Finding Recommendation
PR #43, MagicFollower Self-root collection ignores an explicit sidebar_root_menu: false. Reproduced. Conditional acceptance: the patch is correct for the global candidate list; document the current-root exception, add regression tests, and obtain successful CI.
#41, imbajin Hidden whole-sidebar content remains focusable; disclosure state has several writers and no public API. Split a correctness repair from an optional API. The former has higher priority.
#44, lloydsun Pointer focus followed by a key produces the reported outlines. Reproduced on another platform. Improve the main-content focus treatment; retain useful keyboard cues for scrollable content. The browser heuristic itself is expected.
#42, aucru Existing hiding and divider options do not provide a complete non-link section group with its children. Explain the options separately, obtain the author’s exact minimal example, and implement the missing group behavior if confirmed.
#40, imbajin There is no supported way to append a query-dependent action to local search. A reasonable small extension proposal, not a failure of existing local search. Lower priority than correctness repairs.

Baseline and method

GitHub API reads found four open external issues and one open external PR. The other open issue, #37, is the maintainer’s release tracker. The reviewed external items had no discussion comments or submitted PR reviews at the snapshot time.

Input Verified snapshot
Remote theme main 93ac292014a3cd81f7c41caec4df98ed9d2dc45a
Local theme 75ddc95; its only difference from remote main is release text in CHANGELOG.md
PR #43 head 8eeb8ecaf525097cc56572fe22234db381bfc16a, one changed template line
Local documentation ff0ba39; go.mod still requires OINK v1.0.0
Published release GitHub’s latest release is v1.0.0; no remote v1.1.0 tag was returned
Build tools Hugo Extended 0.166.0, Node 26.9.0, npm 11.19.1
Browser observation macOS, Chromium 153.0.0.0, light theme, real sibling documentation site using a command-scoped module replacement

The Design section’s released-v1.1.0 labels and the local release-preparation commits do not establish that version’s publication. A source change, tag, consumer pin, and hosted deployment remain separate facts. The public consumer was not upgraded as part of this review.

The method combined the complete issue/PR bodies and comments, the exact diff, the bilingual Design contracts, owning templates and JavaScript, a temporary bilingual site under a /sub/ base path, and targeted browser interactions on the documentation site. No theme implementation was changed in the shared checkout.

PR 43: accept the small fix with a precise boundary

The first pass in root-menu-roots.html filters top-level sections by sidebar_root_menu. The second pass collects sections whose sidebar_root_for is self, but omits that filter. A section excluded by the first pass can therefore re-enter through the second pass.

The PR adds the same explicit-false predicate to the second pass:

{{- if and .IsSection (ne .Params.sidebar_root_menu false) -}}

This preserves the existing default for an absent or true value, retains the section constraint and URL deduplication, and does not change navigation-tree or pager ordering. It also preserves language-specific caching. There is no reason to replace this with a broad navigation refactor.

However, root-menu-entries.html subsequently appends the current resolved root when absent. That behavior already exists and is described in the navigation guide. It is not a new PR regression, but it prevents the broad claim that false now hides the root on every page.

The local probe used a top-level Blog root and a nested Docs root, both with sidebar_root_for: self and sidebar_root_menu: false, plus a visible Docs root and a self-root with no visibility override. Results were identical for English and Chinese, retaining the /sub/ language-aware URLs:

Viewed page Before the PR With the PR
An unrelated Docs page Both hidden self-roots appear Both disappear
A page inside the hidden Blog root Blog appears Blog still appears through current-root fallback
A page inside the hidden nested root Nested root appears Nested root still appears through current-root fallback
A visible self-root Appears Still appears; no duplicate

Recommended contract: false removes a root from the site-wide selectable candidate set, while the current root may remain available for orientation. Keeping that existing exception is the smallest compatible interpretation. State it explicitly in both languages. If the intended contract instead means absolute exclusion, the current-root fallback and switcher trigger need a separate, coordinated change; adding one more predicate without checking zero and one-entry states is insufficient.

Before merging:

  1. Add an output-based case to bin/check-shell.py for hidden top-level and nested self-roots, absent/true values, deduplication, and current-root behavior. Cover one-entry degradation and EN/ZH subpaths.
  2. Update the Shell contract and navigation guide together. Correct the PR description’s YAML comment from // to # so its example is pasteable.
  3. Resolve the workflow’s action_required result and run the required checks on the final head. At this snapshot there are no successful check runs or commit statuses for the PR head. The API reports MERGEABLE and UNSTABLE; neither is evidence that tests passed.

The maintainer can add these small finishing changes while preserving the contribution. Do not make acceptance depend on implementing #40 or all of #41.

Issue 41: repair isolation, then expose state

There are two separate findings.

First, whole-sidebar hiding uses transforms and, on desktop, opacity. The drawer and collapse controllers do not remove hidden controls from keyboard navigation. In the browser probe, clicking Collapse sidebar left focus on the now-transparent collapse button; pressing Tab moved focus to the hidden root switcher. The panel had opacity zero and no effective inert or aria-hidden ancestor. This is a reproducible usability defect, not merely a missing integration hook. The mobile closed-panel implementation uses the same kind of off-screen positioning without explicit isolation.

Second, disclosure writes are duplicated across the click controller and responsive relocation and cached active-path hydration. There is no public setter, getter, or committed-state event. Existing storage for overall collapse, width, and scroll position does not persist each branch’s disclosure state across navigation. The authoring guide’s statement that reader expansion state is stored locally needs this distinction.

Recommended repair:

  1. Centralize whole-sidebar isolation at initialization and every open, close, collapse, hover-overlay, restore, and breakpoint transition. Remove isolation before moving focus inside; restore focus to a visible external control before making the content inert.
  2. Isolate the content, preserving the external restore button and the deliberate desktop edge hover target. Applying inert to that pointer sensor would break the existing hover interaction. aria-hidden alone does not prevent keyboard focus; the HTML inert contract addresses interaction as well as accessibility exposure.
  3. Separately route disclosure changes through one commit function, updating aria-expanded, the open class, and localized label before emitting one event. Repeated writes of the current value should be no-ops.
  4. Expose a small setter/getter and event only after specifying stable IDs, invalid-ID behavior, initialization readiness, and restoration order. Keep version/locale storage policy downstream-owned and let the active path win after restoration.

The proposal needs one scope correction: the responsive TOC/backlink/taxonomy groups can move out of the sidebar into the right rail. A controller that only looks up descendants of the current sidebar cannot also own those wide-layout writes. Register OINK-owned targets independently of their current DOM parent, and keep the public sidebar API restricted to its intended registered subset.

Acceptance must check real Tab order and the accessibility tree in hidden states, restored desktop collapse on first load, hover entry/exit, focus return, Escape, backdrop close, scroll unlock, and the 768/1200 breakpoints. Preserve the visible no-JavaScript fallback from #24. Static axe scans and assertions that a drawer can open do not establish these state-transition properties.

Issue 44: real symptom, partly expected behavior

On the documentation configuration page, clicking the article heading, a table header cell, or a code block focused main#td-main-content, div.td-table-scroll, or pre.chroma, respectively. In each case, :focus-visible was false after the click and true after pressing the unbound letter z. The main/code outline changed from none to the browser’s auto outline; the table used the theme’s solid outline. This reproduces the mechanism without the reporter’s Linux compositor or Super key.

The Selectors specification explicitly describes keyboard activity changing focus indication even when the focused element does not change. Therefore the report is useful UX feedback, but the expectation that a mouse-focused element must never acquire a ring after keyboard activity is not a browser correctness requirement.

Recommended treatment:

  • Keep the main element’s skip-link target and focusability. Replace its oversized container outline with a localized, visible content-entry cue, such as a title-area indicator, and verify actual skip-link activation.
  • Keep keyboard-visible focus for scrollable tables and code. Normalize its appearance if needed. Their focusability enables keyboard scrolling.
  • Do not apply global outline: none, remove all tabindex attributes, or blur the active element on arbitrary key presses.
  • If OINK chooses to suppress only the pointer-origin reading path, define that additional behavior explicitly and scope it to these non-editable containers. It needs focused pointer/Tab/skip-link/programmatic-focus tests, including dark and forced-colors modes. A global input-modality framework is disproportionate to this report.

This review supports a focused presentation improvement. It does not support removing the table/code keyboard cues simply to make the symptom disappear.

Issue 42: distinguish hiding from grouping

The question names _index.json and relies on screenshots rather than a source fixture. The original screenshots were not successfully visually inspected in this review; the author’s exact intended first change remains unresolved. Request a small directory tree and its actual index/front matter when replying. Do not assume _index.json is either a supported page source or a typo without that evidence.

The existing options have different meanings:

Option Current behavior and limitation
no_list: true Removes the child list from the section’s content body; does not hide its sidebar row.
hide_summary: true Removes an item from a parent section’s body list; does not change sidebar grouping.
toc_hide: true In the content-tree walker, filters out the node before recursion, also removing its subtree from that tree.
sidebar_root_menu: false Controls root-switcher candidates, not the node’s row in the reading tree; see PR #43.
sidebar_root_link_self: false Redirects a self-root’s row to its parent; does not turn it into a non-link group.
sidebar_divider: true Emits a non-link heading, but the shared renderer does not emit the supplied children in that branch.
build.render: link Suppresses the section HTML while retaining its permalink; the current sidebar still emits a link to it. It is not sufficient by itself.

The temporary site confirmed that a divider section’s child HTML still exists while its sidebar link disappears. A section with only build.render: link has no section HTML but keeps a clickable sidebar row and its child. This matches Hugo’s documented build-option semantics and the theme’s shared node renderer.

For a real “group label with child links, but no directory-page navigation” requirement, first consider completing sidebar_divider for section nodes: retain the existing leaf divider, preserve children for a section, and use a real disclosure button when folding is enabled. Check existing consumers before settling that interpretation; introduce a separate node-level switch only if the divider contract cannot express it compatibly. Keep publishing a section page separate from whether its navigation label is a link.

The change must preserve hierarchy and active-path expansion in both walkers, keep children in the pager, and avoid dead targets in breadcrumbs, search, root switching, Print, and machine-readable navigation when the section page is intentionally unpublished. Hiding a whole node with CSS is not a solution.

Issue 40: a narrow extension is reasonable

Source inspection confirms the stated gap: groupsFor only composes built-in page/action groups, the public Palette object exposes no provider registration, and registerExecutor accepts only built-in action IDs. Static URL commands cannot substitute for a row that carries the current query. The existing Palette/model tests pass; that is evidence that the present feature works, not that this extension exists.

The proposed search-tail slot is a useful upstream boundary if kept small: synchronous data-only row creation, asynchronous activation, local results first, and OINK-owned rendering, selection, keyboard handling, and ARIA. It does not require OINK to bundle an AI provider, credentials, remote search, or a generic plugin system.

Before adopting the proposed API, settle and test:

  1. Exactly which settled text-search states call the provider; preserve empty, command, choice, loading, and default no-extension behavior.
  2. Snapshot the query/locale used to render each row. Preserve native empty and index-error messages, retry behavior, and the distinction between local page count and total selectable rows.
  3. Validate and copy descriptors; render titles and descriptions as text; isolate provider exceptions, duplicate IDs, and invalid descriptors.
  4. Handle synchronous throws and rejected promises, release pending state, reject duplicate activation, cancel stale sessions, and make unregister handles safe when an ID is later reused.
  5. Test handoff to another dialog. Existing Palette close already avoids restoring focus when focus has moved outside; preserve that guard. Define how successful surface handoff differs from cancellation, since a blanket “every close aborts activation” rule can cancel the assistant being opened.
  6. Preserve the default local-only network behavior and conditional bundles. A trusted extension’s documented purity is not an enforceable sandbox.

Promote the accepted API shape into a bilingual Design proposal before implementation. A downstream Ask AI wrapper can continue operating until a tagged release provides the hook. This is not a prerequisite for shipping the small correctness fixes.

Delivery order and ownership

Order Delivery Owning checks and documentation
First PR #43 completion and hidden-sidebar isolation as separate small changes check-shell.py; site responsive/keyboard/accessibility cases; EN/ZH Shell contract and navigation guide
Next Main-content focus styling and clarified grouping behavior Content/reading and navigation checkers as appropriate; browser focus/scroll/skip tests; EN/ZH architecture, shell, and authoring guidance
Later Public disclosure controller, then search-tail API Theme JS tests and check-navigation-contract.py / check-palette.py; real site fixtures; accepted bilingual API contracts

For every behavior change, first run its owning checker, then use the sibling site’s make check, make browser, and make dev workflow for the relevant integration and visual review. Do not make these unrelated proposals into one large sidebar/search rewrite or delay small fixes until every feature exists.

Suggested response content, not posted: acknowledge #43’s filter bug while explaining the current-root exception; accept #41’s isolation defect and split its API request; acknowledge #44’s reproduction with the standard focus explanation; give #42 the option distinctions and request its minimal input; mark #40 as a scoped enhancement rather than a local-search failure.

Historical external issues are already closed. #22 was resolved by enabling Goldmark passthrough, with confirmation from its reporter. #21 received the Mermaid viewer and fixed centered presentation; arbitrary right alignment was explicitly not included. Neither should be silently counted as a new open bug.

Validation and limits

Executed for this review:

  • The owning python3 bin/check-shell.py passed on the local baseline and in an isolated checkout of PR #43’s exact head.
  • The Palette controller and model test files passed, two test files and no failures.
  • Strict temporary Hugo builds before/after the actual PR diff reproduced self-root filtering and fallback in EN/ZH under /sub/; the same fixture demonstrated the grouping limitations.
  • The real bilingual documentation site built with --panicOnWarning using the local theme. Targeted Chromium interactions reproduced all three focus outlines and the desktop hidden-focus defect.
  • The site’s bilingual, rendered-content, and link checks passed, as did all 57 tests in its non-browser suite. The initial make check stopped at the llms.txt snapshot because this report added an index entry. After verifying that one-line addition and updating the golden, the affected and remaining test groups were rerun successfully.

The new bug assertions are investigative probes, not committed regression tests. This was not a full release certification or an all-browser matrix. The local Hugo version was 0.166.0, not the CI-pinned 0.165.0 or the declared 0.160.1 floor. Linux Super-key behavior, mobile accessibility-tree isolation, dark/forced-colors cases, and the author’s exact #42 screenshots still need the acceptance coverage described above. No hosted deployment, release, consumer upgrade, or upstream discussion was changed.

Implementation and acceptance follow-up

The maintainer chose to merge the contributor’s patch first, then complete the repairs and extensions directly on main without another pull request. PR #43 merged as 6e814089. The merge was pulled while preserving the existing local release-note commit. The implementation follow-up is 56bfe37.

Item Implemented behavior Owning acceptance
#43 Both root collectors honor explicit false. Current-root orientation remains compatible; dividers and unpublished sections do not become switcher links. Strict EN/ZH subpath fixtures cover hidden top-level/nested roots, absent/true values, deduplication, current-root fallback, zero and one entry.
#41 One disclosure controller commits ARIA, classes, labels and inert state. A late-safe API supports downstream persistence. Hidden whole-sidebar content is isolated while hover and drawer restoration remain usable. Runtime tests plus browser checks for atomic events, no-op writes, scope, active paths, blocked storage, responsive relocation, focus return, real Tab traversal, Escape, backdrop and breakpoints.
#44 A pointer-origin mark suppresses later incidental container outlines. Tab and fresh programmatic focus retain visible cues; the skip destination outlines the title. Browser checks for article/table/code in light, dark and forced-colors modes, plus keyboard and skip-link regression coverage.
#42 Divider sections keep their children under a non-link label. build.render: never suppresses their own page. Breadcrumb, search, pager, navigation JSON, Book TOC/Markdown and Print agree. Explicit navigation also works under bilingual subpaths. Strict generic/data-tree fixtures, Book depth-three heading checks, EN/ZH browser fixtures and no-JavaScript traversal.
#40 Trusted site scripts can register synchronous data-only search-tail rows and asynchronous activation. Native ordering, ARIA, validation, error isolation, cancellation, unregistering and focus handoff remain OINK-owned. Runtime lifecycle tests and a Chinese browser scenario covering pointer/keyboard selection, literal display text, context snapshots, external-dialog focus and unregistering.

Acceptance against the sibling theme checkout completed on macOS with Hugo Extended 0.166.0, Node 26.9.0 and Chromium:

  • All 44 theme JavaScript tests passed.
  • check-shell.py, check-reading.py, check-palette.py and check-keyboard.py passed. A broader run passed 29 of the other 31 commands from the theme CI configuration. The two local failures were the media checker’s version-specific processed-image hashes and four goldens containing Hugo 0.166’s changed KaTeX output; ordinary navigation markup matched after preserving its existing whitespace. The fixed CI toolchain is verified separately below, rather than rewriting unrelated expected output.
  • make -C ../oink.pgsty.com check passed: bilingual source/rendered/link checks and all 57 non-browser tests.
  • make -C ../oink.pgsty.com browser passed all 141 tests: 30 accessibility, 45 responsive/blog/palette, 16 keyboard, 10 content, 18 code-block, 4 scenario, 5 theme-color and 13 community regressions. The accessibility suite included the complete multilingual sitemap scan.
  • Strict theme fixture output and namespace checks passed. Local Book packaging produced an EPUB with five chapters and zero checker errors, and a 23-page PDF containing all five expected Book pages with zero checker errors.
  • make dev served the real documentation site for visual inspection of desktop light, Chinese dark, collapsed-sidebar restore and a 375px mobile drawer. Escape returned focus to the visible drawer opener. The temporary browser viewport and development server were cleaned up afterwards.

The accepted contracts are in Shell and Architecture, with matching Chinese sources and updated navigation, organization, palette and Print guides. The regression suite belongs to the documentation repository; the theme keeps only its focused checkers and synthetic inputs.

The merged PR’s fixed-toolchain CI passed all three jobs. The final implementation’s CI run also passed on exact revision 56bfe37092a43fc12c0e16f865d3d3407c55cbde: Hugo 0.165.0, browser runtime tests and Book publication all succeeded. This includes the media and four-state golden checks that differed locally on Hugo 0.166.0, plus publication under root and subpath URLs. The supported 0.160.1 floor was not separately retested; the declared continuous-test toolchain remains 0.165.0.

This is source and integration acceptance, not a new release. No new tag was created, the documentation consumer still pins v1.0.0, and production was not upgraded. The sibling documentation changes are prepared on local main for the next theme publication; pushing their new browser gate against the old public pin would test the wrong implementation. No contributor reply was sent, and issues #40, #41, #42 and #44 remain open. The exact original #42 screenshots and the reported Linux Super-key setup were not independently reproduced; the explicit grouping requirement and equivalent pointer-plus-key behavior were tested as described.

Image-copy follow-up

The maintainer also reported preview instructions appearing below images after copying a blog article into a rich-text editor. The blog pins OINK v1.0.0 and enables params.ui.image_zoom. That release and the reviewed main revision inserted a visually hidden text span after each eligible image. Native Chromium copy reproduced the extra Open image preview and Chinese equivalent in the clipboard; this is a theme defect independent of the destination editor.

Theme commit 75052f8 moves the image description and localized action into the button’s aria-label. No helper text node is added to the article. The image alt text, authored captions, native button operation and dialog focus return are preserved. The component contract and image guide document the copy behavior.

check-image-zoom.py passed, as did all 57 non-browser site tests. The focused browser run passed 16 tests: the 14 content-component cases, including four new EN/ZH image/gallery clipboard regressions, and two desktop-light/mobile-dark dialog accessibility cases. The regressions read both plain-text and HTML clipboard data, check text after removing its styling context, and verify retained image URLs, alt text and captions. They also check accessible names.

No live Zhihu editor was used for acceptance. The blog dependency and hosted deployment were not changed; the fix reaches that published consumer after its theme dependency is upgraded and the site is rebuilt.

7.8 - OINK 1.1 release review, 2026-09-20

Five reproduced runtime defects, documentation corrections, validation evidence, and the OINK 1.1 publication follow-up.
Release preparation, not publication

This record separates the reviewed baseline, committed fixes, completed validation, and remaining publication steps. A passing baseline CI run does not certify the later fixes. The sections through Limits preserve that pre-publication snapshot; later release evidence is appended under Publication follow-up.

Scope and baseline

The review starts at theme commit 75052f8a3106d13ef313644836a5ad545135f484, after the community fixes and image-copy repair. It examines the v1.0.0..main change set, the behavior requested by issues #40, #41, #42, #44, and merged PR #43, plus the bilingual documentation and release boundary. The previous review records those original reports and their implementation.

Method: inspect owning JavaScript, templates and contracts; exercise transition boundaries with focused regressions; compare failing assertions before a fix with the repaired implementation; then run the theme checkers and the real sibling documentation site’s integration and browser suites. The review does not add another feature program or claim a comprehensive security audit.

At this snapshot, the public release, documentation go.mod pin and configured public version are still v1.0.0. The blog consumer’s theme pin is unchanged.

Findings and repairs

Five P2 correctness defects were reproduced and repaired in theme commit 08f6563, pushed to main. They concern the new APIs’ ordering and existing focus/navigation behavior; they do not require a new configuration format or a content migration.

Finding Trigger and observed failure Minimal repair
P2: sidebar readiness fires before hydration A consumer awaits OinkSidebar.ready or handles oink:sidebar-ready. The readiness microtask can run between DOMContentLoaded listeners, before the cached sidebar’s active path is hydrated. Consumers see incomplete initial state. Resolve readiness in the next task, after all initialization listeners and aside placement finish; preserve the existing ready Promise and event contract.
P2: a pending action can enter a native choice menu Start an asynchronous search-tail action, then activate a native choice such as theme selection. The pending guard ran after the choice branch, allowing that menu to replace the pending action’s rows. Check pending activation before any row-type branch. After completion, ordinary choice activation is available again.
P2: a collapsed right TOC rail remains focusable Collapse the desktop right rail. Its hidden control and links remain keyboard targets; focus can stay inside the hidden panel. Apply inert and aria-hidden to the rail panel, move focus to the visible restore control, and return it to the column control on restoration. Keep the movable aside outside that isolation when relocated.
P2: arrow navigation skips non-link groups From a child of a divider-only group, Left/a cannot consistently return to the parent disclosure and fold it; the group button is absent from the tree’s focus sequence. Include group disclosure buttons in tree focus navigation and direct-parent traversal. Right/d opens or enters the group; previous/next page navigation still uses links only.
P2: drawer focus wrapping counts inert descendants In the mobile drawer, collapse an aside group and wrap with Shift+Tab. Hidden descendants still counted as focusable can make the wrap fail or leave focus stuck. Exclude controls under inert or hidden, and controls with hidden/collapsed visibility, from the drawer’s focusable set.

Implementation and regression ownership:

Finding Theme implementation Owning regression
Readiness assets/js/sidebar-state.js tests/js/sidebar-state.test.js; site tests/browser/community-feedback.spec.mjs snapshots the active path from both readiness signals in EN/ZH
Pending choice assets/js/command-palette.js tests/js/command-palette.test.js exercises pending extension → native choice → completion → available choice
Right rail assets/js/docs-shell.js Site tests/browser/community-feedback.spec.mjs covers EN/ZH collapse, Tab traversal, restoration, reload and aside relocation across desktop/tablet/mobile
Group keys assets/js/keyboard-nav.js tests/js/keyboard-nav.test.js covers LTR/RTL, arrows/WASD and link-only paging; site community tests exercise real EN/ZH groups
Drawer trap assets/js/docs-shell.js Site community tests collapse the relocated groups, wrap Shift+Tab and Tab, and assert focus never enters an inert or hidden subtree

The readiness and pending-choice regressions failed against the previous implementation before their fixes. The right-rail browser assertions also failed in both English and Chinese before isolation was added. The repaired code is kept small: timing, one earlier pending guard, explicit rail isolation, tree focus targets and the drawer’s visibility filter.

These changes preserve the already accepted behavior: both root collectors honor sidebar_root_menu: false; non-link groups retain their children; pointer focus avoids incidental article outlines while keyboard cues remain visible; search-tail callbacks retain their cancellation and handoff contract. The image preview fix continues to use an accessible name without inserting helper text into copied article content. Final regression results are recorded separately below rather than inferred from code inspection.

Documentation readiness

The current documentation update covers 28 files in 14 EN/ZH pairs:

  • The six Design contract pairs use candidate-v1.1.0 and describe implemented main behavior without announcing a published release.
  • Navigation, layout and front-matter guides match the current root filtering, divider groups, bilingual deployment paths, centered navbar, narrow-screen drawer and in-place navbar reveal behavior.
  • Organization and palette guides explain runtime load order, readiness, feature detection and site-owned persistence/integrations. Keyboard and image guides explain the fixes and the older-version boundary.
  • Installation guidance distinguishes the validation toolchain from the public version. Existing heading IDs remain stable; the new sidebar API heading has a matching Chinese ID.

The two 1.1.0 release-note files and two upgrade-guide files are also prepared. The release note remains a draft/candidate. Both home-page release entries point back to the published 1.0 version. These source edits neither update the site’s module dependency nor deploy new behavior. Historical research remains a dated record and is not rewritten to erase its earlier release-state observations.

Validation snapshot

Counts below are the 2026-09-20 snapshot for theme 08f6563. Site acceptance uses the sibling checkout through a command-scoped module replacement; it does not certify an unpublished module tag or a production deployment. Local site checks used Hugo Extended 0.166.0, Node 26.9.0 and Playwright 1.62.1; the candidate CI used the pinned Hugo 0.165.0 toolchain. The 0.160.1 floor was checked separately with the official binary.

Check Result and scope
Baseline 75052f8 CI Passed all three jobs: pinned Hugo toolchain, browser runtime tests and Book publication. Exact baseline run.
JavaScript unit suite after the fixes Passed: 44 tests.
Official Hugo Extended 0.160.1 Passed the focused i18n and shell checkers; i18n covers 32 catalogs × 194 messages. The real documentation site also passed a production build with --panicOnWarning against the local candidate: 376 pages per language. The draft release is absent and both home-page links point to 1.0.0. This is selected compatibility-floor evidence, not a second complete CI matrix.
Bilingual source and style checks Passed including this report: 129/129 page pairs, 988 source headings, Markdown style and git diff --check.
Final focused theme checkers Passed: shell, palette, keyboard and image zoom.
Final real-site non-browser suite make check passed all 57 tests; 200 rendered content pages, 331 linked HTML pages, 39,803 internal links and 3,863 fragment links were checked. Only the two expected Markdown goldens changed, for the release summary and documentation index. The floor production build also passed links: 327 pages, 39,059 internal links and 3,833 fragments.
Final browser suite make browser passed all 149 Chromium tests in eight suites: 30 accessibility, 45 responsive/blog/palette, 16 keyboard, 14 content components, 18 code blocks, 4 scenarios, 5 theme-color and 17 community regressions. Includes the full multilingual sitemap, six viewport widths, light/dark, forced colors, clipboard and no-script cases.
Agent documentation sample 93/100 (A) across 50 same-origin sampled pages. Sampled links resolve; 49 provide Markdown and all 270 sampled code fences close correctly. The checker warns that the HTML llms.txt discovery hint is missing or too deep.
Rendered review Reviewed the Chinese release note in a desktop dark view and English in a narrow light view. Right-rail collapse removes its descendants from the accessibility tree and moves focus to the restore button; restoring returns focus to the visible rail button.
Final theme revision and its CI 08f6563 passed all three jobs: Hugo 0.165.0, browser runtime tests and Book publication. Exact candidate run.
Public v1.1.0 tag, consumer upgrade and deployment Not performed.

Within this review’s scope, no unresolved implementation blocker remains. The candidate is ready for the publication steps below.

Repeat the checks from sibling checkouts, keeping the published dependency pin intact during development:

# From the theme repository
node --test tests/js/*.test.js
python3 bin/check-shell.py
python3 bin/check-palette.py
python3 bin/check-keyboard.py
python3 bin/check-image-zoom.py

# The site Make targets apply a command-scoped sibling module replacement.
make -C ../oink.pgsty.com check
make -C ../oink.pgsty.com browser

Remaining publication steps

Publication has not been executed. After the final revision passes acceptance:

  1. Finalize CHANGELOG.md, release date and the release record; publish the v1.1.0 tag and GitHub Release from the verified theme revision.
  2. Verify that the module proxy resolves that tag to the intended revision.
  3. Update the documentation consumer pin and version configuration together with its home-page release entry, contract status and release-note draft: false state.
  4. Rebuild and accept the documentation site using the published dependency, without HUGO_MODULE_REPLACEMENTS; deploy it and verify the public routes.

The final sign-off must identify the tested theme revision and distinguish local source acceptance, public module availability and deployed output.

The remaining optional improvement is an earlier, consistent llms.txt discovery hint for agents entering through HTML. This scorecard warning does not invalidate the current Markdown outputs or require a new feature before 1.1. A Safari/Firefox pass and a real Zhihu paste check are useful follow-ups; neither is claimed by this Chromium acceptance run.

Limits

Browser evidence is from Chromium, not a Safari/Firefox matrix. The automated accessibility gate covers theme-owned surfaces and retains its existing exclusions for vendored Redoc and Swagger UI. The native clipboard regressions cover plain text and rich-text HTML, image descriptions and authored captions; they do not certify the live Zhihu editor’s paste behavior. Neither the blog’s dependency pin nor its hosted output was changed or accepted in this review. The focused Hugo floor checks do not establish that every publication path was exercised on that version.

This is a release-readiness review of the named source and behavior. It does not claim unbounded security coverage, production rollout, or support for additional requested features.

Publication follow-up

After this review, the v1.1.0 release was published on 2026-09-20 from 3a18234. This revision changes only the changelog from the accepted 08f6563 implementation. All three release-commit CI jobs passed before the annotated tag and stable GitHub Release were published.

A fresh-cache download using only the official Go module proxy resolved the tag to that exact commit. Its .info, .mod, .zip, version-list entry and signed checksum record were verified. The module checksum is h1:121L5g57ChRCPyidzEBBcln2Co+0zYRQ+XDDXjymd0Q=; the go.mod checksum is h1:pHvbUhJCfseB41n5RGwsF7abT3i32VSTpofLQoq4b7Y=. The public records are the proxy version and checksum entry.

The documentation publication update pins v1.1.0 in go.mod and go.sum, aligns the advertised version and both home-page release entries, publishes both release notes, and promotes the six contract pairs to released-v1.1.0. The historical acceptance tables above continue to describe the earlier sibling-checkout run. Published-dependency validation is tracked separately by the site’s Site checks and Browser quality workflows, with both Go and Hugo module workspaces disabled.

Local validation of this published module passed all 57 non-browser tests, 26 focused Palette/community browser tests, and the strict production build (378 pages per language). The checks ran with GOWORK=off, HUGO_MODULE_WORKSPACE=off, and no HUGO_MODULE_REPLACEMENTS. The complete 149-test browser suite is also run by the publication commit’s Browser quality workflow; its result is separate from the earlier local candidate run.

7.9 - CLI maintenance acceptance on 2026-10-03

Dated source and binary evidence for the R1–R8/A18 local implementation program, preserving its initial audit, failed trials, and final supported acceptance scope.
Historical source and binary evidence

This record preserves earlier R1–R8/A18 acceptance. Command changes do not rewrite those results. The reduced CLI and its Cobra/text/JSON/YAML interface on 2026-10-04 are defined by the current contract and guide. Earlier runtime qualification does not qualify a changed binary automatically.

Finite implementation locally validated

The initial audit is retained below. R1 implementation and owning checks have passed their local scope, including refreshed consumer reports and scoped rendered EN/ZH acceptance. R2’s scoped local gate is also accepted, with a separately tested numeric-equality supplement. R3’s runtime and paired documentation gates have passed and its local stage is accepted. R4 supported implementation and read-only corpus gates have passed locally; guarded canonical documentation validation is recorded separately below. R5 corrected implementation/read-only corpus and guarded canonical documentation gates have passed; its supported local scope is accepted. R6 explicit workspace and optional adapters passed frozen owning/runtime, exact-binary consumer and guarded canonical source/render gates; supported R6/A07/A15 scope is accepted locally. R7 read-only Studio/A16 also passed its browser, four-consumer and guarded canonical rendered gates. R8 reviewed editing/A17 passed its corrected frozen owning/browser, exact-binary consumer and guarded canonical source/render gates. R1–R8 supported scope is accepted locally. The 2026-10-04 supplement refreshes the changed backend and closes current A18 runtime/archive qualification for the three declared targets. Canonical lifecycle promotion/render has a separate exact-byte receipt boundary; public release, adoption and deployment have not occurred.

Scope and evidence rules

The maintenance roadmap defines the authorized R1–R8 scope. The current CLI contract defines its compatibility baseline; the original roadmap does not add Docsy migration, version lifecycle, OpenAPI, theme publication, or the conditional E1–E4 extensions to this program. Hugo remains an external renderer and generated sites remain ordinary Hugo projects.

Stages are accepted in dependency order. Every stage needs a complete usable flow, its owning tests, relevant actual Hugo integration, known limits, a reviewable diff, and accepted EN/ZH contract and guide updates. Passing an aggregate command alone does not close a case. New public behavior moves from the proposal into the owning contract only after its implementation and acceptance evidence exist.

In the tables below, existing, not rerun means code or a named test was inspected but its current runtime outcome was not established. Partial means the first candidate provides a reusable part of the required behavior. Open means new implementation or decisive acceptance evidence is missing. Passed, failed, unverified, and unsupported must describe a specific executed input and scope when later runs are recorded. No historical result is relabeled as a current pass.

Inspected inputs and tools

The initial 2026-10-03 audit read both repositories’ instructions, the documentation README and translation rules, both maintenance PRD languages, the original proposal, the current CLI contract, and existing Go packages and test names. It executed version and Git inspection commands only; it did not run the owning suites or write consumer sources.

Input Observed initial state
Host and Go darwin/arm64; go version go1.27.1 darwin/arm64
Hugo hugo v0.166.0+extended+withdeploy darwin/arm64, Homebrew build dated 2026-09-09
Node and npm v26.9.0; 11.19.1; contributor/documentation tools, not CLI consumer requirements
Git 2.54.0 (Apple Git-157)
CLI source e623d93d589c49e5c58b8fae1bd5db720fc904cb, main; clean initial tracked/untracked status; generated bin/, dist/, tmp/ ignored
Documentation source 907d873eb05cfc2e194f492462dfa94849e93474, main; 184 initial porcelain entries, including existing proposals, contracts, guides, and unrelated content changes
Embedded Starter 137843b25bacd76ddd1f7ce71330bf2e3155b954; provenance and license already recorded by internal/starter
Declared theme baseline github.com/pgsty/oink v1.1.0 in Starter and the three selected sites; effective resolved bytes still require each acceptance run

The 2026-09-29 acceptance record contains historical first-candidate checks. It supplies useful reproduction inputs, but does not prove the new maintenance scope. Existing dirty files are preserved; this initial research addition does not accept or overwrite them.

Stage requirements and implementation evidence

Stage Required complete flow and invariants Initial implementation evidence Acceptance evidence still needed
R1 Shared page identity, languages, publication state, source provenance, actual outputs, translations and observed references from Hugo; oink.yaml owns check policy only; links/translations/style share analysis; severity and exclusions cannot hide required incompletion; trustworthy locations Partial: internal/site isolated snapshots and Page.OutputFormats probe, internal/outputcheck, internal/report; no shared translation/page facts or policy commands at initial audit Real Hugo routes, aliases, mounts, unlisted/generated-source cases and language relationships; public focused-check/policy cases; required unknown/tool/build/input failures remain 2; source locations only when reliable
R2 Three language layouts; strict/manual and localized policies; duplicate, missing and draft states; explicit versioned review records bind source language and source/translation hashes; bounded native syntax rules; effective-theme coverage; visible versioned baseline; reviewed fixes validate before narrow apply Open: no translation/review/native-rule/baseline public command at initial audit; rendered-reference checks remain reusable A04–A07; valid/invalid reviewed content corpus; no mtime review inference; disabled/localized languages handled; acknowledged findings stay visible; missing required checks stay incomplete; fix preservation
R3 Preserve thin default build/dev; build --check checks and manifests one strict Hugo output, exports only to new/empty target; digest/provenance manifest and optional minimal public identity; both local CI templates upload the same tree; release diagnosis; explicit-network public verification Partial: direct wrappers, strict isolated checks and licensed workflow inputs exist; managed build/export, digest verification, CI plans and public verify are absent at initial audit A08–A10; exactly one Hugo build; stale-byte rejection; revision/dirty/input/theme/tool/settings/coverage provenance without secrets or machine paths; workflow customization/conflicts/provenance and immutable source input; example address policy; fallback/language/resource/canonical/timeout/auth/rate-limit HTTP fixtures
R4 new, snippets and editor setup create ordinary inputs without overwrite; docs/blog/book/project profiles compose one licensed Starter; upgrades provide readable diff and old/new routes, aliases and enabled outputs; unsupported migrations give manual action; existing protections survive Partial: fixed archive language profiles and hash-bound single-site module upgrade with candidate validation, backups, dirty/workspace/replacement/vendor protection A11–A12; all new profile/language combinations build with ordinary Hugo; unknown editor settings retained; upgrade route/capability regression and readable diff; source provenance and licenses retained
R5 inspect, impact --since, bounded context, preview move; shared plans include touched files, diff, base hashes, translations, attachments, output/route changes and alias advice; candidate validation and stale/concurrent-safe recovery; ambiguous references require review Partial: module-specific upgrade plan/apply primitives; no shared content plans or inspect/impact/context/move flow at initial audit A13–A15; deleting B includes unchanged inbound A; translation/attachment/derived-output impact; uncertain/global changes force full checks; no content execution; candidate/stale/failed-write preservation and ambiguous-link handling
R6 Explicit versioned site registry reuses single-site engine; per-site and aggregate completion; writes only to selected sites; configured preinstalled markdownlint/Vale/lychee adapters normalize findings and declare syntax/network coverage Open: no workspace/adapter public command at initial audit A07/A15/A18; direct/per-site parity; no sibling discovery, implicit installation or default formatting writes; required missing tool 2, optional omission visible, external network uncertainty distinct
R7 Read-only loopback Studio with overview, issues, translation comparison, page relationships and publication views; filters, known sources, actual Hugo preview, comparisons and copied actions; CLI parity; prebuilt assets; explicit allowlist, separate preview origin, Host/Origin/session protection Open: no Studio server or assets at initial audit A16; browser/keyboard/screen-reader/mobile/light/dark/long-list flows; same underlying results as CLI; unauthorized hosts/origins/sessions and preview-to-management requests rejected; Node unnecessary for consumer runtime
R8 Markdown/text and front matter forms, selected components and collision-safe attachments reuse plans; authorized allowed writes with visible diff, hashes and candidate validation; no-op bytes and unknown fields/comments/order/encoding/whitespace retained; unsupported form syntax stays text Open: editing follows accepted read-only R7; no editor API at initial audit A17/A14; byte-identical no-op, surgical YAML field updates and text fallbacks; stale external-editor saves, traversal/symlink escapes and preview requests fail safely; attachments never overwrite; no management API in static publication

R1 local validation

R1 now provides shared Hugo page/translation/source facts, rendered reference and anchor evidence, strict oink.policy/v1 input, check links, --format json, visible reviewed exclusions/external scopes and required-work precedence. Translation and style selections explicitly return required unsupported coverage; they are not implemented engines. Default build/dev remain direct Hugo operations. The following evidence accepts the tested shared-facts/policy scope without closing R2–R8 or the full A01–A18 cases.

Requirement Executed evidence Current outcome
Public result/policy and incomplete precedence make test: all packages and vet; public severity/exclusion/unimplemented-group/JSON-alias tests; TestEveryRequiredUncompletedCoverageFails Passed R1 scope; any required uncompleted status, including not_checked, remains 2
One build and shared facts TestPublicCheckSharesOneBuildAndRenderedFacts Passed; one strict Hugo build supplies page and observed target/anchor facts
Hugo authority and source mapping Actual TestPageFacts* fixtures: translationKey, actual routes/aliases, unknown generated nodes, custom mounts, excluded-page analysis and failure preservation Passed; separate analysis preserves production facts/artifact bytes and source bytes/modes
Repeatable real Hugo gate Corrected make test-hugo includes TestPageFacts* and TestHugoRendered*, alongside Starter and manifest fixtures Passed; scoped route/reference, reviewed external-scope and original-output preservation cases
Fresh Starter Bilingual init, check links, ordinary strict Hugo using isolated provisioned v1.1.0 module archives Exit 0; 223 files, 4,461 references, 66 page facts; dependency preparation remains explicit
R1 documentation source and schema Markdown style under content/docs; bilingual source checker; JSON parse and equality of CLI/docs result schemas; scoped diff whitespace check Passed: 88 Chinese docs, 137/137 source pairs and 1,085 headings; schemas remain additive oink.result/v1 with exits 0/1/2
Final candidate reports and rendered EN/ZH Refreshed current-binary consumer reports; actual rendered source/Markdown/link owning checks below R1 scoped gate passed; existing draft-release omission in production is recorded separately

Earlier offline R1 trials returned 0 without diagnostics on three consumers:

Earlier trial Source files Built files HTML files References Page facts Bytes/modes/Git inventory
OINK documentation 421 1,139 512 74,689 341 Exact before/after equality
PIG project site 858 1,392 424 64,440 248 Exact before/after equality
Repository catalog 2,294 3,287 1,635 851,535 1,572 Exact before/after equality

These earlier reports spell optional unselected coverage not_selected, outside the existing result-schema enum. The final code corrects it to not_checked and includes project.pages coverage. Their measured counts and exact inventories remain valid earlier-binary observations; final JSON conformance is established by the final reports below. Raw evidence stays in task-named local acceptance directories outside consumer sources. These trials do not prove external availability, deployment, Linux runtime or translation/style acceptance.

The final R1 binary was rebuilt from the dirty CLI working tree based on e623d93d589c49e5c58b8fae1bd5db720fc904cb. The recorded input inventory includes file hashes, modes and Git-status identity. Its SHA-256, computed over sorted JSON serialization, is 518260f07f3c916468ee3d56c4eeca03c131514155aa82539564ccd2f3c1f664. The exercised binary SHA-256 is 3deb7e357fc86f6907df60da0769d93f2d41ba5e01949b641548a67d7f459d12. This identifies local inputs and an executed binary, not a maintenance commit, public archive or published module.

Final current-binary trial Source files Built files HTML files References Page facts Acceptance
OINK documentation 421 1,139 512 74,755 341 Exit 0, valid result, complete page facts, exact source bytes/modes/Git preservation
PIG project site 858 1,392 424 64,440 248 Exit 0, valid result, complete page facts, exact source bytes/modes/Git preservation
Repository catalog 2,294 3,287 1,635 851,535 1,572 Exit 0, valid result, complete page facts, exact source bytes/modes/Git preservation

Each final result has check.links: complete and project.pages: complete, both required. Unselected translation/style coverage is not_checked, optional. The final offline make test and vet passed; the corrected actual-Hugo owning target also passed. The recorded tools remain Go 1.27.1, Hugo Extended 0.166.0, Git 2.54.0 on macOS arm64. The three exact before/after inventories were independently compared while preparing this record.

Production output passed rendered Markdown and link checks. Its global translation checker returned 1 solely because the pre-existing draft content/blog/release/1.2.0.md / .zh.md pair is correctly absent from production. The changed R1 pages rendered in both languages. A separate explicit analysis build with HUGO_BUILDDRAFTS, HUGO_BUILDFUTURE and HUGO_BUILDEXPIRED set to true passed all three owning checks: 137/137 paired sources, 1,085 headings, rendered Markdown and rendered links. That view is nonpublishable evidence for excluded sources; it never replaces production output and does not change or publish the draft. No existing draft file was modified to make the global production checker green.

R2 local validation

The local candidate now implements translation policy/status/diff/hash review, syntax-bounded native content rules, visible reviewed baselines and shared oink.plan/v1 preview/validate/apply. Default check requires links, translations and style. Production output and the explicit draft/future/expired analysis are separate; the latter is not publishable. Stable behavior and examples are in the contract and guide. The R2 local gate passed owning checks, final frozen-input consumer reports and rendered bilingual documentation. The exact exercised binary and the subsequent narrow equality fix are recorded separately below; no public release or consumer write is implied.

Requirement Executed owning evidence Outcome and limit
A04 translation relationships/policy Actual TestHugoFilenameDirectoryAndTranslationKeyLayouts; scope, duplicate/missing/disabled-language, draft, strict/localized and selected-constraint tests Passed owning tests; no universal heading/code/localization parity
A05 explicit review and diff Full byte hash/current/source/translation/both-changed, mtime-independent, unknown/unreadable/ambiguous and malformed-record tests; public status/diff/review preview/apply fixtures Passed owning tests; review state is change evidence, not semantic judgment
A06 source boundary and provenance Actual Hugo enabled/disabled canonical title/block attributes and configured passthrough fixtures; front matter/CRLF/BOM/shortcode/code/HTML tests; every public v1.1.0 source/license SHA verified Passed owning tests; unsupported syntax remains incomplete and custom hooks remain outside catalog attestation
A07 baseline scope Capture/visible acknowledgement/new finding/incomplete precedence and malformed-record tests; public baseline preview/apply fixtures Passed R2 baseline scope; external tool adapter acceptance belongs to R6
A14 shared metadata plans Stale bytes/modes/existence/guards; edits during validation; exclusive commit collision; partial restore; later editor bytes/modes/deletion; old open inode write; new-directory children; confinement/identity/diff tests Passed owning tests and vet; candidate/source overlap refused; later move/reference ambiguity remains R5 scope
Frozen runtime gates macOS arm64 make test/vet, owning actual Hugo and focused race runs Passed; logs /tmp/oink-r2-frozen-go-gate.log, /tmp/oink-r2-frozen-hugo-gate.log, /tmp/oink-r2-frozen-race-gate.log; final all-owning-package Hugo gate /tmp/oink-r2-owning-hugo-final.log explicitly includes configured passthrough
Bilingual documentation Narrow source style/pairing/IDs, equal result schemas, scoped whitespace and actual production/analysis node checks Passed scoped gate: 88 Chinese docs, 137/137 source pairs and 1,092 headings; production draft omission separately recorded below

The frozen parser corpus at /var/folders/df/bfm8q07d7bv3kpjf1fjchq4m0000gn/T/oink-r2-source-corpus-lqx25kwr/summary.json records parser input SHA-256 a601200ec4fe275d2bd4baf11d4db7d46a2cc6f1674900c1fd801769e55d12de. Each scope parsed completely with zero findings. The core uses actual Hugo site-source identities from the recorded configuration; supplemental Markdown includes disabled/unpublished files and does not invent routes or relationships. These captures precede the authorized R2 documentation edits.

Corpus Unique actual Hugo source files Supplemental local Markdown Source inventory files Bytes/modes/Git
Starter 52 78 97 Exact before/after equality
OINK documentation 272 274 421 Exact before/after equality
PIG 212 212 858 Exact before/after equality
Repository catalog 1,568 1,572 2,294 Exact before/after equality

Preliminary public-command reports at /var/folders/df/bfm8q07d7bv3kpjf1fjchq4m0000gn/T/oink-r2-final-qu_zprps/summary.json used binary c8d87e6d73d3101fefcb62c5d6845518573c02c400f474dc9f4603afafc774d5 and CLI input inventory d8a75be0e074365a4164b7aaaa27d82a1e844e04406a36c3dd6d39ff2b6e873f. They precede the final parser/doc freeze and are not final acceptance evidence. The initial Starter invocation selected the enclosing evidence folder and returned 2; it was a validation setup error. Selecting its actual site child returned 0, with evidence in /var/folders/df/bfm8q07d7bv3kpjf1fjchq4m0000gn/T/oink-r2-starter-27vmi0w5.

Preliminary check Exit Page facts Built files References Translation statuses Outcome
Correct Starter child 0 66 223 4,461 28 Complete; 97 source files/inventory unchanged
Documentation 0 341 1,139 74,755 144 Complete; 421 source files/inventory unchanged
PIG 0 248 1,392 64,440 120 Complete; 858 source files/inventory unchanged
Repository catalog 1 1,572 3,287 851,535 788 Completed policy check: 10,462 actual HTML_ID_DUPLICATE findings in existing merged-print output; 2,294 source files/inventory unchanged

The repository catalog result is a completed finding outcome, not a passing site or implementation failure. No policy was weakened and no consumer source was changed. Informational review states remain visible.

The final frozen-input reports at /var/folders/df/bfm8q07d7bv3kpjf1fjchq4m0000gn/T/oink-r2-candidate-6xzcs7fk/summary.json exercise binary SHA-256 ff88b407a6cddb9007f94275c65a80ed4c9c4fd13f5e821f9b7a4a8973abaa56 from CLI input inventory bd8c71b55250a89dc15c7534924bb82a5447d6f2628eba23c8cb3d864309ee9f. All four exact source byte/mode/Git inventories were independently compared equal before/after. These reports supersede preliminary public-command trials:

Final check Exit Source files Page facts Built files References Translation statuses
Starter 0 97 66 223 4,461 28
Documentation 0 421 341 1,139 74,825 144
PIG 0 858 248 1,392 64,440 120
Repository catalog 1 2,294 1,572 3,287 851,535 788

No final report has incomplete diagnostics. The repository catalog retains 10,462 actual merged-print HTML_ID_DUPLICATE findings and 788 informational review states; the other sites retain informational unknown review states. This accepts the tested checking behavior and source preservation, without calling that catalog a passing publication.

Kept production docs at /private/var/folders/df/bfm8q07d7bv3kpjf1fjchq4m0000gn/T/oink-site-2201601475/public passed rendered Markdown and links. Global translation checking returned 1 only for the existing draft release 1.2.0 pair absent from production. The separate explicitly nonpublishable draft/future/expired analysis at /private/var/folders/df/bfm8q07d7bv3kpjf1fjchq4m0000gn/T/oink-site-3698773232/public passed all three node checks: 137 pairs/1,092 headings, 216 content pages/41,586 text nodes, and 347 pages/48,682 internal links/4,171 fragments. Evidence logs are /tmp/oink-r2-docs-production-{translations,markdown,links}.log and /tmp/oink-r2-docs-analysis-{translations,markdown,links}.log. Neither analysis output nor authored draft replaced production or was published.

A final review identified optional equal_fields comparing JSON 7.0 with YAML/TOML numeric 7 by representation. The narrow supplement now normalizes decoded numeric values recursively to an exact rational number tag, preserving strings versus numbers, map keys and array order. Tests cover decimals/exponents, negative zero, integers beyond float64 precision, nested differences, source byte preservation and required incompletion for unrepresentable values. Actual-Hugo translation tests passed in /tmp/oink-r2-numeric-translations-gate.log; all public maintenance actual-Hugo cases passed in /tmp/oink-r2-numeric-public-gate.log; owning vet and whitespace checks passed. Supplemental source SHA-256 values are:

Source SHA-256
internal/translations/check.go 24664377e14b4ae2fc554d0d7fde2ec33cc987707250e130fd88d9a25d5e1637
internal/translations/translations_test.go f58f4a305fe9fe3f5500ddfcf85faf3cfa37d72f8c220a1cb16ce4ccfbddb74d

The frozen real-site reports and Linux qualification above/below predate this supplement. Those sites configured no numeric equality constraint, so their recorded outputs are unaffected and were not rerun for this narrow fix. Later full runtime and archive qualification must refresh the subsequent source. The evidence amendments here are authorized documentation writes after the acceptance runs; their before/after preservation scope ends before this amendment.

A18 remains open. macOS arm64 is exercised; an attempted Darwin amd64 runtime on this host failed with arch -x86_64 / posix_spawn: Bad CPU type in executable (/tmp/oink-r2-darwin-amd64-gate.log). This is unavailable host runtime support, not a code failure or Darwin amd64 acceptance. No system installation was made. Native Linux arm64 and Docker Desktop Rosetta-emulated Linux amd64 were both actually executed with the same runtime/schema/license input SHA-256 0f786df68ef3c4844c983a51595f79242d1cb1d2bf6c5b5eb7f2c6415fb8d861. Evidence is retained at /var/folders/df/bfm8q07d7bv3kpjf1fjchq4m0000gn/T/oink-a18-linux-ajbbnvki in arm64-results, amd64-results, commands.json, candidate-inputs.json, preparation.json and qualify.sh. Each target passed 270 test/subtest cases, with no failures and two optional external corpus/provenance skips: full actual-Hugo go test ./..., vet, built CLI version/bilingual init/doctor/full check/translation status and missing-Hugo exit 2 smoke. JSON stdout and source byte/mode inventories were verified. Go 1.27.1 ran on Linux arm64; Hugo Extended 0.166.0 architecture assets were SHA-verified. This qualifies those source runtime paths, not final archives or hosted CI. Darwin amd64 remains open; cross compilation does not close it. Later stages and future command/adapter/browser acceptance remain open.

R3 local validation

R3 adds managed build --check, oink.artifact/v1 sealing/export/local verification, explicit-network HTTP verification, release diagnosis and guarded local CI generation. The default build/dev path remains ordinary Hugo. The executed runtime and paired contract/guide gates passed; R3 is locally accepted. Hosted CI and deployment were not executed.

Requirement Executed owning evidence Outcome and limit
A08 one checked artifact Public fake/actual Hugo one-renderer tests; exact export, manifest/marker, post-check byte/mode/missing/extra/symlink tampering, failure/concurrency and source-preservation tests Passed local scope; a failed/incomplete check cannot seal/export; local artifact verification does not rebuild
A09 both CI providers Offline deterministic generation, pinned source/Hugo archives and action revisions; safe bootstrap archives; guarded public preview/apply/stale-input cases; actual-Hugo original-input binding Passed local configuration scope; every existing generated target is refused and custom workflows remain unchanged
A09 upload identity Both local provider rehearsals and TestProviderUploadRehearsalPreservesActualSealedManifestIdentity Passed: one managed build, separate verification, then the same tree; GitHub tar includes the hidden marker, Cloudflare rehearsal receives that verified directory; no provider upload executed
A09 custom workflow diagnosis Generated-plus-other-custom and standalone-custom/no-metadata public tests, actual-Hugo preview and owning vet Passed supplement in /tmp/oink-r3-ci-custom-owning-gate.log and /tmp/oink-r3-ci-custom-vet-gate.log; each unrepresented workflow stays unknown, informational and optional release.ci: not_checked, including beside valid generated metadata
A10 deployed identity Local HTTP fixtures for all recorded files/routes/languages, marker, canonical/base/inert-template behavior, HTTP 200 fallback, wrong bytes/language/build, missing resources/Markdown/search JSON Passed local fixture scope; definite mismatches return 1; browser JavaScript is explicitly unchecked
A10 unknown network state Explicit network/credential refusal, timeout before headers/during body, authentication/rate-limit/server errors, required marker absence, bounded body/gzip and redirect/no-cookie fixtures Passed local fixture scope; incomplete states return 2 with remaining requests unknown; no public deployment was contacted
Frozen runtime gates Full tests/vet, owning actual Hugo and focused race checks Passed on macOS arm64 in /tmp/oink-r3-frozen-go-gate.log, /tmp/oink-r3-frozen-hugo-gate.log, /tmp/oink-r3-frozen-race-gate.log; the subsequent custom-CI change has the focused supplement above

The latest single-binary corpus is retained at /var/folders/df/bfm8q07d7bv3kpjf1fjchq4m0000gn/T/oink-r3-ci-final-ahc4csjk/summary.json. It compiled an exact captured CLI input copy, with binary SHA-256 425845c1d2db7b1cd3c3cdb5f28475cb06ba6f656054909759e2359a39925dd2, 67 runtime/schema/license inputs SHA-256 6789a3a0235ff8d81453b9bfde37824979eac7d56af3710da390e4d2ef8479dc, and 108 broader CLI inputs SHA-256 4ca473a4cb586d232baeb4cee029b281469c5bb03c831cc199b95451e6832c60. Runtime inputs remained exactly equal after all runs. Live tools were Go 1.27.1 and Hugo Extended 0.166.0 on Darwin arm64. The manifest’s normalized Hugo version excludes vendor build text and private paths.

Each run used offline build --check with fresh external destination/manifest paths, the optional marker and retained isolated work. These existing local consumer inputs were checked without --release; their configured workspaces were preserved. Every raw report records exactly one strict Hugo renderer, zero incomplete diagnostics and zero required unfinished coverage.

Final managed build Exit Source files Copied source inputs Page facts Built files References Exported files Local artifact verify
Starter 0 97 94 66 223 4,461 224 0
Documentation 0 421 427 341 1,139 74,825 1,140 0
PIG 0 858 861 248 1,392 64,440 1,393 0
Repository catalog 1 2,294 2,299 1,572 3,287 851,535 None Not exported

Both inventories compare exact bytes, modes and file types before/after; the primary inventory also compares logical Git state. Git sites include tracked and non-ignored untracked sources; the non-Git Starter includes its existing generated files and lock. The supplemental copied-source inventory also includes ignored workspace/editor metadata read by snapshots, excluding existing generated output/cache trees. Counts alone are not the proof. All four comparisons were exactly equal.

The repository catalog retains 10,462 existing merged-print HTML_ID_DUPLICATE findings, so neither destination nor manifest was created. This is a complete policy finding, not a passing publication or implementation failure. Production review states number 28/143/120/786; analysis includes unpublished pages, explaining the earlier R2 144/788 counts. Existing custom workflow information remains visible, and Starter’s example address is a warning in this non-release run.

The three fresh exports match the retained independent ordinary-Hugo trees in every original file’s SHA-256, size and mode. The sole extra file is .well-known/oink-build.json. Their original source inventories and complete manifest input hashes match the earlier capture, so reusing those ordinary trees does not substitute different inputs. The helper source SHA-256 is 13e4957a3d7847eb28c8b1eeba3588a4f4a9982c2bfca2ebc729ab2827159607; its binary SHA-256 is b2699fe7a7aa3a34c41f9e4aba4b22d39cf8d0c156a369f3dfc4ca8c8c0fbce5. Raw helper and ordinary evidence are in /var/folders/df/bfm8q07d7bv3kpjf1fjchq4m0000gn/T/oink-r3-candidate-wb643dhh; the temporary compilation source was removed after building the helper.

Earlier R3 captures remain historical: the first capture preceded runtime freeze; the first frozen capture at oink-r3-final-pisrr21h preceded custom-CI diagnosis. An initial /Users/vonng/pgsty/PIG selection returned 2 because that different repository is not the intended site; corrected pig.pgsty.com passed. Those setup trials are retained, not relabeled as candidate failures. This latest corpus supersedes their managed-build results. The CI templates/bootstrap are independently authored from recorded primary provider contracts; no provider implementation code was incorporated.

The scoped R3 documentation gate passed at /var/folders/df/bfm8q07d7bv3kpjf1fjchq4m0000gn/T/oink-r3-docs-render-pljj5aqd/summary.json. Ten paired contract/guide/roadmap/index/overview edits were installed only after matching their original bytes/modes; recorded hashes are at /var/folders/df/bfm8q07d7bv3kpjf1fjchq4m0000gn/T/oink-r3-doc-drafts-s0b3g48l/applied-files.json. Source style passed 88 Chinese docs; translation coverage passed 137 pairs and 1,099 headings; schemas remained equal and scoped whitespace passed. Actual production Markdown passed 214 pages/41,871 text nodes; links passed 345 pages/48,344 internal links/4,171 fragments. Production translations returned 1 only for the unchanged draft release 1.2.0 absent from production. A separate explicitly nonpublishable draft/future/expired analysis passed Hugo and all three owning checks: 137 pairs/1,099 headings, 216 pages/42,177 text nodes and 347 pages/48,720 links/4,199 fragments. All 421 canonical and 488 copied source files retained exact bytes/modes throughout these rendered checks. Analysis was not published and did not replace production. This acceptance amendment follows that frozen preservation boundary.

This gate does not claim hosted workflow execution, uploads, publication, minimum-version combinations, browser behavior or current Linux/Darwin amd64 qualification. A18 remains open; historical Linux R2 results retain their original source hash. Authorized bilingual evidence/contract/guide writes occur after these preservation inventories and are outside their no-write scope.

R4 authoring and upgrade acceptance

The supported R4 implementation and read-only corpus scope are locally accepted after frozen owning/full gates. This record covers profiles, ordinary authoring/editor/snippets and bounded upgrade views. Guarded canonical documentation promotion and fresh scoped rendered validation also passed as recorded below; R5–R8 and final A18 qualification stay open.

Executed profile evidence Outcome and limit
One fixed licensed archive Snapshot verification against commit 137843b25bacd76ddd1f7ce71330bf2e3155b954 passed without --write; archive SHA e55bde279715f6d8d19d3d88671a2cf7561b515be46915b0f12c640d0ce1d958 and MIT license unchanged; projection metadata/script match
Composition and preservation Default/explicit project byte parity; selected archived model/localized home, invalid profile, nonempty target, concurrent validation/publication and cancellation recovery tests passed; unit/vet/race gates passed
Ordinary Hugo All four profiles × three language choices × root/subpath passed 24 actual warning-strict offline builds using provisioned public OINK v1.1.0; complete source byte/mode/no-extra-file and rendered-reference checks passed
Public init workflow Four profiles with en/en,zh, actual subsequent root/subpath Hugo URL facts/checks, workflow/license preservation and default parity passed; unknown/nonempty refusals 1, missing/failed Hugo 2, empty/absent targets and pure JSON/separate logs verified
Public authoring and source identity Actual candidate/apply/ordinary Hugo, review-unknown and source preservation passed in /tmp/oink-r4-authoring-public-gate.log; fresh-directory/site guards and vet passed in /tmp/oink-r4-new-input-race.log and /tmp/oink-r4-public-core-vet.log. Actual ignored input refuses 2 without a saved plan or source writes even when source groups are disabled; selected draft peers still force analysis identity in /tmp/oink-r4-authoring-sourceproof-gate.log. Supported owning scope passed
Bounded upgrade owning gate Seven actual-Hugo synthetic pinned module-fixture cases, observed-stream digest/inventory fidelity, independent cross-page alias-retarget blocking, source/concurrency/exclusive installation and later-edit rollback protection passed under race; vet passed. Final hardening logs /tmp/oink-r4-hardening-owning-gate.log, /tmp/oink-r4-hardening-final-focused.log, /tmp/oink-r4-hardening-vet.log; final public/full frozen gates passed
Integrated authoring/editor hardening Actual-Hugo language-directory plan/apply/ordinary builds, link/never new-source refusal, external schema/license/full-mode/module identity and legacy schema reproof, shared translation/baseline/CI regression and full Starter docs→new draft peer→editor→check→ordinary Hugo flow passed. /tmp/oink-r4-app-authoring-hardening-gate.log (58.241s), focused race and vet passed; post-candidate external mutation proof /tmp/oink-r4-app-external-during-validation.log passed. Opaque saved external-input hashes and canonical workspace-origin guards passed /tmp/oink-r4-external-plan-binding-final.log, /tmp/oink-r4-workspace-origin-gate.log and their vet logs. Frozen full-stage, corpus and scoped canonical rendered documentation gates passed
Actual language mounts Standalone ordinary per-language contentDir and explicit site-matrix fixtures each passed config/mounts/strict-render with source bytes/modes unchanged; Hugo0.166 emits sites.matrix.languages and distinct physical files with reciprocal public translations. /var/folders/df/bfm8q07d7bv3kpjf1fjchq4m0000gn/T/oink-r4-language-mounts-lgmve1sk/summary.json; public actual language-directory plan/apply/ordinary-Hugo integration passed

Owning evidence is retained at /var/folders/df/bfm8q07d7bv3kpjf1fjchq4m0000gn/T/oink-r4-starter-owning-0pv41lw5/summary.json. Logs are /tmp/oink-r4-starter-{unit,hugo,vet,snapshot,race}-gate.log and /tmp/oink-r4-public-init-gate.log, /tmp/oink-r4-public-init-vet-gate.log. Generated source counts are project 94, docs 58, blog 40 and book 34. No Starter checkout edits, release, consumer adoption or deployment occurred.

Final frozen gates all returned 0: make test/vet /tmp/oink-r4-frozen-go-gate.log, make test-hugo /tmp/oink-r4-frozen-hugo-gate.log and actual-Hugo core race /tmp/oink-r4-frozen-core-race-gate.log. The final public flow includes Starter docs → primary/translation draft → editor → check → ordinary Hugo; post-candidate external schema mutation still refuses before source writes. Seventeen owning and three final gate logs are retained verbatim with hashes in the final corpus’s owning-gates.json.

The exact four-site evidence is /var/folders/df/bfm8q07d7bv3kpjf1fjchq4m0000gn/T/oink-r4-corpus-lw2cjyq6/summary.json with bounded summary.compact.json, raw JSON/logs and per-command inventories. Binary SHA is c169b3d4d046c811dca80867068b86fb66ada5c8ce6910dd5cda7353c406f377; 82 runtime-input files bind SHA fdff7f50d49b44f03fa1db79eec6b6c9b9bd5e52f8967a88aed84b3207a7b3c6, which equals the final root inventory with no runtime changes. The broader 139 CLI inputs bind SHA 562e838d9eccb628eac86ae59b9b9587c1e23ad52991ec50eafb1e604e3924da. Driver SHA is d3ac41dc2e18295bfb26134d1a696935c8174913e2801a5766dbf7a1139d89f8. Actual tools were Go 1.27.1 and Hugo 0.166.0 Extended on macOS arm64.

Frozen consumer Primary/copied source files Pages; output files; references Managed build / artifact verify Read-only upgrade / new / editor
Starter 97 / 94 66; 223; 4,461 0 / 0; 224 exported files including marker 0 / 0 / 0
Documentation 421 / 427 341; 1,139; 74,937 0 / 0; 1,140 exported files including marker 0 / 0 / 0
PIG 858 / 861 248; 1,392; 64,440 0 / 0; 1,393 exported files including marker 0 / 0 / 0
Repository 2,294 / 2,299 1,572; 3,287; 851,535 Completed finding 1; no export/manifest Completed finding 1; new/editor not attempted after blockers

Each managed build used exactly one strict production Hugo render and had no required incompletion or uncompleted required coverage. Primary Git-visible source bytes/full modes/logical Git state, supplemental copied inputs and source directory modes matched exactly before/after every command and each complete site flow. Repo’s 10,462 existing merged_print duplicate HTML IDs remain visible; its completed finding is neither a passing artifact nor an implementation failure. No policy or consumer inputs were adjusted.

Consumer upgrade previews selected the available public v1.1.0 pin and did not apply writes. Cross-version route/alias/output regressions use explicit synthetic fixture pins, not an invented published theme release. New/editor plans were validated previews; no consumer plan was saved or applied. Multi-host and unknown relative-alias identities stay incomplete. Nondeterministic output may require a fresh v2 plan preview; browser/universal compatibility, configuration migration and current cross-platform/archive qualification remain outside this scoped result. The prior Linux R2 source hash remains historical; Darwin amd64 and final A18 refresh are still unverified. Authorized bilingual canonical writes occur only after this frozen no-write evidence boundary.

Fresh canonical documentation acceptance passed after the parent applied the ten guarded files. Evidence is /var/folders/df/bfm8q07d7bv3kpjf1fjchq4m0000gn/T/oink-r4-docs-render-v01eima0/summary.json; the promotion manifest is /var/folders/df/bfm8q07d7bv3kpjf1fjchq4m0000gn/T/oink-r4-doc-drafts-3i8bw994/applied-files.json. The frozen c169b3… CLI performed one strict production render and its focused link check returned 0.

Fresh canonical documentation gate Executed result
Source owners Translations 0: 137/137 pairs and 1,104 headings; complete canonical style 0: 137 Chinese files, 181 strong spans, no emphasis; ten-file whitespace check and public JSON schema equality passed
Production rendered Markdown/links Both 0: 214 content pages / 42,214 text nodes; 345 pages / 48,360 internal links / 4,187 fragments
Production translations 1 solely for the pre-existing draft content/blog/release/1.2.0.md absent from production; no new pairing/heading finding
Separate nonpublishable analysis Fresh ordinary Hugo with the actual original snapshot environment/rebased paths and explicit draft/future/expired flags returned 0; all three owners 0: Markdown 216 pages / 42,520 nodes, links 347 pages / 48,736 links / 4,215 fragments, translations 137/137 pairs / 1,104 headings
Source preservation Canonical 421 Git-inventoried files and 427 copied inputs retained exact bytes, modes, Git state and directory modes; production copied 428 and analysis copied 427 files remained unchanged through their checks; analysis build also retained copied full modes

Production output remained separate and was never replaced by the analysis tree; the analysis is not publishable. The temporary helper copied the frozen core without modifying it: helper source SHA 7faea7e726a6c6fb2e0747be1a4428f4c5fb5734fa52b6f981157a5fe37d9989 and helper binary SHA 532638e76f96f8b173c122e512b3bf5fc2c4d4a7130f59c99c2c69e135e87073 are retained with raw logs. This authorized bilingual research amendment occurs after the exact no-write capture boundary and receives narrow source checks separately. R4’s scoped local documentation gate is accepted; this result does not claim publication, deployment, R5–R8 completion or final A18 qualification.

R5 implementation and documentation acceptance

R5’s supported local scope is accepted after focused public/core, corrected frozen full-stage, exact-binary read-only consumer and guarded canonical source/rendered documentation gates. The bounded outcomes remain explicit below. R6–R8, workspace A15 and final A18 qualification stay open. The first promotion and separately authorized post-render status/evidence amendment retain distinct preservation boundaries.

The actual public Git/Hugo flow at /tmp/oink-r5-public-final-flow.log passed in 53.963 seconds. Its committed synthetic site owns its local theme, bilingual pages and binary attachment; ordinary modes 0640 and 0600 remain full current facts while historic Git comparison uses executable bits only. Deleting B selects unchanged inbound A, the remaining translation, removed attachment and actual RSS output. Actual alias-inbound uncertainty, global configuration/template/data and unknown-input changes expand full scope.

Completed inspect/impact/context returns 0 with separate current-check findings 1; check-since retains current quality 1 and full validation scope. Missing/unborn/foreign history returns 2, retaining every known current page/attachment/reference/output with no fabricated prior identity or change. Malformed selectors/limits, missing tools and failed renderer logs are tested. Bounded context gives reasons/versions/source and excerpt hashes, visible omission/truncation and no execution of literal document instructions.

Saved move preview/apply and subsequent ordinary Hugo passed, retaining binary bytes, raw full modes, unrelated files and Git index/revision. Actual opaque HTML/shortcode references remain manual; inline/fenced/opaque spans stay unchanged. Their broken final candidate returns 1, with no saved plan or source writes. Source/config/attachment/mode/fresh-target drift returns 2 and preserves the later edit. A deterministic mutation after the actual candidate renderer also refuses before writes and preserves editor bytes/mode. Focused actual move race passed in 8.286 seconds at /tmp/oink-r5-public-move-race.log; app vet passed at /tmp/oink-r5-public-vet.log.

Before the cached-module supplement, frozen parent make test/vet and make test-hugo both passed at /tmp/oink-r5-frozen-go-gate.log and /tmp/oink-r5-frozen-hugo-gate.log (actual app fixtures 185.709 seconds). Actual move/source race and vet passed /tmp/oink-r5-move-hugo-gate.log, /tmp/oink-r5-source-move-race-gate.log and its vet counterpart; full inventory/mode/selector plan safety passed /tmp/oink-r5-plan-owning-final.log.

A first frozen consumer trial exposed an actual cached-public-module guard gap: resolved module inputs present in the original graph were absent from a fresh outer candidate hash. It returned false incomplete 2, without source writes. Content plans now resolve/capture the same module inputs before comparison, retaining legacy metadata/authoring plan scopes. The separate checksum-verified public OINK v1.1.0 regression passed preview, fresh saved apply and ordinary bilingual Hugo in 27.42 seconds (package 28.220) at /tmp/oink-r5-public-cached-module-move.log, including raw modes, binary bytes, unrelated inputs and Git preservation. Corrected current-binary corpus and supplemental race evidence remain separate from the earlier unaccepted trial.

The corrected current candidate passed full make test/vet at /tmp/oink-r5-corrected-frozen-go-gate.log and actual make test-hugo at /tmp/oink-r5-corrected-frozen-hugo-gate.log (app 278.787 seconds). The cached/public and committed/in-site move safety race passed in 38.578 seconds at /tmp/oink-r5-public-cached-seam-race.log; its app vet also passed.

/tmp/oink-r5-corrected-runtime-freeze.json records 96 runtime inputs with SHA-256 e5b6e0eda972116dbb94a8086668e6ef34bfaa56138cf31f1f71f4832c477842 and 165 broader CLI inputs with SHA-256 4965a0c92cb6126f67a6dabd548c7c25ee5d7c9c57e14cce5e55ebb7a22fca2d. The corrected binary SHA-256 is d7675aecca2f77b1eb37bb4f664c3314cf5207149e6abbb86523686c5c50bff0. These are local working-input/executable identities, not a new commit or published archive. The corrected four-consumer capture completed 16 commands in 831.825 seconds at /private/var/folders/df/bfm8q07d7bv3kpjf1fjchq4m0000gn/T/oink-r5-corpus-corrected-y2eue81h. Its bounded final-receipt.json has SHA-256 b86e0e6c7dbfbaed62c845c03a55d963068f7d9a16771de5ff3b0974e76fce3b; the receipt retains 12 copied owning gate logs, all 20 observed move routes and links to the complete raw JSON/logs and per-move classifications.

Site Primary/copied inputs/directories Inspect/context Current check Impact Move preview
Starter 97/94/21 0/0 0 2: no Git baseline 0: validated, unapplied
Documentation 421/427/109 0/0 0 0: complete historical comparison 1: six candidate missing references
PIG 858/861/52 0/0 0 2: historical foreign-input provenance incomplete 1: 32 candidate missing references
Repository 2294/2299/48 0/0 1: 10,462 existing duplicate HTML IDs 2: unborn HEAD baseline unavailable 1: the same existing duplicate IDs

Starter and Repository impact retain known current facts without inventing prior pages or changes. PIG’s actual baseline is complete (theme v1.0.0 versus current v1.1.0), but required foreign-input provenance is incomplete, so the comparison expands full scope and returns 2. These are distinct outcomes. Documentation impact completes with 192 captured input changes, 343 affected prior/current pages and full scope. All completed fact queries expose current quality findings separately; Repository inspect/context remain 0.

Documentation move proves 18 rewrites and four routes. Two ordinary literal /docs/admin/comments/ occurrences in content/docs/customize/repository.md at lines 216 and 313 remain manual because repeated source/output occurrences cannot be attributed precisely across ordinary and print outputs. Their six missing candidate references block validation. PIG moves two Markdown files and four binary attachments, with eight proven page/processed-resource routes. Equal-byte paired outputs prove processed featured_hu_* resources, but not new URLs for the four original absolute image references /article/pgext-day/{featured,topic,venue,schedule}.webp. Their 32 candidate missing references block validation; no guessed original-asset rewrites occur. These ordinary Markdown limits are separate from opaque HTML/shortcode limits.

Repository move proves ten rewrites and four routes; its candidate has only the same 10,462 existing duplicate-ID findings, with no new missing reference or required incomplete finding. Starter’s zero-link bilingual move is validated. All four moves remain unapplied, no consumer plans were saved and no consumer source writes occurred. Failed candidates have validated: false. All JSON stdout is pure. Primary/copied inputs, full modes, directory inventories and logical Git/index state are unchanged; ignored copied inputs are included. The Git metadata inventory excludes immutable object storage. The runtime and broader CLI inventories still match the captured identities. The receipt does not qualify another platform, browser runtime or deployment.

The first ten-file guarded canonical promotion was qualified in 62.37 seconds with the corrected frozen binary and runtime hashes above. The separate rendered receipt is /var/folders/df/bfm8q07d7bv3kpjf1fjchq4m0000gn/T/oink-r5-docs-render-ks2tw82c/summary.json, SHA-256 06ae844a4b3f1c01bb5faa8a21091d5461c28aab592d12c4310fb34bc176c5d4.

Canonical documentation gate Executed result
Source owners Translation 0: 137/137 pairs, 1,109 headings; style 0: 137 Chinese files, 181 strong spans, no emphasis; scoped whitespace and public JSON schema equality passed
Frozen CLI Production check links returned 0 using the exact corrected binary
Fresh ordinary production Hugo Build 0; Markdown 0: 214 pages/42,571 nodes; links 0: 345 pages/48,376 links/4,203 fragments
Production translation owner 1 only for the pre-existing draft release-1.2 omission from ordinary production output; no new R5 discrepancy
Separate ordinary analysis Hugo Fresh nonpublishable -DFE build 0, without the CLI probe; Markdown 0: 216 pages/42,877 nodes; links 0: 347 pages/48,752 links/4,231 fragments; translations 0: 137 pairs/1,109 headings
Input preservation All per-command and overall guards passed: 421 primary files, 427 copied inputs, 109 directories and 36 mutable Git files retained bytes/full modes/logical Git state; both isolated source copies remained unchanged

The analysis tree did not replace production output and is not publishable. The permanent first-promotion applied-files.json in /var/folders/df/bfm8q07d7bv3kpjf1fjchq4m0000gn/T/oink-r5-doc-drafts-t3_klnck retains the ten authorized files and their original modes. This separately authorized post-render amendment touches only six paired proposal/index/research files, after the recorded no-write boundary; the qualified contract and guide bytes remain frozen. Its guarded originals, prepared diff and focused source checks are retained separately. It does not retroactively claim these later evidence bytes were in the earlier rendered capture, and no full corpus or rendered rerun is inferred from the amendment.

Core owning logs /tmp/oink-r5-frozen-core-hugo.log, /tmp/oink-r5-owning-race.log and /tmp/oink-r5-owning-vet.log passed. A13 impact and A14 move safety passed their required supported CLI scope; A15 bounded context passed, while workspace/direct parity remains R6 scope. No consumer source write, commit, publication, network deployment, remote model integration or incremental speed claim is made.

R6 workspace and adapter acceptance evidence

R6 supported scope is accepted locally after frozen owning/runtime, exact-binary consumer parity/preservation and guarded canonical source/render gates. The supported registry/tool fields belong in the contract and guide. R1–R6 are accepted locally; historical receipts remain intact. A07 adapter and A15 workspace/direct/context supported scope passed the gates below; R7/R8 and final A18 remain open.

The registry is independently versioned oink.workspace/v1: strict one-document regular YAML, 1–64 entries, at most 256 KiB, exact ASCII names, literal relative/absolute directories, proven canonical identities and overlap refusal. Missing-site results stay per-site incomplete while later selected sites run. Selection preserves registry order; no default named site, sibling discovery, Hugo settings duplication or automatic multi-site apply is provided. Optional tools extend oink.policy/v1, with pinned protocol versions, configuration/full-mode provenance and typed omissions/coverage.

Workspace owning receipts

Focused gate Executed local evidence
Registry core Strict fields/document/bounds/names/literal paths, existing aliases/case-inode ancestry, duplicate/overlap refusal, missing-directory listing and exact subset order; go test -race ./internal/workspace -count=1 passed in 1.414 seconds, /tmp/oink-r6-workspace-core-race.log
Public actual Hugo OINK_TEST_HUGO=1 go test ./internal/app -run '^TestPublicR6Workspace' -count=1 -v passed in 10.498 seconds, /tmp/oink-r6-workspace-public-hugo.log
Public race The same workspace public suite with -race passed in 12.426 seconds, /tmp/oink-r6-workspace-public-race.log; excluded commands specifically reject registry selection
Vet go vet ./internal/workspace ./internal/app completed with exit 0, /tmp/oink-r6-workspace-vet.log
Public outcomes Actual bilingual committed fixture sites retain direct diagnostics/coverage/exit parity for links and full checks. A missing first site yields 2 while later clean/finding sites yield 0/1; explicit subsets preserve registry order, invalid unregistered siblings remain untouched and human output retains findings
Selected application Saved translation-review preview is validated but unapplied; a different registered name is refused before writes and preserves plan/source bytes/full modes/Git. Explicit matching-name apply writes only its planned review file; other registered and unregistered sites remain unchanged

These are owning fixture outcomes, not consumer adoption or permission to apply plans to actual consumers. The inspected core workspace.go SHA-256 is cf2cbc9509e8c83eedf6d8833c9eb0ea6492de9a85c959798112fa3f105213f4; its owning test is 9070a8e2c3e58680f6567f2394160ec682bf0457c068c2addf354921e7612d6b; the public test is 3e57a6417ae2e7604f7cb06933759bb06a2f40758ff7059848593cedbaa6570a. All three inspected files retain mode 0600. These owning source captures are covered by the frozen all-runtime inventory below; their individual hashes do not identify the exercised binary.

Corrected protocols and stage gates

Protocol or gate Recorded status
Actual markdownlint-cli 0.49.1 and Vale 3.24.0 Corrected public trial passed: exactly one finding mapped to the original UTF-8/BOM/CRLF line; excluded front matter/shortcode/math/raw HTML/enabled attributes/code produced no false original attribution
Actual lychee 0.24.2 Corrected trial actually reached the local HTTP fixture: 200 → 0, 404 → 1, 401/403/429/503/timeout → required 2. Optional offline → 0, required offline → 2, both with zero HTTP requests
Final focused actual-tool receipt /tmp/oink-r6-public-actual-tools-final.log passed in 15.192 seconds; the earlier corrected 14.686-second run is retained as prior evidence. Node preload and discovered JS configuration did not execute; source full modes/Git were preserved
Fake/protocol failure receipt /tmp/oink-r6-public-fake-tools-final.log passed in 13.089 seconds: malformed output, version mismatch, timeout, unsafe configs, required missing/optional/group omissions and raw stderr normalization
Focused public race/vet /tmp/oink-r6-public-tools-race.log passed in 30.273 seconds across fake and actual cases; /tmp/oink-r6-public-tools-vet.log completed with exit 0
Frozen runtime inputs Parent freeze at 2026-10-03T10:58:01.807947Z, /tmp/oink-r6-runtime-freeze.json: 103 runtime inputs bind b85affd96378b45bfc56a996b0c5672d02ee4c6cc9bc95335fa5072f6c42a03b; 179 broader CLI inputs bind fbb8176ebc58f1aa26336f4e6036cf9bd5f7a0d62b142f16532b50a8071e9fbe. The exercised 0.3.0-r6-local binary SHA-256 is aa8b347fbe01071f9da729f4d98aa2f50d7264456be6c5f05771bcfadadc371f
Frozen owning suites Full Go/vet completed with exit 0, /tmp/oink-r6-frozen-go-gate.log; full actual Hugo plus pinned tools completed with exit 0, /tmp/oink-r6-frozen-hugo-gate.log (app 382.832 seconds). Workspace/core/protocol/source-mask/policy/report race and vet receipts passed and are copied into the final receipt
Four consumer sites Qualified: exact-binary direct/aggregate diagnostics, coverage, exit, identity and registry-order parity for all four sites; per-command/overall source byte/full-mode/type/logical and mutable Git/ignored-input/directory guards passed. Aggregate completed 4, finding 1, incomplete 0, exit 1
Canonical EN/ZH Passed: guarded first ten-file promotion, source owners and fresh ordinary production/nonpublishable rendered evidence; only the known production draft-release omission remains
Stage decision Supported R6/A07/A15 scope accepted locally after the required receipts; R7/R8/final A18 open; no public release, consumer source write, adoption or deployment

The durable focused-tool receipt is /private/var/folders/df/bfm8q07d7bv3kpjf1fjchq4m0000gn/T/oink-r6-tools-6bf67ltv/r6-public-tools-acceptance.json, SHA-256 16b6e47fc0618c76d2f9e3680a4112b6e47b478af8aabd3f2fc84821f840cc8a. It binds the provision record, executable/configuration evidence and 1,422 resolved Node package files. Markdownlint reports original content/tools.md line 7, bytes[80:92] (ppears here.); Vale reports the same line, bytes[71:78] (BADTERM). Each of the seven network cases actually makes one HTTP request. These records do not certify every transitive interpreter, another runtime target or the full consumer corpus.

The exact-binary consumer receipt is /private/var/folders/df/bfm8q07d7bv3kpjf1fjchq4m0000gn/T/oink-r6-corpus-59_asiyr/final-receipt.json, 42,212 bytes, SHA-256 0ad86afaf235bdcff0c474e76b08e0591591a7b22c7992b02e20fb17975d029e; the completed summary binds 467b66eb6d178829508115050d4243909313acf1b4d59317ecda37ab7383ca55. It retains 14 copied owning/completion gate logs. The six original operations sum to 314.912887 seconds, excluding candidate compilation and receipt-only correction. workspace list returns 0; four direct full checks return 0/0/0/1; the aggregate returns 1 with all four sites completed.

Site Preserved source files Direct/aggregate child exit Diagnostics/coverage Recorded finding boundary
Starter 97 0/0 28/29 Translation review information only
Documentation 421 0/0 144/34 Translation review information only
PIG 858 0/0 120/41 Translation review information only
Repository 2,294 1/1 11,250/29 Existing 10,462 duplicate-ID findings and 788 translation review information items

Direct and aggregate child identities, order, every diagnostic and coverage record match. All four consumer sources retain full modes/types, logical and mutable Git metadata, ignored copied inputs and directory inventories after every operation and overall; the complete root CLI inventory also still equals its frozen capture. The 478,603,149-byte direct repository JSON and 635,470,795-byte aggregate JSON were validated streamingly rather than truncated. Optional-tool protocols are qualified by their separate pinned-tool fixtures; the consumer registry is task-local and writes no consumer policy.

The initial acceptance driver overwrote a summarized result’s command string with invocation argv, producing a false parity exception after all six CLI operations and their per-operation guards had completed. The failed driver and summary remain preserved as pre-correction.r6_qualify.py and pre-correction.summary.json. Receipt completion corrected only invocation metadata, verified unchanged raw-result SHA-256 values and header commands, retained all original full-stream diagnostic/coverage digests, and rechecked overall consumer/root guards. No CLI runtime correction or Hugo/CLI rerun was needed. The receipt-only completion took 2.002 seconds and exited 0 in /tmp/oink-r6-corpus-receipt-completion.log.

The executed driver SHA-256 is 4d2a360c6f7f6f96c38698bd189bc4d4b2cb02a7509858920d752897fdd85988; the corrected driver is 4870f5c0374fcc11ad1a6b2e3aefe36f493b6f9f4666c293993dc6a59df8a11b; the receipt-completion driver is cd50d3fe704370f73fa4e7d94ce8e4bc925d11ec8ef04d463aacec37c2053daf. The streaming helper binds 144f778cdb7907372797b47b97f817f340e70423701a2a958dee589281a9a11c, and the inventory helper binds d3ac41dc2e18295bfb26134d1a696935c8174913e2801a5766dbf7a1139d89f8. This receipt qualifies local darwin/arm64 with Go 1.27.1, Hugo Extended 0.166.0, Node 26.9.0 and Apple Git 2.54.0. It does not refresh final A18, qualify Darwin amd64 or another platform, apply a consumer plan, publish or deploy. At corpus capture, canonical promotion and actual rendered EN/ZH owning gates were separate pending work. The later receipt below closes that boundary; the first-promotion bytes do not claim this post-render amendment retrospectively.

The initial actual-tool trial was preparation evidence, not a passed qualification. It exposed Darwin /var versus /private/var staging identity, actual loopback proxy routing, and an invalid inline-block-attribute/line assertion in the Vale fixture. Staging is now canonical; the Vale fixture uses a real standalone block attribute without changing the source-mask boundary. The qualified child environment forwards literal NO_PROXY/no_proxy host-list data while omitting proxy URLs/credentials and Node preload settings. Neither an empty proxy environment nor NO_PROXY=* established the tested Darwin loopback path; no universal operating-system proxy bypass is claimed.

Supported source diagnostics require proven original ranges; rendered lychee locations remain output file/DOM pointers, with no inferred Markdown line. Offline lychee is not invoked. Authentication/rate-limit/server/transport uncertainty cannot become required success through severity, exclusions or baseline acknowledgement. External fragments, browser execution and remote content identity are not proven. The declarations, output envelope and required-incomplete precedence remain independent from final platform/archive qualification; Darwin amd64 and final A18 are still open.

The first guarded ten-file promotion and its fresh rendered qualification are now complete. The receipt is /private/var/folders/df/bfm8q07d7bv3kpjf1fjchq4m0000gn/T/oink-r6-docs-render-dcwtcmyl/summary.json, 555,297 bytes, SHA-256 ee153932900dc6f1ec62beef1a75927fc60b857efccfbcc558bf0e2c2b12cc04. The 64.17-second run used the exact qualified aa8b347f…371f binary and unchanged 103-input b85affd9…a03b runtime inventory recorded above.

First-promotion documentation owner Actual result
CLI production links 0; one strict production Hugo renderer, no analysis build
Ordinary production Hugo / Markdown / links 0 / 0 / 0; 214 content pages and 43,376 text nodes; 345 HTML pages, 48,438 internal references and 4,259 fragments
Ordinary production translations 1 only for the existing draft content/blog/release/1.2.0.md absent from production; not a new R6 failure
Independent ordinary nonpublishable Hugo / Markdown / links / translations All 0; 216 content pages and 43,682 text nodes; 347 HTML pages, 48,814 internal references and 4,287 fragments; 137/137 pairs and 1,118 headings
Source owners / schema Translation, style and whitespace all 0; 137/137 pairs, 1,118 headings; 137 Chinese files, 181 strong marks, zero emphasis marks; CLI/documented result schema both bind 7468c2d04cde8a368ce0ba44a1f27125b5fca364b6d4672353519b9545b3bdda
Preservation All 12 owner commands, CLI/schema checks and overall comparison preserve 421 primary files, 427 copied inputs, 109 directories and 36 mutable Git files with full modes/types/bytes and logical Git; both private ordinary source copies and all 103 runtime inputs unchanged

The first-promotion installer receipt, oink-r6-doc-drafts-ymjop499/applied-files.json, binds 13c965592d64056d8365aed1927d2d422fadec8adc54ee7050b22e2ea0ad6270. It retains captured actual original inodes in private temporary storage, preserving later old-open-handle writes; recovery also preserves later target edits or deletion. The subsequent paired status/evidence amendment has its own full-byte/full-mode guards and source-owner receipt. It updates current notes, command status and this ledger, preserving earlier receipts and configuration examples. Its new bytes were not inputs to the 64.17-second rendered run, and that run is not claimed as their rerender. Supported R6/A07/A15 is accepted locally after these gates; R7/R8 and final A18 remain open. No duplicate corpus, public release, consumer plan application/adoption or deployment is claimed.

R7 read-only Studio candidate evidence

R7 implements the embedded five-view browser and authenticated loopback API candidate described in the contract and guide. R1–R6 historical sections and their exact receipts remain unchanged. Frozen core/browser and exact-binary four-consumer qualification and guarded canonical rendered gates passed within the declared scope. R7/A16 supported read-only scope is accepted locally; R1–R7 are accepted. R8 editing and final A18 remain open.

Focused native and browser evidence

Owning boundary Evidence status
Native/public parity Actual shared check reports preserve diagnostic/coverage/exit identity for 0/1/2; explicit selected workspace startup, cleanup/signal and no-source-write proofs are recorded separately by the owning test receipts
HTTP management/source/preview Literal-loopback selection; exact Host/origin/Bearer checks; no arbitrary request paths/writes; captured source/diff bounds and source-mode/output inventory guards; focused core/new browser and current corpus receipts below bind this supported scope
Initial held browser 14 axe checks with zero violations and 14 screenshots; five desktop light views, captured BOM/CRLF source/diff, desktop dark, mobile dark and all five 320-pixel light views plus capture changes. Synthetic actual-Hugo fixture retains 228 native diagnostics and coverage parity; copied suggestions use a private test clipboard, leaving the host clipboard unchanged
Browser preview attack Actual attack script executes in the isolated preview, but parent access, management fetch and popup are blocked; token query refused. Actual draft-only page remains production 404. This proves the tested browser/CSP scope, not an OS network sandbox
Snapshot preservation Captured source instructions/HTML stay literal data; the initial capture retains its old bytes before refresh after an explicit task-fixture external edit. Source full modes/Git/directories preserved except that declared fixture edit; changes show the actual modified captured input
Preceding held browser Passed refreshed held UI/backend receipt: all 14 axe checks zero violations and 14 screenshots, including declared/captured theme rows. This predates the partial-preview runtime correction and does not qualify that new runtime
New partial-preview browser Passed new frozen partial-preview runtime: 14 axe checks with zero violations and 14 screenshots; actual Hugo normal HTML 200 and 67,108,865-byte output 413; native 1/228 diagnostics and coverage retained, required partial coverage visible and Studio/refresh 2

Initial browser receipt: /private/var/folders/df/bfm8q07d7bv3kpjf1fjchq4m0000gn/T/oink-studio-browser-AfwaYi/summary.json, SHA-256 930fbd1ab86806069b963bff2e3e95aaa07e3634400cec65e2b8c7922e2a1707. Its exact binary binds 92e5b962e40fbe828a0b006f3ae76a2bddf4ad7e8b1c5b6967e365b8f1827879; the subsequent declared/captured theme metadata row is not claimed tested by that preceding binary. The qualified local versions are Node 26.9.0, Playwright 1.62.1, @axe-core/playwright 4.13.0 and Chromium 151.0.7922.34. These are explicitly prepared contributor dependencies, not consumer runtime requirements or automatic installations. Clipboard evidence covers the actual UI click with a private clipboard implementation, not the whole host clipboard.

The refreshed held UI/backend browser receipt is /private/var/folders/df/bfm8q07d7bv3kpjf1fjchq4m0000gn/T/oink-studio-browser-vn0ofb/summary.json, SHA-256 520603779f712539865c6e9ef7a9ad3ad21ec1906adfb067ec607cc69d071b7e, with exact binary 73bf90c69dce84849ee20ddfbfe825b9f2dd46f0cd37a228ce1b041a83afa33f. All 14 axe runs and 14 screenshots passed, including declared/captured theme metadata and all five views at 320 pixels. It retains the same bounded synthetic-fixture/source/preview/clipboard claims above for that preceding runtime. It does not qualify the later partial-preview correction; new browser, full-stage, consumer and rendered-documentation qualification remains separate.

The initial parallel whole-suite trials in /tmp/oink-r7-frozen-go-gate.log and /tmp/oink-r7-frozen-hugo-gate.log failed and are not qualification receipts. The failures were existing ten-second CI-test deadlines under parallel package load and a graph test observer refreshing its own Git index. The isolated CI target sets then passed in 12.149 and 2.291 seconds; the controlled Git-observer graph run passed in 1.354 seconds. Only internal/projectgraph/hugo_test.go changed: its read-only observer disables Git optional locks, filesystem monitoring and the untracked cache. The ordinary actual-Hugo graph run passed in 1.562 seconds after that test-only correction. No runtime or embedded UI bytes changed.

The corrected freeze is recorded in /tmp/oink-r7-corrected-runtime-freeze.json: the 113 runtime inputs retain 15a7de85a1ae9e6a73d8ea6570aa4f97bdd0ad5677ad7ca996fdd081ad43f7b5; the 193 broader inputs now bind 67c6d36cf91d175f208f79cdd4d337aab6d2ef71e43453b20394b677678725e8, with only the test-observer file changed from the preceding 85ad60d24c93e899020fbdcd34f8252c578253ce5afaf8652e561a432ecc8067 freeze. Corrected serial Go tests and vet passed in /tmp/oink-r7-corrected-go-gate.log. The corrected serial actual-Hugo/pinned-tool suite also passed in /tmp/oink-r7-corrected-hugo-gate.log, SHA-256 2aed822ff6fc8be04919aa74ca6ada721789232c14c1d77f1d44113bc0d235a7; the application package took 220.782 seconds. The independent post-Go source-preservation audit is /private/var/folders/df/bfm8q07d7bv3kpjf1fjchq4m0000gn/T/oink-r7-postgo-audit-qygh7bcc/receipt.json, SHA-256 5ee45fe041536243bc1229054516835c61b27909a4f296ac226b1a138e4bc8dc. It verifies the full physical/logical input guards, not Hugo or consumer results.

The durable corrected owning-gate receipt is /tmp/oink-r7-corrected-owning-gates.json, SHA-256 c7f94a740e33a7349886b3f3689419719f857f39031375e80ca384a3dac36e67. It binds both successful serial runs to the corrected freeze and preserves the failed trials as unqualified. The prepared documentation renderer now requires the completed consumer receipt to prove a private captured-source rebuild byte-identical to the final browser binary. Its source-only independent audit, oink-r7-docdriver-audit-ig_r8ud1/receipt.json, binds SHA-256 858cc48bf602fbdb26fcbda03c78ca485338b1295d64257d32cfb14b24f1ade3 and prepared driver f25d9ac4bf1a7ef43d5526b7b3cbadf84dd64ad8a57fd82d1c82e76fdb2b3435. That audit did not execute or qualify canonical rendering.

The first consumer driver trial, oink-r7-corpus-h1lOFl, stopped with KeyError('preview_base_path'): it indexed a field legitimately omitted when the actual preview base path is empty. Its private rebuild was byte-identical to the final browser binary 73bf90c69dce84849ee20ddfbfe825b9f2dd46f0cd37a228ce1b041a83afa33f, and all four consumer source inventories and the root inventory were preserved. That failed driver run does not qualify the four-consumer gate. The fresh oink-r7-corpus-corrected-cByXTa driver changes only those two accesses to get(..., ''), with SHA-256 4c409acacbb9b82e658e6705eddefd9a3541def5c63c340b65678a6c9a8354e4. That fresh run subsequently failed when the repository produced an inventoried file larger than 64 MiB: the preceding runtime refused all production preview. Its native check retained outcome 1, while required unavailable preview made Studio outcome 2. Starter, docs and PIG completed that run with outcome 0; all four source inventories and the root inventory stayed preserved. The failed cByXTa trial is retained and does not qualify the four-consumer gate. Neither empty-base-path driver correction changed runtime or consumer sources.

The parent then authorized a narrow runtime/test correction for partial preview. It keeps the 64 MiB limit, exposes guarded production files within the limit, returns 413 for the exact skipped oversized paths and keeps required studio.preview coverage incomplete. Native check outcome remains unchanged; Studio still returns 2 for required incomplete preview. The embedded UI is held unchanged. All preceding browser/owning/binary/corpus receipts describe their earlier runtime boundaries, not this new runtime. The new owning/browser gates are recorded separately below rather than inferred from those earlier receipts; exact-binary four-consumer qualification remains separate. The old failed capture proved that an output exceeded 64 MiB but did not expose its captured path/size; ignored repository output is not evidence for that capture’s identity. The new bounded coverage detail will record actual omitted relative paths, sizes and count. A prepared real-Hugo browser fixture adds static/oversized.bin at 64 MiB plus one byte to exercise an available guarded HTML preview, an exact skipped-file 413, native outcome 1 and required partial-view outcome 2. That fixture preparation alone was not browser qualification; the subsequent completed browser proof is recorded below.

The new partial-preview freeze is /tmp/oink-r7-partial-preview-runtime-freeze.json, SHA-256 c426ce3e641ed7b39bb711a26006306cab22e761c5062f2164f10deb4bea8765. Its 113 runtime inputs bind 4900ae05abbdf4409b0be54f276fb4135269cf0a49e9071013ccf42544d35c84; 193 broader inputs bind 8b172cef2b228e2642f0139d6cc569136e86843f818e52e412fa4a2d56add25d. Only internal/studio/preview.go changed among runtime inputs; the broader changes also include its test and scripts/test-studio.mjs. All three UI files retain their exact bytes and modes. Focused core final race passed in 1.748 seconds, with vet and scoped whitespace checks also passing. Its receipt, oink-r7-partial-preview-owning-a56dunn4/receipt.json, binds SHA-256 7b6ecb491f283d04fe54347e564dba426b1a84d152040a1d945af54bc67756ac. The initial sparse-fixture mode trial is excluded: host umask 0077 made a requested 0640 file actually 0600; explicit fixture chmod to 0640 corrected that setup without changing production behavior. Focused proof covers normal 200, oversized GET/HEAD 413, changed identity 409, private path 404 and refusal for other unknown output errors. It does not substitute for the subsequent independent broader browser/owning/corpus/render gates.

Whole serial Go tests for the new partial-preview freeze then passed in 61.481 seconds, and vet passed in 0.571 seconds. Completed logs are /tmp/oink-r7-partial-preview-go-gate.log, SHA-256 be7d6eccf99a6f4c1b8f09d1fb782455c7cbd3bad4a2f37e2f0e9da916bcb313, and /tmp/oink-r7-partial-preview-vet-gate.log, the empty SHA-256 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855. The independent held-input audit oink-r7-partial-held-audit-o6p98i1l/receipt.json, SHA-256 e567efc5f580db9395afab8ad36c4db842c3db95db442eb1cb1c740dbd43ec31, verified all 113/193 inputs and physical/logical identities during that parent Go/vet run. It is not a post-suite or browser/corpus/render completion claim. The new whole actual-Hugo/pinned-tool invocation subsequently completed with exit 1 after 285.649 seconds. Its sole failure was the parent’s unavailable Markdownlint preparation path /md/node_modules; all other actual cases passed. The log remains a failed invocation: /tmp/oink-r7-partial-preview-hugo-gate.log, SHA-256 1ab6b8cfd399d484e08a1d1f05d25475754caa731991dd1eec1cca03cf6ce970. The sole owning case was rerun with the exact provisioned /markdownlint/node_modules executable, with no source/runtime change, and passed: application package 2.317 seconds, wall 3.265 seconds. Its receipt is /tmp/oink-r7-partial-preview-corrected-tools-gate.json, SHA-256 62b75e563e8074995ed9dd354434e653b2f5f2c6d20767226286d0c08d4c667c; log SHA-256 is e9bddac210654d219d9c5d6ebabaa3b91a0f5f4de4daf228b3ae21aeaac7673a. The executable comes from provision receipt 268e601e81bc03a263296d57257b85635371bda642d0632532a7d9318c981461. The independent case-matrix/held-source audit verifies cumulative executed actual-owning-case coverage 0 from the failed whole invocation plus that corrected case. Its receipt, oink-r7-partial-case-matrix-audit-16_bl9hr/receipt.json, binds SHA-256 0a9e4a1a1e1a08f597becb2f27e743c9f23df672c713c2757241704edb16b51e. All 113/193 physical/logical inputs remain frozen. Optional TestArtifactCorpus and TestPublishedRuleSourceProvenance cases were explicitly skipped. This never relabels the whole invocation as exit 0, nor claims those skipped cases executed.

The new post-Go input audit, oink-r7-partial-postgo-audit-g1hcqdtl/receipt.json, SHA-256 b369737ec48456f673c850ea702cb3cb7efffb8ecc129d87e00dc03af82b2e3b, then confirmed the complete held 113/193 physical/logical inputs after Go/vet. That scope does not claim whole Hugo, browser or consumer completion.

The new partial-preview browser passed against exact binary f39d6754f7ad13599e4e849394e0f470b2c6f26edf96ce40f199d27b65a8030e, version 0.4.0-r7-local, 16,000,578 bytes and mode 0700. Its summary is /private/var/folders/df/bfm8q07d7bv3kpjf1fjchq4m0000gn/T/oink-studio-browser-cE9be3/summary.json, SHA-256 9099e6c407fd0f9de3c29ce80e03f034a4223d7d7a7c1f1378052e8b9084e0ae; provenance SHA-256 is 85b9f537fb09eecbb09d133b53a297c78184c11200ab0938734c0d10f3449095. The private oink-r7-browser-partial-ZZIqzZ/source-binding.build.json, SHA-256 7a89ab8318c3a38455ab6ce12bcdbc53ae5ce0674fb5aaaa1df0fcf68a093399, binds all 113 runtime and 193 broader source inputs plus physical identities before capture/build/after to the new freeze; root inputs stayed unchanged. All 14 axe checks had zero violations and all 14 screenshots passed, retaining the keyboard/mobile/light-dark/source/clipboard/security checks described above. Actual Hugo emitted oversized.bin at 67,108,865 bytes, reached through its rendered /sub/oversized.bin link and returning 413; ordinary actual HTML returned 200. Required partial preview coverage stayed visible; 228 typed native diagnostics and native coverage/outcome 1 matched the CLI/API/UI, while Studio and the subsequent refresh returned 2. Source preservation still excludes only the declared task-fixture external edit. This is the bounded synthetic browser proof, not a completed four-consumer or canonical render gate.

Another prepared, unexecuted corpus driver had assumed that an available normal preview always appends a studio.preview coverage row. Actual normal Starter/docs/PIG Overviews do not emit that row; the preparation assumption was corrected without changing native coverage. The repaired fresh oink-r7-corpus-partial-pZLwY0 driver, SHA-256 47778df62505beeb7432985be927f1b001e03824e9dee3a6dbed9d9b2dbe049c, was reviewed against those three retained actual Overviews and the current partial browser capture. Normal availability still requires its actual preview URL and independent HTML 200; a partial capture retains its actual required row, omitted count/identities and 413. The preparation audit is oink-r7-partial-driver-correction-audit-sa98cm6k/receipt.json, SHA-256 a519a5bc6ae83438146ff4710d53f5edb0e656a05d0532c02123e5771416f07e. Its earlier f762 preparation was not executed or qualified. The parent has released the corrected driver for a fresh all-four run; its completed qualification is recorded next.

The fresh four-consumer qualification completed with driver outcome 0 in 234.6425 seconds. Its current summary is /private/var/folders/df/bfm8q07d7bv3kpjf1fjchq4m0000gn/T/oink-r7-corpus-partial-pZLwY0/summary.json, SHA-256 d7b5a4f1607b6f75ae6a596c19cbab28685fb67dac750096173060ed097c8bf5; log /tmp/oink-r7-partial-corpus-gate.log binds SHA-256 a7b724500569bd594d8e01502ec1956eb089cc9153b04b693992a9322013c811. The durable current corpus qualification.receipt.json binds SHA-256 4e5df7c3fda9f0b763091af3e6cb85c68c736c319a1de68030b81d5cd5b384bc; primary source inventories contain 97/421/858/2,294 files respectively. The private captured-source rebuild is byte-identical to the new browser binary f39d6754f7ad13599e4e849394e0f470b2c6f26edf96ce40f199d27b65a8030e. Runtime 113/broader 193 inputs and root physical identities stayed frozen; every operation and the overall boundary preserve all four consumer bytes, full modes/types, logical/mutable Git, ignored copied inputs and directories.

Consumer Native outcome Typed diagnostics Studio outcome Actual captured pages
Starter 0 28 review-info records 0 66
docs 0 144 review-info records 0 343
PIG 0 120 review-info records 0 248
repo 1 10,462 existing duplicate-ID findings plus 788 review-info records 2 1,576

Nested native headers, typed diagnostics, coverage and exit match direct CLI checks exactly for all four sites. Issues were fully paginated; other views were bounded samples, with captured physical source and translation diff available on all four. The first three actual production previews returned HTML 200; they emit no studio.preview omission row, and the driver invents none. The repository normal HTML returned 200 with 60,100 bytes. Its current capture exposes exactly four oversized print paths; each actual HEAD returned 413 with zero response-body bytes:

Captured omitted relative path Captured byte size
_print/pkg/index.html 73,976,221
_print/pkg/pgsql/index.html 69,903,999
zh/_print/pkg/index.html 73,086,240
zh/_print/pkg/pgsql/index.html 69,052,754

These identities come from current bounded capture detail and live requests, not the earlier ignored-output clues. Required studio.preview remains incomplete, so repository Studio 2 retains native 1. Actual analysis-only draft routes returned production 404 in docs and repo; that test was explicitly not applicable in Starter/PIG without a unique captured draft route. Workspace subset/full/healthy-subset sessions selected only registered sites and closed listeners without captures; unknown or selected missing sites returned 2 before startup. This is local Darwin/arm64 CLI/API evidence with Hugo 0.166.0 Extended, Go 1.27.1 and Git 2.54.0; browser scope remains the separate synthetic fixture. No source writes, install, publication, adoption or deployment occurred. Independent final corpus audit oink-r7-final-corpus-audit-xr3_u5dt/receipt.json, SHA-256 b8a8eedf5c899fe5830bdde959783c46b3f191ab144c0d3798077555d55238fc, verifies raw typed native/API/shutdown parity, all 132 operation preservation comparisons and four overall guards without rerendering or new HTTP requests. Only guarded canonical promotion/render and the explicit R7/A16 stage decision remain pending; R8 and final A18 remain open.

For temporary disk capacity, the parent retired only three explicitly created private Go build caches, totaling 366,184,826 bytes, as recorded in /tmp/oink-r7-private-cache-retirement.json. Sources, binaries and qualification evidence were retained; no global, user or system cache was removed. This preparation action is not a runtime correction or a qualification gate.

Remaining stage gates and promotion boundary

Required gate Current status
Frozen runtime input/binary identity New partial-preview freeze binds 113 runtime inputs 4900ae05abbdf4409b0be54f276fb4135269cf0a49e9071013ccf42544d35c84 and 193 broader inputs 8b172cef2b228e2642f0139d6cc569136e86843f818e52e412fa4a2d56add25d; all UI bytes/modes unchanged. Source-bound browser binary f39d6754f7ad13599e4e849394e0f470b2c6f26edf96ce40f199d27b65a8030e passed; fresh private consumer rebuild is byte-identical
Full Go/vet and actual Hugo New whole serial Go/vet and browser passed. New actual-Hugo/pinned-tool whole invocation remains exit 1 for a preparation path; sole corrected owning case passed 0, yielding independently verified cumulative executed actual-case coverage 0; two optional cases explicitly skipped
Four consumers Completed exact-binary CLI/API qualification; native 0/0/0/1, Studio 0/0/0/2, exact nested native parity and source byte/full-mode/type/Git/ignored-input/directory guards; repository partial preview remains required incomplete
Canonical paired sources/render First guarded TEN promotion and scoped actual render passed; the post-render status amendment has separate fresh source checks and is not claimed rerendered
Stage decision R7/A16 supported local scope accepted; R1–R7 accepted locally, R8 and final A18 remain open

The next prepared documentation installer retains the actual captured old inode outside canonical source storage on both success and recovery, without unlinking its last name after an earlier target identity check. This private helper hardening and its new recovery fixture are a new preparation boundary; executed R6 installers/hashes/receipts remain immutable and are not retroactively claimed to contain it. R6’s successful promotions already retained originals. No consumer plan writes, release, adoption or deployment are implied by this candidate documentation or the local browser fixtures.

The completed first-promotion rendered gate is /private/var/folders/df/bfm8q07d7bv3kpjf1fjchq4m0000gn/T/oink-r7-docs-render-n8tw2tbw/summary.json, SHA-256 35e79f51d39803b3e4cdf134ed277957dd627acba42e0e0dc785e4745ec3c481, with log SHA-256 de3a07eac661c15805070e0ed2e364a71ebbd38e15d8907aab3bdf716e95131d and elapsed 63.33 seconds. Exact qualified binary f39d6754f7ad13599e4e849394e0f470b2c6f26edf96ce40f199d27b65a8030e passed production CLI links with one strict Hugo build. Ordinary production Hugo without a probe passed rendered Markdown (214 pages/44,075 text nodes) and links (345 pages/48,482 internal links/4,303 fragments). Its translation owner retained exit 1 only for the existing nonpublished release 1.2.0 draft. Independent draft/future/expired analysis passed Markdown (216 pages/44,381 nodes), links (347 pages/48,858 internal links/4,331 fragments) and translations (137 pairs/1,129 headings); it did not replace production output. Source translation/style/whitespace checks passed and CLI/docs schema 7468c2d04cde8a368ce0ba44a1f27125b5fca364b6d4672353519b9545b3bdda remained identical. All twelve operations, schema and overall guards preserved 421 primary files, 427 copied inputs, 109 directories, 36 mutable Git files and 113 runtime inputs.

The first guarded promotion receipt oink-r7-root-promotion-p9g1u7pz/summary.json, SHA-256 323a5ce267e39aaf8f97dc4a12cccbdde83730155a199efb5f3815e29b334e4a, verifies actual original inodes retained outside canonical source. Its post-apply receipt lookup initially used 0 instead of 00; that metadata-only driver failure is preserved, followed by receipt finalization with unchanged raw guards. The successful source installation was not reapplied. Executed R6/R7 helpers and first-promotion receipts remain immutable.

R7/A16 supported read-only local scope is accepted after the frozen cumulative owning-case/browser/corpus and canonical gates above. The original whole-Hugo invocation still has exit 1; the corrected sole tool case plus independent matrix establishes cumulative executed-case coverage. Repository native 1 and required partial preview/Studio 2 remain visible. R1–R7 are accepted locally; R8 editing and final A18 remain open. This post-render status/evidence amendment has its own byte/full-mode guards, unchanged headings/command fences, paired source checks and retained-inode installer fixtures. Its new bytes are not claimed tested by the preceding 63.33-second render; no additional rendering, consumer write, public release, adoption or deployment is implied.

R8 accepted reviewed editing evidence

R8/A17 supported editing scope is accepted locally after the corrected frozen owning/browser/corpus and guarded canonical rendered gates recorded below. CLI edit text, field, snippet and attachment preview the same bound oink.edit/v1 intent used by explicit studio --edit. Saved-plan apply or explicit acknowledged Editor Apply owns selected source writes. The default Studio session remains read-only. This section retains capture-time candidate facts and trials, followed by the completed current qualification; it does not extend earlier R1–R7 evidence to changed code.

Candidate scope and preservation

Known site-owned UTF-8 Markdown is bounded to 1 MiB. Full text and supported ordinary top-level YAML scalar forms retain the declared BOM/line-ending and source-span preservation boundaries; unsupported form shapes remain text. Exact value_json numeric tokens avoid browser Number rounding. Scalar forms bound numeric literals to 4,096 bytes and absolute decimal exponent 10,000; larger/nonfinite constructs remain manual text. Field JSON is at most 1 MiB; escaped lone surrogates refuse, valid Unicode pairs are supported. Catalog components use original UTF-8 body byte offsets; attachments require actual leaf-bundle identity, at most 4 MiB and an exclusive new clean basename. Source hashes, full modes, all site/external inputs, regenerated intent and fresh actual Hugo validation bind the same shared guarded application path.

The Editor displays the complete UTF-8 review, selected-file base/after identity and native candidate result; the review is capped at 2 MiB and its literal bytes are hash-checked before acknowledgement. The actual selected candidate HTML is draft/future/expired analysis, visibly nonpublishable and separate from the original production preview. Required candidate-view incompletion may raise the proposal/session to 2 without changing native findings. For page-file edits, the selected candidate source hash/full mode matches its reviewed After state; attachment/no-op proposals retain the selected page’s reviewed Base state. Stale/replayed plans, attachment collisions and untrusted preview requests are refused; applied-with-refresh-error remains explicitly applied.

Focused preparation receipts

Candidate evidence Current observation and boundary
Pure editing core Owner’s focused exact-numeric tests passed for 18446744073709551615 and 7.12345678901234567890123456789, exact no-op raw bytes and changed final decimal digit; broader frozen receipt pending
Actual DFE output ownership Live ordinary output is copied through a confined os.Root, exclusive target files and guarded streaming reads; files over 64 MiB can be captured while serving limits remain unchanged
Copy cancellation/race Context-aware helper focused 0 in 0.703 s, race 0 in 1.856 s, vet/whitespace 0; actual first-chunk cancellation retains partial output, source bytes/modes/identity unchanged; source FIFO replacement cannot block before descriptor proof
Helper log identities Focused c227a88210ab0dc46b24eaff50a347d5c494e9ce23f5bdef5d5b822efab4976f; race 13f0616d55fd4df791ecded0712a18096392c88cb9b849383414c305e50b6779; vet is empty SHA-256 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
Read-only candidate integration review Selected actual HTML URI/base prefix and inventory, retained private DFE lifetime, source SHA/full-mode equality, native versus view coverage and cancellation reviewed; no new material defect found within this code review scope
First Editor browser trial Harness stopped on ambiguous global Open editor selector; retained as failed trial, no UI qualification claim
Corrected-selector Editor trial Desktop field/component/binary apply checks and axe checks passed before 320 px draft-review horizontal overflow failed; retained original trial, not a final browser pass
Narrow layout correction Editor review hashes/receipt text wrap and intrinsic widths are bounded; prior CSS and failure evidence retained separately. Development rerun passed 12 axe checks/screenshots, including real 320 px dark review and light receipts/refusals; final frozen-source/binary rerun pending

The helper evidence is focused file-copy/cancellation qualification, not the whole editing application or all platform support. Browser trials describe their actual stopped scope. They do not establish final A17, consumer adoption, deployment or a successful current frozen browser binary.

The preceding preparation rows were captured before the first complete R8 freeze. They remain development history. The first whole frozen gates later passed for binary 84b804d3246a5be581e44884ed910fa3f45d8be29734b8babdeeb763a11fa882 (0.5.0-r8-local), runtime 123/58517b8e98b80df6642be4ee6275a0074ec187768b20e009f41da2607b635d46 and broader 212/d81335c78413acc60e27adee0ac794862285e41cb2a3a7687ec820c1065ba003. The whole-gate summary is b49f4a3272af3e3dcc92e7e9b38d4289bb49cb19aa3e9354ea148d2d8cb2cea8: Go tests 0/56.523 s, vet 0/0.904 s, whole actual Hugo plus all three pinned tools 0/320.044 s, core race 0/16.610 s and public R8/helper race 0/48.319 s. Its source guards passed. These receipts qualify those earlier bytes only.

The first frozen browser receipt eb6977235ef9ae6cec28651b5654eb101685af67abe0cbed0acb0f8375458d9c binds that same binary and source freeze. Editor had 12 axe runs with zero violations and 12 screenshots; retained read-only Studio had 14/zero/14. The actual form preserved the literal 1e400, its planned after hash and diff, then discarded it; exponent ±10,001 and a 4,097-byte numeric literal refused locally without an API request. Four acknowledged field/component/binary/draft applications occurred only in disposable fixtures. Default read-only refusal, stale preservation, no-op source bytes, preview isolation and native-result independence passed. This is development browser evidence for the first freeze, not a four-consumer or current corrected-runtime acceptance.

The first exact-binary consumer trial then stopped at Starter after 37.533 s. Its immutable failed-trial receipt is 853a8397ba4c527c03aa3cc9ac7cacc41c0ab0379549d145b874f1d1ecc3901c. Two failures are recorded separately. A driver event hash depended on JSON object-key order even though recursive comparison proved API/CLI arrays equal: 28 diagnostics, 29 coverage rows and native exits 0/0. Separately, repeated resolved cache capture produced a genuine duplicate module input .gitattributes; required candidate graph capture became incomplete, mutation outcome 2, with native check still 0, no actual selected DFE HTML and no applicable lease. The public published-cache fixture reproduced that defect; its retained failing log is 50ab704e715665096e0f36391bb1364841c3a2b2ac88dd262915ce53fb66afa6. All 48 recorded per-operation source proofs and four overall consumer guards preserved bytes, full modes, types, Git, ignored copied inputs and directories; root inputs stayed exact. No Apply or saved plan was performed. This stopped trial has no completed four-consumer qualification claim.

The narrow runtime correction gathers complete resolved-module rows before committing additions. Identical repeated or reordered captures retain the original inventory. Changed hashes/full modes, added or removed paths within an existing scope, missing scopes, conflicting identities or capture errors refuse without refreshing prior evidence or appending partial additions; non-module rows remain exact. Site owning receipt e7b284b9dece1c5f2b696cd76166d2286fcbec69e6c48912ab9a78204bdb980b records focused 0/0.746 s, site 0/2.123 s, focused race 0/1.958 s and vet 0/0.167 s. This compares reobserved complete rows; it does not lock module files against concurrent writers.

The corrected published-cache receipt 6358dc81f06e34b789cb47a0f4442d6d036d2e33423a06f5dbe85b3064766da6 records actual github.com/pgsty/[email protected] from a task-local copied checksummed archive, with no downloads or replacement. Original and candidate graphs each have 1,256 unique inputs, including 1,198 module inputs. Full API/CLI typed diagnostics, coverage, exits and plan identity match; actual selected DFE HTML returns 200, with source bytes/full modes/Git unchanged and no Apply or saved plan. Owning race passed in 30.255 s; vet passed. Its corrected race log is c7d21a30b8af141d9d9604a80ddf9cf3f608320b97a441a06a742376e2119551.

The current complete corrected freeze is fb276500a3d2643bd0aa220f8bebb380fce2c98493d62b0502b6497b2f02949f, runtime 123/cdf629eeb4bbef6d4d88ee27fe3fb0a73b07b6bf6438336e033a18fb7feb1c17 and broader 212/f6e305e792733a550814eb841615d12fa14a9a6bb2a97c4ada85f7275183e579. Only source_inputs.go, its owning test and the public published-cache test differ from the first freeze; held UI/helper bytes and all full modes remain unchanged. The rebuilt candidate is bd25f9e0b35ec10e227aabf9582ae40b0b367390f64b93668de6ae85222c3d71 (0.5.0-r8-local). Its observed corrected source-bound browser receipt 33a698985a55c14c3e64e981da1f8e74c686497083dc0edb241406f185e3eeb8 again reports Editor 12/zero/12 and read-only 14/zero/14 with complete 123/212 pre/post source preservation. Corrected whole owning gates, the fresh four-consumer corpus and protected canonical rendering are still pending at this evidence amendment. R8/A17 is not stage accepted; final A18 stays open.

At the next evidence observation, the corrected whole gates completed for that held bd25f9e0…22c3d71 binary and fb276500…02949f freeze. Summary 208f156c0954e803eccbada678a4689683dc1576c543c379e5cc04b3497ef772 records Go tests 0/57.826 s, vet 0/0.521 s, whole actual Hugo plus all three pinned tools 0/378.847 s, core/site/Studio race 0/17.937 s and public R8/attachment/output-helper race 0/85.159 s. Every gate’s source pre/post guard passed. The actual-Hugo log has 434 top-level passes and zero failures; the two optional external fixtures TestArtifactCorpus and TestPublishedRuleSourceProvenance remained explicitly skipped. Those skips are not claimed as executed corpus or provenance qualification.

The corrected whole actual-Hugo log is 4eb1a2afdce2adbe570b10922fd53b6d8954f7c95747370c3c661e94d2f71a05; Go log ea59463e9649ffe2f8aff9da66c91cf6895c86fde96a524db23c89cd4eb35925, core race cdd3d761b5ca7b5e986b25aee3129d65663e3e5ebb83eecb6fbb080387298a58 and public race bb610bdcd7a299cb9b66f4c69e30e246c20546efae47653c01d350b1026ea2de. The corrected browser-only evidence above remains bound to the same current source and binary. The separately authorized fresh four-consumer trial is in progress; no completed corpus, canonical render, R8/A17 acceptance or final A18 qualification is inferred from these owning gates.

The 434 passes and two optional skips above are top-level counts. The same whole invocation also skipped the nested Unix-socket refusal fixture because the Darwin temporary pathname exceeded the socket limit. A first shorter private-path trial still skipped: receipt 93106854ca890b497d3c74522b895f597ac60cec55ced42cad7b187d334da200 retains process exit 0 but explicitly records no actual socket execution and failed qualification. It is not relabeled as a passing fixture.

A subsequent nonresolved short private TMPDIR executed the same frozen socket fixture under race detection without a skip: 0/2.954 s. Receipt 531a503b3b91e1b423c2be61b92ed806d3a813738c38d57e5ec122577b4337f9 and log 236c84f1842ffce76174c834f3888718a109cec6377ada6f3242b02f551f00b3 bind fb276500…02949f and all 123/212 logical/physical/Git inputs unchanged before/after. This supplies the actual socket-refusal case without changing source or the original whole invocation’s skip history. Corpus, canonical render, R8/A17 stage acceptance and final A18 remain pending.

The preceding pending-corpus statements record their observation times. The corrected four-consumer trial subsequently completed in 845.705 s. Summary af4fc53326163c4a03aa2982c1f01363fbbdd5a447c9baed3639bd8599d46370 and qualification receipt 4d6fd02543c1920497e1a1bb0a68fcf89b0546130fd9cc12c1df391b7e673e75 bind a byte-identical private rebuild of bd25f9e0…22c3d71, the complete held 123/cdf629ee…feb1c17 runtime and 212/f6e305e7…5183e579 inputs. Original failed corpus, published-cache regression and first frozen browser/gate bytes remain separate historical evidence; all 2,383 entries of the first failed trial retained their exact bytes/full modes/types.

Corrected consumer Native/current and native candidate exit API candidate outcome Full diagnostics/coverage Actual selected analysis HTML
Starter 0 / 0 0 28 / 29 200, 48,149 bytes, /blog/design/content-model/
Documentation 0 / 0 0 144 / 34 200, 61,738 bytes, /blog/oink/immersive-reading/
PIG 0 / 0 0 120 / 41 200, 55,641 bytes, /404/
Repository 1 / 1 2 11,250 / 29 200, 92,352 bytes, /blog/infra/2020-12/

These are actual selected Hugo HTML routes in the separate, visibly nonpublishable draft/future/expired candidate view; each expected candidate marker was present. API and CLI matched full typed diagnostics/coverage, native exits, plan ID, Base/After hashes and full modes, unified diff and the selected page’s proposed source. The complete literal API review and its hash were independently validated. No consumer Apply or saved plan occurred, and no listeners remained. The repository retained 10,462 existing duplicate-ID findings and 788 information records. Its four actual PRINT outputs remained required partial-preview incompletion: _print/pkg/index.html 73,976,221 bytes, _print/pkg/pgsql/index.html 69,903,999 bytes, zh/_print/pkg/index.html 73,086,240 bytes and zh/_print/pkg/pgsql/index.html 69,052,754 bytes. Each bounded HEAD request returned 413 with zero body; selected in-limit HTML remained 200. Native 1 remained unchanged, proposal/session 2 and Apply refusal stayed visible. The other three complete previews had no invented explicit complete-coverage row: actual guarded HTML 200 supplied that evidence.

Exactly 53 protected operations each checked all four sources: 212 per-operation source proofs plus four overall proofs, with source bytes/full modes/types, copied ignored inputs, directories and logical/mutable Git unchanged. Each source proof compared four inventory categories, yielding 864 raw inventory pairs including the overall comparisons. All 53 root guards and the final complete 123/212 logical/physical inputs also matched. All issues and pages were paginated; the other five view endpoints were sampled to their first 50 records, with one known source and one bounded diff per site. Full native/CLI record parity used the declared bounded complete-record codec; object order was canonicalized while array order, types, null and field presence remained significant. This does not claim every relationship was visually reviewed or every source was edited.

Independent audit receipt 6b72ca06d8392a5271fc40757f176f26e1144c21eec93dbd035e0a1bd645657b verified 69 artifact hashes, complete bounded API/spool/shutdown typed records and the large native/CLI raw-file digest bindings. It did not separately repeat multi-gigabyte native semantic scans. Its additive receipt 335f137663d4ec0b2a0d3e49c8d70b9918f86078ab6264855171a2644d8aa6c6 also verified the fresh current root’s complete logical Git inventory against the freeze; the original audit stayed immutable. Corrected owning, socket, browser and four-consumer supported scopes are qualified. Canonical TEN promotion/rendering, the R8/A17 stage decision and final A18 remain pending.

The preceding R8 candidate/trial statements retain their capture-time scope. The reviewed first TEN promotion subsequently passed through the guarded retained-inode installer, root receipt 7cd9b4604d2340b9e46965a26281c921b967060909d518b8b4b31e5f42d0120c. Its actual original source inodes remained retained outside the documentation site; unselected source/copied inputs, directories and Git, and complete CLI 123/212 logical/physical inputs stayed unchanged.

The separately authorized canonical render then completed exactly once in 67.21 s, summary bb0d0710294f810fb14284f7b5b0329befbd290b66c21397b9a45e8382287fb6, qualification receipt 32d3ffeca43bc9ad4615edcca0d3cc47cc932bbc93c6576724c800e0a62405b1. It used the exact qualified bd25f9e0…22c3d71 binary and corrected 123/212 freeze. Actual CLI production links passed 0 with one strict Hugo build. Independent ordinary probe-free production Hugo/Markdown/links passed: 214 Markdown pages/44,691 nodes and 345 link pages/48,532 internal references/4,351 fragments. Its translation owner retained 1 solely for the existing release/1.2.0 draft absent from production. Separate explicitly nonpublishable draft/future/expired Hugo analysis passed Markdown (216 pages/44,997 nodes), links (347 pages/48,908 references/4,379 fragments) and all translations 0. Analysis did not replace production output.

Source translations passed 137/137 pairs and 1,143 headings; Chinese style passed 137 files/181 strong spans/zero emphasis, whitespace passed and schema SHA-256 7468c2d04cde8a368ce0ba44a1f27125b5fca364b6d4672353519b9545b3bdda matched exactly. All 12 commands, schema and overall guards preserved 421 primary source files, 427 copied inputs, 109 directories and 36 mutable Git files, plus all 123 runtime/212 broader CLI logical/physical inputs. The qualification receipt binds 60 canonical inventory pairs, 15 CLI guard pairs and six private-copy source pairs; it claims no consumer writes or deployment.

R8/A17 supported local editing scope is accepted after corrected owning, socket, source-bound browser, exact-binary four-consumer preservation and these guarded canonical gates. R1–R8 are accepted locally; native repository findings and required partial-preview 2/Apply refusal remain visible. Final A18 current Linux/runtime/archive qualification stays open. The separate platform-authority audit 762571dab9a07651ac8e4c71764bfef292f8d5eba729a089e72d9d755b7e2d7c confirms that the initial contract qualifies actually exercised architectures: macOS arm64, native Linux arm64 and emulated Linux amd64. Darwin amd64 remains an experimental archive with failed actual execution/unverified runtime; its history is preserved, and no successful cross compilation becomes a runtime pass. Both current Linux runtimes and final archives still require fresh proof.

This post-render status/evidence amendment has separate full-byte/full-mode and inode guards, unchanged stable IDs/command fences, paired source checks and retained-inode installer fixtures. Its new bytes were not rendered by the preceding 67.21-second run. No repeated rendering, consumer source write, public release, adoption or deployment is implied.

Required gate matrix

Required gate Current status
Final immutable runtime/source freeze and exact CLI binary Corrected complete 123/212 freeze and bd25f9e0…22c3d71 bind completed owning/browser/corpus/canonical scope; first-freeze trials separate
Public CLI/JSON/exit, stale source/config/external-input and guarded writer tests Corrected public R8/attachment/output-helper race, whole Go/vet/actual Hugo and canonical stage gates passed
Frozen whole Go/race/vet and actual Hugo/ordinary Hugo after selected application Corrected whole Go/vet/actual Hugo and core/public race passed; 434 top-level passes, zero failures, two optional external fixture skips explicit; first trials remain separate
Editor browser five-view parity, text/forms/components/binary attachments, exact numeric/no-op, stale rejection and preview isolation Corrected source-bound Editor 12 zero-violation axe runs/12 screenshots and read-only 14/zero/14 passed; bound completed corpus and canonical acceptance
Exact-binary four-consumer read-only qualification Corrected all-four completed in 845.705 s; full typed native/API/CLI candidate parity, 212 per-operation source proofs plus four overall, no Apply/save/source writes; first failed trial preserved
Protected canonical TEN promotion, EN/ZH source/schema/style/whitespace and actual production/analysis render Guarded first promotion and independent exact-binary 67.21 s render passed; only known draft translation omission in production; rendered and status bytes separately bound
R8/A17 stage decision Supported local scope accepted after corrected whole owning/browser/corpus/canonical gates; R1–R8 accepted locally
Final A18/platform/archive delivery Open; compile success alone is not runtime qualification

Passed and pending entries are explicit; later gates are not inferred successes. Prior R1–R7 sections, whole-invocation failures and scoped acceptance receipts remain unchanged. Temporary qualification files stay outside canonical content and Git; first canonical promotion/rendering has exact receipts, while this status amendment remains a guarded proposal. No public release or deployment is claimed.

Current runtime completion supplement on 2026-10-04

This supplement records the current candidate on 2026-10-04 (Asia/Shanghai). The dated page URL and all initial 2026-10-03/R1–R7 records remain unchanged. The preceding R8 stage and browser/render receipts are historical input-bound proofs; they do not qualify subsequently changed backend bytes. The three UI files retain exactly the browser-qualified bytes and full modes. Current Go, Hugo, platform, archive and four-consumer checks refresh the changed backend.

The first current ARM offline unit run exposed a real output-copy integrity gap: adding a directory entry on ext4 could retain the parent’s allocation size and observed timestamp. That failed run stopped before later qualification steps. The bounded correction captures and rechecks actual sorted directory membership and entry identity, alongside regular-file byte/full-mode proofs. Only internal/app/studio_output.go and its owning test changed. The failed receipt and independent audit are retained; a failure never becomes a passed run.

The preceding integrity-correction qualification freeze is 683daca0e522193c7ff1b0de6ac2fee5d2fca080811bf184a8dfd5b90a33f224: 123 runtime inputs hash to d346ad15cd4239004e32e1b9f30d727eaf156be0187dc165ca874032a7cf962a, and 212 complete CLI inputs hash to 2abd1a044d8192b07f9bbc06b55dc8b4544d66ca17b8867971cec702ba3af088. The 0.5.0-r8-local Darwin arm64 candidate is 74ad94e73557f6538cd64edd1766d6df92c596d98411031159d94af072c186ec. The observed integrity-correction receipts below bind that source scope; old R2 Linux and earlier R8 binary receipts retain their historical scope. The later one-test fixture amendment has its own complete source identity and completed formal qualification boundary, recorded below.

The current complete source freeze is now 196245a3ba09305e34b86539c8eb79f1473e4373ee47aa1f56f8933b04a42d43. The 123 runtime inputs remain exactly d346ad15cd4239004e32e1b9f30d727eaf156be0187dc165ca874032a7cf962a; the 212 complete CLI inputs are 2c487bfb4c65ed40ff78356b2860de627e6ac1afa0da2df433b09345dab7f5b0. Only the owning published-cache test changed, to source 54c10ef89310256b5f4c165c7de5de9668e1d4d2991b71751076680141dbe779. The fixture amendment is separately guarded; production bytes and all semantic assertions remain unchanged. Formal qualification of this complete source, including current eight owning gates, reproduced archives and full plain-Go AMD/ARM runs, passed. Root A18 proof 2c018cb2afa3f26699a9e6b5a0971096246b12405fde5a27e43a9e213e46da60 binds all three declared supported targets and five reproduced archives. Earlier receipts retain their captured inputs; they are not relabeled as runs of this amended test source.

Current proof and preserved earlier input boundary Observed result and bound receipt
New complete-source formal qualification Freeze 196245a3ba09305e34b86539c8eb79f1473e4373ee47aa1f56f8933b04a42d43, owning test 54c10ef89310256b5f4c165c7de5de9668e1d4d2991b71751076680141dbe779, unchanged runtime123. Current eight gates, host/archive and both full plain-Go Linux flows passed, bound by root A18 proof 2c018cb2afa3f26699a9e6b5a0971096246b12405fde5a27e43a9e213e46da60; this does not claim final document bytes were already rendered
Narrow integrity correction Owning receipt 6966d768025497b45958073d4c53a2a2981065c8a95857834dcf6a4faa4f0201; independent audit 6cb5d2eabf57b41079026a38a674f46def9f56a15df17ad27e671e4f765798df
Prior-source six frozen owning gates Build, full offline Go unit/vet, whole actual Hugo/pinned tools, core race and public R8/output-helper race all 0; elapsed 3.501/68.257/3.909/333.801/37.070/79.670 seconds. Summary b40b7787b3da8dc1e0763812b6dde529b4b5b69fe479d79940f1161223124e1d; independent audit 20780662b7ff35019b2c8c84e6dc763f9351ae0816f6ef7a7789f7a15be99167
Eight current frozen owning gates Selected published-cache actual Hugo and race, build, full offline unit/vet, whole actual Hugo/pinned tools, core race and public R8/output-helper race all 0. Current summary d6272fcc4dfab114aecfcdf19a7e2b78f1e931817331b460f43ff2056bf754a4; raw whole Hugo has 435 top-level passes, no failures, two optional top-level skips and the explicit long-path socket child skip. Runtime/binary bytes remain identical
Prior-source Darwin arm64 and archives Current extracted candidate runs outside the checkout with no consumer Node requirement. Seventeen commands and eight actual process tests, including child signals, passed without process-test skips. Two fresh release directories contain byte-identical five archives and checksums; source/license/provenance/canonical tar checks pass. Summary bcb4d7599e965c1b3cfe7fe698ca14061ad53d45e7a194337aeebb8d37aa77c1; independent audit 60a04771365d8be15ac91fbbd8d485b019aae081598e468018861ca5734e387c
Current Darwin arm64 and deterministic archives Seventeen extracted-archive/ordinary-Hugo/process commands passed expected exits, with missing Hugo explicitly 2; eight actual signal/process cases ran without skips. Two independent fresh builds reproduced five byte-identical archives from current complete source. Summary 3890fd8468b6bce5271bb32ffa1a18bd5daf99c19c43becac0be8e3b908a5d57; source, tools, module-cache and smoke-source guards remained equal
Prior-source Linux arm64 Actual nonroot Linux arm64 on ext4 with Go 1.27.1, Hugo Extended 0.166.0 and Git 2.47.3: full offline Go unit/vet, all 13 required pure top-level pass records and the membership case plus its four children without skips, 10 selected actual-Hugo cases without skips, native rebuilt archive identity, installed bilingual/offline/ordinary-Hugo/missing-Hugo 2 JSON and signal/source-mode checks passed. Guest summary 409990bc1425f4bf219f8911a71581af6e68729865580121dbeb6d85a06d2ea7; outer receipt a022e40f068703cd59ce6d6a7fb6530cce6907681baa26eb1dfc77c09f0c8898; exported-record audit 24afc50f6f860394d1ebfa7a8b754ddd9cb97f9e88a0dcfcbcb659193ecbfe5f
Current Linux arm64 Current nonroot Linux arm64 on ext4, native ARM through QEMU HVF, Go1.27.1/HugoExtended0.166.0/Git2.47.3: full offline unit/vet (370 top-level passes), all13 decisive pure cases and4 membership children without skips,10 selected actual-Hugo cases without skips, exact native/installed current archive identity and bilingual/offline/ordinary-Hugo/signal flows passed. 24 commands reach expected exits including missingHugo2. Guest 268102f69c0950f9d2994d22cd2fd290fc11e24bd6d6f916fd70a93ca4946c74; outer f3c066fdc9b97feff92160346185a1af978a5172eed5c81904ac7c0e5fc6c982; source/SDK/borrowed/old-task guards equal and owned VM reaped. Default optional unit skips retain their named gating reasons; no full Linux Hugo-suite/browser/linter claim
Prior-source Linux amd64 failed trial Unqualified after the preserved current TCG trial failed: outer receipt 3543664ba5590f2ba5a8f676b196bb636b72bc819913289f415d0a8a841c1bdb, guest summary 16075204d287713c7f7650c0a65dd289dd4bd83db07c9ba4b85b3f21244d5240. Full offline units (370 top-level passes), vet and the first three selected Hugo cases passed. The published-cache candidate request hit the test HTTP client’s 90-second deadline; candidate parity, the remaining six selected Hugo cases, native rebuilt archive and installed archive smokes were not reached. Deadline review 12917b9eb89e3abc5893e08da3b6b6e20743dcb4c14e6f7ba8561628e3566934. A18 stays open; no future preflight or full qualification result is inferred
Current Linux amd64 Current nonroot Linux amd64 on ext4, QEMU TCG emulation, Go1.27.1/HugoExtended0.166.0/Git2.47.3: full offline unit/vet (370 top-level passes), all13 decisive pure cases and4 membership children without skips,10 selected actual-Hugo cases without skips, exact native/installed current archive identity and bilingual/offline/ordinary-Hugo/signal flows passed. 49 commands reach expected exits including missingHugo2. Guest 3a1a32979efc843de8b95b7c13824026e17f71c06d4c458b738c0b9583fb4723; outer 30cf4950cc83fa0732047d9a0f89bb59e68779ee2e8f5c755724c9679be265e3; source/SDK/borrowed/old-task guards equal and owned VM reaped. Default optional unit skips retain their named gating reasons; no full Linux Hugo-suite/browser/linter claim
Runtime-equivalent preceding four-consumer candidate corpus Source epoch 683daca0…33f224; runtime123/binary74ad is byte-identical to current 196245a3…42d43. The corpus was not rerun after the test-only amendment. 853.249 seconds; native/candidate-native 0/0/0/1, API/view 0/0/0/2; diagnostics 28/144/120/11250, coverage 29/34/41/29. Summary a1e98ca3e10095a1134381666bacf256f8e8827cd3900c9e811b7120de4c2974, receipt 05c4562a50d9f83ba2c99879ec841870c5e753199e41792bd5bc718cf8046e7b, independent audit 3a1b0b6e3a8c6b1a0d82c5f82b46c84b1e44d6c30bab655610cb9e86e6a30b47; final TEN bytes have their own render boundary
Final canonical lifecycle and rendered checks The exact promoted TEN bytes require independent canonical rendering and navigation/URL receipts; earlier rendered proofs do not qualify these amended bytes

The preceding six-gate b40b7787b3da8dc1e0763812b6dde529b4b5b69fe479d79940f1161223124e1d, host/archive bcb4d7599e965c1b3cfe7fe698ca14061ad53d45e7a194337aeebb8d37aa77c1, and ARM outer a022e40f068703cd59ce6d6a7fb6530cce6907681baa26eb1dfc77c09f0c8898 / guest 409990bc1425f4bf219f8911a71581af6e68729865580121dbeb6d85a06d2ea7 / audit 24afc50f6f860394d1ebfa7a8b754ddd9cb97f9e88a0dcfcbcb659193ecbfe5f remain passed only for their captured source. They are retained alongside the new exact-source proof, not overwritten or relabeled. Historical 26 axe checks/screenshots and 22 codec cases are carried with unchanged UI/codec/runtime inputs, not claimed re-executed.

The initial max-CPU AMD trial stays failed: receipt 3543664ba5590f2ba5a8f676b196bb636b72bc819913289f415d0a8a841c1bdb, guest summary 16075204d287713c7f7650c0a65dd289dd4bd83db07c9ba4b85b3f21244d5240. The test client timed out after 90 seconds awaiting candidate headers; candidate parity and the remaining six selected Hugo cases/native rebuild/installed smokes were not reached. Guest inputs stayed exact; the host guard recorded only a .git directory timestamp change, whose cause was not proven. The separate qemu64 one-test preflight also failed at the unchanged 90-second HTTP client deadline: outer receipt fc68173ccdfd8ce263ecdf082a533d9da666a4cc2e1e5e29880ee827286132ac, guest summary e350ff65feeee166ffac1d337db9bbd70d3895b1fb6d93df0a30ca4de09019fc. The named case took 177.71 seconds, compared with 176.64 seconds in the first trial; no CPU-model speedup is inferred. Its inputs remained exact and its VM was reaped. Neither failed trial is relabeled as a pass.

A later, explicitly nonqualifying Go-overlay diagnostic preserved the same production source and every original semantic assertion. Outer receipt 0bc6d563b7cd9ca862717c2123ee0836d83b6b927d00204a0b031049c38e93f0 and raw-bound classification 66a1422cdb79ab9f1cf683f441ade0ce4adb4a7a666d524c4b9ed98ebee28708 record a passed named case in 352.40 seconds. Original capture took 26.254 seconds, Studio capture 26.211, candidate HTTP 94.312, direct preview 94.318 and CLI preview 81.962. Both graphs retained 1,256 unique inputs, including 1,198 module inputs. The old original-capture context was expired by the HTTP result; fresh independent direct/CLI contexts completed normally. All 20,564 host guards and five guest command guard pairs stayed exact; the owned VM was cleanly reaped. This diagnostic altered test budgets and is not exact-source or full A18 qualification. The scoped owning-fixture amendment now uses a 300-second budget for that candidate request and fresh direct/CLI operations, about 3.18 times the slowest observed operation. General/original-capture 90-second limits, restoration of the shared client, shutdown 15 seconds and Go’s default ten-minute cap remain unchanged. These are test fixture limits, not a product performance SLA. Formal plain-Go AMD/ARM and current archive qualification is recorded in the current table above; the diagnostic itself remains nonqualifying.

This preceding corpus qualifies the unchanged runtime CLI against its captured, unchanged pre-final-TEN consumer inputs. It does not qualify subsequently amended canonical document bytes; the final TEN has a separate rendered receipt boundary.

The consumer driver compared complete typed diagnostics, coverage, native exit, PlanID, selected Base/After/full modes, unified diff and selected source between API and CLI. It separately verified the complete literal API review and its hash. For attachments and no-ops the selected page retains reviewed Base; page-file edits match reviewed After. Nonissue views are bounded samples; all issues and pages are paginated. The 53 protected operations have 212 all-four per-operation source proofs plus four overall proofs (864 raw inventory pairs across four categories) and 53 root pairs. Seventy-one retained artifacts are bound. No Apply, saved plan or consumer write occurred. The completed corpus’s file-only collector needed two preserved metadata corrections for absent historical trial/self-test files; no CLI/Hugo operation was rerun. The existing 22 negative codec cases are historical checks of unchanged codec bytes, not a newly executed self-test.

The repository retains its 10,462 pre-existing duplicate-ID findings and 788 review-info records. Its selected actual DFE HTML is available, while four oversized actual PRINT files stay unserved (413, zero response body): _print/pkg/index.html 73,976,221 bytes, _print/pkg/pgsql/index.html 69,903,999, zh/_print/pkg/index.html 73,086,240 and zh/_print/pkg/pgsql/index.html 69,052,754. The 64 MiB per-file preview bound is unchanged: required partial-preview incompletion remains 2, native findings remain 1, and Apply is refused. This is an expected diagnostic outcome, not a failed preservation check.

Linux prerequisites were prepared in exclusively owned guests from signed Debian metadata: exactly ten new packages and three approved existing-package updates, verified before and after installation. SDK/Hugo/module caches were provisioned separately and reused offline. Qualification runs as an ordinary user on ext4; cached inputs and all 212 source files’ bytes/full modes stay guarded. Optional tools/browser tests are not silently claimed on guests without those prerequisites: default unit skips retain their actual gating/not-applicable reasons, while all required pure top-level cases, the no-skip membership children, selected Hugo and signal cases must execute. Linux amd64 uses QEMU TCG on the ARM host and is explicitly emulated. Darwin amd64 remains an experimental archive: actual execution returned Bad CPU type (errno 86), with no Rosetta installation or claimed supported runtime. Windows is outside the declared scope.

The current Linux archive digests are ac883e54a1df0b820696279c63881ba75a00d279f507330128fe8d5aff59c52e (arm64, 4,552,687 bytes) and 2dde43bf94ef35aac2111b07dcb9b2766fbf9f883fe39ccd646d14a98b94d734 (amd64, 5,034,668 bytes). The preceding 683daca0…33f224 archive digests c191383af21913be6940ec41be11755b3d985344bbc0f65cc3f5de16424a96a4 and 6531b27d889260afe804c1f49f37541fbae46e57b5d17a20178c28cb51968794 remain historical. Cross-compilation alone does not establish runtime support. SDK/guest preparation failures, the first ext4 membership failure, and earlier private host metadata/resources trials remain immutable evidence. Local completion does not establish a commit, public release, consumer adoption, hosted CI execution, deployment or public-site verification. Uninvoked E1–E4 extensions are separate inactive scope and do not hold finite R1–R8 completion open.

Acceptance case ledger

This ledger combines the initial audit with accepted R1–R7 evidence and the qualified R8 candidate gates. Each full case stays open until its entire outcome is recorded; an accepted stage does not close later-stage scope.

Case Required outcome Code or checker evidence Status and missing decisive evidence
A01 One oink.result/v1 JSON result; stderr logs; policy 1, required incompletion 2 Protocol/public R1–R8 commands, frozen owning tests and exact-binary CLI/API reports; unchanged result schema; current eight owning gates/Linux qualification plus unchanged-runtime carry-forward of the preceding corpus in #a18 Passed supported current command scope; future added commands require their own evidence
A02 Hugo resolves slug/url/permalinks/aliases, mounts, unlisted pages and language roots Real PageFacts/manifest/custom-mount/translationKey fixtures; preserved ordinary artifacts; final consumer facts R1 scope passed; later stage-specific use of those facts requires its own acceptance
A03 Definite local missing routes fail; outside origin/path and declared external scope classified honestly Actual rendered-reference fixture plus subpath/policy regressions and final real sites Passed the required A03 scope; external availability remains explicitly unchecked
A04 Filename, directory and translationKey; duplicate/missing/draft cases; strict/localized policy R2 translation engine, actual Hugo/public commands, final reports and numeric supplement Passed R2 required scope
A05 Absent record unknown; changed source/translation hash visible; no mtime inference R2 hash/status/diff, public preview/apply and final reports Passed R2 required scope
A06 Real fences, inline code, shortcodes, HTML, attributes, unknown fields and protected text boundaries R2 actual syntax/provenance fixtures, reviewed corpus and final reports Passed R2 required scope; catalog and unsupported-source limits remain explicit
A07 Acknowledged findings visible; new findings block per policy; required unavailable tools cannot pass R2 baseline/public plans; R6 fake/actual protocol, missing/unsafe/offline/network-uncertainty and required-precedence fixtures passed Supported scope passed; required unavailable/uncertain tools remain 2
A08 Post-check bytes invalidate manifest; provider uploads verified tree without another build R3 manifest/export/tampering/public one-build tests; final ordinary-Hugo comparison and provider rehearsal Passed R3 required local scope; provider upload not executed
A09 Both CI templates; custom workflows preserved; permissions/variables/provenance and stale plan protection R3 offline generation/bootstrap, public preview/apply/stale-input tests, custom workflow supplement and local rehearsal Passed R3 required local scope; custom workflows remain unknown and unchanged; hosted CI not executed
A10 Reject HTTP 200 fallback, wrong language/build, missing resource/canonical mismatch; incomplete timeout/auth/rate-limit R3 explicit-network local HTTP and public result fixtures, including required identity absence Passed R3 required fixture scope; public deployment and browser runtime not verified
A11 All declared profiles/languages; target protection; ordinary Hugo; unknown editor settings retained R4 24 ordinary Hugo/public profiles, full Starter authoring/editor flow, snippets, actual mounts, source identities, JSONC preservation and external schema reproof Required supported R4 local implementation/corpus scope passed; documented unsupported editor inputs remain explicit
A12 Readable diff and route comparison; dirty/workspaces/replacement/vendor; recovery/concurrency Frozen actual-Hugo seven synthetic pinned cases, public upgrade, source/external guards, observed alias retarget and guarded partial rollback Required bounded R4 local implementation/corpus scope passed; unknown redirects/multihost remain incomplete and no automatic config migration is claimed
A13 Deleted B finds unchanged inbound A; translations/attachments/derived outputs; global full scope R5 committed Git/actual Hugo deletion, alias-inbound, global/uncertain input and unavailable-baseline fixtures; exact-binary consumer reports Passed required supported R5 scope; unavailable or unproven historical inputs stay explicit 2
A14 Candidate before apply; stale/hash/write failures preserve later edits; ambiguous references unchanged R2/R4 shared safety, R5 full-mode/inventory moves and R8 regenerated intent/fresh-input candidate validation, guarded writer and stale/late-editor/attachment tests; current eight owning gates/Linux qualification plus unchanged-runtime carry-forward of the preceding corpus in #a18 Passed supported R5 CLI and R8 CLI/Studio editing scope; ambiguous or unavailable required inputs still block
A15 Workspace/direct parity; selected writes only; bounded context with paths/versions/reasons; no content execution R5 bounded captured-source/context fixtures and four-site queries; R6 registry/direct/aggregate parity and explicit-name saved apply with other sites preserved Supported context/workspace scope passed; no implicit batch writes
A16 Five useful CLI-parity views; keyboard/mobile/light/dark; source/preview isolation Accepted R7 evidence retained; historical source-bound R8 read-only 14 axe/screenshots and Editor 12 axe/screenshots with unchanged UI bytes; current backend gates, ARM and corpus separately verified, native/API parity, preview isolation, four consumers and canonical render passed; current eight owning gates/Linux qualification plus unchanged-runtime carry-forward of the preceding corpus in #a18 Passed supported local views; required partial-preview incompletion/native findings remain visible; no universal browser/platform claim
A17 No-op bytes; YAML unknown/comment/order preservation; stale-save and attachment collisions rejected Corrected frozen core/public/guarded-writer race, actual Hugo/tools, source-bound Editor/read-only browsers, exact-binary four-consumer proposal parity/preservation and guarded canonical source/render passed in #r8; current eight owning gates/Linux qualification plus unchanged-runtime carry-forward of the preceding corpus in #a18 Passed supported local editing scope; required partial preview/native findings still block Apply; final A18 separate
A18 Actual declared macOS/Linux runtimes; child signals; provisioned offline runs; honest unsupported inputs Current freeze/source and repeated five-archive reproduction; Darwin arm64, native Linux arm64 and emulated Linux amd64 nonroot ext4/full offline unit-vet/selected Hugo/native archive/signal smokes passed in #a18 Passed current declared runtime/archive scope; optional guest prerequisites remain explicit skips; Darwin amd64 is experimental/unverified and Windows outside scope

Candidate sites and source preservation

The selected acceptance inputs are the embedded Starter plus three distinct maintained consumer sites. They reuse the historical corpus without writing consumer sources. The Starter source checkout is provenance input; generated profile trials use disposable directories.

Input in the sibling checkout layout Initial observed identity and purpose Current candidate acceptance
oink-starter / generated Starter Source 137843b, two initial status entries; licensed fixed archive, language/profile/root/subpath trials R1 bilingual init/check and R4 all-profile ordinary/public authoring flows passed; archive/license unchanged
oink.pgsty.com Source 907d873 with existing changes; bilingual documentation/regression and explicit local-theme trial Final R1 check and source preservation passed; local-theme evidence remains distinct from public-pin evidence
pig.pgsty.com Source 75050c0, five initial status entries; root Docs/Blog rewrites and nonrendering sidebar entries; declared v1.1.0 Final R1 check and source preservation passed
repo.pgsty.com Unborn main, no HEAD revision; materialized untracked sources, generated catalog and declared v1.1.0 Final R1 check and source preservation passed; revision remains unknown

For each run, record effective module source and versions, flags/network policy, exit/result/coverage, raw evidence location, and preservation outcome. Before/after inventories must include all tracked and non-ignored untracked source bytes and modes, Git status/index state, workspace/replacement files, and effective vendor inputs. Compare exact inventories; unchanged file counts alone do not prove preservation. Keep reports, isolated candidates, output and caches outside consumer sources and outside Git. Full-build timing comparisons must use the same current input baseline before any incremental speed claim.

Owning checks and documentation gate

Start with the smallest affected Go packages and public behavior tests. The existing repository gates are make test (offline tests and vet) and make test-hugo (actual Hugo Starter, snapshot, manifest and public command fixtures). The owning Hugo gate now runs all owning packages without the old narrow test-name filter; new fixtures must remain in that gate. Use a race run for concurrent plan/server changes when the focused tests justify it. Unit fixtures remain offline; networking requires explicit invocation.

For documentation, preserve EN/ZH heading number, order and stable explicit IDs. The narrow source checks are:

node scripts/check-markdown-style.mjs content/docs/design/research
node scripts/check-doc-translations.mjs

Both source checks passed after adding this record and its Chinese peer: eight Chinese research files passed the style checker; translation source coverage was 137/137 pairs with 1,082 source headings. These checks establish source style, pairing and explicit translated IDs only. Rendered acceptance was not run by this documentation audit.

After building the relevant site, complete the rendered documentation gate:

npm run _check:markdown-style
npm run _check:translations
npm run _check:rendered-markdown
npm run _check:rendered-links

make build validates the declared published pin. make check selects the sibling theme for the complete non-browser regression suite; these inputs cannot substitute for one another. Studio requires its own actual browser and accessibility acceptance. A passing prose source check does not prove rendered bilingual output or Studio interaction.

Delivery state and remaining limits

State Current completion evidence; history retained above
Local implementation Finite R1–R8 supported implementation completed locally, including Studio/read-only and opt-in reviewed editing; current A18 runtime/archive scope passed. Canonical lifecycle rendering is bound separately to these exact bytes
Local validation Historical R1–R8 owning/browser/corpus/render records retained; current 2026-10-04 backend correction and eight current owning gates, actual three-target runtime/archive checks and unchanged-runtime carry-forward of the preceding four-consumer preservation/parity passed in #a18. Required repository findings/partial preview remain visible. Rendered navigation/URL checks have a separate exact-byte receipt boundary
Commits Baseline CLI commit identified; no maintenance commit established by this record
Archive and runtime qualification Current corrected source: Darwin arm64, native Linux arm64 and QEMU-TCG-emulated Linux amd64 passed installed archive/offline/signal/filesystem flows; two fresh builds reproduce all five archives. Darwin amd64 remains experimental/unverified after actual failed execution
Public distribution and consumer adoption Not performed by this work
Deployment and public-content verification Not performed by this work; local HTTP fixtures can prove the verifier without cloud credentials

The finite R1–R8 implementation and required current A01–A18 runtime/archive scope have decisive local evidence. Canonical lifecycle rendering requires a separate receipt for these exact new documentation bytes; preceding rendered evidence does not qualify them. Uninvoked E1–E4 and experimental/unsupported platforms do not add unfinished core requirements. Publication, pushing, deployment, hosted CI and consumer writes remain separate unperformed actions; known repository findings and required preview incompletion remain diagnostic limitations, not hidden successes.

7.10 - CLI acceptance snapshot, 2026-09-29

Executed Starter, real-site, offline, upgrade, and reproducible-archive checks for the local CLI candidate, with final acceptance and publication kept separate.
Local implementation and acceptance completed

The local 0.1.0-dev implementation passed the checks recorded here. The CLI source is committed as e623d93; public publication, downstream adoption, and production deployment remain separate and have not been performed.

Inputs and method

The CLI lives in the independent oink-cli Go repository. Its accepted boundary is the CLI and result contract, with reproducible user steps in the usage guide. Hugo remains an external renderer; generated sites contain normal Hugo inputs.

Input Observed baseline
Host macOS, darwin/arm64
Go go1.27.1
Hugo 0.166.0+extended+withdeploy
CLI 0.1.0-dev, local commit e623d93d589c49e5c58b8fae1bd5db720fc904cb
Embedded Starter Commit 137843b25bacd76ddd1f7ce71330bf2e3155b954, complete licensed Git archive
Generated theme pin Public github.com/pgsty/oink v1.1.0, with recorded Go checksums
Documentation-site theme Local theme HEAD b0af631 plus uncommitted changes; this is not the public module’s byte identity

The Starter archive hash is e55bde279715f6d8d19d3d88671a2cf7561b515be46915b0f12c640d0ce1d958. Its recorded projections select an existing language profile, pin OINK v1.1.0, and set enableGitInfo: false for a new directory. The last projection was required by an observed failure: the original enableGitInfo: true caused a warning-strict build to fail before the site’s first Git commit. No Git repository or commit was created to hide that failure.

Checks used disposable source copies, module/render caches, and output directories. Original Starter and consumer source trees were not written by the CLI checks. Existing unrelated theme and documentation edits were retained. Counts below are snapshots of those inputs and CLI revisions, not thresholds that later documentation edits must preserve.

Starter and ordinary Hugo

All six ordinary-Hugo cases passed --environment production --panicOnWarning, with provisioned modules and isolated caches:

Language profile Root URL /manual/ subpath Reported Hugo pages
en Passed Passed EN 90
en,zh Passed Passed EN 91, ZH 89
all Passed Passed EN 91, ZH 89, FR 89

The tests checked expected language roots and representative Docs, Blog, and Book outputs, and compared generated source bytes before and after Hugo. The public CLI’s init command also passed separately for all three profiles, with zero diagnostics and 94 generated source files per profile. The three profiles differ in the selected root configuration; untranslated sample files remain in the snapshot and are disabled through the existing profiles.

Starter package unit, race, and vet checks passed. Its failure cases exercise nonempty and symlink targets, validation failure, target replacement after planning, cancellation rollback, concurrent modification/deletion, and archive path rejection. The regeneration script reproduced the fixed archive, provenance, and license exactly.

Real-site inspection snapshot

Each run below returned CLI exit 0 with zero recorded diagnostics. Counts describe rendered artifacts and inspected references; they are not counts of authored pages or independent users.

Site shape and theme source Files HTML files References Machine artifacts
Three-language Starter, public v1.1.0, release check at /manual/ 316 142 7,042 6
OINK documentation/regression site, local theme HEAD b0af631 plus dirty changes 1,127 506 72,562 8
PIG project site, root Docs/Blog route rewrites, public v1.1.0 1,392 424 64,440 4
Repository documentation with generated catalog, public v1.1.0 3,287 1,635 851,535 12

The last three are distinct local consumer repositories. PIG and the catalog site validate published-pin resolution. The OINK documentation run validates the explicitly selected local theme changes; it cannot be substituted for a public-pin or deployed-site acceptance result. Source instructions were read before these read-only pilots.

The inspection covered the implemented HTML link/anchor/resource and emitted machine-artifact checks. It did not execute JavaScript, check external URLs, inspect hosting redirects, or perform browser, accessibility, and visual acceptance. The completed Hugo manifest enumerated 261, 766, 662, and 3,192 output declarations respectively. Every supported enabled machine output was required by its actual language and URL. Before/after manifests compared all tracked and non-ignored untracked source bytes, modes, and Git status: unchanged for all four sites (94, 415, 858, and 2,294 source files respectively).

Two real regressions were fixed during this work. A NAVJSON template that rendered only English had previously hidden the missing Chinese output; it now returns policy exit 1 with the missing output location. PIG’s intentional build.render: link sidebar entries were initially mistaken for missing pages; Hugo’s effective build parameters now exclude them, with direct and cascaded regression cases. A paired build of the bilingual Starter also proved all 223 ordinary artifacts byte-identical before and after adding the isolated probe.

Offline execution and upgrade

On macOS, a full check of an initialized bilingual Starter passed under sandbox-exec with (deny network*), after dependency provisioning. The result was exit 0, zero diagnostics, 223 files, 95 HTML files, 4,461 references, and four machine artifacts. A separate English init also passed under the same OS-level network denial, returning exit 0, zero diagnostics, and the expected 94 generated files. These are executed network denial tests for those operations, not Linux firewall tests or evidence for every possible consumer’s remote-resource workflow.

A cold-cache fixture requiring example.invalid/[email protected] returned CLI exit 2 and retained Hugo’s original module lookup disabled by GOPROXY=off evidence. A missing dependency was therefore reported as incomplete work, without silently enabling resolution.

A separate temporary site exercised a real public-module upgrade from v1.0.0 to v1.1.0. The original consumer was not used as a write target:

Operation Observed result
Preview Exit 0; candidate validated; applied: false; plan named only go.mod and go.sum
--write --expect-plan Exit 0; the matching plan was validated and applied
Repeat the same target version Exit 0; candidate validated; no proposed changes and applied: false

Unrelated dirty README.md content and untracked user-note.txt survived all three operations. The preview and write shared the same plan ID and before/after module-file hashes. This proves the exercised single-site path; it does not establish vendor refresh or an upgrade performed by an independent user. Replacement, workspace, dirty-file, rollback, and failure-protection cases passed the final focused Go tests and race run. Only go.mod and go.sum changed on write; backup manifests retained original bytes. Preview and repeat executions preserved all source bytes.

Real thin-wrapper smoke tests also passed in a disposable initialized site. build --json returned 0 and produced index.html. dev --json served HTTP 200, forwarded SIGINT to Hugo, and closed the listener. Hugo exited 0; the cancelled wrapper reported 2 under the documented cancellation semantics. These runs used provisioned local caches without --network.

Final make test (all packages plus vet), make test-hugo (ordinary Hugo, workspace/config precedence, output manifest, missing-language regressions), and go test -race ./... passed. All three public init profiles were rerun under OS-denied networking; the cold dependency fixture again returned 2.

Archive and installation preparation

One frozen CLI source snapshot produced four binary archives and one source archive, plus SHA256SUMS. Rebuilding independently from the source archive produced the same SHA-256 values for all five archives. The tested packaging input hash was:

b07c5b98ef787dfe9924ce7b50c57d018c6149ec493124bb0103551a01535547

This final snapshot supersedes the intermediate archive experiments. All five archive checksums were verified and reproduced from the extracted source archive. Both source and binary archives include the versioned JSON schema, licenses, dependency pins, and Starter provenance. Local make install into a temporary prefix and the installed binary’s --version succeeded.

Target Evidence
darwin/arm64 Compiled; host binary executed; local installation path exercised
darwin/amd64 Cross compiled only; not executed on that architecture
linux/amd64 Cross compiled only; not executed on Linux
linux/arm64 Cross compiled only; not executed on Linux

The archive builder records toolchain, flags, source-input hash, and platform limits. It prepares local files only. There is no public download URL or published installation tag established by this test.

Reproduce the relevant checks

From a CLI checkout with dependencies already provisioned:

make build
make test
make test-hugo
go run scripts/snapshot-starter.go --source ../oink-starter

To repeat rendered-site checks in the sibling layout, keep JSON and logs outside each consumer’s source tree:

oink_acceptance_dir="$(mktemp -d)"
mkdir "$oink_acceptance_dir/reports"
./bin/oink init "$oink_acceptance_dir/my-docs" --languages all
./bin/oink check --site "$oink_acceptance_dir/my-docs" --release \
  --base-url https://example.org/manual/ --json \
  > "$oink_acceptance_dir/reports/starter.json" \
  2> "$oink_acceptance_dir/reports/starter.log"
HUGO_MODULE_REPLACEMENTS="github.com/pgsty/oink -> $(cd ../oink && pwd)" \
  ./bin/oink check --site ../oink.pgsty.com --json \
  > "$oink_acceptance_dir/reports/docs.json" \
  2> "$oink_acceptance_dir/reports/docs.log"
./bin/oink check --site ../pig.pgsty.com --release --json \
  > "$oink_acceptance_dir/reports/pig.json" \
  2> "$oink_acceptance_dir/reports/pig.log"
./bin/oink check --site ../repo.pgsty.com --release --json \
  > "$oink_acceptance_dir/reports/catalog.json" \
  2> "$oink_acceptance_dir/reports/catalog.log"

On a macOS host providing sandbox-exec, after initializing a bilingual site:

./bin/oink init "$oink_acceptance_dir/my-bilingual-docs" --languages en,zh
sandbox-exec -p '(version 1) (allow default) (deny network*)' \
  ./bin/oink check --site "$oink_acceptance_dir/my-bilingual-docs" --json \
  > "$oink_acceptance_dir/reports/offline.json" \
  2> "$oink_acceptance_dir/reports/offline.log"
sandbox-exec -p '(version 1) (allow default) (deny network*)' \
  ./bin/oink init "$oink_acceptance_dir/offline-en" --languages en --json \
  > "$oink_acceptance_dir/reports/offline-init.json" \
  2> "$oink_acceptance_dir/reports/offline-init.log"

The upgrade guide describes preview, plan review, and explicit application. Use a separate review copy for write-path testing. For the archive experiment, keep the same Go toolchain and release version:

make release VERSION=0.1.0-dev DIST=dist/first
mkdir -p dist/rebuild
tar -xzf dist/first/oink_0.1.0-dev_source.tar.gz -C dist/rebuild
make -C dist/rebuild/oink_0.1.0-dev_source release \
  VERSION=0.1.0-dev DIST=dist
cmp dist/first/SHA256SUMS \
  dist/rebuild/oink_0.1.0-dev_source/dist/SHA256SUMS

Limits and delivery state

State At this snapshot
Local implementation Six first-stage commands and versioned result format exist
Executed validation The runs described above passed for their recorded inputs
Owning checks and documentation-site make check Passed after implementation and bilingual-document updates
Commit, tag, push CLI committed locally as e623d93; no tag, remote, or push. Documentation changes remain local alongside existing work
Public CLI release or distribution Not performed
Consumer source adoption or production deployment Not performed by these checks
Independent-user study or adoption No measured 4-of-5 / 15-minute study, retention, or independent-team adoption data

Raw JSON, logs, source-preservation manifests, upgrade recovery evidence, and archive-verification results are retained locally under the CLI checkout’s ignored tmp/acceptance/; archives are in dist/first/. They are local evidence, not published downloads. No browser suite was run because this delivery changes CLI behavior and prose, not theme presentation or interaction.

No Docsy conversion is implemented in this first-stage candidate. The pilots above already use OINK and cannot validate arbitrary Docsy or MDX migration. The roadmap retains bounded Docsy assessment and later migration, theme descriptor, version lifecycle, OpenAPI, MCP, and Studio as separate proposals. No future capability is accepted or counted complete by this local evidence record.

8 - Design proposals and PRDs

The canonical bilingual home for OINK PRDs and designs that are still being evaluated.
Non-normative material

A proposal describes behaviour that may not exist. Current behaviour is defined by the contracts, accepted decisions, implementation, and owning checkers. Never use a proposal as a configuration reference.

This section is the canonical home for OINK product requirement documents, RFC-style designs, and unresolved maintainer proposals. Do not create a local plan/, plans/, proposal/, or parallel design tree in the theme repository or the documentation repository.

Active proposals

Proposal Current boundary
Backlinks and knowledge graph G1 (static backlinks) is accepted, implemented on the theme’s main branch, and ships with OINK 0.8.0; the local and global graphs (G2/G3) remain draft
Media convergence Partially implemented; the media-result contract and Landing resource metadata shipped, M3 resolved for native-image processing, retirement (M4) open
OINK CLI and the next product stage Independent Go repository and first-stage boundary accepted; local CLI candidate implemented, not publicly released; later theme, migration, adoption, versioning, OpenAPI, and platform stages remain proposals
Visual presets and appearance switching Paper/Slate locally implemented; Ink/Terminal remain research; see the accepted decision and dated acceptance record

The bulk agent-index proposal retired after the outputs shipped. Its stable behaviour now belongs to Architecture, and user steps belong to Agent-ready output. The Book publication proposal likewise retired after BookManifest and the EPUB/PDF tooling shipped. The stable behaviour belongs to Architecture and Writing a book; dated downstream adoption evidence belongs to Consumer evidence. Remaining consumer adoption does not keep an upstream design proposal active. Both proposal drafts remain available in Git history.

The generated-configuration-schema proposal has been retired through the lifecycle: the behaviour is documented normatively in Configuration, the long-lived rationale moved to the generated configuration schema decision, and the draft text is preserved by Git history.

CLI workspaces and adapters

Explicit workspaces and optional adapters remain in the current reduced CLI. The current contract and usage guide define the command boundary. The dated R1–R8 and A18 record is historical source/binary-bound evidence. It does not qualify later command or output changes. The finite maintenance roadmap remains retired from active navigation; no public CLI release or deployment is established.

Where a new PRD goes

Create one English-primary page and its Simplified Chinese peer:

content/docs/design/proposals/<slug>.md
content/docs/design/proposals/<slug>.zh.md

Use explicit, stable English heading IDs in both files. Keep code, keys, paths, versions, and API names unchanged in Chinese. A proposal begins with visible draft status and includes:

  1. status, owner, date, and affected contract surface;
  2. context and evidence;
  3. goals and explicit non-goals;
  4. proposed behaviour and output/accessibility/security boundaries;
  5. compatibility and migration impact;
  6. implementation and owning-checker plan;
  7. acceptance criteria and open decisions;
  8. a decision log for later changes to the proposal itself.

Large experiments may add a dated page under ../research/, but temporary logs and generated artifacts stay outside Hugo content and outside Git.

Lifecycle

draft proposal
    ├── rejected/superseded → remove from the active tree; preserve Git history
    └── accepted
          ├── implementation + owning checker
          ├── affected EN/ZH contract
          ├── accepted Design decision when rationale is durable
          └── changelog, migration, and user docs when their audiences need them

Acceptance does not turn the PRD into a second contract. Move stable behaviour into the owning contract, stable rationale into Decisions, and user steps into the relevant guide. Then retire the proposal from active navigation. A local build, commit, tag, public module, consumer pin, and deployment remain separate completion states.

Review gate

Before implementation, reviewers confirm that the proposal does not duplicate an existing shell, resolver, component family, or data authority. During implementation, a changed design updates this bilingual proposal before code silently diverges. Acceptance requires the narrow theme checker, the real documentation site, rendered EN/ZH, relevant outputs, accessibility, and responsive review.

Read-only Studio candidate

Studio is removed from the current CLI on 2026-10-04. Use oink dev, an ordinary editor, and structured inspect/check reports. The R7 record preserves historical acceptance of the earlier browser implementation.

Reviewed editing

General source editing is removed from the current CLI. Guarded new, move, review records, and baseline plans remain. Old editing plans are rejected. The R8 record remains historical evidence rather than the current command API.

8.1 - Backlinks and knowledge graph

A draft three-stage design for deriving backlinks and local or global graph views from ordinary Hugo links.
G1 implemented; G2/G3 remain draft

On 2026-08-27 every G1 open decision was resolved and G1 (static backlinks) was accepted. It is implemented on the theme’s main branch and ships with OINK 0.8.0. The local and global graphs (G2/G3) stay draft pending real-world evidence from G1; their names and configuration are not public API until accepted.

Premise

Reverse navigation and a view of connected pages are properties of the link graph, not of [[wikilink]] spelling. Hugo already accepts ordinary Markdown links and ref / relref. OINK can derive a graph from content authors already write, without adding a parser, Goldmark extension, or parallel authoring syntax.

The first value is backlinks, not visualization. A static inbound-link list is useful without JavaScript and can degrade into print and Markdown. An interactive graph remains an optional enhancement over that complete list.

Goals and non-goals

Goals:

  • derive one language-local link index per build;
  • show deterministic inbound links on a page;
  • optionally show a bounded local neighbourhood;
  • optionally publish a whole-site view and a machine-readable graph;
  • preserve ordinary preview when an edited link is stale or incomplete.

Non-goals:

  • introducing [[wikilink]] syntax;
  • indexing external, mailto:, same-page anchor, or self links;
  • executing JavaScript to discover links already present in content;
  • turning a visualization into the only way to navigate;
  • promising perfect extraction from arbitrary shortcode parameters or raw HTML.

Delivery stages

Stage Deliverable Runtime Independent value
G1 Language-local link index and backlink list None Reverse navigation in HTML, Print, and Markdown
G2 Local graph around the current page Existing ECharts plus a small local runtime Spatial view with G1 as the accessible fallback
G3 Global graph page and graph data output Same runtime Whole-site exploration and machine-readable edges

Each stage is accepted separately. G1 does not wait for G2, and G2 does not force every page to load graph code.

Extraction contract

The proposed index scans source content once per language and records one edge per source/target pair. It strips fenced code and inline code before extracting ordinary Markdown links and ref / relref; then it resolves only internal pages, removes fragments for page identity, drops self-links, and deduplicates repeated references.

The implementation must test at least:

  • duplicate links collapse to one edge;
  • fenced and inline code produce no edge;
  • external, protocol-relative, mail, same-page anchor, and self links are excluded;
  • ref and relref are included;
  • each language produces an independent graph;
  • an unresolved derived edge warns or is reported by the focused checker without making ordinary hugo server unusable.

Raw source scanning has known omissions. A URL stored in a custom shortcode parameter or raw <a href> may not appear. Those omissions must be documented instead of hidden behind a claim of a complete semantic graph.

G1 renders an aside group in the right rail, a sibling of the table of contents and the taxonomy clouds: what is on this page beside what points at this page. The group is expanded by default and shows the first eight entries; the rest fold behind a native disclosure so a heavily referenced page cannot swallow the rail. The switch is the site key params.ui.backlinks (bare boolean, default off); a page overrides it with the prefix-free front matter key backlinks, and a section can cascade it. Order is deterministic: the stable page path — language-independent, naturally grouped with navigation, and needing no second ordering authority. The group uses ordinary links and is omitted when there are no inbound pages.

Unresolvable derived edges are dropped silently and recorded as a known gap: G1 is a local navigation enhancement, not a link checker, and having it report broken links for the site would only duplicate warnings.

Print and Markdown keep the readable list. RSS omits it unless feed-level research demonstrates that backlinks improve an article feed rather than creating noisy site navigation.

Interactive graph boundary

G2 reuses the locally vendored ECharts graph series. The current page is the centre; direct inbound and outbound neighbours form the default depth. A hard node cap prevents unreadable or expensive views. Keyboard focus, text alternatives, reduced motion, forced colours, narrow screens, and print are acceptance requirements, not later polish.

If JavaScript or ECharts is unavailable, G1 remains complete and visible. The runtime is loaded only on pages that render a graph and must join the existing feature-bundle key so unlike pages cannot collide in the asset cache.

Global output

G3 may add a dedicated graph page and an opt-in JSON output. The JSON schema would contain a version, language, nodes, and directed edges with stable URLs; it would not expose local file paths or unpublished pages. The output must be derived from the same index as G1 and G2 so three representations cannot drift.

Compatibility and migration

Ordinary Markdown remains unchanged, so content migration is unnecessary. Configuration names remain undecided until a prototype proves the smallest surface. The default for every interactive or global output is off; a static backlink list may be considered separately because it is local navigation with no network or browser state.

Acceptance criteria

Acceptance requires a focused graph checker, extraction fixtures, HTML/Print/ Markdown goldens, strict-build negative cases, browser accessibility and responsive tests, and a real bilingual-site build. Performance is measured on a representative large site, but a dated prototype timing is not a permanent budget.

Open decisions

Every G1 question is resolved (see the decision log). Still open, and owned by G2/G3:

  1. Does the local graph expose one depth or a tightly capped second depth?
  2. Which page metadata, if any, is useful enough to enter graph JSON?
  3. Is G3 useful enough to justify a new output format before G1 and G2 have production evidence?

Decision log

  • 2026-08-19: Drafted the three-stage design.
  • 2026-08-27: Resolved and accepted G1, scheduled for OINK 0.8.0. G1 is opt-in: the site key params.ui.backlinks is a bare boolean defaulting to off, pages override with backlinks, and no shell-type gating — policy belongs to the site and the page, not the shell. Ordering simplifies to a single stable-page-path sort, dropping the section → weight → title chain: one deterministic authority is enough for reverse navigation, and a multi-level sort would be a second navigation authority. Unresolvable edges drop silently and are recorded as a known gap, never warned. G2/G3 and the graph data output keep waiting for production evidence.
  • 2026-08-27: Design review moved the block from the page end to the right rail. Backlinks are page metadata and pair with the table of contents, while the page end is the reader’s completion zone — share, feedback, provenance, pager, comments. The rail group also adds the eight-entry cap, with the rest behind a native disclosure.

8.2 - Media convergence

A draft for the remaining convergence between content images, numbered figures, Landing media, and featured-image selection.
Partially implemented

M1 (the shared media-result contract) and M2 (Landing resource metadata) are implemented on the theme’s main branch, and M3 is resolved as option 2: processing stays exclusively on native Markdown images, and the full fig source form remains a container whose parameter list deliberately excludes command/options. M4 (compatibility retirement) stays open pending a consumer inventory. The sections below are the original design record.

Current baseline

The content image hook, numbered fig, cards, and galleries resolve local page resources, section resources, global assets, static files, and explicit remote URLs through content/image-resolve.html. Raster resources can contribute intrinsic dimensions and processing derivatives. HTML Zoom eligibility is marked with data-td-image-zoom; the build-time detector only checks that theme-emitted marker.

Standalone Markdown images can already combine caption or Book numbering with processing and a link. Numbered image figures share td-figure and td-book-figure semantics. Landing media passes the shared URL trust policy, while featured images intentionally use a ranking resolver because their job is to select a representative image rather than render one explicit source.

Remaining problem

The shared safety boundary is stronger than the shared media model. Landing media still does not obtain the same page-resource metadata and processing result as body images. Featured-image selection and explicit image resolution have separate result shapes. Some compatibility class names remain in markup, and Book’s full fig form cannot express every processing option available to the native image hook.

The design question is therefore no longer “replace seven image entry points.” It is whether the remaining surfaces can share a small result contract without erasing their different semantics.

Goals and non-goals

Goals:

  • define one normalized media-result shape for URL, source URL, dimensions, alternative text, attribution, processability, and external status;
  • let explicit content images, Landing media, and representative images reuse that shape where their source semantics overlap;
  • keep figure markup and Zoom eligibility single-owned;
  • decide whether the full fig form needs processing or whether authors should use the native image form for processed numbered images;
  • retire compatibility markup only after consumer evidence and a release note.

Non-goals:

  • adding a third-party lightbox or remote image service;
  • changing image Zoom from opt-in to site policy by accident;
  • giving galleries a new caption, sequence, or carousel model;
  • merging non-image Book targets such as tables, equations, and examples into an image-only base class;
  • making featured-image ranking identical to explicit image resolution.

Proposed phases

M1 — Result contract

Document the fields returned by the content and representative-image resolvers, then extract the intersection into one internal media-result contract. Keep source ranking in the featured resolver and source resolution in the content resolver. This is an internal refactor with byte-stable output.

M2 — Landing resource metadata

Allow Landing items to resolve eligible local resources through the media contract, gaining intrinsic dimensions and the same URL/security decision. Explicit width and height in Landing data continue to win. Remote and static sources remain valid but cannot pretend to have processable-resource metadata.

M3 — Full figure capability decision

Choose one of two answers:

  1. add processing arguments to the full fig source form and normalize them through the same processing helper; or
  2. keep processing exclusively on native Markdown images and document full fig as the container for arbitrary numbered block content.

No implementation should leave both answers half-supported. Markdown/LLMS must link to the documented source or derivative consistently in both forms.

M4 — Compatibility retirement

Inventory downstream CSS and JavaScript before removing old image-element classes or attributes. If a compatibility name is still used, retain it for a documented release window or migrate the owning site in the same release train.

Safety, output, and accessibility

  • Image URLs keep the shared scheme and remote-host policy.
  • Missing required alternative text warns and renders a decorative fallback only where the current contract permits it.
  • Width and height never claim metadata that an SVG, static file, or remote source did not provide.
  • Linked images are not Zoom targets; the runtime preserves dialog focus, keyboard close, reduced motion, and narrow-screen containment.
  • Print, Markdown, RSS, and LLMS strip interaction markers while retaining the intended image, caption, attribution, number, and link.

Acceptance criteria

Each phase owns byte-level HTML and Markdown evidence, content and Landing resolver tests, URL/security checks, image-processing tests, Book targets, gallery/Zoom browser tests, and real-site EN/ZH narrow-screen review. The proposal is accepted only after the M3 capability choice is explicit.

Open decisions

  1. Is one shared result struct enough, or would a common lower-level URL/resource record keep resolver ownership clearer?
  2. Should Landing consume resource attribution, or only dimensions and URL?
  3. Does full fig processing solve a real consumer need now that native images support numbering, captions, links, and processing together?
  4. Which emitted compatibility names are still used by real consumers?

8.3 - OINK CLI and the next product stage

The accepted independent CLI boundary and local first-stage candidate, with later adoption, theme, migration, and content-model proposals kept explicit.
Local first-stage candidate; later roadmap remains draft

The independent Go repository pgsty/oink-cli and first-stage development were authorized on 2026-09-29. Its six commands now have a local 0.1.0-dev implementation, with final local acceptance recorded separately. Current behavior belongs to the CLI decision and result contract and usage guide. This is not a public CLI release or independent-user adoption record. Theme 1.2, its tooling descriptor, Docsy migration, version lifecycle, OpenAPI, MCP, and Studio remain proposals.

Record Value
Status Repository choice and first-stage scope accepted; local candidate implemented and validated; later roadmap remains draft
Owner OINK maintainers; final local acceptance and public release remain separate
Date 2026-09-29
Scope OINK theme, independent CLI, existing Starter, and documentation site
Affected contracts Architecture, configuration/diagnostics, outputs, migration, and later version navigation/API content
Source snapshot Theme HEAD 3a18234, documentation HEAD 85f16bf, Starter HEAD 137843b, plus the explicitly identified local work below

Recommendation

Create a separate oink-cli repository, publish one executable named oink, and keep OINK as the single product identity. The theme renders content; the CLI helps people initialize, inspect, validate, upgrade, and eventually migrate their sites. The documentation site continues to own public guides, bilingual design records, and integration acceptance.

The repository choice and Go implementation are now accepted and exist locally. Public publication remains a separate action. The first-stage behavior has moved to the CLI contract; the dated acceptance record identifies executed checks and remaining limits. This roadmap stays active for its later stages and adoption targets.

The first release should improve the path from an existing repository to a reliable publication. Its four substantive workflows are doctor, check, init, and upgrade. dev and build may provide small, transparent Hugo shortcuts. A supported Docsy migration path follows evidence from actual input repositories. Version lifecycle and OpenAPI generation come after that first usable release, with one major content-model project active at a time.

Keep the theme usable without installing the CLI. For generated content, this means committing or otherwise delivering the generated Hugo inputs: removing the CLI must still leave a site that ordinary Hugo can build. Regenerating those inputs remains a separate operation.

Product position and target user

Recommended public description:

OINK is a local-first documentation toolkit built on Hugo, publishing engineering knowledge for readers and agents.

Retain “Hugo theme” in installation and discovery pages because it describes what users install. “Knowledge compiler” is a useful architectural direction, but it is not yet evidence that OINK owns a new product category. A new name should not obscure the current Markdown/Hugo path.

Prioritize Git-oriented maintainers of open-source infrastructure, developer tools, and multilingual technical documentation. Their immediate jobs are to get a site working, diagnose a failure, keep upgrades safe, and move existing content without losing URLs or meaning. Existing maintained sites provide regression coverage; independent teams provide adoption evidence. Those are different kinds of evidence.

For the first stage, non-goals include a visual CMS, hosted accounts, a deployment control plane, a package marketplace, an LLM runtime, a semantic-search service, and a new rendering engine. Books, blogs, and landing pages remain supported, but their feature catalogs do not drive this roadmap.

Evidence and changes to the research recommendation

This proposal considers the supplied strategy report and checks it against the local implementation, bilingual Design section, Starter, and current primary documentation. It does not treat the report’s star counts, effort estimates, commercial prices, or market claims as verified demand.

Observation Product consequence
OINK already has a public Starter, generated configuration schemas, migration scripts, publication tools, and focused theme checkers Productize selected workflows instead of starting a second implementation of everything
The front-matter schema deliberately omits type constraints It is not a complete executable validator; strict checks must respect the owning resolvers and actual Hugo output
Existing version support includes a cross-site menu, archive banners, and optional path concatenation The gap is lifecycle and reliable page correspondence, not another menu or banner
Current version documentation explicitly uses independent Hugo builds Preserve that model initially; do not silently introduce a multi-version renderer inside one build
OpenAPI widgets become specification links outside HTML and have documented accessibility exclusions Static, accessible endpoint content is a concrete future improvement
Backlinks are implemented; G2/G3 remain draft A graph visualization is not an already-accepted commitment
bin/update-consumers.py exists in this working tree as uncommitted local work Its release-resolution and preservation rules are useful design input, not a claim of shipped CLI functionality
The theme and documentation trees contain substantial unrelated local changes This proposal records recommendations; it does not certify or release that work

The supplied report correctly emphasizes adoption and an optional tools layer. Four changes make it executable:

  1. Put safe upgrades alongside initialization and diagnosis. Existing users have an immediate, testable maintenance need.
  2. Separate maintainer regression checkers from consumer checks. A synthetic-fixture checker is not automatically a general-purpose site validator.
  3. Treat migrations as supported input profiles, not a promise of complete Docsy or arbitrary MDX conversion.
  4. Replace the simultaneous versioning/OpenAPI/graph/platform program with sequential decisions. A feature list and hour estimates are not a staffed delivery plan.

Current competitors validate the workflow direction, not demand for OINK itself. Mintlify’s CLI exposes preview, validation, and link checking. Nimbus combines scaffolding and agent-readable output while remaining pre-1.0. Docusaurus makes version snapshots explicit and warns about their maintenance/build cost. Copying Nimbus’s entire source-owned UI model would shift upgrade work to OINK consumers; use that pattern for small generated recipes, while retaining the upgradable theme module.

Why a separate repository

Option Benefit Cost Decision
Extend Python scripts under theme bin/ Fastest small maintenance improvements; same-change tests Weak installation/distribution experience; no cohesive public command contract Retain for internal and historical tooling
Put cmd/oink in the theme’s root Go module One checkout and atomic source edits Mixes a Hugo asset module with application dependencies, binary releases, and consumer support Do not choose for the public CLI
Use an isolated Go submodule in the theme repository Atomic repository changes without sharing Go dependencies Separate module tags and releases still need management; easier to reach into unpublished theme internals Viable fallback for a time-boxed prototype, not the preferred product home
Create pgsty/oink-cli Clear executable boundary, independent releases, no need for users to clone theme internals Requires explicit compatibility and cross-repository acceptance Accepted; local Go repository created

This is a release and responsibility decision, not a claim that a monorepo is technically impossible. A nested module can isolate dependencies. Conversely, separate repositories create a real coordination cost: a renderer change may require two pull requests, paired contracts, and a compatibility test. OINK already operates a theme/site/Starter split, so that cost is acceptable if the public boundary stays small.

Theme and CLI releases must not share a forced version number. A proposed oink CLI 0.1.x should work with a tested OINK 1.1.0 baseline and the next supported theme release. Compatibility is declared per capability. Unsupported functionality must be reported, rather than interpreting every schema from the newest theme as valid for every old site.

Do not create separate repositories for the linter, migration engine, OpenAPI generator, or a shared SDK now. They can begin as internal CLI packages. The binary can be built in Go without importing Hugo’s internal Go packages or making the theme module depend on the CLI.

Responsibility map

Surface Owner Boundary
Layouts, components, style, navigation, search, accessibility, output semantics pgsty/oink Executes inside Hugo and the static site
Theme defaults, owning resolvers, generated schemas, output schemas pgsty/oink Authoritative theme behavior and its projections
Theme implementation checks and narrow invalid-input fixtures pgsty/oink Remain maintainer tools, even if implemented in Python or JavaScript
Environment diagnosis, consumer checks, initialization, upgrade; later migration transformations pgsty/oink-cli Initial commands implemented locally; migration remains proposed
OpenAPI parsing and generated source, later version snapshot orchestration Proposed later pgsty/oink-cli capabilities Produces ordinary Hugo inputs; does not own final rendering
Small official site skeleton and language profiles pgsty/oink-starter Single source for CLI initialization; pinned snapshots can be embedded in CLI releases
Guides, examples, PRDs, accepted rationale, EN/ZH integration/browser review pgsty/oink.pgsty.com Continues as the canonical public documentation and regression site
Hosting credentials, account setup, deployment authorization Consumer workflow Existing CI/provider tools; first CLI release does not deploy
                          optional oink CLI
                  init / doctor / check / upgrade
                      later migrate / generate
                               |
                               v
              user-owned Markdown + Hugo config + data
                               |
                     Hugo Extended + OINK theme
                               |
               HTML / Print / Markdown / search / indexes
                               |
                  readers / agents / optional adapters

The CLI reads Hugo’s effective configuration, the resolved theme’s published contract artifacts, and rendered outputs. It must not guess the final page tree from filenames or maintain a second navigation resolver. Hugo config already exposes effective configuration; module inspection must also account for replacements, workspaces, and vendoring.

Next theme release: proposed OINK 1.2

This section remains draft. The local CLI candidate works against the published OINK v1.1.0 baseline; neither a 1.2 release nor a new tooling descriptor is accepted or required by the first-stage CLI decision.

Give this release an adoption objective: a site can explain its configuration and output capabilities to tools, and upgrade without adopting a new authoring model. The release should be small enough to ship independently of the broader roadmap.

Priority Requirement Acceptance
P0 Package a small, versioned tooling descriptor beside existing schemas, describing available schema/output contracts and supported toolchain boundaries Descriptor is checked against owning implementation; CLI can inspect it from the resolved module; no extra per-page output or runtime request
P0 Make selected high-value configuration diagnostics actionable: parameter, invalid value, expected form, fallback, and owning guide Cover actual onboarding failures such as Goldmark/output/language wiring; retain ordinary preview warnings and strict publication failure
P0 Preserve one navigation and Markdown authority across reader and machine outputs Existing output and navigation checks continue to cover language, ordering, subpath, and opt-in behavior; no duplicate CLI renderer
P0 Release with a tested Starter snapshot and a reviewed downstream adoption record Validate published module resolution separately from sibling replacement builds; record consumer pins and deployments independently
P1 Add stable identifiers to the small set of diagnostics consumed by tooling, if the prototype shows they are necessary A focused owning checker verifies each identifier; the CLI never relies on parsing all human warning prose

The descriptor is release metadata, not a new configuration authority. Configuration schemas continue to be generated from current authorities; optional shape validation remains owned by its resolver/checker. Do not create a generic renamed-key registry in conflict with the current diagnostic decision. Migration transformations belong to explicit CLI profiles, not a permanent compatibility path in templates.

No new visual component family is required for 1.2. Correctness, accessibility, and already-demonstrated regressions can still justify changes. Existing media work retains its own acceptance scope; this roadmap does not make completion of every draft a release condition.

First CLI release: proposed 0.1

The commands below are implemented in the local 0.1.0-dev candidate. Their current flags, result semantics, and limits are defined by the CLI contract and usage guide; public distribution and final acceptance are separate states.

Command User outcome First-release boundary
oink doctor Understand why the site cannot run or why its environment differs from CI Inspect Hugo Extended/version, module pin and effective source, Starter/toolchain requirements, essential configuration, and enabled outputs; no repair by default
oink check Know whether a publication build and its local references are valid One strict build into isolated output, then check local links/anchors/assets and enabled machine outputs; report coverage and unsupported checks
oink init my-docs Start a small, neutral site that can be maintained without the CLI Generate from a pinned Starter snapshot into a new/empty target; select the supported language profile and explicit theme pin
oink upgrade --to <tag> See the exact changes needed for a theme upgrade Preview first; --write applies a reviewed scope after validation; protect unrelated module dependencies, user changes, and vendored output
oink dev / oink build Use a memorable entry point without learning a second build system Thin Hugo invocations with visible effective arguments; build uses publication strictness; direct Hugo remains fully supported

check is the single quality entry point. Avoid separate overlapping lint, validate, audit, and check products in 0.1. Later --scope options can separate source hints from rendered-output validation when users need the distinction.

Diagnosis and quality scope

Start with high-confidence, actionable failures: wrong toolchain, unresolved theme, malformed required configuration, missing local link targets/anchors/assets, and inconsistent enabled output references. Resolve routing and anchor truth from Hugo’s output, including language and base-path handling. Do not label a valid custom front-matter key as invalid merely because an editor schema does not list it.

Disabled optional outputs are not missing-output errors. The local candidate does not check translation completeness; any future completeness rule must use the languages and coverage policies the site actually declares. Duplicate titles, orphan pages, missing descriptions, prose style, and freshness remain later optional observations after real false-positive review. Static inspection is not a claim that browser accessibility or interaction tests passed.

The local candidate freezes oink.result/v1: structured diagnostics have stable rule IDs, severity, known locations, explanations, actions, and explicit coverage. JSON stdout contains only the result; logs go to stderr, and no command waits for input. Exit meanings are 0 for completed work with no blocking findings, 1 for policy findings, and 2 for required incomplete work. Required unsupported checks cannot succeed. The result contract owns the detailed fields; line numbers are never invented for build-derived findings.

Keep raw Hugo errors available as subprocess evidence. Their translated wording is not the CLI protocol. A future SARIF export can project from the same result without changing rule semantics.

Upgrade and file preservation

The existing consumer-upgrade script provides valuable local precedents: distinguish declared pin from resolved version, disable both workspace mechanisms for release verification, recognize module replacements, and inspect _vendor. Port those behaviors with focused tests; do not shell out to unpublished Python files while claiming a standalone Go binary.

For 0.1, upgrade one explicitly selected site. Multi-site fleet discovery stays with the maintainer script until a consumer need is demonstrated. A normal check may examine a deliberate local theme replacement; check --release must verify the declared published release without those replacements. It should report a conflicting go.mod replacement rather than edit it away.

Preview the proposed touched files and verify the prospective upgrade before applying it. Back up only those files, refuse changes to files that changed since the preview, and preserve unrelated dirty work. A failed operation must describe what was and was not applied, with a recovery path that does not overwrite subsequent edits. A dirty repository is not a reason to block read-only diagnosis. Refreshing vendor content is a separate explicit action; changing go.mod alone is not an upgrade of vendored output.

Do not commit, push, deploy, alter global agent settings, or install system packages as side effects of initialization or repair. Creating a new named directory is the requested initialization action; transforming existing files defaults to a preview. Do not build a generic workflow engine to implement these bounded operations.

Distribution and offline behavior

The local candidate currently provides a tested source/Make installation path and archive preparation. A published Homebrew formula and public download/tag installation remain future distribution work. Runtime qualification currently covers macOS arm64; the other archive targets are cross-compiled candidates, not exercised platforms.

Use a Go executable with release archives/checksums and a Homebrew installation path. Initially qualify macOS and Linux on the architectures actually tested; mark other targets experimental until their filesystem and process behavior is validated. The installed CLI itself needs no installed Go toolchain, Python, Node, or account. Hugo remains an external renderer; first module resolution still needs the site’s documented Git/Go/Hugo toolchain.

Embed or ship an exact, licensed Starter snapshot for deterministic initialization. Do not fetch a moving main branch on every invocation, and do not maintain handwritten CLI copies of Starter configuration. Check embedded/template drift during CLI release.

Distinguish a cold installation from offline operation. Downloading Hugo, the theme, or an uncached template requires connectivity unless supplied locally. Once dependencies are present, local diagnosis/check/build paths must work without external services. An offline request must fail clearly on a cache miss, never silently fetch. External URL checking, remote specifications, and other network actions are separate opt-ins. No default telemetry or background update check is required.

Migration: the first expansion

Start with a documented Docsy input profile selected from actual candidate sites, reusing the current migration fixtures and report model as evidence. Existing OINK 0.4/0.6 transformations are not proof that arbitrary Docsy sites can already migrate. Validate configuration, navigation, assets, languages, and routes as well as Markdown syntax.

Proposed workflow: oink migrate --from docsy --source <site> --output <new-site>. Assessment comes before writing; application uses an explicit flag and a separate destination. Every source item receives one primary status: unchanged-compatible, transformed, manual-review, or unsupported. Counts must reconcile, with reasons and source locations. Custom templates and dynamic behavior remain visible manual work.

Acceptance means source preservation, idempotent supported transforms, no edits inside literal code examples, valid local references, and an explicit old-to-new route report. Unchanged URLs are preferred; changes require a redirect plan appropriate to the hosting target. HTML build success alone does not establish semantic parity or production redirects.

Do not promise “one command migrates any Docusaurus site.” Arbitrary JSX, imports, and embedded React/Vue are programs. Do not execute untrusted source to infer their meaning or silently drop unsupported constructs. Start a second framework only after the first profile is reused successfully without maintainer rescue. Full MDX migration is a later product investment, not an MVP parser task.

Next content capability: version lifecycle

After the first CLI is useful, version lifecycle is the default next candidate because it extends OINK’s existing independent-build model. Move OpenAPI ahead only if real API users provide the stronger repeated need. Do not implement both foundations simultaneously with one primary maintainer.

Theme responsibilities: consistent version identity in the reader surface, reliable page switching, archive status, and correctly scoped search/machine outputs. CLI responsibilities: inspect/list versions, prepare a snapshot, validate page correspondence, and change declared lifecycle state. Use oink --version for the executable; a future oink docs version ... namespace avoids confusing it with documentation versions.

Prefer a small version manifest with version label, source reference, base URL, status, and default selection. Keep independent per-version builds and existing external archives. CLI-managed manifests may produce checked-in Hugo configuration; in that mode the manifest is authored and configuration is a checked projection. Existing manually managed params.versions remains supported. The initial prototype must settle this projection before freezing its format.

Page correspondence needs a logical page key scoped by documentation family, language, and version. Reuse a suitable existing translationKey or explicit stable key before inventing universal UUIDs. Missing peers should be disclosed and lead to a defined version/section landing page, not a fabricated equivalent or an unchecked concatenated URL. Route aliases handle moves separately from page identity.

Distinct historical content should ordinarily keep its own canonical URL; do not point every old page at the newest version. Language alternates must refer to genuine translated peers in the same version. Default search and agent bundles stay inside the selected language/version. A cross-version collection, if later needed, is explicit. Archiving preserves the source and records how its built artifact is retained; it is not deletion and does not silently redeploy an immutable archive.

NAVJSON v1 currently has closed object schemas. Adding version or identity fields therefore requires an explicit new schema/output contract or a separate artifact, not a supposedly harmless addition to v1. No change to current page identity is justified merely to reserve space for a future graph.

Following capability: static OpenAPI reference

The first OpenAPI product should be a read-only static reference generator. The CLI parses a local specification and supported local references, emits ordinary Markdown/Hugo data, and records source provenance. The theme supplies accessible semantic presentation and the existing output pipeline. Ordinary Hugo then builds HTML, Print, Markdown, search, and agent indexes from those generated pages.

Start with operations, parameters, request/response bodies, and linked schema descriptions. Explicitly declare the supported OpenAPI versions and constructs after a parser spike; unsupported constructs cannot disappear silently. Use operation identity scoped to the API/specification; a missing operationId can derive a method/path key with a warning about identity changes. Reused operationId values across different APIs must not collide.

Keep human-authored guides separate from generated facts. Generation must be deterministic, record source hashes and generator version, detect stale output, and refuse to overwrite unexpected human changes. Check generated source into the site, or supply it as a versioned build input, so rendering itself remains CLI-independent. Resolving remote references is an explicit preparation step; normal generation must not traverse arbitrary external URLs.

Acceptance requires a real user specification in addition to a toy example, complete accounting of supported operations, cyclic-reference handling, stable routes, semantic content in all selected outputs, and accessibility checks with no inherited Swagger/Redoc exclusion for the new static renderer. Measure a representative large specification before promising a throughput target.

Keep existing Swagger/Redoc integrations compatible. Interactive requests, credential handling, SDK generation, mock servers, and an API testing platform are outside this first compiler increment.

Architecture and compatibility rules

Keep CLI internals modest: command handling, Hugo/process integration, diagnostics, template loading, and bounded file changes. Add migration and OpenAPI packages when their stages begin. This is a suggested decomposition, not a plugin ABI or public SDK.

Three boundaries need versioning: the CLI’s machine result format, the theme’s public schema/output contracts, and each supported migration/generation input profile. Prefer capability checks over a single “requires newest OINK” rule. A newer unsupported schema must produce a useful compatibility diagnosis.

The CLI cannot import a sibling theme’s private Python modules, depend on a local checkout layout, or download executable checks at runtime. Port selected consumer operations with behavior tests. Keep template-internal checkers in the theme, and make future changes to exposed consumer rules update their owning contract. Existing scripts stay available during the transition; retire duplication only when the replacement covers the supported cases.

No CLI configuration file is required initially. Hugo retains rendering configuration. If repeated usage later justifies a tool-policy file, it may hold ignored paths, rule severity, or a reviewed baseline, but must not mirror params.ui, navigation, languages, or module pins. A reviewed lint baseline cannot suppress a failed Hugo build, an unreadable input, or an unsupported required check.

Roadmap and staffing assumption

The planning envelope below is retained as the original proposal, not as an execution log. Stages 0 and 1 now have a local first-stage candidate; this does not complete the publication, independent-user study, migration, or later content-model outcomes. Actual evidence belongs in the acceptance record.

The following is an 8–12 week first-stage planning envelope, assuming roughly one full-time implementation owner plus part-time documentation/review help. It is not a commitment or a claim about actual staffing. Toolchain qualification, recruitment, and bilingual review consume time; reduce scope before adding nominal parallel workstreams.

Stage Timing from approval Deliverable Exit evidence
0: establish the boundary Weeks 1–2 Accept repository choice; collect failure examples; define result format and supported baseline; prototype read-only doctor/check against current 1.1.0 Starter plus at least three varied real repositories; failures and coverage omissions recorded
1: complete the daily workflow Weeks 3–6 Doctor/check, pinned init, thin dev/build; prospective single-site upgrade and file-preservation tests New users can diagnose a seeded failure; ordinary Hugo still builds generated sites; no unexplained source changes
2: release a bounded product Weeks 7–12 Proposed theme 1.2 + CLI 0.1; compatibility record; docs; qualified installation; limited Docsy migration assessment/pilot First-run study and repeat upgrade use; migration limitations are explicit; published pins and consumer adoption checked separately
3: validate migration and one content model Months 4–6 Harden the first migrator; choose version lifecycle or OpenAPI based on users; propose theme 1.3 / CLI 0.2 as needed At least two real repositories use the chosen workflow; accepted contract precedes compatibility promises
4: earn expansion After month 6 The other content capability, then optional recipes/provenance or agent transport where justified Repeated use and maintenance capacity; no automatic commitment to a SaaS product

If stage 2 overruns, remove migration writing from that release and keep its assessment report. Do not cut upgrade preservation, truthful diagnostics, or independence from the CLI. If no independent team wants the migration profile, stop expanding framework coverage and investigate onboarding/positioning instead.

Freshness/ownership is a later optional quality feature, initially a report whose findings users actually act on. A modification date must never be presented as verification. Ship a handful of useful official page recipes before a registry. Graph G2/G3, MCP, analytics adapters, executable examples, Studio, and managed services each need a specific user problem and capacity decision; they are not dates on this roadmap. Existing static agent outputs make MCP less urgent than adoption.

Acceptance and product measures

The user/adoption measures below remain targets. Maintainer-run local pilots validate implementation and preservation; they do not establish independent teams, first-user success rates, retention, or production adoption.

Area Initial target or required property
First successful use With prerequisites already installed, at least 4 of 5 unfamiliar target users reach local preview and a passing strict check within 15 minutes without maintainer intervention; record cold installation separately
Maintenance value At least three real sites use diagnosis/checks and repeat a supported upgrade; every failure has an actionable report
Diagnostic precision Triage all blocking findings in the pilot; aim for less than 5% false positives in an explicitly counted labeled sample, not an unmeasured headline
Integrity Zero silent content loss; every migration input is accounted for; repeat transforms have no diff; user edits and unrelated dependencies survive
Independence Initialized/generated sites build through ordinary Hugo with provisioned dependencies; optional CLI and output features remain optional
Offline behavior Run the qualified local workflow with outbound access denied after provisioning; record cache misses and explicitly networked features separately
Compatibility Current tested theme baseline and candidate release, pinned site regression toolchain, root/subpath, and EN/ZH cases; do not imply that every Hugo version above the floor was tested
Adoption Seek five independent pilot teams within the first stage, and track which reach production and continue using the result at 30/90 days; this is a validation target, not observed traction

Use independently maintained production sites as the main adoption measure, verified through public references or voluntary user confirmation. A stable documentation site should not stop counting merely because it has no commit in 60 days. Track theme upgrade recency separately from retention. Stars, download counts, internal consumer count, and agent-generated volume are supporting signals, not proof of independent adoption.

Track time to first local success, time to production, upgrade effort, and manual migration effort separately. Deployment can depend on accounts and providers outside the CLI, so do not equate successful local validation with publication. Do not add default telemetry to obtain these measures.

Implementation ownership and validation

Change Owning validation
Tooling descriptor and schema compatibility A focused theme descriptor check plus generate-config-schema.py --check and relevant parameter checks
Diagnostics exposed to consumers Owning resolver/checker cases; CLI diagnostic result/exit-code tests
Existing output behavior check-agent-indexes.py, output/security/navigation checks appropriate to the changed surface
Init and upgrade CLI tests against pinned Starter snapshots and repositories with replacements, vendor content, unrelated dependencies, and dirty target files
Migration Ported/extended transform cases, source-preservation and repeat-run checks; reviewed real-site route/content evidence
Future version/API presentation Theme output checks plus bilingual documentation-site integration, browser, accessibility, responsive, and visual review

Run the smallest owning check first. Public behavior changes still require implementation, checker, and both language contracts in one coordinated delivery. Use the sibling site’s make check, make browser, and make dev workflow for actual integration and visual acceptance. Do not move public regression scenarios into the theme’s synthetic fixture tree, or force consumer installations to install the maintainer Node test stack.

For a release, separately record local checks, commits, tags, published module/binary resolution, consumer pins, and deployment. Theme release adoption continues through the maintained consumer inventory procedure. A coordinated issue/checklist can join the repositories; a new orchestration framework is unnecessary.

Open decisions and stop conditions

Repository selection and first-stage implementation are settled locally. Remaining release decisions include qualified platforms, the public distribution channel, compatibility claims justified by executed evidence, and independent pilot recruitment. The acceptance record identifies the actual local toolchain and selected sites; it does not make future platforms or users validated.

Result and exit semantics are frozen for the local candidate in the CLI contract. The minimal theme descriptor and stable theme warning identifiers remain separate proposals, not prerequisites retroactively added to this first CLI. Before a versioning beta, settle manifest projections, archive retention, and page correspondence. Before an OpenAPI beta, settle the supported spec subset and generated-source ownership.

Reconsider the separate CLI investment if pilots only need a tiny maintenance script, if rules must repeatedly duplicate template semantics, or if maintaining distribution consumes more effort than the measured user benefit. Keep successful standalone scripts in that case. Reorder versioning versus OpenAPI when evidence changes; do not expand the total concurrent scope.

Decision log and sources

Date Record
2026-09-29 Draft created from the supplied strategy research and local source review. Recommends a separate optional CLI, a small adoption release, bounded migrations, and sequential content capabilities. No implementation or repository creation accepted by this document.
2026-09-29 Subsequent user authorization accepted the independent Go repository and first-stage development. A local 0.1.0-dev candidate implements doctor/check/init/upgrade/dev/build; stable behavior moved to the CLI decision and usage guide. Local acceptance passed for CLI commit e623d93; public release, independent adoption, and all later-stage proposals retain separate states.

Local authorities consulted: Architecture, generated schema decision, migration boundary, version behavior, OpenAPI limits, and graph proposal status. The source inspection also covered theme bin/, schema/nav.v1.schema.json, the existing Starter, and documentation-site build/check commands. Local in-progress changes are not represented as published release evidence.

External primary sources were checked on 2026-09-29: the linked Mintlify command reference, Nimbus repository, Docusaurus versioning guide, and Hugo config/module documentation. They inform comparisons; they do not validate OINK market demand or the proposed schedule.

8.4 - OINK CLI maintenance roadmap

The historical R1–R8 requirements record for documentation maintenance and Oink Studio, retained alongside acceptance evidence and the current reduced CLI contract.
Implemented requirements record

The finite R1–R8 supported local implementation and A18 runtime/archive scope passed for the historical source and binaries recorded in the acceptance supplement. The current reduced CLI requires its own validation. This dated requirements record is retained at its original URL and anchors, with historical planning text and failed trials preserved. Stable behavior belongs to the CLI contract and guide; historical evidence belongs to the 2026-10-04 supplement. It retires from active navigation; uninvoked E1–E4 are separate inactive scope. Rendered navigation/URL verification requires its own receipt for these exact promoted bytes.

Complete documentation maintenance before building a local visual workbench. The proposed product should help a maintainer check a change, understand its effects, review a safe modification, and publish the exact artifact that passed checks. Studio should expose these same capabilities.

Record Value
Status Implemented; R1–R8 supported local scope and current A18 runtime/archive qualification passed; rendered lifecycle verification has a separate exact-byte receipt boundary
Owner OINK maintainers; implementation and review assignments remain to be confirmed
Date 2026-10-03
Baseline Local CLI 0.1.0-dev, commit e623d93; Hugo Extended 0.166.0 and Go 1.27.1 on macOS arm64
Completion scope R1–R8 and the acceptance cases below; conditional extensions have separate entry criteria
Affected surfaces CLI command/result contract, Starter projection, consumer CI, translation policy, maintenance operations, local Studio, EN/ZH guides
Schedule assumption One full-time developer with scheduled documentation and review support; estimates are planning judgments

Background and evidence

The original CLI roadmap accepted an independent Go executable and narrowed the first implementation to doctor, check, init, single-site upgrade, dev, and build. This proposal adds a bounded maintenance program. Docsy migration, version lifecycle, OpenAPI generation, and theme 1.2 retain their own scopes.

The 2026-10-03 local audit reran the Go suite and actual Hugo integration tests. A bilingual initialized site passed checks over 223 files and 4,461 references. The PIG consumer site passed over 1,392 files and 64,440 references; 858 source files and its Git state were unchanged. These are local validation observations, not public distribution, independent adoption, or deployment evidence.

The audit also reproduced four limits. A missing rendered link failed check while build succeeded. Ordinary HTML references outside the configured base path were marked untested. doctor --release accepted a Starter still using https://example.org/. Both embedded deployment workflows called Hugo without the CLI’s additional checks. Translation completeness and readable upgrade diffs were absent. These findings define the first increments.

Product goal and users

Prioritize maintainers of multilingual engineering documentation and small teams maintaining several Hugo sites. Their recurring jobs are reviewing translations, preventing broken publications, updating dependencies, and reorganizing content without losing references or public URLs.

The product succeeds when an ordinary consumer repository can use one quality entry point locally and in CI, inspect the affected pages, and apply a reviewed change while preserving unrelated work. CLI, Studio, and Agent callers must receive the same findings and change plans.

Feature selection

Capability from the supplied design Decision Delivery
Links, anchors, attachments and machine outputs Strengthen existing checks and explain uncovered cases R1–R3
Environment diagnosis, preview and strict builds Complete release diagnosis and add opt-in verified builds R1, R3
Translation completeness and protected structure Build as a primary product capability R2
Initialization and CI configuration Extend the fixed Starter and manage reviewed CI changes R3–R4
Native content rules and project style Implement a small deterministic core; optional general tools R2, R6
New content, snippets and editor setup Implement ordinary Hugo inputs with overwrite protection R4
Safe upgrades and migration preflight Add diffs and candidate comparisons; framework migration remains separate R4
Page moves, renaming and impact analysis Implement after page relationships and change plans are dependable R5
Issue panels and translation comparison Build a read-only local Studio first R7
Multiple sites Add an explicit site registry over the same single-site engine R6
EPUB, PDF and offline packaging Conditional adapter to distributed publication tools E1
Executable documentation examples Conditional, explicit execution profiles E2
Agent inspection, impact and context Implement deterministic local operations R5
AI translation and semantic review Conditional proposals after deterministic maintenance works E4
Sources, evidence and knowledge dependencies Limit this program to build/review provenance and observed page relationships Wider knowledge management deferred
Rich editing, live collaboration and native desktop apps Deliver safe Markdown editing only; defer the broader platform R8; remainder deferred

Scope and non-goals

R1–R8 are the finite completion scope for this PRD. Each can deliver value and be accepted separately. Suggested CLI versions 0.2, 0.3, and 0.4 identify release candidates, not required public tags or theme versions.

The program does not include a renderer, universal migration engine, hosting account manager, deployment API, built-in LLM, vector database, remote editor, real-time collaboration, native desktop shell, or full WYSIWYG editor. Existing provider workflows perform deployment. Publication permissions and credentials remain consumer-owned.

Shared project facts and check policy

This scope is accepted locally. The original requirements below are retained as proposal history; current behavior and flags belong to the CLI contract.

Extend the existing isolated Hugo analysis rather than introducing a second configuration parser or navigation authority. Proposed internal facts include page identity, language, publication state, actual output URLs, known source files, translation relationships, and observed rendered references.

Use Hugo’s public Page.Translations and Page.OutputFormats for relationships and outputs. Page.File can provide provenance, but some pages have no backing file. Such findings must retain an output location and unknown source state. Any temporary probe must leave ordinary published outputs unchanged after its removal.

Introduce oink.yaml only for check selection, severity, translation policy, reviewed exclusions, and tool/workflow options. Hugo continues to own languages, titles, menus, URLs, and site configuration; module files own theme versions. Initially provide check links, check translations, and check style over one shared analysis. Keep --json; --format json may be an additive alias.

Report blocking errors, warnings, and suggestions through the existing error, warning, and info severities. Unsupported required tools or input shapes remain exit 2. Policy cannot turn failed builds, unreadable inputs, or incomplete required checks into success. Source locations need reliable mapping; otherwise report the actual output and pointer.

Translation maintenance

This scope is accepted locally. The original requirements below are retained as proposal history; current behavior and flags belong to the CLI contract.

Support filename languages, language-specific content directories, and translationKey relationships as resolved by Hugo. Coverage policies select required languages for an explicit content scope; disabled languages and intentional localizations must not become missing-translation errors. Check duplicate identities and configured draft/publication requirements. If a production build omits a source needed to assess policy, use an explicit analysis view; do not confuse that view with publishable output.

Provide two policies: strict correspondence for manuals, and localized content for blogs or product pages. Strict policy can require explicit IDs, declared placeholders, selected code blocks, and necessary fields to agree. Localized policy checks only declared shared constraints. Heading counts and all code blocks must not become universal requirements.

Propose translations status, translations diff <page>, and an explicit review-record operation. A versioned review record binds the translation to a source content hash or Git revision, plus the translation hash and declared source language. A missing record means unknown; a changed hash means changed since review, not automatically a bad translation. Recording review requires a user-requested write and must never happen just because a checker ran.

Native content rules and baselines

This scope is accepted locally. The original requirements below are retained as proposal history; current behavior and flags belong to the CLI contract.

Start with a small catalog of high-confidence rules drawn from actual consumer failures: malformed supported component/attribute usage, conflicting explicit IDs, known deprecated forms, and configured protected content. Code, inline code, shortcode bodies, raw HTML, and attributes require their real syntax boundaries. Do not apply regular expressions indiscriminately.

Use contracts from the effective theme version. An editor schema that omits types is not a complete strict validator. Missing compatible metadata must produce explicit limited coverage rather than validate against the latest theme. Do not require a future theme release to finish basic checks.

A visible versioned baseline can acknowledge existing findings with stable fingerprints, reasons, and review metadata. Reports show acknowledged and new findings separately. Baseline updates are explicit and reviewable; they cannot hide required incomplete work. Formatting and prose suggestions are optional. Automatic fixes first produce a diff, then validate a candidate before applying a narrow set of files.

Verified publication and CI

This scope is accepted locally. The original requirements below are retained as proposal history; current behavior and flags belong to the CLI contract.

Preserve the current transparent build default. Add an explicit managed build --check workflow: one strict Hugo build, selected checks over the same output, then export only that verified artifact to a new or empty destination. Do not delete arbitrary directories or mix stale files into a verified tree. Default build must continue to say when extra checks were not run.

A local versioned manifest records source revision and dirty state when known, source-input hash, effective theme identity, Hugo/CLI versions, build settings, base URL, check coverage, and file digests. Secrets and machine paths must not be copied into public metadata. If a public build marker is enabled, it contains only the minimum identity needed for verification. Artifact changes after validation invalidate the recorded result.

Propose ci init github-pages and ci init cloudflare-pages --mode direct-upload. Generate local configuration only, explain variables and permissions, and record template provenance. Detect existing workflows, preview diffs, preserve unknown modifications, and require explicit application. Both use the same quality engine and upload the verified output without another Hugo build. Before a public CLI release, templates must accept a documented immutable source/archival input rather than assume a nonexistent download tag.

Extend release diagnosis with an example-address warning, a release-policy error when publication checks require a real address, effective local source commit/dirty state when available, and comparisons with supported generated CI settings. Unknown custom CI is reported as unknown. A local checkout’s commit does not attest to a published module; vendor byte identity remains separate.

Propose verify --site URL --manifest FILE with explicit network permission. Check representative pages, languages, resources, search/Markdown outputs, canonical addresses, and artifact identity. HTTP 200 from a generic fallback must fail identity checks. Timeout, authentication, rate limiting, or an absent required identity produce unknown/incomplete results, not invented success. Local HTTP fixtures test this without deploying to a provider.

Authoring and upgrade assistance

Add new, a small snippet catalog, and explicit editor-schema setup. Create page bundles, selected translation drafts, and ordinary front matter while refusing existing files. Translation drafts are not completed translations. Editor hints follow the effective theme and preserve existing editor settings.

Extend init with docs, blog, book, and project profiles by composing one licensed Starter source; do not maintain four copied template trees. Existing projects get diagnosis and reviewed proposals rather than replacement config.

Keep the explicit-tag, single-site upgrade protections. Add readable unified diffs and baseline/candidate route and capability comparisons. Report removed URLs, changed aliases and missing previously enabled outputs. A clean candidate build alone does not prove compatibility. Proposed configuration migrations need a documented transform and tests; otherwise return a manual action. Conflicting replacements and vendor refresh remain explicit owner operations.

Impact analysis and safe content changes

This supported scope is accepted locally. The original proposal requirements below remain as history; current behavior, limits and flags belong to the captured-facts contract, move contract and guide.

Provide inspect <page>, impact --since <ref>, and context <task> over the shared facts. Inspect shows provenance, publication state, references, translations, and outputs. Context packages relevant local material with versions, paths, selection reasons, and size limits; no vector service or LLM is required. Document content is data and cannot authorize executing commands.

Initially check --since may still perform a full check and say so. Later optimization must include changed targets, their inbound references, translations, and derived outputs. Deleting B must still inspect unchanged A that links to B. Configuration, templates, navigation, or uncertain dependency changes expand the scope to a full check. Cache data is disposable evidence, not authority.

Propose move <source> <target> as preview by default. A plan contains touched files, readable diffs, base hashes, translations, attachments, route changes, and an alias recommendation. Only confidently understood links can be rewritten; ambiguous template/shortcode references require review. Apply verifies bases, validates an isolated candidate, protects concurrent edits, and retains recovery information. A failing or stale plan does not partially overwrite user work.

Workspaces and optional tools

An explicit workspace registry names selected site directories. It reuses the single-site engine, reports per-site results and aggregate completion, and allows writes only to explicitly selected sites. It must not discover and upgrade every sibling repository automatically or duplicate Hugo settings.

Optional markdownlint, Vale, and lychee adapters use explicitly configured, already provisioned tools and normalize their findings. Required missing tools return 2; optional omissions remain visible. Exclude syntax the adapter cannot understand rather than rewrite it. Ambiguous external-link failures require a network-status distinction. Tool installation and network access are separate actions, and generic formatters never overwrite content by default.

R6 accepted local boundary

The explicit registry and optional adapters are accepted locally in supported R6 scope. Stable fields and limits are documented in the registry contract and tool contract; user steps belong to the guide. The accepted R7/R8 boundaries below retain their own evidence and limits.

oink.workspace/v1 names 1–64 literal directories in one regular YAML file bounded to 256 KiB, without duplicating Hugo settings or discovering siblings. Exact names, canonical root identity, registry-order selection, per-site 0/1/2 parity and explicit-name saved-plan application are the supported workspace boundary. Already provisioned markdownlint-cli 0.49.1, Vale 3.24.0 and lychee 0.24.2 extend each site’s policy, with captured configuration and typed protocol/source/network coverage. They do not install or format tools/content. Lychee needs explicit network consent; ambiguous external failures remain unknown rather than definite broken links.

Frozen Go/vet, actual Hugo/pinned-tool and owning race gates have passed. The exact binary also passed four-site direct/aggregate diagnostic/coverage/exit parity and all source byte/full-mode/Git/ignored-input/directory guards. Initial preparation failures remain excluded, and the receipt-driver-only command metadata correction is recorded without a CLI runtime correction or rerun. Guarded canonical EN/ZH source/rendered checks passed, and supported R6/A07/A15 scope is accepted locally in the R6 record. Current A18 runtime/archive qualification passed; Darwin amd64 remains experimental/unverified. No release, consumer adoption, source write or deployment is inferred from focused tests.

Read-only Oink Studio

Build a local Web interface with project overview, issue panel, translation comparison, page relationships, and publication panel. These views use the same core results as CLI/CI. Support filters, known-source navigation, actual Hugo preview, change comparisons, and copying proposed actions. A large graph or embedded editor is not needed for this acceptance.

Default to loopback and an explicit site allowlist. Separate untrusted rendered content from the management origin; handle Host/Origin checks and session authorization before adding write APIs. Ship prebuilt UI assets with the CLI; Node is a contributor build dependency, not a consumer runtime requirement. Cover keyboard operation, screen-reader labels, mobile layouts, light/dark themes, and readable long diagnostic lists.

R7 candidate boundary

The read-only Studio candidate now serves an embedded five-view browser and an authenticated literal-loopback API over the same native checks and captured Hugo facts. Stable candidate boundaries are in the contract and guide. Explicit existing-site/registry selection, typed paginated findings, source/diff/hash states, actual production preview and separate optional analysis coverage preserve the CLI authority.

Frozen native/browser/core-case and exact-binary four-consumer receipts now qualify the supported read-only scope, including explicit partial-preview incompletion. They exercise native 0/1/2 parity, keyboard/mobile/light/dark flows, literal source data and separate-origin preview attacks. R7/A16 passed guarded canonical promotion/rendered gates and is accepted locally. R1–R8 supported scope and current A18 runtime/archive qualification passed. No consumer Node requirement, implicit installation, source writes, public release/adoption or deployment is introduced.

Safe Markdown editing

Add Markdown editing, front matter forms, selected component insertion, and attachments after the read-only workbench is accepted. Reuse the CLI change-plan engine and actual Hugo preview; there is no second save/validation mechanism.

An unchanged open/save cycle must preserve bytes. Updating one field preserves unknown fields, comments, order, encoding, and unrelated whitespace. Detect external-editor changes and refuse stale saves. If a front matter form cannot preserve a construct, keep it editable as text and explain the form limitation. Do not round-trip the whole document through a generic serializer.

Writes need an authorized local session, an allowed directory, base verification, and a visible diff. Reject path traversal, symlink escapes, and requests from untrusted preview content. Attachments must not overwrite existing files. Publishing a static site never adds these management APIs to it.

R8 accepted editing boundary

R8 now has one source-preserving proposal engine for CLI edit text, field, snippet and attachment, and the explicit studio --edit flow. Default Studio remains read-only. Known site-owned Markdown, exact source hashes, supported top-level YAML scalars with text fallback, original catalog byte-boundary insertion and new-only leaf-bundle attachments share the same saved plans and guarded writer. The complete visible review binds plan/file/full-mode identities; candidate HTML comes from actual selected nonpublishable Hugo analysis, with native findings and required view incompletion kept distinct.

The accepted local interface is documented in the contract and guide. Corrected frozen public/Go/race/vet, actual/ordinary Hugo, Editor browser accessibility/mobile and exact-binary four-consumer preservation gates passed. R8/A17 supported local scope also passed guarded canonical source/render gates and is accepted in the R8 record. Earlier failed browser/preparation trials are evidence of those trial inputs, not qualification of later bytes. R1–R8 supported scope and current A18 runtime/archive qualification passed. Darwin amd64 stays experimental/unverified; public release, adoption and deployment are separate unperformed states.

Delivery sequence and schedule

The following is a one-developer estimate, not a measured productivity claim. T0 is the implementation start after scope approval; no calendar start date has been committed. Dependencies are sequential acceptance gates. Additional staff can parallelize independent tests and UI work, but cannot remove those gates.

Stage Effective weeks Delivery Acceptance gate
R1 1–2 Shared facts, check policy, focused scopes, trustworthy locations Hugo owns routes/relationships; required incomplete checks cannot pass
R2 3–5 Translation policy/review state, native checks, visible baseline Three language layouts; strict/localized cases; reviewed fixes preserve files
R3 6–8 Verified build artifacts, CI init, release diagnosis, deployed-site verification One checked artifact is uploaded; stale bytes and HTTP 200 fallback are detected
R4 9–11 New content, profiles, snippets/editor setup, upgrade diffs/comparisons Ordinary Hugo build; dirty/replaced/vendor and route-regression cases remain safe
R5 12–15 Inspect, impact, context, move and shared change plans Unchanged inbound links and translations are included; stale plans cannot write
R6 16–17 Explicit workspaces and optional check adapters Per-site parity; required unavailable tools are incomplete; no implicit installs
R7 18–20 Read-only Studio and its security boundary Five useful views; CLI/UI findings agree; accessibility and preview isolation pass
R8 21–24 Safe Markdown/forms/attachments with conflict review No-op save has zero diff; comments/unknown fields survive; concurrent saves fail safely

Allow another 4–6 weeks for integration, false-positive review, cross-platform execution, and repairs, distributed across the gates. Total planning range is 28–30 effective weeks. At roughly half-time availability, elapsed calendar time may be roughly twice that; this is an assumption to revisit, not a promise.

R1–R3 yield a proposed 0.2 quality/publication candidate around weeks 9–10 including early reserve. R4–R6 yield a proposed 0.3 maintenance candidate around weeks 19–20 cumulatively. R7–R8 yield a proposed 0.4 local Studio candidate around weeks 28–30 cumulatively. Public publication is a separate authorized action; local candidates do not require releasing every stage.

Conditional extensions

Extension Entry criterion Proposed boundary Separate estimate
E1 Publication exports At least two maintained books need a recurring export workflow Reuse distributable EPUB/PDF tools and package local artifacts; declare external dependencies 1–2 weeks after R3/R4
E2 Executable examples Explicit owners identify runnable examples and disposable test environments Reviewed execution profiles, time/resource limits, offline default; never execute discovered prose automatically 3–5 weeks after R5
E3 MCP An existing Agent integration needs capabilities beyond invoking JSON CLI results Thin adapter over inspect/check/impact/context/plans; same permissions and diagnostics 1–2 weeks after R5
E4 AI review and translation Deterministic translation maintenance works and a reviewed evaluation corpus exists User-selected provider, explicit network/cost settings, proposals bound to source hashes; no automatic source writes 3–6 weeks for a limited experiment after R5

These estimates are outside the R1–R8 total. Activate an extension only for its stated use case; a future need is not an unfinished core milestone. Remote Studio, live collaboration, native shells, general knowledge provenance, vector retrieval, and universal framework migration require separate PRDs and evidence.

Architecture and compatibility

Keep Go for core operations and use subprocesses for Hugo and optional tools. Extend existing packages when they own the behavior; add a package only with its capability. Do not create a generic plugin platform, public SDK, or shared service layer before an actual consumer needs it.

Preserve oink.result/v1, exit meanings, and the default thin wrappers. New diagnostic details and command data may be additive; changed field semantics need a new result version. Version review records, baselines, plans, build manifests, and workspace registries independently. Detect supported capabilities from the actual theme; do not require all users to install the latest release.

Read/check/preview, local file application, networking, example execution, and deployment are distinct side effects. No telemetry, background updater, credential discovery, arbitrary directory cleanup, global configuration change, commit, push, or deployment happens as a maintenance side effect. Read-only consumer trials preserve sources, replacements, workspaces, and vendor bytes.

Acceptance cases and owning checks

Case Required outcome Primary owner
A01 JSON and completion One JSON result on stdout; clean stderr separation; findings 1, required incompletion 2 internal/report, internal/app, Schema
A02 Hugo truth Slug/url/permalinks/aliases, custom mounts, unlisted pages and language roots follow actual Hugo results internal/site, internal/outputcheck, actual Hugo fixtures
A03 Subpaths A definite project-local missing route fails; outside-origin/path references remain classified honestly; declared external scopes avoid false positives Output checker and policy tests
A04 Translations Filename, directory and translationKey layouts; duplicate/missing/draft cases; strict/localized policy Translation engine and public-command tests
A05 Review state No record is unknown; changed source hash is visible; mtime never determines review state Translation/review-record tests
A06 Content syntax Fences, inline code, shortcodes, HTML, attributes, custom fields and configured protected text do not generate invented findings Native-rule tests and real content corpus
A07 Baselines and adapters Acknowledged findings remain visible; new findings fail policy; unavailable required tools cannot pass Policy/adapter tests
A08 Artifact identity Modify a file after check and manifest verification fails; provider upload uses the same exported tree without rebuilding Managed-build and workflow tests
A09 CI preservation Both templates, existing customized workflows, permissions/variables, preview/apply conflict and provenance Starter/CI tests and local workflow rehearsal
A10 Public verification HTTP 200 fallback, wrong language/build, missing resource, canonical mismatch, timeout/auth/rate limit Local HTTP fixtures, no required cloud account
A11 Initialization and authoring Supported profiles/languages; empty-target protection; generated sites build with ordinary Hugo; editor config preserves unknown settings internal/starter, authoring and Hugo tests
A12 Upgrade Readable diff, old/new routes, dirty files, both workspaces, replacement/vendor, failure recovery and concurrent edits internal/upgrade, public-command/Hugo tests
A13 Impact Deleted B finds unchanged A; translations/attachments/derived outputs included; global changes expand scope Impact and Git-baseline fixtures
A14 Change application Candidate validation before apply; hash conflicts and failed writes preserve subsequent edits; ambiguous references are not rewritten Shared plan/apply and move tests
A15 Workspace and context Per-site results match direct invocation; selected writes only; bounded context gives paths/versions/reasons without executing content Workspace/context tests
A16 Studio parity Five views show the same results as CLI; usable keyboard/mobile/light/dark flows and source/preview separation Studio browser/accessibility tests
A17 Editor preservation No-op save is byte-identical; YAML comments/unknown values/order survive; stale saves and attachment collisions are rejected Editor/browser and shared apply tests
A18 Runtime and recovery Test actual declared macOS/Linux targets; signals stop child processes; cached operations work offline; unsupported inputs remain explicit Process/integration/installation tests

Run the smallest owning tests before broader integration. Keep Go unit fixtures offline. Repeat actual Hugo tests after parser, snapshot, probe, initialization, or upgrade changes. Preserve focused checks rather than making consumers run theme internals or a browser suite for every document modification.

At each candidate, record tool versions and source identities, then test the Starter plus three distinct maintained sites read-only. Compare source bytes, modes, and Git state before and after. Tests for new native rules need a reviewed valid/invalid corpus; fix false positives before enabling a blocking default. Measure full-build time against the same current-site baseline before promising incremental speed. Functional correctness takes priority over check counts.

Completion and release evidence

For each stage, provide implemented behavior, known limits, focused tests, actual integration results, updated EN/ZH contracts/guides, and a reviewable diff. Track individual requirement/case statuses; passing an aggregate command does not automatically close every requirement. This PRD is complete only when R1–R8 and their required acceptance cases are satisfied.

Keep implementation, local validation, commits, archive/runtime qualification, public distribution, consumer adoption, provider deployment, and public content verification separate. Cross-compilation is not runtime acceptance. Missing credentials or an unpublished download URL do not justify claiming remote delivery, nor require building a hosting control plane.

After supported behavior is accepted, move it into the owning CLI contract, usage guides, and durable decisions. Retire the corresponding proposal sections through the existing lifecycle. Do not make this PRD a permanent second manual.

Decisions and stop conditions

Confirm staffing and start date before turning relative weeks into calendar dates. Decide supported runtime targets, review-record storage details, initial native-rule catalog, and precise additive command flags in R1. These are bounded implementation choices within this scope, not reasons to reopen the product boundary or wait for an entire theme release.

If preservation or correctness work exceeds a stage estimate, move its optional convenience work later; never remove stale-write protection, truthful completion, or ordinary-Hugo compatibility. If repeated corpus review shows a rule cannot be trustworthy, keep it advisory or remove it. If a form cannot preserve source bytes, keep that syntax in text mode. Conditional extensions do not enter the critical path merely because implementation would be interesting.

Decision log

Date Record
2026-10-03 Created from the current CLI audit and the supplied feature goals. Proposed R1–R8, optional extension gates, resource assumptions and executable acceptance cases. No new CLI capability, release, consumer adoption or deployment is claimed by this document.
2026-10-03 R1 shared Hugo facts and check policy passed local owning/actual-Hugo checks. Stable behavior moved into the CLI contract and guide; the acceptance record tracks final refreshed reports and rendered EN/ZH evidence separately. R2–R8 and conditional extensions remain open; no public distribution, adoption or deployment is claimed.
2026-10-03 R2 translation scopes/hash reviews, syntax-bounded native rules and visible baselines now use shared guarded file plans. Local owning, actual-Hugo and focused race gates passed; final refreshed consumer and EN/ZH documentation acceptance remains pending in the record. Implemented behavior is in the contract and guide. R3–R8 remain open.
2026-10-03 R2 final corpus and bilingual documentation gates passed. R3 one-render checked export, exact file identity, guarded CI plans for both providers, release diagnosis and explicit-network HTTP verification passed their scoped local gates, including custom-workflow discovery. Stable behavior moved to the contract and guide; exact evidence and A08–A10 outcomes belong to the maintenance record. R4–R8, final A18 runtime/archive refresh and Darwin amd64 remain open. No public distribution, hosted CI execution, adoption or deployment is claimed.
2026-10-03 R4 supported local scope passed frozen Go/vet, actual Hugo/race and exact-binary read-only Starter/docs/PIG/repository gates. One unchanged licensed Starter composes all profiles/languages; ordinary new/editor/snippet flows and source/external-input guards passed, as did readable bounded upgrade views and alias/output regression protection. Stable behavior belongs to the contract and guide; exact A11/A12 evidence and remaining limits belong to the record. R5–R8, final A18 runtime/archive refresh and Darwin amd64 stay open. No release, consumer writes/adoption or deployment occurred.
2026-10-03 R5 corrected frozen Go/vet, actual Hugo/race and exact-binary four-consumer read-only gates completed. Inspect/context complete for all sites; historical impact and move blockers remain explicit. Guarded canonical source/rendered gates and stage acceptance are pending. Stable behavior belongs to the contract and guide; the record identifies A13/A14/context evidence, the cached-module correction and exact preservation receipt. R6–R8, workspace A15 and final A18 remain open; no consumer writes or deployment occurred.
2026-10-03 R5 supported inspection/impact/bounded-context and guarded move scope is accepted locally after corrected frozen owning gates, exact-binary four-consumer preservation and first-promotion canonical source/rendered gates. The production translation owner retains only its known draft-release omission; separate nonpublishable analysis passes all owners. Stable behavior belongs to the contract and guide; the record retains exact outcomes and the separate post-render evidence boundary. A13/A14 supported CLI scope passed; A15 context passed while workspace/direct parity remains R6. R6–R8 and final A18 remain open; no release, consumer writes/adoption or deployment occurred.
2026-10-03 R6 explicit registry and bounded optional-tool candidate implemented; focused workspace and corrected actual-protocol trials passed, with preparation failures kept separate. Final runtime/corpus/canonical gates and A07/A15 stage acceptance remain pending in the R6 record. R7/R8 and final A18 remain open; no release, consumer write or deployment.
2026-10-03 R6 frozen Go/vet, actual Hugo/pinned tools, owning race and exact-binary four-consumer direct/aggregate parity/preservation qualified. The completed receipt records six original operations, existing repository duplicate-ID findings and a driver-only command-summary correction over unchanged raw outputs; no CLI/Hugo rerun or runtime fix was needed. Canonical source/render checks and explicit R6/A07/A15 stage acceptance remain pending in the record. R7/R8/final A18 remain open; no consumer source writes, release or deployment.
2026-10-03 R6 supported explicit-registry and optional-tool scope is accepted locally after frozen Go/vet, actual Hugo/pinned tools, race, exact-binary four-consumer parity/preservation and guarded canonical source/render gates. A07 adapters and A15 workspace/direct/context supported scope passed; the R6 record separates first-promotion rendered bytes from this post-render status/evidence amendment. R7/R8 and final A18 remain open; no public release, consumer source writes/adoption or deployment.

| 2026-10-03 | R7 read-only embedded Studio candidate and authenticated loopback views implemented; frozen core/browser and exact-binary four-consumer qualification completed within the declared scope; guarded canonical promotion/render and explicit R7/A16 acceptance remain pending. R1–R6 remain accepted; R8/final A18 open; no consumer writes, release or deployment. |

| 2026-10-03 | R7/A16 supported read-only Studio is accepted locally after frozen cumulative executed-case/browser proof, exact-binary four-consumer parity/preservation and guarded canonical source/render gates. First-promotion rendered bytes remain distinct from this post-render status amendment; whole invocation failure and explicit partial-preview incompletion stay visible. R1–R7 accepted; R8/final A18 open; no public release, consumer write/adoption or deployment. |

| 2026-10-03 | R8 CLI/opt-in Editor source-editing candidate implemented; default Studio remains read-only. Pure-core/streaming-output focused evidence is recorded and failed browser trials retained. Final frozen public/browser/consumer/canonical gates and A17 stage acceptance remain pending in the R8 record. R1–R7 remain accepted; R8/final A18 open; no public release or consumer writes/deployment. |

| 2026-10-03 | R8/A17 reviewed CLI/opt-in Editor supported local scope is accepted after corrected frozen whole owning/browser gates, exact-binary four-consumer proposal parity/source preservation and guarded first canonical promotion/actual rendering. R1–R8 are accepted locally; the R8 record separately binds first-rendered and current status bytes, retaining every failed trial, native finding and required partial-preview incompletion. Final A18 current Linux/archive qualification remains open; no consumer writes, public release, adoption or deployment. |

| 2026-10-04 | Current backend integrity correction, refreshed owning/Hugo/race, carried unchanged-runtime four-consumer preservation and three declared runtime/archive qualifications passed; see the dated completion supplement. Finite R1–R8 implementation is complete locally. Retain this requirements record and anchors, retire it from active navigation, and keep stable behavior in the contract/guide. Final canonical rendered lifecycle verification remains separate; E1–E4 have not been invoked. No public release, adoption or deployment. |

8.5 - Visual presets and appearance switching

Paper and Slate ship in 1.2.0; Ink and Terminal are explicitly enabled experiments awaiting design acceptance.
Phase 1 released in 1.2.0; Ink/Terminal opt-ins available

Paper, Slate and the Appearance menu ship with OINK 1.2.0. The architecture contract, accepted decision and acceptance record own phase-one behavior and evidence. A subsequent Ink/Terminal experiment provides actual selectable output; this proposal remains active for their design acceptance. The October 4 injected screenshots are research prototypes; they are distinct from the October 5 screenshots of actual theme output.

Status and surface

Field Value
Status Phase 1 released in 1.2.0; Ink/Terminal explicit opt-ins
Owner OINK maintainers
Date 2026-10-04
Baseline Theme main after v1.1.0 with unreleased 1.2.0 work; documentation site pinned to v1.1.0
Affected contracts Architecture: Trust, CSS, and accessibility (font roles, accent roles, inline-code colour), Shell (theme control), Landing, Configuration decision, Brand guide
Phase 1 Paper preset, Slate preset, default changed to Paper, reader switching between Paper and Slate
Later phases Ink/Terminal design acceptance; Folio and Canvas names reserved only

Context and evidence

The baseline and limitations below record the October 4 research input, before phase 1.

OINK ships one visual identity, referred to here as Slate: a cool blue-grey canvas (#f1f4f8 / #0b1119), navy text, steel-blue links (#245f94), copper accents, Inter for interface and prose, Chakra Petch for display and wordmark, IBM Plex Mono for code and technical labels, a blueprint grid and glow on the Landing hero, and a crimson inline-code pair. It is defined by Bootstrap custom properties in assets/scss/td/_brand.scss, shell tokens in assets/scss/td/shell/_tokens.scss, and font roles in assets/scss/td/_tokens-typography.scss.

PG.CENTER, an independent site, has a warm editorial reading style that maintainers want as the future OINK default. Its presentation tokens live in media/css/pgsql.css of that project. Measured on its local preview (2026-10-04, light and dark, home, Docs index, long manual page, component manual):

Role Light Dark Note
Canvas #f7f6f3 #161513 warm white / warm black
Raised surface #ffffff #1d1c19 cards, code blocks
Secondary surface #efede8 #262420 table header, hover
Ink #21201c #ece9e3 body text
Secondary text #56534c #b6b1a7
Lines and washes ink at 4.5–22 % alpha light ink at similar alpha no tinted greys
Radius 12 px / 8 px same
Shadow 0 2px 10px rgba(33,32,28,.07) black-based warm, soft
Motion 160 ms cubic-bezier(.2,.7,.2,1) same

Typography is IBM Plex Sans (variable, 400–600) for interface and prose, IBM Plex Mono for code, dates and versions, and Chakra Petch for the wordmark only. The component-manual pages are the best long-form model: lead paragraph 17 px capped at 70ch, h2 followed by a hairline that runs to the edge, framed tables with a header band and no zebra, monochrome callouts with a 3 px rule.

The following PG.CENTER elements are site identity, not reusable reading rules: the PostgreSQL brand blue #336791 family, wine content links, version-state colours, release strips, search-kind badges, wiki tones, the duotone hero, and the 144-character measure of the imported PostgreSQL manual. Two values fail WCAG AA (muted text 3.67:1, link hover 4.22:1) and are corrected below rather than copied.

Measured OINK docs typography for comparison: 16 px / 1.7 body, ≈ 76ch measure, h1 36 px / 700, h2 24 px / 600, code 14 px. PG.CENTER Docs index: 15.5 px / 1.7, ≈ 120ch.

Current limitations

  • Colours are tied to data-bs-theme only. No attribute selects a second palette, and several surfaces bypass tokens: the Landing primary button (#2f6793 with navy glows), the grid, scrims (rgba(4,10,18,.45)), print colours, asciinema surfaces, and the giscus stylesheets.
  • About 85 literal border radii and several literal shadows make a flat preset impossible without a radius and shadow scale.
  • The light/dark control expands on hover or focus. Touch readers cannot reach “follow system”; the trigger mixes aria-pressed and aria-expanded; Esc does not close it. The Landing mobile drawer has no theme control.
  • contrast-on-canvas.html hard-codes Slate canvas luminance for the theme_color warning.
  • dark_mode is opt-in (false by default), so the palette and its menu are absent unless a site enables them.

Goals and non-goals

Goals:

  • one site key selects the default visual preset; Paper becomes the default;
  • Slate remains available and reproduces current output for sites that choose it;
  • readers can switch Paper and Slate instantly, without reload, independent of light/dark/system mode;
  • the configured default renders without JavaScript and with storage unavailable;
  • presets share templates, components, and layout geometry; they change paint and type;
  • local fonts only, ordinary Hugo build, no new runtime framework or required build tool.

Non-goals:

  • implementing Ink, Terminal, Folio, or Canvas in phase 1;
  • copying PG.CENTER brand colours, version UI, or page structures;
  • per-page or per-section presets (section colour remains theme_color);
  • changing layout geometry, density, or navigation structure per preset in phase 1;
  • theming Swagger UI, ReDoc, or third-party embeds beyond their existing light/dark handling.

Preset model

This table and the phase-one configuration below retain the original scope. The later experiment adds explicit ink/terminal configuration and menu-list entries; true still offers the stable set plus the site default. The architecture contract owns current behavior.

Preset Direction Phase 1 Reader menu
paper Warm editorial minimalism Implemented, default Yes
slate Technical minimalism (current OINK) Implemented Yes
ink Typographic minimalism, Swiss-inspired Spec + research prototype No
terminal Terminal-inspired utilitarian Spec + research prototype No
folio Academic / book publishing Name reserved No
canvas Playful geometric / creator Name reserved No

Reserved names are rejected by validation until implemented, with a warning that names the stable presets.

Configuration

params:
  ui:
    preset: paper        # paper | slate        (theme default: paper)
    preset_menu: false   # false | true | [paper, slate]
  • preset selects the site default. Invalid or reserved values warn through the existing validation path and fall back to paper. Publishing gates turn the warning into a failure.
  • preset_menu controls the reader choice. false renders no style group and emits no preset-init script; true offers every stable preset; a list offers a subset that must contain preset. Following the dark_mode precedent, the default is false; the documentation site enables it; starter adoption is outside this change.
  • preset is site-level only. Page and section overrides are not supported: switching identity per page would break reader expectation and the stored choice.
  • The Appearance menu exists when either dark_mode.show_menu or a style choice is enabled. A site with dark_mode: false and preset_menu: true shows only the Style group.

Relationship to existing keys

Precedence, lowest to highest:

  1. Slate base tokens on :root / [data-bs-theme] (unchanged selectors).
  2. Preset tokens on [data-td-preset=X].
  3. params.ui.typography: system — collapses font roles to system faces after the preset blocks, so it still requests no brand font in any preset.
  4. params.ui.fonts — emitted inline after the stylesheet at :root; equal specificity and later source order beat preset font roles. Explicit fonts always win.
  5. theme_color / theme_color_dark — page and section accent backgrounds only. They override the preset accent; they never touch links or inline code.
  6. Site _styles_project.scss — last in the bundle.

typography: technical remains the name for “use the preset’s bundled faces”. The preset decides which bundled faces those are (Paper: Plex Sans; Slate: Inter + Chakra Petch).

Reader state

Two independent dimensions:

Dimension Attribute Storage Values
Style data-td-preset on <html> localStorage['td-preset'] stable preset names
Mode data-bs-theme (+ .dark-mode, vendor data-theme mirror) localStorage['td-color-theme'] light, dark, auto
Situation Result
First visit Server renders data-td-preset="<site preset>" and data-td-site-preset; no script needed
Reader chooses a preset Applied at once, stored, td-preset-change dispatched
Reader chooses the preset marked “Default” Storage key removed; future site default changes reach this reader
Next page, refresh, other language Inline head script applies the stored value before first paint
Stored value no longer offered Removed; site default used
Storage unavailable Choice applies to the current page; the menu states that it will not persist
JavaScript disabled Site default preset renders in its light palette, as the current theme does without script; no style or mode control is usable
Style change Never writes td-color-theme; mode change never writes td-preset
Other tab changes the value storage event applies it

The inline script runs before the stylesheet, beside the existing dark-mode script. It validates the stored value against the allowed list embedded at build time, sets the attribute, and updates the theme-color meta and the pre-paint canvas colour for the preset and mode. It is emitted only when the menu offers more than one preset. Independently of the menu, the static pre-paint <style> and the resolved theme-color meta in head.html are rendered from the site default preset’s canvases instead of the current hard-coded #0b0d12, #ffffff, and #000000.

On switch the runtime sets data-td-preset-switching for one frame to suppress colour transitions, records the first visible heading or block as a scroll anchor, applies the attribute, and restores the anchor offset, again after document.fonts.ready because Plex Sans and Inter have different metrics. Focus, open menus, and form state stay untouched. Phase 1 uses no cross-fade or View Transition.

Appearance menu

Three options were compared:

Option Assessment
Keep the hover menu, add a style row Keeps the touch and keyboard gaps; hover-only discovery
Separate style and mode buttons Two icons in a crowded navbar; mobile drawer gets longer
One Appearance disclosure with two radio groups Chosen: one entry point, works with touch and keyboard, scales to more presets

Behaviour:

  • Trigger: one icon button (aria-expanded, aria-controls, label “Appearance”). It replaces the current theme button in the navbar and in the shell footer line. Sun means the current light state; moon means dark. The t shortcut keeps toggling light/dark.
  • Panel: a non-modal popover containing two native fieldset radio groups. The October 5 revision uses Style: icon-and-name buttons in two columns, with a preset-colored icon and no preview letters or experiment badges. The site default is identified by its tooltip and accessible name. Light: a segmented Light / Dark / System control. Selection applies immediately and the panel stays open so readers can compare.
  • Keyboard: Enter/Space or ArrowDown opens and focuses the checked radio; arrow keys move within a group (native radio behaviour); Tab moves between groups; Esc closes and returns focus to the trigger; focus leaving the panel or an outside click closes it.
  • Feedback: the selected option has a tinted background and accent border; keyboard focus has a separate outline. Changes are announced through native radio semantics; no extra live region.
  • Restore default: selecting the site’s default preset clears the stored choice. No separate reset button is needed.
  • Mobile (< 768 px): the trigger stays in the compact header and is also offered in the docs drawer footer and in a new row of the Landing mobile drawer. The panel opens as a bottom sheet with 44 px targets, the same two groups, and a close button. The sheet is a modal <dialog> opened with showModal(), so it lives in the top layer: the prototype showed that the sticky header’s backdrop-filter otherwise becomes the containing block of a position: fixed sheet and the drawer’s stacking context hides it.
  • Command palette: a switch_preset action next to switch_theme.

dark-mode.js keeps its storage key and attributes. It must sync the checked state of the Light radios and listen to their change events instead of the current aria-pressed buttons.

Token architecture

All presets compile into the single existing main.css. Fonts are declared with @font-face and are downloaded only when a rule uses them, so offering a preset costs CSS bytes but no font bytes until it is selected.

// Slate: existing selectors and values, unchanged
:root, [data-bs-theme='light'] { … }
[data-bs-theme='dark'] { … }

// Every other preset
[data-td-preset='paper'] { /* light tokens + font roles */ }            // (0,1,0)
[data-td-preset='paper'][data-bs-theme='dark'],
[data-td-preset='paper'] [data-bs-theme='dark'] { /* dark tokens */ }   // (0,2,0)

// Then: [data-td-typography='system'] font block (moved after presets)

Rules:

  1. Token parity. Each dark block redeclares every token of its light block, so Slate dark never leaks into another preset. A checker enforces it.
  2. Dark islands. The descendant form covers nested data-bs-theme="dark" islands (Landing code plate, previews).
  3. Font roles only at (0,1,0), so params.ui.fonts keeps winning.
  4. Accent indirection. Presets set --td-preset-accent (and -rgb, -hover); --td-accent defaults to it. theme_color keeps writing --td-accent and therefore overrides the preset in both modes.
  5. Slate stays attribute-free. data-td-preset="slate" matches no override block, so current site overrides of brand tokens behave exactly as today.
  6. Geometry is shared. Presets do not change grid columns, sidebar width, or breakpoints in phase 1.
  7. Preset-specific rules are few and scoped to [data-td-preset=X] in one partial per preset. Anything two presets need becomes a token.

New shared tokens required before Paper (phase 1): --td-shell-scrim, Landing --td-grid / --td-glow / primary-button tokens, --td-callout-tint, --td-code-inline-bg, --td-hairline, and a brand font role (--td-brand-font-family, default var(--td-display-font-family)) so the wordmark can keep Chakra Petch while Paper’s display headings use Plex Sans.

The original phase-two plan proposed global radius, shadow and density scales. The October 5 experiment instead scopes those changes to owned components; a wider token refactor is not a prerequisite for trying the designs.

Contract change: the architecture contract currently fixes inline code to a crimson pair. This proposal makes --bs-code-color a preset token (Slate keeps crimson, Paper uses an ink chip). theme_color still never touches it.

Fonts

Preset UI / body / heading Display Brand (wordmark) Meta Code New bytes
Paper IBM Plex Sans IBM Plex Sans Chakra Petch IBM Plex Sans IBM Plex Mono Plex Sans
Slate Inter Chakra Petch Chakra Petch IBM Plex Mono IBM Plex Mono none
Ink Inter Inter Inter Inter (tabular) IBM Plex Mono none
Terminal Plex Mono chrome, Plex Sans prose IBM Plex Mono IBM Plex Mono IBM Plex Mono IBM Plex Mono none after Paper

Paper vendors @fontsource-variable/ibm-plex-sans (OFL-1.1) into third_party/ with a VENDOR.json entry: Latin, Latin Extended, Cyrillic, Cyrillic Extended, Greek and Vietnamese subsets, normal and italic, weights 100–700. PG.CENTER’s normal-only 400–600 subset is 40,240 B (latin) + 25,868 B (latin-ext); exact sizes are recorded at vendor time. Italic is required because OINK prose uses emphasis and PG.CENTER’s synthesized italic is not acceptable. The full small subsets preserve the existing locale coverage; the browser loads only the ranges actually used. All 12 font files are recorded in VENDOR.json.

Chinese, Japanese and Korean use system stacks placed after the Latin face: -apple-system, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Noto Sans CJK SC', 'Noto Sans SC', sans-serif. IBM Plex Sans SC was rejected because its files are megabyte-scale. Monospace stacks insert CJK sans families before the generic monospace keyword so mixed code keeps a predictable CJK face.

typography: system continues to request no brand font: the system block follows every preset block and resets all roles, including brand.

Serif. Phase 1 uses no serif. Latin serif headings beside CJK sans headings look inconsistent, Windows’ default CJK serif renders poorly at heading sizes, and a serif costs another font. A later opt-in display-only serif can be reviewed with the side-by-side mockup produced for this proposal.

Preset specifications

Shared foundation

Belongs to every preset, not to Slate:

  • layout geometry, breakpoints, sidebar/TOC widths, ≈ 76ch prose measure;
  • body 1rem / 1.7 for prose, 0.875rem for chrome, 0.8125rem for meta;
  • type scale ratios (h1 2.25rem, h2 1.5rem, h3 1.25rem, h4 1rem) — presets tune weight and tracking, not size, in phase 1;
  • focus ring: 2 px accent outline with 2 px offset, never removed; forced-colors fallbacks unchanged;
  • semantic status colours (note, tip, important, warning, caution) keep their hue; presets change tint strength and frame;
  • syntax highlighting keeps the existing light/dark Chroma palettes in phase 1;
  • motion tokens 100/150/250 ms; prefers-reduced-motion disables transitions;
  • WCAG AA: 4.5:1 body, 3:1 large text and UI boundaries, in both modes.

Paper

Warm editorial minimalism. Warm paper, ink text, quiet hairlines, soft shadows, and generous but not loose reading rhythm. It serves long-form reading: lower blue light on large canvases, less chrome contrast, and a typeface (Plex Sans) with open counters that reads well at 16 px.

Token Light Dark
Canvas --bs-body-bg #f7f6f3 #161513
Raised --td-brand-elev, --td-pre-bg #ffffff #1f1e1a / #121110
Secondary surface #efede8 #1f1e1a
Body #21201c (15.09:1) #ece9e3
Secondary text #56534c (7.10:1) #b6b1a7
Tertiary text #6b665d (5.27:1) #958f84 (5.68:1)
Border ink 12 % light ink 13 %
Link / hover #2b5f8c (6.23:1) / #1d68a5 (5.43:1) #7db5e6 (8.36:1) / #a3cdf3
Accent (copper) #9c5530 (5.17:1) #d99a6c
Inline code ink on ink-6 % chip light ink on 8 % chip
Shadow sm / md 0 2px 10px / 0 14px 38px ink 7 % / 13 % black 35 % / 50 %
Radius code 12 px, cards 12 px, controls 8 px same

Rules specific to Paper: headings Plex Sans 600 with −0.006em (h1 −0.012em); h2 followed by an edge hairline; framed tables (radius 10, header band, no zebra); callouts with a 4 % (dark 6 %) semantic wash and a single 3 px rule; hairline blockquote; Landing without grid or glow, primary button from tokens with a warm shadow, hero title 600 / −0.025em; selected navigation rows on a warm neutral ground with 9 % (dark 12 %) accent mixed in. Links remain blue: a reading convention, not decoration. Motion 160 ms ease-out for hover and popovers; no movement on scroll.

Slate

Technical minimalism. The current OINK appearance, unchanged: cool blue-grey canvas, navy ink, steel blue and copper, Inter body, Chakra Petch display, Plex Mono labels and metadata, blueprint grid and hero glow, crimson inline code, 8–12 px radii. Selecting preset: slate must reproduce v1.1 token values; a checker compares them. Grid, glow, Chakra display headings, mono metadata and crimson inline code are Slate identity. Layout, focus, status colours, and the shell structure are shared foundation.

Ink

Typographic minimalism, Swiss-inspired information design. Black, white and neutral grey; one red accent; hierarchy carried by size, weight and alignment rather than colour, shadow or rounded surfaces.

Token Light Dark
Canvas #ffffff #0b0b0b
Body #141414 #ededed
Secondary / tertiary #474747 / #636363 #b5b5b5 / #8f8f8f
Surface #f4f4f4 #161616
Link ink, underlined; hover red light ink, underlined; hover red
Accent #c8102e (5.88:1) #ff5c4d
Radius / shadow 0 / none 0 / none

Differences from Slate: no tinted canvas, no blue, no grid texture, no shadows, no rounded corners; links are identified by underline, not hue; headings use Inter 700–800 with tight tracking instead of Chakra Petch. Differences from Paper: neutral not warm, flat not soft, ruled not hairline, underline links not blue. Distinctive rules: 2 px black rule above h2; h1 800 / −0.035em; uppercase tracked h4, table headers and callout titles; selected navigation row marked by a 3 px red bar, not a fill; tabular numerals.

Terminal

Terminal-inspired utilitarian design. Structure and information expression, not CRT effects: monospaced chrome, command and path notation, compact controls, strong panel borders, amber or teal accents.

Token Light Dark
Canvas #f4f5f2 #0c0f0e
Body #1d211f #d3dbd6
Secondary #4a514d #9aa59f
Surface #e9ebe6 #141a18
Link (teal) #0a6560 (6.31:1) #4cc9bd
Accent (amber) #935400 (5.47:1) #f0a73a
Radius 2 px 2 px

Mono scope: navigation, headings, labels, metadata, breadcrumbs, buttons and code use IBM Plex Mono. Prose paragraphs, lists and table bodies use Plex Sans with platform CJK fallbacks, because long monospaced paragraphs and mixed CJK/Latin mono lines read poorly. Distinctive rules: ## prefix before headings rendered with content: '## ' / '' so assistive technology ignores it; bracketed callout labels ([NOTE]); the selected navigation row is inverted with a ▸ marker; 1 px strong panel borders; static ▍ caret in the hero. No scanlines, glow, blinking, or typing animation.

Difference matrix

Paper Slate Ink Terminal
Temperature warm cool neutral neutral-green
Canvas light #f7f6f3 #f1f4f8 #ffffff #f4f5f2
Canvas dark #161513 #0b1119 #0b0b0b #0c0f0e
Prose face Plex Sans Inter Inter Plex Sans
Heading face Plex Sans 600 Inter 600–700 Inter 700–800 Plex Mono
Display / wordmark Plex Sans / Chakra Chakra / Chakra Inter / Inter Plex Mono
Link signal blue steel blue underline + red hover teal
Accent copper copper red amber
Radius 8–12 8–12 0 2
Shadow soft warm navy-tinted none none
Section rule h2 trailing hairline none 2 px top rule ## marker
Selected row warm tint accent tint red bar inverted + ▸
Inline code ink chip crimson ink chip ink chip, bordered
Landing texture none grid + glow none none
Chrome density standard standard standard compact

Page density

Density follows the task, not the preset: the Landing hero allows the largest display type and brand expression; Docs prose keeps 1rem / 1.7 and ≈ 76ch; sidebar, TOC, parameter tables, search results and the command palette keep compact rows (0.875rem, 1.4–1.5 line height). Presets may change paint inside these zones but not their spacing in phase 1. Terminal’s compact chrome is a phase 2 density token.

Runtime surfaces

Surface Phase 1 impact
Blog, Book, taxonomy Tokens only; Book captions keep the prose face
Search dialog and command palette Scrim tokenized; selected row uses --td-shell-primary-dim
Mermaid, ECharts Colours baked at init on data-bs-theme. Add a data-td-preset observer only if charts take preset colours; phase 1 keeps mode-only chart palettes
asciinema Surface tokens; re-mount only if the code face changes (not in phase 1)
giscus Needs one stylesheet per preset and mode, re-posted on td-preset-change
Swagger UI, ReDoc Keep vendor styling and current light/dark handling
Print Tokenize navy and cool greys; print always uses a light palette from the active preset
404 Its own <html> must carry the new attributes

Accessibility, security, and output

  • Every preset palette passes WCAG AA for body, secondary and tertiary text, links, and accents in both modes (values above). theme_color contrast warnings compute against the active site default preset’s canvases.
  • The menu uses native radios; no role="menu". Focus is never trapped except in the mobile bottom sheet, which is modal and restores focus.
  • prefers-reduced-motion and forced colors keep current behaviour.
  • The init script is inline, static, and derived from validated configuration; the stored value is matched against a build-time allowlist before use.
  • No external font or script request is added. Output adds two <html> attributes, one inline script, and CSS.

Compatibility and migration

Changing the default to Paper changes every site that does not set preset.

  • Sites that want the current look add params.ui.preset: slate; the upgrade note leads with this one line. Slate output must equal v1.1 tokens.
  • Sites with custom brand overrides in _styles_project.scss: light overrides on :root keep working under Paper by source order; dark overrides on [data-bs-theme='dark'] are outranked by Paper’s dark block. Such sites should choose Slate or move overrides to [data-td-preset='paper'][data-bs-theme='dark']. The upgrade note and brand guide document this.
  • theme_color, typography, and fonts keep their meaning and precedence.
  • Sites with dark_mode: false still get one light palette, now Paper.
  • The release that changes the default must state it as a visible change. Whether that release is a minor (1.x) or major version is an open decision.
  • A consumer inventory should report sites with brand overrides before the default change is published.

Implementation plan

Phase 1, in dependency order. Each step names its owning checker.

  1. Tokenize Slate leaks. Landing primary button, grid, glow, scrims, print colours, asciinema surfaces; add --td-preset-accent, brand font role, and per-preset canvas luminance in contrast-on-canvas.html. Slate output must stay byte-for-byte equivalent in computed colour. Checkers: check-landing.py, check-output.py, check-font-tokens.py.
  2. Vendor IBM Plex Sans. third_party/, VENDOR.json, licence file. Checker: check-vendor.py.
  3. Preset tokens. New assets/scss/td/_presets.scss (imported after _brand.scss); the implementation keeps Paper in that file instead of a separate presets/_paper.scss. Place preset font roles before the system typography reset. Checkers: extend check-font-tokens.py (Plex Sans family, system block order, token parity between light and dark blocks).
  4. Configuration. hugo.yaml defaults (preset: paper, preset_menu: false); a resolver partial used by validate.html, document-attrs.html, layouts/404.html, and head.html (init script, theme-color, pre-paint canvas). Regenerate the schema. Checkers: check-params.py (accepted, invalid, reserved), generate-config-schema.py --check, check-namespace.py.
  5. Appearance menu. Shared partial used by navbar.html, shell/footer-line.html, and the Landing mobile drawer; preset.js runtime (or a section of dark-mode.js); dark-mode.js radio sync; palette action switch_preset; i18n strings in all 32 catalogs. Checkers: check-shell.py, check-actions.py, i18n checker, tests/js/preset.test.js, tests/js/dark-mode.test.js.
  6. Third-party surfaces. Per-preset giscus stylesheets and re-post.
  7. Documentation. EN/ZH architecture, shell and landing contracts; brand guide (presets, migration, fonts); configuration reference; changelog and upgrade note.
  8. Site validation. make -C ../oink.pgsty.com check, browser (add preset switching, persistence, storage failure, no-JS, EN/ZH, desktop/mobile, light/dark cases), and dev for visual review.

Acceptance criteria

The following are the original acceptance targets. Executed checks and remaining limits are recorded separately in the October 5 acceptance record:

  • With no preset key, output carries data-td-preset="paper" and renders Paper with JavaScript disabled.
  • preset: slate produces computed colours and font roles equal to v1.1 across the checker fixtures.
  • Switching style never changes td-color-theme; switching mode never changes td-preset; both survive navigation, reload, and language switch.
  • Invalid stored values are removed; storage failure leaves the page usable and shows the non-persistence note.
  • No first-paint flash between presets in Chromium, Firefox and WebKit at normal and throttled CPU.
  • Scroll position after a switch stays within one line of the anchor.
  • typography: system triggers no font request in any preset; params.ui.fonts overrides preset faces.
  • theme_color overrides the accent in both modes under Paper and Slate.
  • The menu is fully operable with keyboard, touch, and screen readers; axe reports no new violations.
  • All palettes meet the contrast table in both modes.
  • Presets add no external font or script dependency; explicitly configured services such as Giscus remain separate. --panicOnWarning builds pass.

Open decisions

  1. Resolved for phase 1: preset_menu: false; the docs site enables it.
  2. Target resolved for release preparation: 1.2.0, with a prominent Paper-default notice and the preset: slate compatibility setting. Published in 1.2.0.
  3. Resolved for phase 1: the wordmark role is brand.
  4. Whether a display-only serif becomes a Paper option after phase 1.
  5. Whether charts (Mermaid, ECharts) should take preset colours in phase 2.

Ink and Terminal backlog

Implemented experimentally: both palettes, existing font roles, prose link and selection signals, heading treatments, scoped geometry, compact Terminal navigation, Giscus palettes, print and the existing switching mechanism. No new font file, animation or runtime is added. See the experiment record for actual output and verification.

Before stable promotion, review long-page red accent density and CJK underline weight in Ink; numbered headings, mono wrapping and dense parameter tables in Terminal; Windows/Android fallback faces and manual screen-reader speech. Mermaid/ECharts and API vendors remain mode-only for this experiment. Wider geometry/density tokens and preset-colored charts require a separate decision.

Decision log

Date Change
2026-10-04 Draft created with Paper/Slate phase-1 scope, Ink/Terminal research specs, Appearance menu choice, and token architecture
2026-10-05 Phase 1 implemented locally; defaults, brand role and mode-only chart scope accepted; release version undecided and no publication performed
2026-10-05 Subsequent explicit Ink/Terminal experiments implemented; stable menu policy retained; design acceptance remains open
2026-10-05 Release preparation targets 1.2.0; simplified Style/Light controls and current-state icons replace the earlier swatch proposal; no tag or deployment created