apc-model-parser 0.1.0__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 (52) hide show
  1. apc_model_parser-0.1.0/.github/workflows/ci.yml +37 -0
  2. apc_model_parser-0.1.0/.github/workflows/docs.yml +47 -0
  3. apc_model_parser-0.1.0/.github/workflows/release.yml +50 -0
  4. apc_model_parser-0.1.0/.gitignore +31 -0
  5. apc_model_parser-0.1.0/AGENTS.md +379 -0
  6. apc_model_parser-0.1.0/PKG-INFO +111 -0
  7. apc_model_parser-0.1.0/README.md +98 -0
  8. apc_model_parser-0.1.0/docs/api.md +7 -0
  9. apc_model_parser-0.1.0/docs/chatlogs/2026-06-01_bootstrap-model-parser.md +91 -0
  10. apc_model_parser-0.1.0/docs/chatlogs/2026-06-01_ci-mkdocs-github-pages-pypi.md +80 -0
  11. apc_model_parser-0.1.0/docs/decisions/0001-python-cli-with-julia-backend.md +54 -0
  12. apc_model_parser-0.1.0/docs/decisions/0002-codegen-over-serialized-system.md +56 -0
  13. apc_model_parser-0.1.0/docs/decisions/0003-explicit-expression-ir.md +53 -0
  14. apc_model_parser-0.1.0/docs/decisions/0004-target-mtk-v11-idioms.md +61 -0
  15. apc_model_parser-0.1.0/docs/decisions/0005-cli-verbs-parse-emit.md +55 -0
  16. apc_model_parser-0.1.0/docs/decisions/index.md +33 -0
  17. apc_model_parser-0.1.0/docs/deployment/ci-cd.md +65 -0
  18. apc_model_parser-0.1.0/docs/design/ir-specification.md +126 -0
  19. apc_model_parser-0.1.0/docs/design/language-strategy.md +83 -0
  20. apc_model_parser-0.1.0/docs/design/model-parser.md +147 -0
  21. apc_model_parser-0.1.0/docs/design/storing-mtk-models.md +102 -0
  22. apc_model_parser-0.1.0/docs/index.md +31 -0
  23. apc_model_parser-0.1.0/examples/README.md +42 -0
  24. apc_model_parser-0.1.0/examples/models/model_monod_simple.ini +38 -0
  25. apc_model_parser-0.1.0/examples/models/model_thermal_tank.ini +52 -0
  26. apc_model_parser-0.1.0/examples/run.sh +27 -0
  27. apc_model_parser-0.1.0/julia/ModelParserJL/Project.toml +19 -0
  28. apc_model_parser-0.1.0/julia/ModelParserJL/README.md +40 -0
  29. apc_model_parser-0.1.0/julia/ModelParserJL/src/ModelParserJL.jl +114 -0
  30. apc_model_parser-0.1.0/mkdocs.yml +64 -0
  31. apc_model_parser-0.1.0/pyproject.toml +52 -0
  32. apc_model_parser-0.1.0/schemas/canonical-ir.schema.json +529 -0
  33. apc_model_parser-0.1.0/src/model_parser/__init__.py +18 -0
  34. apc_model_parser-0.1.0/src/model_parser/backends/__init__.py +5 -0
  35. apc_model_parser-0.1.0/src/model_parser/backends/julia_mtk.py +159 -0
  36. apc_model_parser-0.1.0/src/model_parser/cli.py +195 -0
  37. apc_model_parser-0.1.0/src/model_parser/frontends/__init__.py +9 -0
  38. apc_model_parser-0.1.0/src/model_parser/frontends/expr_parser.py +183 -0
  39. apc_model_parser-0.1.0/src/model_parser/frontends/exprtk_ini.py +204 -0
  40. apc_model_parser-0.1.0/src/model_parser/io.py +48 -0
  41. apc_model_parser-0.1.0/src/model_parser/ir/__init__.py +55 -0
  42. apc_model_parser-0.1.0/src/model_parser/ir/expr.py +87 -0
  43. apc_model_parser-0.1.0/src/model_parser/ir/model.py +133 -0
  44. apc_model_parser-0.1.0/src/model_parser/schema.py +29 -0
  45. apc_model_parser-0.1.0/src/model_parser/validation/__init__.py +9 -0
  46. apc_model_parser-0.1.0/src/model_parser/validation/validators.py +195 -0
  47. apc_model_parser-0.1.0/tests/conftest.py +44 -0
  48. apc_model_parser-0.1.0/tests/test_cli.py +73 -0
  49. apc_model_parser-0.1.0/tests/test_expr_parser.py +66 -0
  50. apc_model_parser-0.1.0/tests/test_exprtk_ini.py +36 -0
  51. apc_model_parser-0.1.0/tests/test_julia_mtk.py +61 -0
  52. apc_model_parser-0.1.0/uv.lock +900 -0
@@ -0,0 +1,37 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main, master]
6
+ pull_request:
7
+ branches: [main, master]
8
+
9
+ concurrency:
10
+ group: ci-${{ github.workflow }}-${{ github.ref }}
11
+ cancel-in-progress: true
12
+
13
+ jobs:
14
+ test:
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - uses: astral-sh/setup-uv@v5
20
+ with:
21
+ enable-cache: true
22
+ python-version: "3.11"
23
+
24
+ - name: Sync environment
25
+ run: uv sync --all-groups
26
+
27
+ - name: Ruff check
28
+ run: uv run ruff check .
29
+
30
+ - name: Ruff format (check only)
31
+ run: uv run ruff format --check .
32
+
33
+ - name: Pytest
34
+ run: uv run pytest
35
+
36
+ - name: MkDocs strict build
37
+ run: uv run mkdocs build --strict
@@ -0,0 +1,47 @@
1
+ # Publish MkDocs to GitHub Pages (Settings → Pages → Source: GitHub Actions).
2
+ name: Docs
3
+
4
+ on:
5
+ push:
6
+ branches: [main, master]
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: read
11
+ pages: write
12
+ id-token: write
13
+
14
+ concurrency:
15
+ group: pages
16
+ cancel-in-progress: false
17
+
18
+ jobs:
19
+ build:
20
+ runs-on: ubuntu-latest
21
+ steps:
22
+ - uses: actions/checkout@v4
23
+
24
+ - uses: astral-sh/setup-uv@v5
25
+ with:
26
+ enable-cache: true
27
+ python-version: "3.11"
28
+
29
+ - name: Sync environment
30
+ run: uv sync --all-groups
31
+
32
+ - name: Build site
33
+ run: uv run mkdocs build --strict
34
+
35
+ - uses: actions/upload-pages-artifact@v3
36
+ with:
37
+ path: site
38
+
39
+ deploy:
40
+ needs: build
41
+ runs-on: ubuntu-latest
42
+ environment:
43
+ name: github-pages
44
+ url: ${{ steps.deployment.outputs.page_url }}
45
+ steps:
46
+ - id: deployment
47
+ uses: actions/deploy-pages@v4
@@ -0,0 +1,50 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v[0-9]+.[0-9]+.[0-9]+"
7
+ - "v[0-9]+.[0-9]+.[0-9]+.*"
8
+
9
+ permissions:
10
+ contents: write
11
+ id-token: write
12
+
13
+ jobs:
14
+ build-and-publish:
15
+ runs-on: ubuntu-latest
16
+ environment:
17
+ name: pypi
18
+ url: https://pypi.org/project/apc-model-parser/
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+
22
+ - uses: astral-sh/setup-uv@v5
23
+ with:
24
+ enable-cache: true
25
+ python-version: "3.11"
26
+
27
+ - name: Sync environment
28
+ run: uv sync --all-groups
29
+
30
+ - name: Build sdist and wheel
31
+ run: uv build
32
+
33
+ - name: Upload build artefacts
34
+ uses: actions/upload-artifact@v4
35
+ with:
36
+ name: dist-${{ github.ref_name }}
37
+ path: dist/
38
+
39
+ - name: Create GitHub Release with assets
40
+ uses: softprops/action-gh-release@v2
41
+ with:
42
+ files: dist/*
43
+ generate_release_notes: true
44
+ env:
45
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
46
+
47
+ - name: Publish to PyPI
48
+ uses: pypa/gh-action-pypi-publish@release/v1
49
+ with:
50
+ packages-dir: dist/
@@ -0,0 +1,31 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .eggs/
6
+ build/
7
+ dist/
8
+ .venv/
9
+ .pytest_cache/
10
+ .ruff_cache/
11
+ .mypy_cache/
12
+
13
+ # uv
14
+ # uv.lock is committed; the environment is not.
15
+
16
+ # Julia
17
+ /julia/**/Manifest.toml
18
+ *.jl.cov
19
+ *.jl.*.cov
20
+ *.jl.mem
21
+
22
+ # Generated artifacts (examples write here)
23
+ /examples/outputs/
24
+
25
+ # MkDocs (local build; CI deploys via artifact)
26
+ site/
27
+
28
+ # Editor / OS
29
+ .DS_Store
30
+ .idea/
31
+ .vscode/
@@ -0,0 +1,379 @@
1
+ # Coding agent guidelines for model-parser
2
+
3
+ This file defines how AI coding agents (e.g. Cursor, Copilot) and human
4
+ contributors collaborate on this repository. Human reviewers should treat it as
5
+ the canonical contributor guide. It is **public**: describe behaviour only in
6
+ terms of this project and its published design — avoid internal codenames or
7
+ unrelated repositories.
8
+
9
+ **Authoritative product specification:**
10
+ [`docs/design/model-parser.md`](docs/design/model-parser.md). When CLI
11
+ semantics, the IR shape, scope, or roadmap change, update that document (and the
12
+ relevant `docs/decisions/NNNN-*.md` ADR) in the same change series as the code.
13
+
14
+ ## What this repository is
15
+
16
+ `model-parser` converts process-model definitions **to and from a canonical
17
+ intermediate representation (IR)**. It owns the model-*scaffold* contract: the
18
+ parser, AST, normalizer, IR data model + JSON Schema, validators, and the
19
+ IR→backend lowerings (codegen). It is one tool in the Advanced Process Control
20
+ ecosystem and must remain usable **standalone**.
21
+
22
+ ```text
23
+ authoring (ExprTk INI) --parse--> AST --normalize--> canonical IR (JSON)
24
+ |
25
+ emit julia --> ModelingToolkit .jl
26
+ emit cpp --> (planned) realtime C++
27
+ ```
28
+
29
+ ## Language
30
+
31
+ - **All content in English:** code, comments, docstrings, commit messages,
32
+ user-facing CLI strings, `docs/chatlogs/` entries, and Markdown under `docs/`.
33
+ - Keep domain vocabulary consistent with the org glossary and design docs:
34
+ *scaffold*, *parameter set*, *scenario*, *AST*, *canonical IR*, *profile*,
35
+ *lower*, *export*, *conformance*, *provenance*.
36
+
37
+ ## Platform and paths
38
+
39
+ - **Targets:** Linux, macOS, and Windows where the CLI runs. Prefer `pathlib`,
40
+ avoid hard-coded host-specific paths in tests and examples; use `tmp_path` in
41
+ pytest and placeholders in docs (`/tmp`, `example.ini`).
42
+ - **Julia side:** `julia/ModelParserJL/` must remain loadable on the supported
43
+ Julia versions stated in that package; document any extra env assumptions in
44
+ the design doc or `examples/README.md`.
45
+
46
+ ## Two languages, one contract
47
+
48
+ | Concern | Home |
49
+ |---|---|
50
+ | CLI, AST, IR data model, JSON Schema, validators, **codegen** | **Python** (`src/model_parser/`) |
51
+ | IR → ModelingToolkit `System`, in-memory build, conformance reference | **Julia** (`julia/ModelParserJL/`) |
52
+
53
+ Both consume the **same** IR JSON. Do not re-implement expression semantics in
54
+ more than one place without IR-level tests. See
55
+ [`docs/decisions/0001-python-cli-with-julia-backend.md`](docs/decisions/0001-python-cli-with-julia-backend.md).
56
+
57
+ ## Python tooling — strictly uv
58
+
59
+ Use **uv** for every environment and dependency operation.
60
+
61
+ - `uv add <pkg>` / `uv add --dev <pkg>` to add dependencies.
62
+ - `uv remove <pkg>` to remove them.
63
+ - `uv run <cmd>` to execute commands in the project environment.
64
+ - `uv sync` / `uv sync --all-groups` to recreate the environment from the
65
+ lockfile (use **`--all-groups`** so dev dependencies match CI).
66
+ - **Do not** hand-edit `[project.dependencies]` or `[dependency-groups]` in
67
+ `pyproject.toml`; those are owned by `uv add` / `uv remove`.
68
+ - **Do** edit `[project]` metadata (including `version`), `[project.scripts]`,
69
+ and tool configuration (`[tool.ruff]`, `[tool.pytest.ini_options]`, …).
70
+
71
+ ## Code style
72
+
73
+ | Area | Rule |
74
+ | --- | --- |
75
+ | Python | 3.11+ per `requires-python` |
76
+ | Linter / formatter | **ruff** (line length **100**, target **py311**) |
77
+ | Type hints | Required on every **public** function and method |
78
+ | Docstrings | Google-flavoured on public APIs; module docstring atop each `.py` |
79
+ | Imports | ruff isort; absolute imports (`model_parser.…`) |
80
+ | Julia | follow the prototype style in `apcplants/ProcessModelingJL`; `Pkg` for deps |
81
+
82
+ Local checks (must match CI):
83
+
84
+ ```bash
85
+ uv sync --all-groups
86
+ uv run ruff check .
87
+ uv run ruff format --check .
88
+ uv run pytest
89
+ uv run mkdocs build --strict
90
+ ```
91
+
92
+ ## Architecture principles
93
+
94
+ ### Pure logic vs. I/O separation
95
+
96
+ **Pure modules** parse, normalize, validate, and lower — no filesystem,
97
+ subprocess, or network access. Keep `ir/`, `frontends/`, `backends/`, and
98
+ `validation/` pure and deterministic.
99
+
100
+ **I/O modules** read/write files and implement the CLI. `io.py` and `cli.py` own
101
+ filesystem access; the CLI must stay a thin shell over the pure functions.
102
+
103
+ ### One transformation ≈ one module
104
+
105
+ - `frontends/<format>.py` — one authoring format → IR (`parse` + `normalize`).
106
+ - `backends/<target>.py` — IR → one target view (`lower` / codegen).
107
+ - `validation/` — semantic and profile checks.
108
+ - `ir/` — the data model and expression tree; the shared contract.
109
+
110
+ Adding a backend means **one new `backends/*.py` + tests**, never editing every
111
+ other backend (that is the whole point of the hub-and-spoke IR).
112
+
113
+ ### The IR is the single semantic truth
114
+
115
+ - The IR describes a **scaffold only** (structure + equations + roles + units +
116
+ metadata). Parameter sets and scenarios (incl. initial values `x0`/`u0`) are
117
+ **out of scope** — frontends drop them with a `WARN`.
118
+ - The IR is **versioned JSON** (`ir_version`, SemVer). A breaking schema change
119
+ requires migration notes and an ADR.
120
+ - Every IR carries a **content hash** over its semantic body; downstream
121
+ artifacts reference scaffolds by hash, not path.
122
+ - Expression semantics live in the **explicit expression tree**
123
+ (`ir/expr.py`), never in per-backend string rewrites.
124
+
125
+ ### Codegen, not serialized objects
126
+
127
+ The durable form of an MTK model is the **IR JSON + generated `.jl` script**,
128
+ never a serialized `System`. Generated Julia targets **MTK v11** idioms
129
+ (`System(eqs, t)`, `mtkcompile`, `t_nounits`/`D_nounits`, no `@mtkmodel`, no
130
+ `structural_simplify`). See ADRs 0002 and 0004.
131
+
132
+ ### Determinism & idempotency
133
+
134
+ Re-running `parse`/`emit` on unchanged input must produce byte-identical output
135
+ (stable ordering, stable number formatting). The content hash depends only on
136
+ the semantic body.
137
+
138
+ ### Status vocabulary and exit codes
139
+
140
+ CLI diagnostics use `OK` · `WARN` · `ERROR`. Exit codes:
141
+
142
+ | Code | Meaning |
143
+ | --- | --- |
144
+ | `0` | success; no `ERROR` diagnostics |
145
+ | `1` | at least one `ERROR` (e.g. validation failed) |
146
+ | `2` | invalid usage / load failure before meaningful work |
147
+
148
+ ### CLI conventions
149
+
150
+ - Long options in kebab-case (`--from`, `--profile`, `--output`/`-o`).
151
+ - The two core verbs are `parse` (authoring → IR) and `emit <target>` (IR →
152
+ view). Supporting commands: `validate`, `inspect`, `ast`, `schema`.
153
+ - Breaking CLI changes require a SemVer bump and release notes.
154
+
155
+ ## Scope guardrails
156
+
157
+ **In scope:** authoring-format parsing, the canonical IR + JSON Schema, semantic
158
+ & profile validation, IR↔backend lowering / codegen, conformance fixtures.
159
+
160
+ **Out of scope:** parameter identification, scenario execution / simulation,
161
+ result storage, controller synthesis, deployment, UI. Those are sibling tools
162
+ that *consume* the IR.
163
+
164
+ If a change request conflicts with the design doc, stop and resolve via an
165
+ explicit doc + ADR update — do not silently expand scope.
166
+
167
+ ## Work tracking (ADRs and issues)
168
+
169
+ - **ADRs:** numbered files under [`docs/decisions/`](docs/decisions/) (`NNNN-title.md`),
170
+ indexed in [`docs/decisions/index.md`](docs/decisions/index.md). One decision per
171
+ file; never delete — supersede with a newer ADR and update the index.
172
+ - **Issues:** use GitHub issues for backlog items that are not yet an ADR; link
173
+ ADRs from issue/PR text when a decision motivates the change.
174
+ - Prefer **small, reviewable slices** (one coherent feature, bugfix, or refactor
175
+ per PR when practical). For larger work, split PRs and reference the same ADR
176
+ or design section in each.
177
+
178
+ ### Recommended slice workflow (maintainers & agents)
179
+
180
+ 1. **Align scope** — confirm design doc + ADR if behaviour or IR contract changes.
181
+ 2. **Implement & test** — code and automated tests for that slice; keep pure vs I/O
182
+ boundaries intact.
183
+ 3. **Verify locally** — `uv sync --all-groups`, ruff check/format, `uv run pytest`,
184
+ `uv run mkdocs build --strict` (same as CI).
185
+ 4. **Docs** — update `docs/design/model-parser.md`, schema regeneration command, or
186
+ ADR in the **same** change series when the public contract moves.
187
+ 5. **`docs/chatlogs/…`** when the slice materially changes behaviour or governance.
188
+
189
+ ## Testing
190
+
191
+ - Framework: **pytest** (`uv run pytest`).
192
+ - **Unit tests** for pure logic: expression parser, frontend, backend codegen,
193
+ validators.
194
+ - **CLI smoke tests** with `typer.testing.CliRunner`.
195
+ - **Conformance** (as it grows): an IR fixture plus expected Julia output and/or
196
+ expected trajectories, shared between the Python codegen and `ModelParserJL`.
197
+
198
+ ## Documentation system
199
+
200
+ **Target stack** (align when a docs site is added): **MkDocs** with the **Material**
201
+ theme and **mkdocstrings** (or equivalent) for Python API reference; Julia API
202
+ docs may stay in `julia/ModelParserJL/` README until unified.
203
+
204
+ **Intended layout** (keep consistent with [`docs/design/model-parser.md`](docs/design/model-parser.md)):
205
+
206
+ - `docs/index.md` or root `README.md` — overview, install (`uv`), quick `parse` /
207
+ `emit` examples (when expanded).
208
+ - `docs/design/` — product spec (`model-parser.md`) and deep architecture notes.
209
+ - `docs/decisions/` — ADRs (`NNNN-title.md`) + `index.md`.
210
+ - `docs/chatlogs/` — session summaries (see **Session summaries** below).
211
+ - `docs/deployment/` — optional summaries for CI/CD, packaging, and release
212
+ process once those exist in-repo.
213
+
214
+ **Rules:**
215
+
216
+ - User-facing prose lives under `docs/`; IR semantics and the CLI contract stay
217
+ accurate relative to the design doc and JSON Schema.
218
+ - Do not commit secrets, real tokens, or customer-specific identifiers; use
219
+ placeholders (`example.com`, generic model names).
220
+ - Once `mkdocs.yml` exists, CI **should** run `uv run mkdocs build --strict`; until
221
+ then, Markdown in `docs/` remains valid without a site build.
222
+
223
+ **Generated artefacts:**
224
+
225
+ - [`schemas/canonical-ir.schema.json`](schemas/canonical-ir.schema.json) is
226
+ committed output; regenerate with
227
+ `uv run model-parser schema -o schemas/canonical-ir.schema.json`
228
+ whenever the IR Pydantic models change, in the same change series as the code.
229
+
230
+ ## Runnable examples (`examples/`)
231
+
232
+ - Keep **small, well-commented, copy-paste-friendly** inputs under
233
+ [`examples/models/`](examples/models/) and expected outputs under
234
+ [`examples/outputs/`](examples/outputs/) where helpful for reviewers and users
235
+ (not only pytest).
236
+ - Prefer **English** comments and placeholders — never real proprietary model text
237
+ or secrets.
238
+ - When a session ships **user-visible CLI or IR** behaviour, add or refresh an
239
+ **`examples/…`** entry in the **same** merge series as the chatlog when feasible.
240
+ - Document how to run examples in [`examples/README.md`](examples/README.md)
241
+ (e.g. `examples/run.sh`, `uv run model-parser …`).
242
+
243
+ ## Session summaries (`docs/chatlogs/`)
244
+
245
+ **Required** after substantive sessions (multi-step implementation, non-trivial
246
+ design discussion, or when behaviour or doc intent shifts).
247
+
248
+ ### Commit helper (mandatory placement)
249
+
250
+ Every chatlog MUST begin with a **`## Commit helper`** section (level-2 heading).
251
+
252
+ **Order of file contents**
253
+
254
+ 1. **YAML frontmatter** (when used) MUST be the **first bytes** of the file (`---` … `---`).
255
+ 2. Immediately after the closing `---`, **`## Commit helper`** MUST be the **first
256
+ Markdown body content** — nothing above it except frontmatter.
257
+ 3. Next, **`## How to try`** MUST follow **`## Commit helper`** (not the long narrative).
258
+ 4. If frontmatter is omitted, **`## Commit helper`** is the first line, then **`## How to try`**, then the narrative.
259
+
260
+ **Commit helper MUST contain**
261
+
262
+ - **`SemVer / version bump`:** **no bump** vs **PATCH / MINOR / MAJOR**, tied to
263
+ what shipped (docs-only vs user-visible CLI/IR vs breaking schema or CLI). See
264
+ [**Versioning, releases, and release notes**](#versioning-releases-and-release-notes).
265
+ - **`Tags / GitHub Release`:** always fill — **None** (stack on default branch) or
266
+ **Tag after bump** with exact tag string (e.g. `v0.2.0`), pre-release if needed,
267
+ and whether a **`release.yml`** (or future) workflow should run on tag push.
268
+ - **Suggested commit message:** one imperative subject; optional Conventional scope
269
+ (`feat(cli): …`, `fix(ir): …`). Use `BREAKING CHANGE:` footer when applicable.
270
+ - **Copy-paste git commands:** fenced `bash` with `git add` paths and
271
+ `git commit -m '…'`. If the version bump belongs in the same commit, note updates
272
+ to `pyproject.toml` and `src/model_parser/__init__.py`.
273
+ - **Git order when tagging:** never tag before the release commit exists locally:
274
+ **commit / push branch (with version bump)** → **`git tag -a vX.Y.Z` on that
275
+ commit** → **`git push origin vX.Y.Z`**.
276
+
277
+ ### How to try (mandatory unless N/A)
278
+
279
+ Immediately after **`## Commit helper`**, include **`## How to try`** with concrete
280
+ `uv run model-parser …` steps, pytest selection, or `examples/…` commands. For
281
+ pure docs/chatlog hygiene: **`N/A (docs-only).`**
282
+
283
+ ### Session narrative
284
+
285
+ After **How to try**, summarize shipped modules, CLI/IR edge cases, and regression
286
+ risks in English (**goal → shipped surface → decisions → follow-ups**).
287
+
288
+ **Naming:** `YYYY-MM-DD_short-topic.md` (ASCII slug).
289
+
290
+ **Frontmatter (YAML):** at least `title`, `topic`, `date_added`, `tags: [chatlogs]`,
291
+ and `links:` to touched specs or code (e.g. `AGENTS.md`, `docs/design/model-parser.md`).
292
+
293
+ **Not a substitute for:** ADRs, tests, or commit messages — those remain the source
294
+ of truth for the contract.
295
+
296
+ ## CI/CD (GitHub Actions)
297
+
298
+ Workflows live under [`.github/workflows/`](.github/workflows/). Maintainer-facing
299
+ detail (Pages setup, PyPI trusted publishing, tag order) is in
300
+ [`docs/deployment/ci-cd.md`](docs/deployment/ci-cd.md).
301
+
302
+ | Workflow | Trigger | Purpose |
303
+ | --- | --- | --- |
304
+ | [`ci.yml`](.github/workflows/ci.yml) | Push / PR to `main` or `master` | `uv sync --all-groups`, ruff, pytest, **`mkdocs build --strict`** |
305
+ | [`docs.yml`](.github/workflows/docs.yml) | Push to `main`/`master`, `workflow_dispatch` | Build MkDocs and deploy to **GitHub Pages** |
306
+ | [`release.yml`](.github/workflows/release.yml) | Push SemVer tags `vX.Y.Z` (and pre-release suffixes) | `uv build`, GitHub Release + assets, **PyPI** (OIDC) |
307
+
308
+ **Rules:**
309
+
310
+ - CI commands must be reproducible locally (`uv run …`); the **local check block**
311
+ above must stay aligned with `ci.yml`.
312
+ - **PyPI** uses **trusted publishing** via `pypa/gh-action-pypi-publish` with GitHub
313
+ **environment** `pypi` — configure the same environment name on PyPI’s pending
314
+ publisher (or adjust the workflow if PyPI omits environment). Distribution name:
315
+ **`apc-model-parser`**; import package and CLI: **`model_parser`** / `model-parser`.
316
+ - **GitHub Pages:** repository **Settings → Pages → Source: GitHub Actions** (one-time).
317
+ Published site: `https://advanced-process-control.github.io/model-parser/` (must
318
+ match `site_url` in `mkdocs.yml`).
319
+
320
+ ## Versioning, releases, and release notes
321
+
322
+ ### Semantic versioning
323
+
324
+ Follow **[Semantic Versioning 2.0.0](https://semver.org/)** (`MAJOR.MINOR.PATCH`).
325
+
326
+ - **`0.y.z`:** breaking CLI or IR schema changes are allowed but must be called out
327
+ in release notes and usually accompanied by an ADR.
328
+ - **`1.0.0` onward:** honour SemVer for the **documented** public surface: stable
329
+ CLI flags, `ir_version`, JSON Schema compatibility as advertised.
330
+
331
+ ### Single source of truth for the running version
332
+
333
+ Bump **both** in the same release series until tooling ties them automatically:
334
+
335
+ - `project.version` in [`pyproject.toml`](pyproject.toml)
336
+ - `__version__` in [`src/model_parser/__init__.py`](src/model_parser/__init__.py)
337
+
338
+ Keep them identical for each release tag.
339
+
340
+ ### Tags and GitHub Releases
341
+
342
+ - **Ordering:** merge the commit(s) that bump version fields; **then** create an
343
+ **annotated** tag **`vX.Y.Z`** on **that** commit; **then** `git push origin vX.Y.Z`.
344
+ Release automation (when added) should trigger on tag push; the leading `v` is
345
+ a common convention — match whatever `release.yml` expects.
346
+ - **Release notes:** use GitHub auto-generated notes where helpful; edit for IR or
347
+ CLI highlights.
348
+ - **Optional `CHANGELOG.md`:** if introduced, follow
349
+ [Keep a Changelog](https://keepachangelog.com/) and update it in the release
350
+ commit series.
351
+
352
+ ### Artefacts
353
+
354
+ - **`uv build`** produces `dist/*.tar.gz` and `dist/*.whl`. A release workflow can
355
+ attach them to GitHub Releases and PyPI.
356
+ - **Wheel tags** and `requires-python` must match actual dependency and runtime
357
+ support; adjust classifiers in `pyproject.toml` as the project matures.
358
+
359
+ ### Pre-releases
360
+
361
+ Pre-release tags (e.g. `v0.2.0-rc.1`, PEP 440–compatible) are fine for testers.
362
+ Mark GitHub Releases as **pre-release** when behaviour or schema is unstable.
363
+
364
+ ## Security
365
+
366
+ - Never commit secrets (tokens, private URLs with embedded credentials).
367
+ - Logs and user-visible errors must not echo full file contents of untrusted inputs
368
+ at high verbosity without care — follow the design doc for diagnostic redaction
369
+ policy as it evolves.
370
+ - Generated Julia/C++ must not embed secrets from authoring files; treat IR JSON as
371
+ shareable engineering data unless the org classifies it otherwise.
372
+
373
+ ## Commit conventions
374
+
375
+ - Imperative, concise subjects (`add mtk v11 emit path`, not `added path`).
376
+ - Prefer one logical change per commit.
377
+ - Reference GitHub issues or ADR filenames when it aids traceability.
378
+ - [Conventional Commits](https://www.conventionalcommits.org/) shape where it
379
+ helps (`feat`, `fix`, `docs`, `refactor`, `test`, `chore`, `ci`).
@@ -0,0 +1,111 @@
1
+ Metadata-Version: 2.4
2
+ Name: apc-model-parser
3
+ Version: 0.1.0
4
+ Summary: Convert process-model definitions to and from a canonical intermediate representation.
5
+ Project-URL: Repository, https://github.com/Advanced-Process-Control/model-parser
6
+ Author: Advanced Process Control
7
+ License: MIT
8
+ Keywords: codegen,exprtk,intermediate-representation,modelingtoolkit,process-model
9
+ Requires-Python: >=3.11
10
+ Requires-Dist: pydantic>=2.6
11
+ Requires-Dist: typer>=0.12
12
+ Description-Content-Type: text/markdown
13
+
14
+ # model-parser
15
+
16
+ **Convert process-model definitions to and from a canonical intermediate
17
+ representation (IR).**
18
+
19
+ `model-parser` is the Advanced Process Control toolbox component that owns the
20
+ *model scaffold* contract and the transformations around it. It parses an
21
+ authoring format (today: the ExprTk-style INI used by the MPC / simulation
22
+ toolchain) into a normalized, backend-independent **canonical IR**, and lowers
23
+ that IR into target views — starting with a generated **ModelingToolkit (Julia)**
24
+ model script.
25
+
26
+ ```text
27
+ authoring (ExprTk INI) --parse--> AST --normalize--> canonical IR (JSON)
28
+ |
29
+ emit julia --> ModelingToolkit .jl
30
+ emit cpp --> (planned) realtime C++
31
+ ```
32
+
33
+ The IR is the single semantic contract. Adding a backend means writing one
34
+ `lower` + one `export`, not an N×N mesh of view-to-view translators. See
35
+ [`docs/design/model-parser.md`](docs/design/model-parser.md) for the
36
+ authoritative product specification and
37
+ [`docs/decisions/`](docs/decisions/) for the design decision records.
38
+
39
+ ## Why two languages?
40
+
41
+ | Concern | Home | Why |
42
+ |---|---|---|
43
+ | CLI, AST, IR, JSON Schema, validators, **codegen** | **Python** (this package) | Parsing & orchestration strength; no symbolic runtime needed to emit code. |
44
+ | IR → MTK `System`, simulation, analysis, conformance | **Julia** ([`julia/ModelParserJL`](julia/ModelParserJL/)) | Natural fit for ModelingToolkit; reference for parity tests. |
45
+
46
+ The Python CLI builds and validates the IR and **generates** Julia code; the
47
+ Julia package can additionally load an IR directly into an MTK `System` for
48
+ in-memory, dynamic workflows. Both consume the *same* IR. See
49
+ [ADR 0001](docs/decisions/0001-python-cli-with-julia-backend.md).
50
+
51
+ ## Install
52
+
53
+ This project uses **[uv](https://docs.astral.sh/uv/)** for everything.
54
+
55
+ ```bash
56
+ uv sync --all-groups # include dev tools (ruff, pytest, mkdocs)
57
+ uv run model-parser --help
58
+ ```
59
+
60
+ ## CLI
61
+
62
+ ```bash
63
+ # 1. ExprTk INI -> canonical IR JSON
64
+ uv run model-parser parse examples/models/model_monod_simple.ini -o monod.ir.json
65
+
66
+ # 2. canonical IR -> ModelingToolkit (v11) Julia model
67
+ uv run model-parser emit julia monod.ir.json -o monod.jl
68
+
69
+ # Supporting commands
70
+ uv run model-parser validate monod.ir.json --profile julia-analysis
71
+ uv run model-parser inspect monod.ir.json
72
+ uv run model-parser ast examples/models/model_monod_simple.ini # debug tree
73
+ uv run model-parser schema -o schemas/canonical-ir.schema.json # export schema
74
+ ```
75
+
76
+ `parse` accepts `--from exprtk-ini` (default). `validate` accepts either an IR
77
+ `.json` file or an INI file (parsed on the fly). Exit codes: `0` success,
78
+ `1` validation errors, `2` usage / load failure.
79
+
80
+ ## How "stored MTK models" work
81
+
82
+ The persisted, version-controlled form of a model is the **IR JSON** plus the
83
+ **generated `.jl` script** — *not* a serialized `System` object. ModelingToolkit
84
+ v11 changed `System` internals significantly (precompilation, removal of
85
+ `defaults`, deprecation of `@mtkmodel`), so serializing the live object is
86
+ brittle across versions. Regenerating from the IR is the durable path. See
87
+ [ADR 0002](docs/decisions/0002-codegen-over-serialized-system.md) and
88
+ [`docs/design/storing-mtk-models.md`](docs/design/storing-mtk-models.md).
89
+
90
+ ## Development
91
+
92
+ ```bash
93
+ uv sync --all-groups
94
+ uv run ruff check .
95
+ uv run ruff format --check .
96
+ uv run pytest
97
+ uv run mkdocs build --strict # same as CI
98
+ ```
99
+
100
+ Documentation site (after [Pages](https://docs.github.com/en/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site#publishing-with-a-custom-github-actions-workflow) is set to GitHub Actions):
101
+ [https://advanced-process-control.github.io/model-parser/](https://advanced-process-control.github.io/model-parser/)
102
+
103
+ ## Scope
104
+
105
+ **In scope:** authoring-format parsing, the canonical IR data model + JSON
106
+ Schema, semantic & profile validation, IR↔backend lowering/codegen.
107
+
108
+ **Out of scope:** parameter identification, scenario execution, simulation
109
+ result storage, controller synthesis, deployment. Those are sibling tools that
110
+ *consume* the IR. The parser stays small (see
111
+ [`AGENTS.md`](AGENTS.md) scope guardrails).