This is the multi-page printable view of this section. .
Design proposals and PRDs
- 1: Backlinks and knowledge graph
- 2: Media convergence
- 3: OINK CLI and the next product stage
- 4: OINK CLI maintenance roadmap
- 5: Visual presets and appearance switching
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:
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:
- status, owner, date, and affected contract surface;
- context and evidence;
- goals and explicit non-goals;
- proposed behaviour and output/accessibility/security boundaries;
- compatibility and migration impact;
- implementation and owning-checker plan;
- acceptance criteria and open decisions;
- 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
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
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;
refandrelrefare included;- each language produces an independent graph;
- an unresolved derived edge warns or is reported by the focused checker
without making ordinary
hugo serverunusable.
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.
Backlink output
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:
- Does the local graph expose one depth or a tightly capped second depth?
- Which page metadata, if any, is useful enough to enter graph JSON?
- 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.backlinksis a bare boolean defaulting to off, pages override withbacklinks, 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
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
figform 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:
- add processing arguments to the full
figsource form and normalize them through the same processing helper; or - keep processing exclusively on native Markdown images and document full
figas 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
- Is one shared result struct enough, or would a common lower-level URL/resource record keep resolver ownership clearer?
- Should Landing consume resource attribution, or only dimensions and URL?
- Does full
figprocessing solve a real consumer need now that native images support numbering, captions, links, and processing together? - Which emitted compatibility names are still used by real consumers?
3 - OINK CLI and the next product stage
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:
- Put safe upgrades alongside initialization and diagnosis. Existing users have an immediate, testable maintenance need.
- Separate maintainer regression checkers from consumer checks. A synthetic-fixture checker is not automatically a general-purpose site validator.
- Treat migrations as supported input profiles, not a promise of complete Docsy or arbitrary MDX conversion.
- 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 |
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 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, 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-themeonly. No attribute selects a second palette, and several surfaces bypass tokens: the Landing primary button (#2f6793with 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-pressedandaria-expanded; Esc does not close it. The Landing mobile drawer has no theme control. contrast-on-canvas.htmlhard-codes Slate canvas luminance for thetheme_colorwarning.dark_modeis opt-in (falseby 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
presetselects the site default. Invalid or reserved values warn through the existing validation path and fall back topaper. Publishing gates turn the warning into a failure.preset_menucontrols the reader choice.falserenders no style group and emits no preset-init script;trueoffers every stable preset; a list offers a subset that must containpreset. Following thedark_modeprecedent, the default isfalse; the documentation site enables it; starter adoption is outside this change.presetis 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_menuor a style choice is enabled. A site withdark_mode: falseandpreset_menu: trueshows only the Style group.
Relationship to existing keys
Precedence, lowest to highest:
- Slate base tokens on
:root/[data-bs-theme](unchanged selectors). - Preset tokens on
[data-td-preset=X]. params.ui.typography: system— collapses font roles to system faces after the preset blocks, so it still requests no brand font in any preset.params.ui.fonts— emitted inline after the stylesheet at:root; equal specificity and later source order beat preset font roles. Explicit fonts always win.theme_color/theme_color_dark— page and section accent backgrounds only. They override the preset accent; they never touch links or inline code.- 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. Thetshortcut keeps toggling light/dark. - Panel: a non-modal popover containing two native
fieldsetradio 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 withshowModal(), so it lives in the top layer: the prototype showed that the sticky header’sbackdrop-filterotherwise becomes the containing block of aposition: fixedsheet and the drawer’s stacking context hides it. - Command palette: a
switch_presetaction next toswitch_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.
Rules:
- Token parity. Each dark block redeclares every token of its light block, so Slate dark never leaks into another preset. A checker enforces it.
- Dark islands. The descendant form covers nested
data-bs-theme="dark"islands (Landing code plate, previews). - Font roles only at (0,1,0), so
params.ui.fontskeeps winning. - Accent indirection. Presets set
--td-preset-accent(and-rgb,-hover);--td-accentdefaults to it.theme_colorkeeps writing--td-accentand therefore overrides the preset in both modes. - Slate stays attribute-free.
data-td-preset="slate"matches no override block, so current site overrides of brand tokens behave exactly as today. - Geometry is shared. Presets do not change grid columns, sidebar width, or breakpoints in phase 1.
- 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-motiondisables 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 |
| 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_colorcontrast 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-motionand 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 equalv1.1tokens. - Sites with custom brand overrides in
_styles_project.scss: light overrides on:rootkeep 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, andfontskeep their meaning and precedence.- Sites with
dark_mode: falsestill 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.
- Tokenize Slate leaks. Landing primary button, grid, glow, scrims, print
colours, asciinema surfaces; add
--td-preset-accent,brandfont role, and per-preset canvas luminance incontrast-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. - Vendor IBM Plex Sans.
third_party/,VENDOR.json, licence file. Checker:check-vendor.py. - Preset tokens. New
assets/scss/td/_presets.scss(imported after_brand.scss); the implementation keeps Paper in that file instead of a separatepresets/_paper.scss. Place preset font roles before thesystemtypography reset. Checkers: extendcheck-font-tokens.py(Plex Sans family, system block order, token parity between light and dark blocks). - Configuration.
hugo.yamldefaults (preset: paper,preset_menu: false); a resolver partial used byvalidate.html,document-attrs.html,layouts/404.html, andhead.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. - Appearance menu. Shared partial used by
navbar.html,shell/footer-line.html, and the Landing mobile drawer;preset.jsruntime (or a section ofdark-mode.js);dark-mode.jsradio sync; palette actionswitch_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. - Third-party surfaces. Per-preset giscus stylesheets and re-post.
- Documentation. EN/ZH architecture, shell and landing contracts; brand guide (presets, migration, fonts); configuration reference; changelog and upgrade note.
- Site validation.
make -C ../oink.pgsty.com check,browser(add preset switching, persistence, storage failure, no-JS, EN/ZH, desktop/mobile, light/dark cases), anddevfor 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
presetkey, output carriesdata-td-preset="paper"and renders Paper with JavaScript disabled. preset: slateproduces computed colours and font roles equal tov1.1across the checker fixtures.- Switching style never changes
td-color-theme; switching mode never changestd-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: systemtriggers no font request in any preset;params.ui.fontsoverrides preset faces.theme_coloroverrides 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.
--panicOnWarningbuilds pass.
Open decisions
- Resolved for phase 1:
preset_menu: false; the docs site enables it. - Target resolved for release preparation:
1.2.0, with a prominent Paper-default notice and thepreset: slatecompatibility setting. Published in 1.2.0. - Resolved for phase 1: the wordmark role is
brand. - Whether a display-only serif becomes a Paper option after phase 1.
- 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 |