simdoc 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,8 @@
1
+ # Environments
2
+ .venv/
3
+ venv/
4
+
5
+ # build
6
+ build/
7
+ *__pycache__*
8
+ *.pyc
@@ -0,0 +1 @@
1
+ 3.13
simdoc-0.1.1/AGENTS.md ADDED
@@ -0,0 +1,121 @@
1
+ # AGENTS.md
2
+ # Guidance for coding agents working in this repo.
3
+
4
+ ## Scope and repo status
5
+
6
+ - This repository is currently a design/spec repository.
7
+ - The only substantive guidance is in `docs/handoff_260203_1.md` and `docs/llm-persona.md`.
8
+ - There is no `src/` or `tests/` directory yet, and no build or lint config files.
9
+ - Follow the specs exactly; do not invent behavior beyond the locked decisions.
10
+
11
+ ## Cursor/Copilot rules
12
+
13
+ - No Cursor rules found in `.cursor/rules/` or `.cursorrules`.
14
+ - No Copilot rules found in `.github/copilot-instructions.md`.
15
+
16
+ ## Build, lint, and test commands
17
+
18
+ There is no configured tooling yet (no `pyproject.toml`, `pytest.ini`, etc.).
19
+ When implementation lands, prefer the `uv` workflow and `pytest` for tests.
20
+
21
+ ### Expected/placeholder commands (use once config exists)
22
+
23
+ - Install deps: `uv sync`
24
+ - Run app/build (if added later): `uv run python -m simdoc` (placeholder)
25
+ - Lint (if ruff is added): `uv run ruff check src tests`
26
+ - Format (if ruff/black is added): `uv run ruff format src tests`
27
+ - Type-check (if mypy is added): `uv run mypy src`
28
+
29
+ ### Tests
30
+
31
+ - Run all tests (pytest expected): `uv run pytest`
32
+ - Run a single file: `uv run pytest tests/test_tables.py`
33
+ - Run a single test by name: `uv run pytest -k test_render_table`
34
+ - Run a single test node: `uv run pytest tests/test_tables.py::test_render_table`
35
+
36
+ Note: Update this section when real commands/configs land.
37
+
38
+ ## Code style and architecture guidelines
39
+
40
+ ### Language and tooling
41
+
42
+ - Python only.
43
+ - Favor a minimal dependency footprint.
44
+ - Use `uv` for packaging and execution once added.
45
+
46
+ ### Design constraints from the spec
47
+
48
+ - Document is append-only; no insert/reorder in v0.
49
+ - Public API is explicit convenience methods on the document object.
50
+ - Output is deterministic; spacing and newlines are strictly controlled.
51
+ - No implicit line wrapping; renderer is canonical authority.
52
+ - Markdown is the only export in v0.
53
+
54
+ ### Module boundaries (from spec)
55
+
56
+ - Public API is exported from `simdoc/__init__.py` only.
57
+ - Internal modules are private and subject to change.
58
+ - Rendering logic lives in a dedicated renderer module.
59
+ - Blocks are private record types; no public block classes in v0.
60
+
61
+ ### Imports
62
+
63
+ - Order: standard library, third-party, local.
64
+ - Keep imports explicit; avoid wildcard imports.
65
+ - Avoid dynamic imports and reflection.
66
+
67
+ ### Formatting and layout
68
+
69
+ - Keep code readable and straightforward.
70
+ - Prefer explicit loops and clear control flow over clever expressions.
71
+ - Avoid dense comprehensions that reduce clarity.
72
+ - Keep functions small and single-purpose.
73
+ - Use blank lines to separate logical sections.
74
+
75
+ ### Types
76
+
77
+ - Type hints are preferred for public APIs.
78
+ - Use simple, explicit types; avoid complex generics unless necessary.
79
+ - Prefer `Path` or `PathLike` for file paths when I/O is involved.
80
+
81
+ ### Naming
82
+
83
+ - Public API names should match the spec: `Doc`, `h1..h6`, `h`, `p`, `ul`, `ol`,
84
+ `code`, `table`, `hr`, `to_markdown`, `save`.
85
+ - Private modules and types should be prefixed or placed in private modules.
86
+ - Use descriptive names; avoid abbreviations unless standard.
87
+
88
+ ### Error handling
89
+
90
+ - Use a single custom base exception for document/render issues (per spec).
91
+ - I/O should raise standard Python exceptions.
92
+ - Prefer early validation where it is cheap and deterministic.
93
+
94
+ ### Determinism requirements (critical)
95
+
96
+ - Exactly one blank line between top-level blocks.
97
+ - Exactly one trailing newline at document end.
98
+ - Normalize newlines to `\n` internally.
99
+ - No automatic line wrapping.
100
+ - Markdown rendering is the canonical, deterministic output.
101
+
102
+ ### Markdown-specific behavior
103
+
104
+ - Headings are ATX only, levels 1..6.
105
+ - Unordered lists use `-`, ordered lists use `1.` for all items.
106
+ - Code fences must be safe; auto-escalate fence length deterministically.
107
+ - Table rendering must be valid GFM; escape pipes and convert newlines to `<br>`.
108
+ - For list-of-dicts tables: headers order is deterministic (sorted if not provided).
109
+
110
+ ### Testing conventions (expected)
111
+
112
+ - Golden-file tests should compare output to `tests/fixtures/golden/*.md`.
113
+ - Unit tests should cover fence escalation, table escaping, list rendering, spacing.
114
+ - Keep tests deterministic and focused on renderer invariants.
115
+
116
+ ## Contribution notes for agents
117
+
118
+ - Do not add new behavior beyond the locked spec without explicit instruction.
119
+ - Avoid introducing frameworks or heavy abstractions.
120
+ - Keep changes minimal, readable, and aligned with the spec.
121
+ - Update this file if tooling or structure changes.
simdoc-0.1.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 placerte
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
simdoc-0.1.1/PKG-INFO ADDED
@@ -0,0 +1,53 @@
1
+ Metadata-Version: 2.4
2
+ Name: simdoc
3
+ Version: 0.1.1
4
+ Summary: A simple Python package for deterministic Markdown documents
5
+ License-File: LICENSE
6
+ Requires-Python: >=3.13
7
+ Description-Content-Type: text/markdown
8
+
9
+ # simple-document
10
+
11
+ Deterministic, append-only Markdown documents with a small, explicit Python API.
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ uv add simple-document
17
+ ```
18
+
19
+ Or with pip:
20
+
21
+ ```bash
22
+ pip install simple-document
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ```python
28
+ from simdoc import Doc
29
+
30
+ doc = Doc()
31
+ doc.h1("Simple Document")
32
+ doc.p("First line\nSecond line")
33
+ doc.ul(["alpha", ["beta", "gamma"], "delta"])
34
+ doc.code("print('hi')", lang="py")
35
+ doc.table([
36
+ {"b": "x|y", "a": "1\n2"},
37
+ {"a": None, "b": "ok"},
38
+ ])
39
+ doc.hr()
40
+
41
+ markdown = doc.to_markdown()
42
+ doc.save("example.md")
43
+ ```
44
+
45
+ ## Example script
46
+
47
+ Run the full example that exercises every block type:
48
+
49
+ ```bash
50
+ uv run python examples/example_all_blocks.py
51
+ ```
52
+
53
+ The script writes `examples/example_output.md`.
simdoc-0.1.1/README.md ADDED
@@ -0,0 +1,45 @@
1
+ # simple-document
2
+
3
+ Deterministic, append-only Markdown documents with a small, explicit Python API.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ uv add simple-document
9
+ ```
10
+
11
+ Or with pip:
12
+
13
+ ```bash
14
+ pip install simple-document
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ ```python
20
+ from simdoc import Doc
21
+
22
+ doc = Doc()
23
+ doc.h1("Simple Document")
24
+ doc.p("First line\nSecond line")
25
+ doc.ul(["alpha", ["beta", "gamma"], "delta"])
26
+ doc.code("print('hi')", lang="py")
27
+ doc.table([
28
+ {"b": "x|y", "a": "1\n2"},
29
+ {"a": None, "b": "ok"},
30
+ ])
31
+ doc.hr()
32
+
33
+ markdown = doc.to_markdown()
34
+ doc.save("example.md")
35
+ ```
36
+
37
+ ## Example script
38
+
39
+ Run the full example that exercises every block type:
40
+
41
+ ```bash
42
+ uv run python examples/example_all_blocks.py
43
+ ```
44
+
45
+ The script writes `examples/example_output.md`.
@@ -0,0 +1,349 @@
1
+ # handoff\_260203\_1.md — simdoc v0 (working name) — Locked specs
2
+
3
+ > **Scope:** early specification for the v0 “boring core” of **simdoc**.
4
+ >
5
+ > **Format:** uniquely-identified decisions. Specs are stable unless explicitly re-opened.
6
+
7
+ ---
8
+
9
+ ## Specs
10
+
11
+ ### Core mental model
12
+
13
+ - **S-260203\_1-1** — The document is an **append-only** ordered list of blocks.
14
+ - **S-260203\_1-1.1** — All core API methods **append** blocks (no insert/move/reorder in v0).
15
+ - **S-260203\_1-1.2** — No AST/editor model in v0; editing features are out of scope.
16
+
17
+ ### Public API shape
18
+
19
+ - **S-260203\_1-2** — Public API uses **explicit convenience methods** on the document object.
20
+ - **S-260203\_1-2.1** — Core methods include: `h1..h6`, `h(level, text)`, `p(text)`, `ul(items)`, `ol(items)`, `code(text, lang=None)`, `table(...)`, `hr()` , plus `to_markdown()`.
21
+ - **S-260203\_1-2.2** — No exposed block classes or `add(Block)` mechanism in v0.
22
+ - **S-260203\_1-2.3** — API favors readability/immediacy; extension points are deferred.
23
+
24
+ ### Deterministic formatting rules
25
+
26
+ - **S-260203\_1-3** — simdoc output is fully **deterministic** by default.
27
+ - **S-260203\_1-3.1** — Exactly **one blank line** between all top-level blocks.
28
+ - **S-260203\_1-3.2** — Documents end with **exactly one** trailing newline (`\n`).
29
+ - **S-260203\_1-3.3** — **No automatic line wrapping**.
30
+ - **S-260203\_1-3.4** — Newlines are normalized to `\n` internally.
31
+
32
+ ### Internal representation
33
+
34
+ - **S-260203\_1-4** — Internally, a document is an ordered list of **private block records**, not raw Markdown strings.
35
+ - **S-260203\_1-4.1** — Blocks are only created via builder API (no Markdown parsing).
36
+ - **S-260203\_1-4.2** — `to_markdown()` is the canonical renderer in v0; formatting/escaping/spacing is enforced centrally.
37
+ - **S-260203\_1-4.3** — Future exports may either render from block records or use Markdown as an intermediate.
38
+
39
+ ### Code blocks
40
+
41
+ - **S-260203\_1-5** — `doc.code(...)` must always render **valid Markdown**, regardless of code content.
42
+ - **S-260203\_1-5.1** — Auto-select a **safe fence** for `to_markdown()`.
43
+ - **S-260203\_1-5.2** — Escalate fence length as needed (deterministic).
44
+ - **S-260203\_1-5.3** — Optional language tag, passed through verbatim (no validation in v0).
45
+ - **S-260203\_1-5.4** — Normalize newlines to `\n`, otherwise preserve code content exactly.
46
+
47
+ ### Tables
48
+
49
+ - **S-260203\_1-6.1** — Tables render as **GFM pipe tables**.
50
+ - **S-260203\_1-6.2** — Alignment supported but optional.
51
+ - **S-260203\_1-6.2.1** — Default alignment is **left** for all columns.
52
+ - **S-260203\_1-6.3** — Table rendering must always emit **valid GFM**.
53
+ - **S-260203\_1-6.3.1** — Escape pipes in cells: `| → \|`.
54
+ - **S-260203\_1-6.3.2** — Newlines inside cells become literal `<br>`.
55
+ - **S-260203\_1-6.3.3** — No other mutation (no trimming/wrapping/sanitizing beyond validity).
56
+ - **S-260203\_1-6.3.4** — Non-string cell values stringified via `str(x)`; `None` becomes empty cell.
57
+ - **S-260203\_1-6.4** — `doc.table()` accepts both list-of-lists and list-of-dicts.
58
+ - **S-260203\_1-6.4.1** — For list-of-dicts: if `headers` provided, it defines order; else column keys are **sorted deterministically**.
59
+
60
+ ### Lists
61
+
62
+ - **S-260203\_1-7** — Deterministic Markdown lists supported.
63
+ - **S-260203\_1-7.1** — Unordered list marker always `-`.
64
+ - **S-260203\_1-7.2** — Ordered lists always use `1.` for each item.
65
+ - **S-260203\_1-7.3** — Nested lists supported via nested Python lists.
66
+ - **S-260203\_1-7.4** — No blank lines between list items.
67
+
68
+ ### Headings
69
+
70
+ - **S-260203\_1-8.1** — Always ATX headings (`#`, `##`, …).
71
+ - **S-260203\_1-8.2** — Support heading levels h1..h6 only.
72
+ - **S-260203\_1-8.3** — Heading text is single-line; internal newlines normalized to spaces.
73
+ - **S-260203\_1-8.4** — Provide both `h1..h6()` and `h(level, text)`.
74
+ - **S-260203\_1-8.4.1** — `h(level, text)` validates level in `[1..6]`.
75
+
76
+ ### Paragraphs
77
+
78
+ - **S-260203\_1-9.1** — `p(text)` treats input as a single paragraph; no splitting on blank lines.
79
+ - **S-260203\_1-9.2** — No inline formatting helpers (user supplies inline Markdown).
80
+ - **S-260203\_1-9.3** — Preserve internal newlines (after newline normalization).
81
+ - **S-260203\_1-9.4** — Empty/whitespace-only paragraphs are ignored.
82
+
83
+ ### Horizontal rules & callouts
84
+
85
+ - **S-260203\_1-10.1** — Horizontal rules are supported via `doc.hr()`.
86
+ - **S-260203\_1-10.2** — Horizontal rules render using a fixed style: `---`.
87
+ - **S-260203\_1-10.3** — No callout / admonition concept in v0.
88
+
89
+ ---
90
+
91
+ ### Export & I/O
92
+
93
+ - **S-260203\_1-11.1** — Core export surface includes:
94
+
95
+ - `to_markdown() -> str` (canonical renderer)
96
+ - `save(path)` convenience method for writing Markdown to disk
97
+
98
+ - **S-260203\_1-11.1.1** — `save(path)` defaults:
99
+
100
+ - UTF-8 encoding
101
+ - `\n` newlines
102
+ - creates parent directories if missing
103
+ - overwrites existing files by default
104
+
105
+ - **S-260203\_1-11.2.1** — Atomic writes are **not** supported in v0 (deferred).
106
+
107
+ - **S-260203\_1-11.2.2** — `save(path)` returns the written **`Path`** on success.
108
+
109
+ - **S-260203\_1-11.2.3** — `save(path)` raises standard Python I/O exceptions on failure.
110
+
111
+ ### Export adapters (v0 scope)
112
+
113
+ - **S-260203\_1-12.1** — v0 export scope is **Markdown only**.
114
+
115
+ - No built-in HTML or PDF adapters in v0.
116
+ - Future adapters remain feasible due to the internal block record representation.
117
+
118
+ - **S-260203\_1-12.2** — When adapters are introduced, they will be provided via **optional extras** on the core package (e.g. `simdoc[html]`, `simdoc[pdf]`).
119
+
120
+ ---
121
+
122
+ ## Implementation
123
+
124
+ ### I-260203\_1-1 — Project structure
125
+
126
+ The project follows a **minimal, layered layout** separating public API, internal block models, and rendering logic.
127
+
128
+ ```
129
+ simdoc/
130
+ ├─ src/
131
+ │ └─ simdoc/
132
+ │ ├─ __init__.py # Public API (Document class, convenience functions)
133
+ │ ├─ document.py # Document builder (append-only, user-facing methods)
134
+ │ ├─ blocks.py # Private block record definitions (internal only)
135
+ │ ├─ render/
136
+ │ │ ├─ __init__.py
137
+ │ │ └─ markdown.py # Markdown renderer (to_markdown implementation)
138
+ │ └─ util/
139
+ │ ├─ __init__.py
140
+ │ └─ text.py # Escaping, fence selection, newline normalization
141
+
142
+ ├─ tests/
143
+ │ ├─ test_markdown_golden.py
144
+ │ ├─ test_blocks.py
145
+ │ └─ fixtures/
146
+ │ └─ expected_md/
147
+
148
+ ├─ pyproject.toml
149
+ ├─ README.md
150
+ └─ LICENSE
151
+ ```
152
+
153
+ ### I-260203\_1-2 — Module responsibilities
154
+
155
+ - **document.py**
156
+
157
+ - Owns the `Doc` class
158
+ - Implements public builder methods (`h1`, `p`, `code`, `table`, …)
159
+ - Performs light input normalization and validation
160
+ - Appends private block records to the document
161
+
162
+ - **blocks.py**
163
+
164
+ - Defines minimal, immutable block record types (e.g. dataclasses)
165
+ - No rendering logic
166
+ - Not part of the public API
167
+
168
+ - **render/markdown.py**
169
+
170
+ - Pure functions to render block records into Markdown
171
+ - Enforces all deterministic formatting rules
172
+ - Owns fence selection, table formatting, list rendering, spacing rules
173
+
174
+ - **util/text.py**
175
+
176
+ - Small helpers for:
177
+ - newline normalization
178
+ - escaping pipes
179
+ - safe fence computation
180
+ - No knowledge of blocks or document structure
181
+
182
+ ### I-260203\_1-3 — Public API boundary
183
+
184
+ - Only symbols exported in `simdoc/__init__.py` are considered public.
185
+ - All other modules are **internal and subject to change**.
186
+ - Rendering helpers and block definitions are explicitly non-public.
187
+
188
+ ---
189
+
190
+ ## Implementation
191
+
192
+ ### Project layout
193
+
194
+ - **I-260203\_1-1** — Use a standard `src/` layout.
195
+
196
+ ```text
197
+ simdoc/
198
+ ├─ pyproject.toml
199
+ ├─ README.md
200
+ ├─ LICENSE
201
+ ├─ src/
202
+ │ └─ simdoc/
203
+ │ ├─ __init__.py
204
+ │ ├─ doc.py
205
+ │ ├─ _blocks.py
206
+ │ ├─ _render/
207
+ │ │ ├─ __init__.py
208
+ │ │ └─ markdown.py
209
+ │ └─ _io.py
210
+ └─ tests/
211
+ ├─ test_golden_markdown.py
212
+ ├─ test_code_fences.py
213
+ ├─ test_tables.py
214
+ ├─ test_lists.py
215
+ └─ fixtures/
216
+ └─ golden/
217
+ ├─ basic.md
218
+ ├─ code_fences.md
219
+ ├─ tables.md
220
+ └─ lists.md
221
+ ```
222
+
223
+ ### Module responsibilities
224
+
225
+ - **I-260203\_1-2** — `simdoc.doc` contains the public document class and builder methods.
226
+ - **I-260203\_1-3** — `simdoc._blocks` defines private block records (small dataclasses) for:
227
+ - Heading, Paragraph, ListBlock, CodeBlock, TableBlock, Hr
228
+ - **I-260203\_1-4** — `simdoc._render.markdown` implements `render_markdown(blocks) -> str` and all Markdown rules.
229
+ - **I-260203\_1-5** — `simdoc._render.markdown` centralizes escaping/normalization helpers:
230
+ - safe code fence selection + escalation
231
+ - table cell escaping + newline→`<br>`
232
+ - heading newline→space normalization
233
+ - **I-260203\_1-6** — `simdoc._io` implements `save_markdown(text: str, path: PathLike) -> Path` with v0 defaults.
234
+ - **I-260203\_1-7** — `simdoc.__init__` exports only the public surface. Private modules remain private.
235
+
236
+ ### Testing approach
237
+
238
+ - **I-260203\_1-8** — Golden-file tests assert deterministic output by comparing to `tests/fixtures/golden/*.md`.
239
+ - **I-260203\_1-9** — Focused unit tests cover:
240
+ - code fence safety (embedded \`\`\` sequences)
241
+ - table escaping (pipes, newlines)
242
+ - nested list rendering
243
+
244
+ ---
245
+
246
+ ## Definition of Done
247
+
248
+ - **DoD-260203\_1-1** — Public API exists and is documented in README:
249
+
250
+ - `Doc`
251
+ - `h1..h6`, `h(level, text)`
252
+ - `p(text)`
253
+ - `ul(items)`, `ol(items)` (including nested lists)
254
+ - `code(text, lang=None)`
255
+ - `table(...)` (list-of-lists and list-of-dicts)
256
+ - `hr()`
257
+ - `to_markdown() -> str`
258
+ - `save(path) -> Path`
259
+
260
+ - **DoD-260203\_1-2** — Output guarantees are met for all block types:
261
+
262
+ - deterministic formatting
263
+ - exactly one blank line between blocks
264
+ - no automatic line wrapping
265
+ - exactly one trailing `
266
+ `
267
+ - internal newline normalization to `
268
+ `
269
+
270
+ - **DoD-260203\_1-3** — Code fences are always safe:
271
+
272
+ - renderer escalates fence length deterministically
273
+ - resulting Markdown is valid regardless of code content
274
+
275
+ - **DoD-260203\_1-4** — Tables are always valid GFM pipe tables:
276
+
277
+ - pipes escaped (`|` → `\|`)
278
+ - cell newlines become `<br>`
279
+ - deterministic header order for list-of-dicts (sorted keys when headers not provided)
280
+ - `None` becomes empty cell; other values use `str(x)`
281
+
282
+ - **DoD-260203\_1-5** — Lists are deterministic:
283
+
284
+ - unordered marker is always `-`
285
+ - ordered lists always use `1.` per item
286
+ - nested lists render deterministically from nested Python lists
287
+
288
+ - **DoD-260203\_1-6** — Validation and error model is implemented as locked:
289
+
290
+ - permissive-by-default inputs where deterministic rendering is possible
291
+ - builder methods do light normalization / cheap sanity checks
292
+ - renderer is final authority
293
+ - single custom exception base `SimDocError` for document/render issues
294
+ - standard Python exceptions for I/O
295
+
296
+ - **DoD-260203\_1-7** — Mutability & ergonomics:
297
+
298
+ - `Doc` is reusable/appendable after rendering
299
+ - builder methods return `None`
300
+ - `len(doc)` returns block count (optional `block_count` alias)
301
+ - `repr(doc)` is concise/stable (e.g. `Doc(blocks=7)`)
302
+
303
+ - **DoD-260203\_1-8** — Packaging and minimal quality gates:
304
+
305
+ - clean `src/` layout
306
+ - type hints on public API
307
+ - passes unit tests on supported Python versions
308
+
309
+ ---
310
+
311
+ ## Tests
312
+
313
+ - **T-260203\_1-1** — Golden Markdown determinism tests
314
+
315
+ - Compare `Doc(...).to_markdown()` against `tests/fixtures/golden/*.md`.
316
+ - Include at least: `basic.md`, `lists.md`, `tables.md`, `code_fences.md`.
317
+
318
+ - **T-260203\_1-2** — Code fence escalation tests
319
+
320
+ - Inputs containing \`\`\` (and longer backtick runs) must render with a longer fence.
321
+ - Ensure determinism: same input always produces same fence length.
322
+
323
+ - **T-260203\_1-3** — Table escaping tests
324
+
325
+ - Pipes in cells are escaped.
326
+ - Newlines in cells become `<br>`.
327
+ - `None` renders as empty.
328
+ - List-of-dicts column ordering is deterministic.
329
+
330
+ - **T-260203\_1-4** — List rendering tests
331
+
332
+ - `ul` uses `-`.
333
+ - `ol` uses `1.` for all items.
334
+ - Nested list structures render correctly and deterministically.
335
+
336
+ - **T-260203\_1-5** — Spacing and newline invariants
337
+
338
+ - Exactly one blank line between blocks.
339
+ - Exactly one trailing newline.
340
+ - No implicit line wrapping.
341
+
342
+ - **T-260203\_1-6** — I/O smoke tests
343
+
344
+ - `save(path)` writes UTF-8 with `
345
+ ` newlines.
346
+ - Parent directories are created.
347
+ - Overwrites existing file.
348
+ - Returns `Path`.
349
+
@@ -0,0 +1,44 @@
1
+ # persona.md
2
+
3
+ ## TL;DR (Read this first)
4
+
5
+ Technical, pragmatic user. Python only, simple code, minimal tooling.
6
+ Prefers clarity over elegance, explicit logic over abstractions, and fast iteration over perfect design.
7
+
8
+ ---
9
+
10
+ ## Core Persona
11
+
12
+ - Analytical and systems-oriented
13
+ - Values understanding, control, and transparency
14
+ - Uses the AI as a thinking and exploration partner
15
+
16
+ ## Coding Preferences
17
+
18
+ - Language: Python
19
+ - Package / env manager: `uv`
20
+ - Not a software developer → keep code simple and readable
21
+ - Avoid frameworks, patterns, and “clever” architectures
22
+ - Explicit control flow preferred (loops > dense comprehensions)
23
+ - Strong preference for:
24
+ - type hints
25
+ - explicit attributes
26
+ - no string-based magic or reflection (`getattr`, dynamic wiring)
27
+
28
+ ## Workflow & Environment
29
+
30
+ - Linux-centric, keyboard-driven
31
+ - Minimal, low-bloat tools
32
+ - CLI / TUI preferred; GUI only when it clearly adds value
33
+ - Favors local, inspectable artifacts:
34
+ - Markdown
35
+ - plain text files
36
+
37
+ ## Interaction & Output Style
38
+
39
+ - Direct, informal, pragmatic
40
+ - Show assumptions, tradeoffs, and intermediate reasoning
41
+ - “I don’t know” is acceptable if clearly stated
42
+ - Start simple; expand only when justified
43
+ - End with concise summaries or decisions when possible
44
+