# SDOC Authoring Guide @sdoc-authoring { # Meta @meta { type: skill sdoc-version: 0.2 } # About @about { How to write correct SDOC files. Covers document structure, inline formatting, block types (lists, tables, code, blockquotes), and common mistakes. Read the Quick Reference for everyday authoring. Read Common Mistakes before generating SDOC for the first time. } # Quick Reference @quick-reference { # Core Principle @core-principle { Structure comes from explicit brace scoping. \`{ }\` provides unambiguous scope boundaries. Whitespace and indentation are cosmetic — use them freely for readability but they never affect meaning. Write each paragraph as a single long line. Do not hard-wrap body text at 72 or 80 characters. The renderer handles line wrapping based on the viewer's window width. A blank line separates paragraphs. } # Scopes @scopes { A scope is a heading followed by content in braces: ``` # Document Title { # Section @section-id { Paragraph text goes here. # Subsection { Deeper content. } } } ``` {[.] - \`#\` starts a heading (multiple \`#\` characters are allowed but don't affect depth — only nesting does) - \`@slug\` at the end of a heading line assigns a referenceable ID — the \`@\` character followed directly by the slug. There is no \`@id\` keyword. - \`{ }\` delimits the scope's content } The opening brace can appear at the end of the heading line (K&R style): ``` # Title { Content goes here. } # Smart Pointers @smart-ptrs { The @slug goes before the brace. } # Error Handling @error-handling { Another example with a slug. } ``` } # Braceless Scopes @braceless-scopes { A heading not followed by \`{\` creates a braceless scope. Content runs until the next \`#\` heading, a closing \`}\`, or end of file: ``` # Section A Content of Section A. It can span multiple lines. # Section B Content of Section B. ``` Braceless and explicit scopes can be mixed freely in the same document. } # Headingless Scopes @headingless-scopes { A bare \`{ }\` block without a heading creates a section with no title — useful for grouping content: ``` { This content is grouped but has no heading. } ``` Headingless scopes nest like any other scope and can appear anywhere a headed scope can. } # Inline Blocks @inline-blocks { Short content can be written on a single line: ``` # Author { Jane Doe } # Version { 1.0 } ``` Inline blocks must not contain unescaped \`{\` or \`}\` characters. } # Implicit Root @implicit-root { If the first heading is not followed by \`{\`, the document uses implicit root mode — the first heading becomes the document title and everything after it becomes the body: ``` # My Document # Section A Content of Section A. # Section B Content of Section B. ``` This is equivalent to wrapping the entire body in braces after the first heading. Use implicit root for simple flat documents. } # Paragraphs @paragraphs { Consecutive text lines form a paragraph. A blank line or a new scope ends the paragraph. ``` # About { This is the first paragraph. These lines join together. This is a second paragraph. } ``` } # Lists @lists { **Simple bullet lists** — inside a normal scope, a run of \`-\` lines automatically becomes an implicit list. This is the easiest form for simple single-line items: ``` # Features { - Fast parsing - Explicit structure - Easy references } ``` **Explicit list blocks** — use \`{[.]\` (note: no closing \`}\` on this line) for bullet lists or \`{[#]\` for numbered lists. The block is closed by a separate \`}\` after the items. Use explicit blocks when items need body content or for numbered lists: ``` {[.] - Item with details { This paragraph belongs to the item above. Code blocks, nested lists, etc. go here too. } - Simple item } {[#] 1. Install the extension 2. Create a .sdoc file 3. Open the preview } ``` **Important:** \`{[.]}\` with the closing brace on the same line creates an empty list — the brace closes the block immediately. Always put the closing \`}\` on a separate line after the items. Implicit lists work for both bullet (\`-\`) and numbered (\`1.\`, \`2.\`, etc.) items. Each item must be a single line. For multi-line item titles or item body content, use the explicit \`{[.]}\` or \`{[#]\` block form. **Task lists** — checkbox syntax inside explicit list blocks: ``` {[.] - [ ] Pending task - [x] Completed task } ``` **Anonymous list items** — items with no title, just a body block: ``` {[.] { First item, body only. } { Second item, body only. } } ``` **Multi-line titles** — in explicit list blocks, item titles can span multiple lines. Continuation stops at blank lines, list markers, braces, and other command tokens: ``` {[.] - This is a long list item that continues on the next line - Short item } ``` } # Tables @tables { Opened with \`{[table]\` (no closing brace on this line). First row is the header, columns separated by \`|\`. Closed by a separate \`}\`: ``` {[table] Name | Age | City Alice | 30 | NYC Bob | 25 | LA } ``` Optional flags control rendering: ``` {[table borderless] Feature | Status Parser | Complete } {[table headerless] Alice | 30 Bob | 25 } {[table borderless headerless] ![](a.png =100%) | ![](b.png =100%) } ``` \`borderless\` removes borders and row striping. \`headerless\` treats all rows as data (no header). Flags combine in any order. Width and alignment flags control table sizing and position: ``` {[table 60% center] Endpoint | Status /v2/weather | Active } {[table auto] Key | Value Version | 2.0 } {[table 400px right borderless] v2.0 | Current v1.0 | Deprecated } ``` Width: \`auto\` (shrink to content), \`NN%\` (percentage), or \`NNpx\` (pixels). Default is 100%. Alignment: \`left\` (default), \`center\`, or \`right\`. All flags compose freely in any order. To include a literal \`|\` in a cell (e.g. for math), escape it with \`\\|\`: ``` {[table] Expression | Value \|x\| + \|y\| | 8 } ``` # Table Formulas @table-formulas { Cells starting with \`=\` are evaluated as formulas. Hover a computed cell in preview to see the original formula. ``` {[table] Investor | Shares | Ownership Seed Fund | 500,000 | 25% Founder A | 1,000,000 | 50% Founder B | 500,000 | 25% **Total** | =SUM(B1:B3) | =SUM(C1:C3) } ``` Cell references use A1 notation (column letter + row number). Headers are excluded from row numbering, so the first data row is row 1. {[.] - \`=SUM(range)\` — sum of values - \`=AVG(range)\` — arithmetic mean - \`=COUNT(range)\` — number of cells - Arithmetic: \`=B1+B2\`, \`=B1*3\`, \`=(A1+A2)/2\`, \`=-B1\` - Function names are uppercase only - Escape with \`\\=\` for a literal equals sign at the start of a cell } Errors display in red italic: \`#DIV/0!\` (division by zero), \`#VALUE!\` (bad reference), \`#REF!\` (invalid syntax), \`#NAME!\` (unknown function), \`#CIRCULAR!\` (circular dependency). } } # Inline Formatting @inline-formatting { {[table] Syntax | Result \`*text*\` | Emphasis \`**text**\` | Strong \`~~text~~\` | Strikethrough \`\\\`code\\\`\` | Inline code \`\$x^2\$\` | Inline math (KaTeX) \`\$\$E = mc^2\$\$\` | Display math (centered) \`\{+text+\}\` | Positive marker (green) \`\{=text=\}\` | Neutral marker (blue) \`\{^text^\}\` | Note marker (amber) \`\{?text?\}\` | Caution marker (dark amber) \`\{!text!\}\` | Warning marker (orange) \`\{-text-\}\` | Negative marker (red) \`\{~text~\}\` | Highlight (yellow) } Links: \`[Link text](https://example.com)\` or \`[Other doc](./other-file.sdoc)\`. Relative paths resolve from the document's directory. Images: \`![Alt text](path/to/image.png)\` Images with width and alignment: ``` ![Alt](image.png =50%) ![Alt](image.png =50% center) ![Alt](image.png =35% left) ![Alt](image.png =35% right) ![A](a.png =48%) ![B](b.png =48%) ``` Autolinks: \`\\` — angle brackets are optional; bare URLs starting with \`http://\`, \`https://\`, or \`mailto:\` are also auto-linked. Bare email addresses (e.g. \`hello@example.com\`) are automatically rendered as \`mailto:\` links. Math: Use \`\$...\$\` for inline math and \`\$\$...\$\$\` for display math. Use \`\\\`\\\`\\\`math\` code fences for multi-line equations. A plain \`\$\` followed by a digit (e.g. \`\$100\`) does not trigger math mode. } # References @references { Assign a slug with \`@slug\` on a heading, then reference it elsewhere in the same document with \`@slug\`: ``` # Setup @setup { Follow these instructions. } # Usage { Make sure you complete @setup first. } ``` References are **document-local only** — \`@slug\` resolves within the current file. To point a reader to a section in another file, use a link with a fragment: ``` See [Setup](./other-file.sdoc#setup) for details. ``` Do not write \`@setup\` when the target scope is in a different file — it will be flagged as a broken reference. } # Code Blocks @code-blocks { Fenced with triple backticks. Content inside is raw (no parsing): ```` ```javascript function hello() { console.log("Hello!"); } ``` ```` # Include by Link @code-includes { A code block can reference an external file with \`src:\` on the fence line — the content stays in sync: ````` ```json src:./data.json ``` ```json src:./data.json lines:3-5 ``` ````` \`src:\` takes a file path (relative to the document) or URL. \`lines:\` optionally limits to a range (1-based, inclusive). The code block body is replaced by the resolved content. } } # Mermaid Diagrams @mermaid { Code blocks with the \`mermaid\` language tag are rendered as SVG diagrams using the Mermaid JS library. No special syntax is needed — use a standard code fence: ```` ```mermaid graph LR A[Client] --> B[Server] B --> C[Database] ``` ```` Mermaid supports flowcharts (\`graph\`), sequence diagrams (\`sequenceDiagram\`), class diagrams (\`classDiagram\`), state diagrams (\`stateDiagram-v2\`), and more. The Mermaid library is loaded from CDN only when a document contains mermaid blocks. } # Math Blocks @math-blocks { Code blocks with the \`math\` language tag are rendered as display equations via KaTeX: ```` ```math \\int_{-\\infty}^{\\infty} e^{-x^2} \\, dx = \\sqrt{\\pi} ``` ```` Math blocks render as centered display equations with no copy button. The KaTeX CSS is loaded from CDN only when a document contains math content. } # Blockquotes @blockquotes { ``` > This is a quoted line. > Another line in the same quote. ``` } # Horizontal Rules @horizontal-rules { A line of three or more \`-\`, \`*\`, or \`_\`: ``` --- ``` } # Meta Scope @meta-scope { The reserved \`@meta\` scope configures per-file settings and is not rendered in the document body. Use the bare \`@meta\` form (preferred) or the heading form: ``` @meta { type: doc sdoc-version: 0.2 company: Irreversible Inc. confidential: true style: styles/custom.css header: My Header footer: My Footer } ``` The heading form \`# Meta @meta { }\` also works but the bare form is preferred because \`@meta\` is structural, not content. Supported properties (separate each with a blank line): \`type: doc\` or \`type: skill\` or \`type: slides\` — document type. \`company: Name\` — company name, shown in the document footer. \`confidential: true\` — adds a confidentiality notice banner. Uses the \`company\` name if set. Use \`confidential: Custom Entity\` to override with a specific entity name. \`style: path/to/file.css\` — custom stylesheet (replaces default). \`style-append: path/to/file.css\` — additional stylesheet (appended). \`header: text\` — page header text (or use a \`# Header\` sub-scope for rich content). \`footer: text\` — page footer text (or use a \`# Footer\` sub-scope for rich content). \`uuid: ...\` — unique identifier. \`tags: tag1, tag2\` — comma-separated tags. \`sdoc-version: 0.2\` — SDOC format version. A parser warning is emitted when this key is missing from \`@meta\`. } # About Scope @about-scope { The reserved \`@about\` scope provides a discovery summary for the document. It is used by \`list_knowledge\` to describe the file but is not rendered in the document body. Use the bare \`@about\` form (preferred) or the heading form: ``` @about { How to write correct SDOC files. Covers document structure, inline formatting, block types, and common mistakes. } ``` The heading form \`# About @about { }\` also works but the bare form is preferred. } # Escaping @escaping { Backslash escapes special characters: \`\\\\\` \`\\{\` \`\\}\` \`\\@\` \`\\[\` \`\\]\` \`\\(\` \`\\)\` \`\\*\` \`\\~\` \`\\#\` \`\\!\` \`\\\<\` \`\\\>\` \`\\\$\` \`\\+\` \`\\=\` \`\\-\` \`\\^\` \`\\?\` \`\\|\` A line starting with \`\\#\` renders as a literal \`#\` (not a heading). Use \`\\\$\` to prevent a dollar sign from starting math mode. } # Conventions @conventions { {[.] - **Indentation:** cosmetic — use any whitespace you like. Most authors use 4 spaces. - **IDs:** lowercase kebab-case (\`@my-section\`). IDs should be unique within a document. - **Commas:** commas between list items or scopes are allowed but ignored — use them if you find them readable. } } # Scope Types @scope-types { A \`:type\` annotation on a heading gives the scope semantic meaning. Place it after the optional \`@id\`: ``` # User Authentication @auth :requirement { The system shall authenticate users via OAuth 2.0. } # OAuth Flow :specification @oauth-flow { Implements @auth using the authorization code flow. } # Deprecation Notice :warning { This API will be removed in v3.0. } ``` {[.] - Syntax: \`# Title @id :type\` or \`# Title :type @id\` or \`# Title :type\` — both orderings work - The colon requires whitespace before it: \`# Note: Important\` is NOT a type — the colon is part of the title - Any string is valid. Well-known types: \`schema\`, \`example\`, \`requirement\`, \`specification\`, \`definition\`, \`note\`, \`warning\`, \`test\`, \`task\`, \`api\`, \`config\`, \`deprecated\`, \`comment\` - Works with K&R style: \`# Title :warning {\` } The special type \`:comment\` makes a scope that is in the AST but not rendered — useful for editorial notes and AI agent instructions: ``` # TODO :comment { Rewrite this section after the API stabilises. } ``` } # Data Blocks @data-blocks { Add \`:data\` to a JSON code fence to have the parser parse the content into structured data on the AST node: ````` ```json :data { "name": "SDOC", "version": "0.2", "features": ["scopes", "lists", "tables"] } ``` ````` {[.] - Syntax: \` \`\`\`json :data \` — the \`:data\` flag follows the language tag - Invalid JSON produces a parse error - JSON-only in v0.2 - Without \`:data\`, JSON code blocks remain raw text (existing behaviour) } } # Comments @comments { **Line comments** — \`//\` at the start of a line (after optional indentation) skips the line entirely. Not in the AST, not rendered: ``` # Config @config { // TODO: add validation The config file uses JSON format. // hidden from output See @setup for details. } ``` {[.] - Mid-line \`//\` has no effect — URLs like \`https://example.com\` are safe - Inside code blocks: \`//\` has no special meaning } **Comment scopes** — use the \`:comment\` scope type for structured non-rendered content (see @scope-types above). } } # Common Mistakes @common-mistakes { These are easy errors to make — especially for AI agents generating SDOC. # Self-Closing Block Syntax @self-closing-blocks { Writing \`{[.]}\` or \`{[table]}\` with the closing brace on the same line creates an empty block — the \`}\` closes it immediately. Items that follow are bare content outside the block and will not render correctly. **Wrong** — the \`}\` closes the block on the same line: ``` {[.]} - Item one - Item two ``` **Right** — the opening \`{[.]\` has no closing brace; items go inside; a separate \`}\` closes: ``` {[.] - Item one - Item two } ``` Same applies to \`{[#]}\` and \`{[table]}\`. } # Bare Content Inside List Blocks @bare-content-in-lists { Inside a list block (\`{[.]}\` or \`{[#]}\`), the **only** valid children are list items (\`-\`, \`1.\`, \`#\` headings, or anonymous \`{ }\` blocks). Bare paragraphs, code fences, or other content floating between items is a **parser error**. **Wrong** — bare paragraph inside a list block: ``` {[.] - First item title This paragraph is NOT inside a body block. It will cause a parser error. - Second item } ``` **Right** — wrap rich content in a \`{ }\` body block: ``` {[.] - First item title { This paragraph belongs to the item above. So does this one, and any code blocks, nested lists, etc. } - Second item } ``` This is the single most common SDOC mistake. If a list item needs **anything** beyond its title line, that content **must** go in a \`{ }\` body block immediately after the item. } # Unescaped Angle Brackets @unescaped-angles { Angle brackets in regular text (outside of inline code backticks) can be misinterpreted as autolinks. Escape them with \`\\\<\` and \`\\\>\`: **Wrong:** \`observer\` **Right:** \`observer\\\\` or \`\\\`observer\\\`\` Inside backtick code spans, angle brackets are fine — code spans are raw. } # Blank Lines Stop Multi-Line List Titles @blank-lines-in-lists { A list item title can span multiple continuation lines, but a blank line terminates the title. Content after the blank line is bare content in the list block (a parser error) unless wrapped in a body block: **Wrong:** ``` {[.] - This is a long item title that continues here But this is NOT a continuation — it's a bare paragraph (error). } ``` **Right:** ``` {[.] - This is a long item title that continues here { This extra content is properly in a body block. } } ``` } # Cross-Document @slug References @cross-doc-refs { \`@slug\` references are document-local. Using \`@slug\` to refer to a section in another file produces a broken reference error. This is the most common reference mistake — especially in audits, reviews, and documents that discuss other documents, where writing \`file.sdoc @section\` reads naturally but is wrong. **Wrong** — \`@extension\` looks for a local section, not one in \`foundations.sdoc\`: ``` See `foundations.sdoc` @extension for the design rationale. ``` **Right** — use a link with a fragment: ``` See [Extension](./foundations.sdoc#extension) for the design rationale. ``` The fragment (\`#extension\`) matches the target scope's \`@id\`. Use \`\\@\` if you need a literal \`@\` in text without triggering reference resolution. } # @References Inside Link Labels @refs-in-link-labels { Inline \`@references\` are parsed everywhere, including inside link labels. If you mention a scope ID in a link label, escape the \`@\` to prevent it being treated as a reference to the current document: **Wrong:** \`[See domain-model.sdoc @my-section](./domain-model.sdoc#my-section)\` The \`@my-section\` is parsed as a reference and flagged as broken (it does not exist in *this* file). **Right:** \`[See domain-model.sdoc \\@my-section](./domain-model.sdoc#my-section)\` Or simply omit the \`@\` from the label — the URL fragment already carries the target: **Also right:** \`[See domain-model.sdoc § my-section](./domain-model.sdoc#my-section)\` } } }