This is the multi-page printable view of this section. .
Design proposals and PRDs
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 |
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.
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.
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?