softschema 0.1.2__tar.gz → 0.1.3__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.
Files changed (54) hide show
  1. {softschema-0.1.2 → softschema-0.1.3}/.gitignore +6 -0
  2. {softschema-0.1.2 → softschema-0.1.3}/AGENTS.md +12 -2
  3. {softschema-0.1.2 → softschema-0.1.3}/PKG-INFO +87 -20
  4. {softschema-0.1.2 → softschema-0.1.3}/README.md +84 -18
  5. {softschema-0.1.2 → softschema-0.1.3}/docs/development.md +73 -2
  6. softschema-0.1.3/docs/publishing-npm.md +210 -0
  7. softschema-0.1.3/docs/publishing.md +188 -0
  8. {softschema-0.1.2 → softschema-0.1.3}/docs/softschema-guide.md +21 -17
  9. {softschema-0.1.2 → softschema-0.1.3}/docs/softschema-python-design.md +50 -5
  10. {softschema-0.1.2 → softschema-0.1.3}/docs/softschema-spec.md +4 -1
  11. softschema-0.1.3/docs/softschema-typescript-design.md +101 -0
  12. {softschema-0.1.2 → softschema-0.1.3}/examples/movie_page/README.md +2 -2
  13. {softschema-0.1.2 → softschema-0.1.3}/examples/movie_page/movie-page.schema.yaml +46 -74
  14. {softschema-0.1.2 → softschema-0.1.3}/examples/movie_page/spirited-away.md +1 -1
  15. softschema-0.1.3/examples/parity/__init__.py +0 -0
  16. softschema-0.1.3/examples/parity/model.py +94 -0
  17. softschema-0.1.3/examples/parity/parity.schema.yaml +147 -0
  18. {softschema-0.1.2 → softschema-0.1.3}/packages/python/src/softschema/__init__.py +0 -6
  19. softschema-0.1.3/packages/python/src/softschema/canonicalize.py +133 -0
  20. {softschema-0.1.2 → softschema-0.1.3}/packages/python/src/softschema/cli.py +8 -1
  21. {softschema-0.1.2 → softschema-0.1.3}/packages/python/src/softschema/compile.py +32 -24
  22. softschema-0.1.3/packages/python/src/softschema/errors.py +102 -0
  23. {softschema-0.1.2 → softschema-0.1.3}/packages/python/src/softschema/generate.py +7 -5
  24. {softschema-0.1.2 → softschema-0.1.3}/packages/python/src/softschema/models.py +0 -20
  25. {softschema-0.1.2 → softschema-0.1.3}/packages/python/src/softschema/schema_view.py +16 -14
  26. {softschema-0.1.2 → softschema-0.1.3}/packages/python/src/softschema/validate.py +79 -198
  27. softschema-0.1.3/packages/python/tests/test_canonicalize.py +53 -0
  28. {softschema-0.1.2 → softschema-0.1.3}/packages/python/tests/test_cli.py +5 -4
  29. {softschema-0.1.2 → softschema-0.1.3}/packages/python/tests/test_core.py +29 -78
  30. softschema-0.1.3/packages/python/tests/test_errors.py +51 -0
  31. softschema-0.1.3/packages/python/tests/test_parity.py +30 -0
  32. {softschema-0.1.2 → softschema-0.1.3}/packages/python/tests/test_schema_view.py +31 -2
  33. softschema-0.1.3/packages/python/tests/test_skill_mirror_drift.py +32 -0
  34. {softschema-0.1.2 → softschema-0.1.3}/packages/python/tests/test_soft_field.py +2 -2
  35. softschema-0.1.3/packages/typescript/LICENSE +21 -0
  36. softschema-0.1.3/packages/typescript/README.md +54 -0
  37. {softschema-0.1.2 → softschema-0.1.3}/pyproject.toml +12 -2
  38. {softschema-0.1.2 → softschema-0.1.3}/skills/softschema/SKILL.md +17 -7
  39. softschema-0.1.3/tests/golden/README.md +68 -0
  40. softschema-0.1.2/docs/publishing.md +0 -91
  41. softschema-0.1.2/packages/typescript/README.md +0 -26
  42. {softschema-0.1.2 → softschema-0.1.3}/LICENSE +0 -0
  43. {softschema-0.1.2 → softschema-0.1.3}/docs/installation.md +0 -0
  44. {softschema-0.1.2 → softschema-0.1.3}/examples/__init__.py +0 -0
  45. {softschema-0.1.2 → softschema-0.1.3}/examples/movie_page/__init__.py +0 -0
  46. {softschema-0.1.2 → softschema-0.1.3}/examples/movie_page/host_integration.py +0 -0
  47. {softschema-0.1.2 → softschema-0.1.3}/examples/movie_page/model.py +0 -0
  48. {softschema-0.1.2 → softschema-0.1.3}/packages/python/README.md +0 -0
  49. {softschema-0.1.2 → softschema-0.1.3}/packages/python/src/softschema/py.typed +0 -0
  50. {softschema-0.1.2 → softschema-0.1.3}/packages/python/src/softschema/registry.py +0 -0
  51. {softschema-0.1.2 → softschema-0.1.3}/packages/python/src/softschema/soft_field.py +0 -0
  52. {softschema-0.1.2 → softschema-0.1.3}/packages/python/tests/test_generate.py +0 -0
  53. {softschema-0.1.2 → softschema-0.1.3}/packages/python/tests/test_movie_page_example.py +0 -0
  54. {softschema-0.1.2 → softschema-0.1.3}/packages/python/tests/test_warning_codes.py +0 -0
@@ -6,6 +6,9 @@ __pycache__/
6
6
  # C extensions
7
7
  *.so
8
8
 
9
+ # Node / TypeScript
10
+ node_modules/
11
+
9
12
  # Distribution / packaging
10
13
  .Python
11
14
  build/
@@ -223,3 +226,6 @@ __marimo__/
223
226
 
224
227
  # Streamlit
225
228
  .streamlit/secrets.toml
229
+
230
+ # TS bundled doc/skill resources (generated at build by copy-resources)
231
+ packages/typescript/resources/
@@ -9,11 +9,21 @@ Start here:
9
9
  - [Softschema Spec](docs/softschema-spec.md): exact language-neutral artifact format.
10
10
  - [Movie page example](examples/movie_page/README.md): complete Python-backed example.
11
11
 
12
- For implementer reference (only when changing the Python package itself):
12
+ softschema ships two interchangeable implementations with the same CLI and library
13
+ surface: Python/Pydantic (`softschema`, `softschema-py`) and TypeScript/Zod
14
+ (`softschema`, `softschema-ts`). They are held to exact behavioral parity: same flags,
15
+ same canonical JSON Schema sidecar, same validation results, so authoring an artifact is
16
+ identical regardless of which you run.
17
+
18
+ For implementer reference (only when changing a package itself):
13
19
 
14
20
  - [Python Package Design](docs/softschema-python-design.md): module layout, CLI surface,
15
21
  validation layers, and ADR-style decisions.
16
- Skip when only authoring or validating artifacts.
22
+ - [TypeScript Package Design](docs/softschema-typescript-design.md): the Zod port and
23
+ the Python↔TypeScript API parity table.
24
+ - When changing behavior, follow the parity development process (golden-first, then port
25
+ to both) in [docs/development.md](docs/development.md).
26
+ Skip all of this when only authoring or validating artifacts.
17
27
 
18
28
  Key rules:
19
29
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: softschema
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: Soft schema conventions and validation tools for Markdown/YAML artifacts
5
5
  Project-URL: Homepage, https://github.com/jlevy/softschema
6
6
  Project-URL: Repository, https://github.com/jlevy/softschema
@@ -22,7 +22,8 @@ Requires-Python: <4.0,>=3.11
22
22
  Requires-Dist: frontmatter-format>=0.3.0
23
23
  Requires-Dist: jsonschema>=4.20
24
24
  Requires-Dist: pydantic>=2
25
- Requires-Dist: pyyaml
25
+ Requires-Dist: ruamel-yaml>=0.18
26
+ Requires-Dist: strif>=3.1.0
26
27
  Description-Content-Type: text/markdown
27
28
 
28
29
  # softschema
@@ -131,14 +132,41 @@ This repo is simply:
131
132
  defining soft schema file format conventions for Markdown documents with YAML
132
133
  frontmatter.
133
134
 
134
- - A simple [Python CLI](docs/softschema-python-design.md) that handles formatting,
135
- compilation, validation, and other common workflows on soft schema-style documents.
135
+ - A small CLI (and library) that handles formatting, compilation, validation, and other
136
+ common workflows on soft schema-style documents.
136
137
 
137
138
  The CLI can be used directly, as a library, or installed as a skill.
138
139
 
139
140
  For the full conceptual reference and adoption playbooks, see the
140
141
  [Softschema Guide](docs/softschema-guide.md).
141
142
 
143
+ ## Two Synchronized Implementations
144
+
145
+ softschema ships **two complete, fully supported implementations** with the same CLI and
146
+ library surface:
147
+
148
+ - **Python / Pydantic**: [`softschema`](docs/softschema-python-design.md) on PyPI (run
149
+ as `softschema` or `softschema-py`).
150
+ - **TypeScript / Zod**: [`softschema`](docs/softschema-typescript-design.md) on npm (run
151
+ as `softschema` or `softschema-ts`).
152
+
153
+ The TypeScript package is a **synchronized port** of the Python one, held to **exact
154
+ behavioral parity**: equivalent CLI inputs, outputs, and flags; equivalent library APIs;
155
+ the same canonical JSON Schema sidecar (byte-identical, with an equal `schema_sha256`
156
+ fingerprint); and the same engine-neutral validation results.
157
+ Only idiomatic surface details differ (snake_case ↔ camelCase, Pydantic ↔ Zod).
158
+ Authoring or validating an artifact is identical regardless of which you run, so a team
159
+ can adopt either runtime, or both, without divergence.
160
+
161
+ The two are **maintained in lockstep**: every behavior change lands in a shared
162
+ golden-test corpus first, then in both packages, and CI fails if their outputs or
163
+ compiled schemas drift.
164
+ They release together under **the same version number** on PyPI and npm.
165
+ See
166
+ [Keeping Python and TypeScript in Parity](docs/development.md#keeping-python-and-typescript-in-parity)
167
+ for the development process and the parity invariants CI enforces, and
168
+ [Publishing](docs/publishing.md) for the synchronized release.
169
+
142
170
  ## Example Artifact Shape
143
171
 
144
172
  The default shape is Markdown with YAML frontmatter.
@@ -216,7 +244,7 @@ from the audience; IMDb users give it 8.6 out of 10 across more than 850,000 vot
216
244
  | Source | Score | Count |
217
245
  | --- | ---: | ---: |
218
246
  | Rotten Tomatoes Critics | 96% Tomatometer | 225 reviews |
219
- | Rotten Tomatoes Audience | 96% Popcornmeter | |
247
+ | Rotten Tomatoes Audience | 96% Popcornmeter | n/a |
220
248
  | IMDb | 8.6 / 10 | 850,000+ votes |
221
249
  ```
222
250
 
@@ -304,52 +332,91 @@ uv run softschema compile examples.movie_page.model:MoviePage \
304
332
  --out examples/movie_page/movie-page.schema.yaml
305
333
  ```
306
334
 
335
+ ## Try the TypeScript Package
336
+
337
+ The TypeScript CLI takes the same arguments and emits byte-identical output.
338
+ Validate the same artifact against the same language-neutral schema sidecar with a
339
+ pinned zero-install runner:
340
+
341
+ ```bash
342
+ npx softschema@latest validate examples/movie_page/spirited-away.md \
343
+ --schema examples/movie_page/movie-page.schema.yaml \
344
+ --envelope movie
345
+ ```
346
+
347
+ The bundled docs and skill are served identically:
348
+
349
+ ```bash
350
+ npx softschema@latest docs --list
351
+ npx softschema@latest docs guide
352
+ npx softschema@latest skill --brief
353
+ ```
354
+
355
+ Semantic models are written in Zod instead of Pydantic; everything else (flags, result
356
+ JSON, error records, and the compiled sidecar, down to its `schema_sha256`) matches the
357
+ Python package exactly.
358
+ See the [TypeScript Design](docs/softschema-typescript-design.md) doc for the Zod model
359
+ shape and the full Python ↔ TypeScript API table.
360
+
307
361
  ## Use as an Agent Skill
308
362
 
309
- The `softschema` CLI ships as a [`SKILL.md`](https://agentskills.io) following the open
363
+ Both packages ship the same [`SKILL.md`](https://agentskills.io) following the open
310
364
  Agent Skills standard discovered by Claude Code, Codex, Gemini CLI, Cursor, Copilot, and
311
365
  ~20 other coding agents.
312
366
  Pointing an agent at the CLI is enough to bootstrap its understanding of the soft-schema
313
367
  approach: the `--help` epilog routes it to a brief and the bundled docs.
314
368
 
315
369
  ```bash
316
- uvx softschema@0.1.2 --help # entry point with bootstrap pointers
317
- uvx softschema@0.1.2 skill --brief # ~12-line operating brief
318
- uvx softschema@0.1.2 docs guide # full mental model and adoption path
370
+ # Python:
371
+ uvx softschema@latest --help # entry point with bootstrap pointers
372
+ uvx softschema@latest skill --brief # compact operating brief
373
+ uvx softschema@latest docs guide # full mental model and adoption path
374
+
375
+ # TypeScript (same commands, same bundled docs/skill):
376
+ npx softschema@latest skill --brief
377
+ npx softschema@latest docs guide
319
378
  ```
320
379
 
321
- Self-install the skill into a project so any agent working in the repo finds it
322
- natively:
380
+ Self-install the skill into a project so any agent working in the repo finds it natively
381
+ (either package writes the identical mirrors):
323
382
 
324
383
  ```bash
325
- uvx softschema@0.1.2 skill --install
384
+ uvx softschema@latest skill --install
385
+ # or: npx softschema@latest skill --install
326
386
  # writes:
327
387
  # .agents/skills/softschema/SKILL.md (Codex, Gemini CLI, cross-agent installers)
328
388
  # .claude/skills/softschema/SKILL.md (Claude Code mirror)
329
389
  ```
330
390
 
331
- Both mirrors carry a `DO NOT EDIT` marker and the version that wrote them.
332
- Re-run `softschema skill --install` to refresh after upgrading the CLI.
391
+ Both mirrors carry a `DO NOT EDIT` marker.
392
+ Re-run `skill --install` to refresh after upgrading the CLI.
333
393
 
334
394
  ## Repository Layout
335
395
 
336
396
  ```text
337
- docs/ guide, spec, Python package design, and workflow docs
397
+ docs/ guide, spec, package design (Python + TypeScript), and workflow docs
338
398
  examples/movie_page/ complete example with model, host integration, artifact, schema
339
- packages/python/ Python implementation and tests
340
- packages/typescript/ future TypeScript/Zod notes only
341
- skills/softschema/ agent-facing usage guidance
399
+ examples/parity/ cross-language parity fixture (Pydantic + Zod compile to one schema)
400
+ packages/python/ Python/Pydantic implementation and tests
401
+ packages/typescript/ TypeScript/Zod implementation and tests
402
+ skills/softschema/ agent-facing usage guidance (shared SKILL.md source)
403
+ tests/golden/ shared CLI golden corpus, run against both implementations
342
404
  ```
343
405
 
344
406
  The root `pyproject.toml` builds the Python package from
345
- `packages/python/src/softschema`. This keeps the first release simple while leaving room
346
- for a future TypeScript package.
407
+ `packages/python/src/softschema`; `packages/typescript` builds the npm `softschema`
408
+ package with bun. The shared golden corpus and the cross-language conformance test keep
409
+ the two implementations byte-for-byte in sync.
347
410
 
348
411
  ## Development and Contributing
349
412
 
350
413
  See [Development](docs/development.md) for repo setup, common commands, CI checks,
351
414
  release workflow, and pointers to the installation and publishing docs.
352
415
 
416
+ The Python and TypeScript implementations **must be kept in exact sync**. Any behavior
417
+ change goes through the shared golden corpus first and then lands in both packages; see
418
+ [Keeping Python and TypeScript in Parity](docs/development.md#keeping-python-and-typescript-in-parity).
419
+
353
420
  <!-- This document follows common-doc-guidelines.md.
354
421
  See github.com/jlevy/practical-prose and review guidelines before editing.
355
422
  -->
@@ -104,14 +104,41 @@ This repo is simply:
104
104
  defining soft schema file format conventions for Markdown documents with YAML
105
105
  frontmatter.
106
106
 
107
- - A simple [Python CLI](docs/softschema-python-design.md) that handles formatting,
108
- compilation, validation, and other common workflows on soft schema-style documents.
107
+ - A small CLI (and library) that handles formatting, compilation, validation, and other
108
+ common workflows on soft schema-style documents.
109
109
 
110
110
  The CLI can be used directly, as a library, or installed as a skill.
111
111
 
112
112
  For the full conceptual reference and adoption playbooks, see the
113
113
  [Softschema Guide](docs/softschema-guide.md).
114
114
 
115
+ ## Two Synchronized Implementations
116
+
117
+ softschema ships **two complete, fully supported implementations** with the same CLI and
118
+ library surface:
119
+
120
+ - **Python / Pydantic**: [`softschema`](docs/softschema-python-design.md) on PyPI (run
121
+ as `softschema` or `softschema-py`).
122
+ - **TypeScript / Zod**: [`softschema`](docs/softschema-typescript-design.md) on npm (run
123
+ as `softschema` or `softschema-ts`).
124
+
125
+ The TypeScript package is a **synchronized port** of the Python one, held to **exact
126
+ behavioral parity**: equivalent CLI inputs, outputs, and flags; equivalent library APIs;
127
+ the same canonical JSON Schema sidecar (byte-identical, with an equal `schema_sha256`
128
+ fingerprint); and the same engine-neutral validation results.
129
+ Only idiomatic surface details differ (snake_case ↔ camelCase, Pydantic ↔ Zod).
130
+ Authoring or validating an artifact is identical regardless of which you run, so a team
131
+ can adopt either runtime, or both, without divergence.
132
+
133
+ The two are **maintained in lockstep**: every behavior change lands in a shared
134
+ golden-test corpus first, then in both packages, and CI fails if their outputs or
135
+ compiled schemas drift.
136
+ They release together under **the same version number** on PyPI and npm.
137
+ See
138
+ [Keeping Python and TypeScript in Parity](docs/development.md#keeping-python-and-typescript-in-parity)
139
+ for the development process and the parity invariants CI enforces, and
140
+ [Publishing](docs/publishing.md) for the synchronized release.
141
+
115
142
  ## Example Artifact Shape
116
143
 
117
144
  The default shape is Markdown with YAML frontmatter.
@@ -189,7 +216,7 @@ from the audience; IMDb users give it 8.6 out of 10 across more than 850,000 vot
189
216
  | Source | Score | Count |
190
217
  | --- | ---: | ---: |
191
218
  | Rotten Tomatoes Critics | 96% Tomatometer | 225 reviews |
192
- | Rotten Tomatoes Audience | 96% Popcornmeter | |
219
+ | Rotten Tomatoes Audience | 96% Popcornmeter | n/a |
193
220
  | IMDb | 8.6 / 10 | 850,000+ votes |
194
221
  ```
195
222
 
@@ -277,52 +304,91 @@ uv run softschema compile examples.movie_page.model:MoviePage \
277
304
  --out examples/movie_page/movie-page.schema.yaml
278
305
  ```
279
306
 
307
+ ## Try the TypeScript Package
308
+
309
+ The TypeScript CLI takes the same arguments and emits byte-identical output.
310
+ Validate the same artifact against the same language-neutral schema sidecar with a
311
+ pinned zero-install runner:
312
+
313
+ ```bash
314
+ npx softschema@latest validate examples/movie_page/spirited-away.md \
315
+ --schema examples/movie_page/movie-page.schema.yaml \
316
+ --envelope movie
317
+ ```
318
+
319
+ The bundled docs and skill are served identically:
320
+
321
+ ```bash
322
+ npx softschema@latest docs --list
323
+ npx softschema@latest docs guide
324
+ npx softschema@latest skill --brief
325
+ ```
326
+
327
+ Semantic models are written in Zod instead of Pydantic; everything else (flags, result
328
+ JSON, error records, and the compiled sidecar, down to its `schema_sha256`) matches the
329
+ Python package exactly.
330
+ See the [TypeScript Design](docs/softschema-typescript-design.md) doc for the Zod model
331
+ shape and the full Python ↔ TypeScript API table.
332
+
280
333
  ## Use as an Agent Skill
281
334
 
282
- The `softschema` CLI ships as a [`SKILL.md`](https://agentskills.io) following the open
335
+ Both packages ship the same [`SKILL.md`](https://agentskills.io) following the open
283
336
  Agent Skills standard discovered by Claude Code, Codex, Gemini CLI, Cursor, Copilot, and
284
337
  ~20 other coding agents.
285
338
  Pointing an agent at the CLI is enough to bootstrap its understanding of the soft-schema
286
339
  approach: the `--help` epilog routes it to a brief and the bundled docs.
287
340
 
288
341
  ```bash
289
- uvx softschema@0.1.2 --help # entry point with bootstrap pointers
290
- uvx softschema@0.1.2 skill --brief # ~12-line operating brief
291
- uvx softschema@0.1.2 docs guide # full mental model and adoption path
342
+ # Python:
343
+ uvx softschema@latest --help # entry point with bootstrap pointers
344
+ uvx softschema@latest skill --brief # compact operating brief
345
+ uvx softschema@latest docs guide # full mental model and adoption path
346
+
347
+ # TypeScript (same commands, same bundled docs/skill):
348
+ npx softschema@latest skill --brief
349
+ npx softschema@latest docs guide
292
350
  ```
293
351
 
294
- Self-install the skill into a project so any agent working in the repo finds it
295
- natively:
352
+ Self-install the skill into a project so any agent working in the repo finds it natively
353
+ (either package writes the identical mirrors):
296
354
 
297
355
  ```bash
298
- uvx softschema@0.1.2 skill --install
356
+ uvx softschema@latest skill --install
357
+ # or: npx softschema@latest skill --install
299
358
  # writes:
300
359
  # .agents/skills/softschema/SKILL.md (Codex, Gemini CLI, cross-agent installers)
301
360
  # .claude/skills/softschema/SKILL.md (Claude Code mirror)
302
361
  ```
303
362
 
304
- Both mirrors carry a `DO NOT EDIT` marker and the version that wrote them.
305
- Re-run `softschema skill --install` to refresh after upgrading the CLI.
363
+ Both mirrors carry a `DO NOT EDIT` marker.
364
+ Re-run `skill --install` to refresh after upgrading the CLI.
306
365
 
307
366
  ## Repository Layout
308
367
 
309
368
  ```text
310
- docs/ guide, spec, Python package design, and workflow docs
369
+ docs/ guide, spec, package design (Python + TypeScript), and workflow docs
311
370
  examples/movie_page/ complete example with model, host integration, artifact, schema
312
- packages/python/ Python implementation and tests
313
- packages/typescript/ future TypeScript/Zod notes only
314
- skills/softschema/ agent-facing usage guidance
371
+ examples/parity/ cross-language parity fixture (Pydantic + Zod compile to one schema)
372
+ packages/python/ Python/Pydantic implementation and tests
373
+ packages/typescript/ TypeScript/Zod implementation and tests
374
+ skills/softschema/ agent-facing usage guidance (shared SKILL.md source)
375
+ tests/golden/ shared CLI golden corpus, run against both implementations
315
376
  ```
316
377
 
317
378
  The root `pyproject.toml` builds the Python package from
318
- `packages/python/src/softschema`. This keeps the first release simple while leaving room
319
- for a future TypeScript package.
379
+ `packages/python/src/softschema`; `packages/typescript` builds the npm `softschema`
380
+ package with bun. The shared golden corpus and the cross-language conformance test keep
381
+ the two implementations byte-for-byte in sync.
320
382
 
321
383
  ## Development and Contributing
322
384
 
323
385
  See [Development](docs/development.md) for repo setup, common commands, CI checks,
324
386
  release workflow, and pointers to the installation and publishing docs.
325
387
 
388
+ The Python and TypeScript implementations **must be kept in exact sync**. Any behavior
389
+ change goes through the shared golden corpus first and then lands in both packages; see
390
+ [Keeping Python and TypeScript in Parity](docs/development.md#keeping-python-and-typescript-in-parity).
391
+
326
392
  <!-- This document follows common-doc-guidelines.md.
327
393
  See github.com/jlevy/practical-prose and review guidelines before editing.
328
394
  -->
@@ -3,12 +3,16 @@
3
3
  First-time setup of `uv` and Python is covered in [Installation](installation.md).
4
4
  Release workflow and PyPI steps are covered in [Publishing](publishing.md).
5
5
 
6
- Set up the repo:
6
+ Set up the repo (Python deps, Node tooling for hooks, and the git hooks themselves):
7
7
 
8
8
  ```bash
9
- uv sync --all-extras
9
+ make install # uv sync --all-extras + npm install (lefthook)
10
+ make hooks-install # install the lefthook pre-commit hooks
10
11
  ```
11
12
 
13
+ `make install` alone (or `uv sync --all-extras`) is enough to run tests and builds;
14
+ `make hooks-install` additionally wires up the pre-commit hooks described below.
15
+
12
16
  Common workflows:
13
17
 
14
18
  ```bash
@@ -31,6 +35,37 @@ uv build
31
35
 
32
36
  The Python package is built from `packages/python/src/softschema`.
33
37
 
38
+ ### Git hooks (this repo)
39
+
40
+ Hooks are managed by [lefthook](https://lefthook.dev) (`lefthook.yml`), installed with
41
+ `make hooks-install`. The `pre-commit` hook formats staged changes so commits stay
42
+ clean:
43
+
44
+ - **Markdown:** delegates to `make format` (pinned `flowmark-rs` and
45
+ `softschema generate`); the single source of truth, the same command you run locally.
46
+ - **Python:** `ruff format` and `ruff check --fix` on staged `*.py`.
47
+ - **TypeScript:** `biome check --write` on staged files in `packages/typescript`.
48
+
49
+ Bypass for an emergency commit with `git commit --no-verify` (avoid in PRs).
50
+ flowmark runs across the whole tree (it honors `.flowmarkignore` only relative to its
51
+ target arg), so staging any `*.md` reformats all Markdown; this is fast and idempotent.
52
+
53
+ ## TypeScript Package
54
+
55
+ The TypeScript/Zod package lives in `packages/typescript` and builds with bun (bunup and
56
+ biome). Set it up and run its checks:
57
+
58
+ ```bash
59
+ cd packages/typescript
60
+ bun install --frozen-lockfile
61
+ bun run check # biome lint, tsc --noEmit, bun test (+ coverage gate)
62
+ bun run build # copy-resources + bunup → dist/
63
+ ```
64
+
65
+ It publishes to npm as `softschema` (the same name as the PyPI package) and exposes the
66
+ CLI as both `softschema` and `softschema-ts`. The two packages **release together under
67
+ one version number**; see [Publishing](publishing.md).
68
+
34
69
  Documentation changes should follow `common-doc-guidelines.md`
35
70
  (github.com/jlevy/practical-prose).
36
71
  Keep the README short, keep conceptual guidance in `docs/softschema-guide.md`, and keep
@@ -132,6 +167,42 @@ repos:
132
167
  Adapt the paths and the `--model` / `--contract` / `--out` arguments to each schema in
133
168
  your repository.
134
169
 
170
+ ## Keeping Python and TypeScript in Parity
171
+
172
+ softschema ships two implementations, Python/Pydantic (`softschema`) and TypeScript/Zod
173
+ (`softschema`, `softschema-ts`), held to **exact behavioral parity**: equivalent CLI
174
+ inputs/outputs/flags and library APIs, the same canonical JSON Schema sidecar
175
+ (byte-identical, equal `schema_sha256`), and the same engine-neutral validation results.
176
+ Only idiomatic surface differs (snake_case ↔ camelCase, Pydantic ↔ Zod).
177
+
178
+ When you change any behavior, follow this loop so the two never drift:
179
+
180
+ 1. **Golden first.** Write or update the shared scenario in `tests/golden/scenarios/`
181
+ (neutral, runs on both) or `tests/golden/scenarios-{py,ts}/` (per-language
182
+ invocation, identical output) **before** touching code.
183
+ 2. **Implement in Python**, then `uv run pytest` and
184
+ `SOFTSCHEMA_IMPL=py bash tests/golden/run.sh`.
185
+ 3. **Port to TypeScript**, then `bun test` (in `packages/typescript`) and
186
+ `SOFTSCHEMA_IMPL=ts bash tests/golden/run.sh`.
187
+ 4. **Both green and conformance.** Both golden runs and the cross-implementation
188
+ conformance test (the Zod and Pydantic compilers produce an identical canonical
189
+ sidecar) pass in CI.
190
+
191
+ The parity invariants, and where each is enforced:
192
+
193
+ | Invariant | Enforced by |
194
+ | --- | --- |
195
+ | Canonical schema (equal `schema_sha256`) | `compile` and the KitchenSink conformance test (`packages/typescript/test/conformance.test.ts`) and `examples/parity/` |
196
+ | Engine-neutral structural errors | shared message templates (`errors`), the golden corpus |
197
+ | Byte-identical neutral CLI output | the shared golden corpus (run twice via `SOFTSCHEMA_IMPL`) |
198
+ | Equal flag/command surface | per-impl and neutral golden scenarios |
199
+ | Bundled docs/skill resolve from the package | the standalone test (`packages/typescript/test/standalone.test.ts`) |
200
+ | Skill mirrors never go stale | the mirror drift test (`tests/test_skill_mirror_drift.py`) |
201
+
202
+ Semantic invariants that JSON Schema cannot express (Pydantic validators ↔ Zod
203
+ refinements) are implementation-specific by design and tested per-language, not in the
204
+ shared corpus.
205
+
135
206
  <!-- This document follows common-doc-guidelines.md.
136
207
  See github.com/jlevy/practical-prose and review guidelines before editing.
137
208
  -->