guide

Reference import

Controlled conversion of Reference sheets with provenance and loss reports.

smill import reference converts one explicit local file into canonical GFM+. It does not fetch sources, crawl repositories or detect ambiguous formats.

smill import reference python.md \
  --repository https://github.com/Fechin/reference \
  --source-path source/_posts/python.md \
  --revision 82cbc0929f2d7a44e0bb5fc77e0a8442d0230b59 \
  --license GPL-3.0 \
  --attribution 'Fechin/reference contributors' \
  --dry-run --report json

To write a converted file, replace --dry-run with --out sheets/python.md. The parent directory must already exist. Existing files, symlinks and directories are refused, including a previous conversion. There is no force option. Output is exclusively created with mode 0600; a write/sync error may leave a partial new file, but never overwrites an existing file. Dry-run performs no writes.

All provenance flags are required: HTTPS repository URL, clean relative source path, full lowercase 40/64-hex revision, source license and attribution. These are user assertions; import does not verify that the file came from that repository. A SHA-256 of the actual input is recorded so identity can be checked independently. No current time or local absolute path is added. Existing source metadata is refused rather than overwritten.

Conversion and loss report

The Markdown body is preserved byte-for-byte, except unsupported attribute syntax, which is removed and archived under source.original_metadata.removed_attributes with its original line (IMP108, IMP109). The frontmatter is serialized as sorted, indented JSON inside YAML delimiters; JSON is valid YAML. Known fields and unknown data survive, with the following legacy behavior archived under source.original_metadata:

CodeChange or diagnostic
IMP001Missing/invalid explicit provenance (error)
IMP002Existing source provenance would be overwritten (error)
IMP003Canonical serialization/revalidation failed (error)
IMP101plugins archived; no plugin behavior is imported (warning)
IMP102background, class, layout, theme and upstream navigation (any shape) archived as inert data under source.original_metadata; navigation is never activated or derived into canonical placement (warning)
IMP103Raw HTML retained as inert data, future rendering needs sanitization (warning)
IMP104Hexo-style {% ... %} directive retained as inert text (warning)
IMP105intro mapped to description unless one is set; original archived (warning)
IMP106First usable categories entry mapped to category unless set; list archived (warning)
IMP107Invalid tags (non-array, null or empty entries) dropped; original archived (warning)
IMP108Unsupported token removed from a heading group, e.g. .secondary; supported ones such as .cols-2 stay (warning)
IMP109Standalone attribute line such as {.shortcuts} or {.marker-none} removed (warning)
IMP110Standalone {.shortcuts} inside a card removed; the card is marked .keys so its key table renders as keys (warning)
IMP111Upstream cheatsheet.cards or .command heading marker archived as inert source data before shared validation/selection (warning)
IMP112Upstream cheatsheet.updated or cheatsheet.tool_version of any shape archived as inert source data; no date or version is inferred (warning)
Core diagnostic codesUnsupported structural attrs/YAML/GFM+ contract errors

Code blocks/code spans are excluded from directive scanning. This is an explicit limited adapter, not a complete Hexo/plugin-language analyzer. Supported heading tokens are kept: #id and .cols-1–.cols-6 on sections; #id, spans 1–12, .note, .tip, .warning, .navi and .wrap on cards. Fenced code is never rewritten. Validation runs on a copy whose removed bytes are blanked, so every finding and core diagnostic keeps its original line and column. Malformed or conflicting groups that remain still fail before writing. The resulting canonical document is revalidated.

--report json requests a schema-1 JSON report on stdout with explicit provenance, input/output hashes and source-positioned findings. Conversion failures also return JSON findings, stderr diagnostics and exit 1; no output document is written. IO or usage failure returns exit 2. Without --report json, warnings go to stderr and successful conversion has no stdout output. Report order and bytes are deterministic.

Imported licensing/attribution is retained as source metadata. Successful conversion never grants redistribution permission; there is no publish operation in this CLI. The library never executes plugin declarations, snippets, scripts or suggestions.

Benchmark and tests

The pinned Reference Python file at the example revision was read into scratch storage and passed through the adapter: 17 sections, 119 cards, two findings (disabled plugins and inert background configuration). Input SHA-256: e56491b4bbe22d6e58b259b02cd62bfea136cbe79f2704a1631d3b919db8d1d5. A dry run over a 2026-10-14 snapshot of all 216 upstream posts, held only in scratch storage, converts 210; the six failures are genuine source defects (three without frontmatter, a card before any section, an empty title, two attribute groups on one heading). No upstream authored sheet is bundled. Run the optional benchmark against that exact supplied file with SHEETMILL_REFERENCE_PYTHON=/absolute/path/python.md go test ./internal/importer/reference -run TestPinnedPython -v; its hash is checked first. Ordinary CI uses synthetic fixtures and golden conversion/report snapshots, without network fetches. TLDR/navi/Devhints and bulk recognition remain future proposals.

Shared command metadata

Reference cheatsheet.cards is archived unchanged under source.original_metadata.cheatsheet.cards before core validation, with IMP111. Archival preserves semantic JSON values, not YAML token spelling, comments or numeric token types such as the distinction between 1 and 1.0. Upstream .command markers are removed and archived as inert original attributes with positioned findings; they never activate canonical command selection. Upstream cheatsheet.updated and cheatsheet.tool_version are archived the same way (merged into the same cheatsheet entry) with IMP112, whatever their shape: their upstream meaning is unknown, so the import never activates, validates or infers sheet freshness (updated/tool_version). Set them yourself only from verified evidence. Other cheatsheet fields and existing legacy import behavior are retained. Commands inside code examples remain literal content and are never executed.