Skip to content

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

Return to the regular view of this page.

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.

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.

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?

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.

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

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