mklang 0.5.4__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.
- mklang-0.5.4/.env.example +11 -0
- mklang-0.5.4/.github/workflows/ci.yml +53 -0
- mklang-0.5.4/.github/workflows/docs.yml +20 -0
- mklang-0.5.4/.github/workflows/release.yml +108 -0
- mklang-0.5.4/.gitignore +25 -0
- mklang-0.5.4/CHANGELOG.md +328 -0
- mklang-0.5.4/CONTRIBUTING.md +95 -0
- mklang-0.5.4/LICENSE +201 -0
- mklang-0.5.4/PKG-INFO +265 -0
- mklang-0.5.4/README.md +234 -0
- mklang-0.5.4/ROADMAP.md +123 -0
- mklang-0.5.4/SPEC.md +931 -0
- mklang-0.5.4/config/runtime.example.yaml +143 -0
- mklang-0.5.4/config/runtime.schema.json +73 -0
- mklang-0.5.4/conformance/README.md +85 -0
- mklang-0.5.4/conformance/cases/accumulate.yaml +26 -0
- mklang-0.5.4/conformance/cases/budget-exhausted.yaml +26 -0
- mklang-0.5.4/conformance/cases/budget-fanout-charging.yaml +45 -0
- mklang-0.5.4/conformance/cases/call-failed.yaml +35 -0
- mklang-0.5.4/conformance/cases/call.yaml +34 -0
- mklang-0.5.4/conformance/cases/cost-exhausted.yaml +36 -0
- mklang-0.5.4/conformance/cases/escalate-routing.yaml +27 -0
- mklang-0.5.4/conformance/cases/fail-gate.yaml +23 -0
- mklang-0.5.4/conformance/cases/fanout-over.yaml +35 -0
- mklang-0.5.4/conformance/cases/fanout-sample-index.yaml +27 -0
- mklang-0.5.4/conformance/cases/hook-before-prose.yaml +48 -0
- mklang-0.5.4/conformance/cases/hook-false-falls-through.yaml +38 -0
- mklang-0.5.4/conformance/cases/judge-routing.yaml +34 -0
- mklang-0.5.4/conformance/cases/judge-unparseable-fallback.yaml +27 -0
- mklang-0.5.4/conformance/cases/judge-unparseable-halt.yaml +35 -0
- mklang-0.5.4/conformance/cases/linear.yaml +27 -0
- mklang-0.5.4/conformance/cases/no-gate-matched.yaml +23 -0
- mklang-0.5.4/conformance/cases/repair-exhausted-reroutes.yaml +22 -0
- mklang-0.5.4/conformance/cases/repair.yaml +23 -0
- mklang-0.5.4/conformance/cases/tool-state-output-deposit.yaml +38 -0
- mklang-0.5.4/conformance/cases/tool-unknown-halts.yaml +23 -0
- mklang-0.5.4/docs/adr/0001-capability-tiers.md +27 -0
- mklang-0.5.4/docs/adr/0002-fanout-llm-reducer.md +26 -0
- mklang-0.5.4/docs/adr/0003-provider-agnostic.md +24 -0
- mklang-0.5.4/docs/adr/0004-llm-as-runtime-gates.md +35 -0
- mklang-0.5.4/docs/adr/0005-reasoning-first-class.md +25 -0
- mklang-0.5.4/docs/adr/0006-code-hook-gates.md +35 -0
- mklang-0.5.4/docs/adr/0007-resumable-checkpoints.md +48 -0
- mklang-0.5.4/docs/adr/0008-hitl-escalate-suspend.md +40 -0
- mklang-0.5.4/docs/adr/0009-conformance-suite.md +52 -0
- mklang-0.5.4/docs/adr/0010-llm-assisted-lint.md +123 -0
- mklang-0.5.4/docs/adr/0011-mcp-server-surface.md +94 -0
- mklang-0.5.4/docs/experiments/gate-divergence.md +98 -0
- mklang-0.5.4/docs/patterns.md +141 -0
- mklang-0.5.4/examples/expense_approval.mk +89 -0
- mklang-0.5.4/examples/hook_gates.mk +49 -0
- mklang-0.5.4/examples/map_reduce.mk +39 -0
- mklang-0.5.4/examples/react.mk +62 -0
- mklang-0.5.4/examples/research.mk +86 -0
- mklang-0.5.4/examples/self_consistency.mk +61 -0
- mklang-0.5.4/examples/summarize_doc.mk +23 -0
- mklang-0.5.4/examples/triage.mk +139 -0
- mklang-0.5.4/examples/triage.test.yaml +54 -0
- mklang-0.5.4/mkdocs.yml +46 -0
- mklang-0.5.4/pyproject.toml +77 -0
- mklang-0.5.4/schema/mklang.schema.json +269 -0
- mklang-0.5.4/scripts/build-docs.sh +51 -0
- mklang-0.5.4/scripts/gate_divergence.py +294 -0
- mklang-0.5.4/src/mklang/__init__.py +17 -0
- mklang-0.5.4/src/mklang/checkpoint.py +112 -0
- mklang-0.5.4/src/mklang/cli.py +406 -0
- mklang-0.5.4/src/mklang/config.py +50 -0
- mklang-0.5.4/src/mklang/data/mklang.schema.json +269 -0
- mklang-0.5.4/src/mklang/engine.py +570 -0
- mklang-0.5.4/src/mklang/errors.py +36 -0
- mklang-0.5.4/src/mklang/hooks.py +86 -0
- mklang-0.5.4/src/mklang/interpolate.py +44 -0
- mklang-0.5.4/src/mklang/lint.py +140 -0
- mklang-0.5.4/src/mklang/llm/__init__.py +5 -0
- mklang-0.5.4/src/mklang/llm/anthropic.py +129 -0
- mklang-0.5.4/src/mklang/llm/base.py +110 -0
- mklang-0.5.4/src/mklang/llm/mock.py +47 -0
- mklang-0.5.4/src/mklang/llm/openai_compat.py +137 -0
- mklang-0.5.4/src/mklang/loader.py +174 -0
- mklang-0.5.4/src/mklang/model.py +103 -0
- mklang-0.5.4/src/mklang/providers.py +75 -0
- mklang-0.5.4/src/mklang/registry.py +22 -0
- mklang-0.5.4/src/mklang/scripttest.py +219 -0
- mklang-0.5.4/src/mklang/tools.py +135 -0
- mklang-0.5.4/tests/test_anthropic.py +158 -0
- mklang-0.5.4/tests/test_checkpoint.py +355 -0
- mklang-0.5.4/tests/test_conformance.py +30 -0
- mklang-0.5.4/tests/test_cookbook.py +44 -0
- mklang-0.5.4/tests/test_engine_mock.py +541 -0
- mklang-0.5.4/tests/test_golden.py +82 -0
- mklang-0.5.4/tests/test_hardening.py +303 -0
- mklang-0.5.4/tests/test_hitl.py +220 -0
- mklang-0.5.4/tests/test_hooks.py +199 -0
- mklang-0.5.4/tests/test_interpolate.py +18 -0
- mklang-0.5.4/tests/test_lint.py +156 -0
- mklang-0.5.4/tests/test_live.py +69 -0
- mklang-0.5.4/tests/test_loader_checks.py +189 -0
- mklang-0.5.4/tests/test_providers.py +39 -0
- mklang-0.5.4/tests/test_release.py +108 -0
- mklang-0.5.4/tests/test_remediation_002.py +94 -0
- mklang-0.5.4/tests/test_schema.py +38 -0
- mklang-0.5.4/tests/test_scripttest.py +115 -0
- mklang-0.5.4/tests/test_tools.py +108 -0
- mklang-0.5.4/uv.lock +736 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Copy to .env and fill in the keys for the providers you use.
|
|
2
|
+
# .env is gitignored; NEVER commit real keys. Names match config/runtime.example.yaml.
|
|
3
|
+
|
|
4
|
+
DEEPSEEK_API_KEY=
|
|
5
|
+
ANTHROPIC_API_KEY=
|
|
6
|
+
OPENAI_API_KEY=
|
|
7
|
+
OPENROUTER_API_KEY=
|
|
8
|
+
XAI_API_KEY=
|
|
9
|
+
MISTRAL_API_KEY=
|
|
10
|
+
# GEMINI_API_KEY=
|
|
11
|
+
# LOCAL_API_KEY= # usually unused for Ollama/vLLM
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- uses: actions/checkout@v4
|
|
12
|
+
- uses: astral-sh/setup-uv@v5
|
|
13
|
+
- name: Lint
|
|
14
|
+
run: uv run --extra dev ruff check src tests
|
|
15
|
+
- name: Unit tests (MockLLM — no network)
|
|
16
|
+
run: uv run --extra dev pytest -q
|
|
17
|
+
- name: Validate example machines against the schema
|
|
18
|
+
run: >
|
|
19
|
+
uv run --with check-jsonschema
|
|
20
|
+
check-jsonschema --default-filetype yaml
|
|
21
|
+
--schemafile schema/mklang.schema.json examples/*.mk
|
|
22
|
+
- name: Validate the runtime config against its schema
|
|
23
|
+
run: >
|
|
24
|
+
uv run --with check-jsonschema
|
|
25
|
+
check-jsonschema --default-filetype yaml
|
|
26
|
+
--schemafile config/runtime.schema.json config/runtime.example.yaml
|
|
27
|
+
- name: Semantic check + static analysis on examples
|
|
28
|
+
run: |
|
|
29
|
+
uv run mklang check examples/*.mk
|
|
30
|
+
uv run mklang lint --strict examples/*.mk
|
|
31
|
+
- name: Deterministic scenario tests (scripted LLM — no API keys)
|
|
32
|
+
run: uv run mklang test examples/triage.mk --script examples/triage.test.yaml
|
|
33
|
+
- name: Build sdist + wheel
|
|
34
|
+
run: uv build
|
|
35
|
+
|
|
36
|
+
live-smoke:
|
|
37
|
+
# Low-budget smoke against real providers; each test skips itself when its
|
|
38
|
+
# key is absent, so forks without secrets stay green.
|
|
39
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
40
|
+
runs-on: ubuntu-latest
|
|
41
|
+
needs: test
|
|
42
|
+
env:
|
|
43
|
+
DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
|
|
44
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
45
|
+
steps:
|
|
46
|
+
- uses: actions/checkout@v4
|
|
47
|
+
- uses: astral-sh/setup-uv@v5
|
|
48
|
+
- name: Live smoke (active provider)
|
|
49
|
+
run: MKLANG_LIVE=1 uv run --extra dev pytest -q -rs tests/test_live.py
|
|
50
|
+
- name: Live smoke (anthropic)
|
|
51
|
+
run: >
|
|
52
|
+
MKLANG_LIVE=1 MKLANG_LIVE_PROVIDER=anthropic
|
|
53
|
+
uv run --all-extras pytest -q -rs tests/test_live.py
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
name: docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
deploy:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: astral-sh/setup-uv@v5
|
|
17
|
+
- name: Assemble docs source
|
|
18
|
+
run: bash scripts/build-docs.sh
|
|
19
|
+
- name: Build and deploy to GitHub Pages
|
|
20
|
+
run: uvx --with mkdocs-material mkdocs gh-deploy --force --strict
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
concurrency:
|
|
11
|
+
group: release-${{ github.event.release.tag_name }}
|
|
12
|
+
cancel-in-progress: false
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
quality-and-build:
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
with:
|
|
20
|
+
ref: ${{ github.event.release.tag_name }}
|
|
21
|
+
- uses: astral-sh/setup-uv@v5
|
|
22
|
+
- name: Verify tag matches package version
|
|
23
|
+
env:
|
|
24
|
+
RELEASE_TAG: ${{ github.event.release.tag_name }}
|
|
25
|
+
run: |
|
|
26
|
+
package_version="$(uv run python -c 'import mklang; print(mklang.__version__)')"
|
|
27
|
+
test "$RELEASE_TAG" = "v$package_version"
|
|
28
|
+
- name: Lint and unit tests
|
|
29
|
+
run: |
|
|
30
|
+
uv run --extra dev ruff check src tests scripts
|
|
31
|
+
uv run --extra dev pytest -q
|
|
32
|
+
- name: Validate examples and runtime config
|
|
33
|
+
run: |
|
|
34
|
+
uv run --with check-jsonschema check-jsonschema --default-filetype yaml --schemafile schema/mklang.schema.json examples/*.mk
|
|
35
|
+
uv run --with check-jsonschema check-jsonschema --default-filetype yaml --schemafile config/runtime.schema.json config/runtime.example.yaml
|
|
36
|
+
uv run mklang check examples/*.mk
|
|
37
|
+
uv run mklang lint --strict examples/*.mk
|
|
38
|
+
uv run mklang test examples/triage.mk --script examples/triage.test.yaml
|
|
39
|
+
- name: Build docs strictly
|
|
40
|
+
run: |
|
|
41
|
+
bash scripts/build-docs.sh
|
|
42
|
+
uvx --with mkdocs-material mkdocs build --strict
|
|
43
|
+
- name: Build and validate distributions
|
|
44
|
+
run: |
|
|
45
|
+
uv build
|
|
46
|
+
uvx twine check dist/*
|
|
47
|
+
- name: Smoke-test the wheel in a clean environment
|
|
48
|
+
run: |
|
|
49
|
+
uv venv /tmp/mklang-release-smoke
|
|
50
|
+
uv pip install --python /tmp/mklang-release-smoke/bin/python dist/*.whl
|
|
51
|
+
/tmp/mklang-release-smoke/bin/python -c 'import importlib.resources, mklang; assert importlib.resources.files("mklang").joinpath("data/mklang.schema.json").is_file(); print(mklang.__version__)'
|
|
52
|
+
/tmp/mklang-release-smoke/bin/mklang --help
|
|
53
|
+
- uses: actions/upload-artifact@v4
|
|
54
|
+
with:
|
|
55
|
+
name: distributions
|
|
56
|
+
path: dist/*
|
|
57
|
+
if-no-files-found: error
|
|
58
|
+
|
|
59
|
+
live-matrix:
|
|
60
|
+
runs-on: ubuntu-latest
|
|
61
|
+
env:
|
|
62
|
+
DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
|
|
63
|
+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
|
64
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
65
|
+
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
|
66
|
+
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
|
|
67
|
+
XAI_API_KEY: ${{ secrets.XAI_API_KEY }}
|
|
68
|
+
MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }}
|
|
69
|
+
steps:
|
|
70
|
+
- uses: actions/checkout@v4
|
|
71
|
+
with:
|
|
72
|
+
ref: ${{ github.event.release.tag_name }}
|
|
73
|
+
- uses: astral-sh/setup-uv@v5
|
|
74
|
+
- name: Required DeepSeek smoke
|
|
75
|
+
run: MKLANG_LIVE=1 MKLANG_LIVE_PROVIDER=deepseek uv run --all-extras pytest -q tests/test_live.py
|
|
76
|
+
- name: Required OpenAI smoke
|
|
77
|
+
run: MKLANG_LIVE=1 MKLANG_LIVE_PROVIDER=openai uv run --all-extras pytest -q tests/test_live.py
|
|
78
|
+
- name: Cross-provider gate agreement
|
|
79
|
+
run: |
|
|
80
|
+
mkdir -p live-results
|
|
81
|
+
uv run --all-extras python scripts/gate_divergence.py \
|
|
82
|
+
--providers deepseek,openai,anthropic,google,openrouter,xai,mistral \
|
|
83
|
+
--repeats 3 \
|
|
84
|
+
--require-providers deepseek,openai \
|
|
85
|
+
--min-agreement 1.0 \
|
|
86
|
+
--jsonl live-results/runs.jsonl \
|
|
87
|
+
--summary-json live-results/summary.json
|
|
88
|
+
- uses: actions/upload-artifact@v4
|
|
89
|
+
if: always()
|
|
90
|
+
with:
|
|
91
|
+
name: live-provider-report
|
|
92
|
+
path: live-results/
|
|
93
|
+
if-no-files-found: warn
|
|
94
|
+
|
|
95
|
+
publish:
|
|
96
|
+
needs: [quality-and-build, live-matrix]
|
|
97
|
+
runs-on: ubuntu-latest
|
|
98
|
+
environment: pypi
|
|
99
|
+
permissions:
|
|
100
|
+
contents: read
|
|
101
|
+
id-token: write
|
|
102
|
+
steps:
|
|
103
|
+
- uses: actions/download-artifact@v4
|
|
104
|
+
with:
|
|
105
|
+
name: distributions
|
|
106
|
+
path: dist
|
|
107
|
+
- name: Publish distributions to PyPI
|
|
108
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
mklang-0.5.4/.gitignore
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Secrets — never commit
|
|
2
|
+
.env
|
|
3
|
+
.env.*
|
|
4
|
+
!.env.example
|
|
5
|
+
|
|
6
|
+
# Python
|
|
7
|
+
__pycache__/
|
|
8
|
+
*.py[cod]
|
|
9
|
+
*.egg-info/
|
|
10
|
+
.eggs/
|
|
11
|
+
build/
|
|
12
|
+
dist/
|
|
13
|
+
.venv/
|
|
14
|
+
venv/
|
|
15
|
+
.pytest_cache/
|
|
16
|
+
.ruff_cache/
|
|
17
|
+
.mypy_cache/
|
|
18
|
+
|
|
19
|
+
# OS / editor
|
|
20
|
+
.DS_Store
|
|
21
|
+
*.swp
|
|
22
|
+
|
|
23
|
+
# Generated docs site
|
|
24
|
+
site-src/
|
|
25
|
+
site/
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to mklang are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
5
|
+
|
|
6
|
+
**Two version lines** are tracked separately:
|
|
7
|
+
|
|
8
|
+
- **Spec version** — the language, declared per-file via the `mklang:` field
|
|
9
|
+
(currently `"0.2"`).
|
|
10
|
+
- **Package version** — the reference interpreter / tooling, SemVer in
|
|
11
|
+
`pyproject.toml` (currently `0.5.4`).
|
|
12
|
+
|
|
13
|
+
## [0.5.4] — 2026-07-16
|
|
14
|
+
|
|
15
|
+
Release-readiness pass. The language stays **0.2** and runtime semantics are
|
|
16
|
+
unchanged; this release makes the existing interpreter reproducibly distributable.
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- **Trusted Publishing release workflow.** A published GitHub Release now gates
|
|
21
|
+
publication on the full offline suite, strict docs, artifact validation, a
|
|
22
|
+
clean-wheel installation smoke, and the live provider matrix. The publish job
|
|
23
|
+
consumes the already-tested artifacts and uses short-lived PyPI OIDC credentials.
|
|
24
|
+
- **Enforceable live-matrix reporting.** `scripts/gate_divergence.py` can require
|
|
25
|
+
named providers, enforce a minimum agreement rate, write a summary artifact,
|
|
26
|
+
and distinguishes a missing-key skip from a provider/runtime failure.
|
|
27
|
+
- **Release provenance tests.** CI pins package metadata and `mklang.__version__`
|
|
28
|
+
to the same value and tests the required-provider gate offline.
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
|
|
32
|
+
- DeepSeek and OpenAI are the blocking release providers (three divergence runs,
|
|
33
|
+
agreement `1.0`). Anthropic, Google, OpenRouter, xAI, and Mistral are attempted
|
|
34
|
+
when credentials are configured and remain informational.
|
|
35
|
+
|
|
36
|
+
## [0.5.3] — 2026-07-16
|
|
37
|
+
|
|
38
|
+
Third remediation pass (Follow-up 003): closes the residue Remediation 002
|
|
39
|
+
delivered without declaring. The language stays **0.2**; no `.mk` needs changes.
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
|
|
43
|
+
- **`mklang test` — deterministic machine testing without API keys (R3-1).**
|
|
44
|
+
A new subcommand runs a machine against a script of named scenarios with a
|
|
45
|
+
**scripted LLM** (produce texts + judge picks) and scripted tools/hooks —
|
|
46
|
+
fully deterministic, no provider or key. Per-scenario PASS/FAIL with a minimal
|
|
47
|
+
diff (first mismatched key, expected vs actual); exit 0 iff all pass. The
|
|
48
|
+
scripted LLM, `hooks:`/`tools:` bindings, and expectation matcher now live once
|
|
49
|
+
in `src/mklang/scripttest.py`, shared with the conformance runner (all 21 cases
|
|
50
|
+
green through it, unchanged). Ships `examples/triage.test.yaml` (happy path +
|
|
51
|
+
KB-empty escalation). Docs: README "Test your machine without API keys",
|
|
52
|
+
`docs/patterns.md`, `conformance/README.md` cross-reference. CI runs
|
|
53
|
+
`uv run mklang test examples/triage.mk --script examples/triage.test.yaml`
|
|
54
|
+
in the unit-test job (no API keys); the same scenarios are also covered by
|
|
55
|
+
`tests/test_scripttest.py` in the normal pytest run.
|
|
56
|
+
- **Static budget-feasibility check (R3-2).** `mklang check`/`lint` now BFS the
|
|
57
|
+
shortest path (in states) from `entry` to a gate `to: END`. `budget` below it is
|
|
58
|
+
a guaranteed `budget-exhausted` halt, reported as error `budget-infeasible`;
|
|
59
|
+
`budget < shortest + 2` warns (no headroom for a single repair). Fan-out states
|
|
60
|
+
count as 1 (branch counts are data-dependent), so the check is a lower bound —
|
|
61
|
+
the message says so. SPEC §7 documents it next to the charging rule. Host
|
|
62
|
+
pre-validation only; run semantics unchanged.
|
|
63
|
+
- **Dotted second-segment lint on inline context maps (R3-3, completes F7).** When
|
|
64
|
+
a `{{root.key}}` root resolves to an inline dict literal in `context:`
|
|
65
|
+
(`ticket: {body: …}`), the second segment is now validated against the map's
|
|
66
|
+
keys, so `{{ticket.bod}}` is flagged. Skipped for state outputs and runtime
|
|
67
|
+
roots (`human`/`item`/`index`) whose shape is unknowable; deeper than segment 2
|
|
68
|
+
stays out of scope.
|
|
69
|
+
- **Schema-copy identity test (R3-4).** One test asserts `schema/mklang.schema.json`
|
|
70
|
+
and the packaged copy `src/mklang/data/mklang.schema.json` are byte-identical,
|
|
71
|
+
with a failure message naming the sync direction (repo `schema/` is the source).
|
|
72
|
+
- **ADR 0010 — LLM-assisted lint (R3-5, Proposed, design only).** An opt-in
|
|
73
|
+
`mklang lint --llm` that would generate synthetic outputs and measure
|
|
74
|
+
gate-selection stability/overlap (reusing `gate_divergence.py`) to catch
|
|
75
|
+
ambiguous prose `when` conditions. Documents cost model, determinism caveats,
|
|
76
|
+
relation to the conformance suite, and why it is out of 0.5.x. No code.
|
|
77
|
+
|
|
78
|
+
## [0.5.2] — 2026-07-16
|
|
79
|
+
|
|
80
|
+
Second remediation pass. The language stays **0.2**; no `.mk` needs changes.
|
|
81
|
+
|
|
82
|
+
### Changed (observable behavior)
|
|
83
|
+
|
|
84
|
+
- **Default judge model now follows each state's tier (F1).** Previously every
|
|
85
|
+
gate — including the highest-stakes gates on `reasoning` states (refund
|
|
86
|
+
thresholds, legal matters, human escalation) — was judged by the cheapest
|
|
87
|
+
(`fast`) model, silently deviating from SPEC §2.1. Gate judging now uses the
|
|
88
|
+
state's own effective tier by default. **This changes which model judges your
|
|
89
|
+
gates.** To restore the previous single-model behavior, set the provider
|
|
90
|
+
`judge:` key in the runtime config — it is now an explicit, opt-in *global*
|
|
91
|
+
override, no longer the default (it ships commented out in
|
|
92
|
+
`config/runtime.example.yaml`). The chosen judge model is recorded in the trace
|
|
93
|
+
as `judge_model` on every `gate_via: llm` step. Gate-divergence numbers
|
|
94
|
+
collected before this change are not comparable with those after; re-run
|
|
95
|
+
`scripts/gate_divergence.py` (now with a `--judge-tier` flag) to refresh them.
|
|
96
|
+
|
|
97
|
+
### Fixed
|
|
98
|
+
|
|
99
|
+
- **Strict judge-reply parsing (F2).** The bare-number fallback no longer grabs
|
|
100
|
+
the *first* digit anywhere in the reply (a verbose judge's "Condition 1 fails…"
|
|
101
|
+
misread as choice 1). Parse order is now: strict JSON, then a whole-reply bare
|
|
102
|
+
number, then the **last** number in the reply (models conclude with the answer).
|
|
103
|
+
The last two are traced as `judge_parse` (anomaly-adjacent, not a fallback). The
|
|
104
|
+
judge system prompt now forbids extra numbers; SPEC §5 constrains conformant
|
|
105
|
+
judges to terse instruct-style replies.
|
|
106
|
+
- **`sample` branch diversity (F3).** Each `sample: N` branch now sees its own
|
|
107
|
+
`{{index}}` (0-based), so a prompt can say "you are branch {{index}}, take a
|
|
108
|
+
different approach" (Tree-of-Thought, debate) instead of relying on temperature
|
|
109
|
+
alone. `{{index}}` is now available in both fan-out forms; `{{item}}` remains
|
|
110
|
+
`over`-only.
|
|
111
|
+
|
|
112
|
+
### Added
|
|
113
|
+
|
|
114
|
+
- **`unresolved-interpolation` lint rule (F7).** `mklang lint` flags any `{{path}}`
|
|
115
|
+
whose first segment no `context:` key / state `output:` / (inside a fan-out)
|
|
116
|
+
`item`/`index` provides — the silent-typo bug (`{{kb_answr}}` → empty string).
|
|
117
|
+
`item`/`index` referenced outside a fan-out state are flagged too. First-segment
|
|
118
|
+
only (dotted tails can't be checked statically); warning by default, error under
|
|
119
|
+
`--strict`.
|
|
120
|
+
- **`--strict` rejects unsupported `mklang:` versions (F6).** An unknown language
|
|
121
|
+
version stays a warning by default but becomes a hard error (`version-unsupported`)
|
|
122
|
+
under `mklang check --strict` / `lint --strict` / the new `run --strict`.
|
|
123
|
+
- **`0600` checkpoints (F5).** Checkpoint files (full blackboard as plaintext JSON)
|
|
124
|
+
are written owner-only. SPEC §11 now lists checkpoints as an asset and documents
|
|
125
|
+
the plaintext-at-rest surface (host-side mitigation; encryption is a v0.2
|
|
126
|
+
non-goal). The `--checkpoint` help notes the plaintext content.
|
|
127
|
+
- **Conformance coverage for hook precedence and `tool` states (F8).** The case
|
|
128
|
+
format gains scripted `hooks:` (boolean sequences) and `tools:` (list or
|
|
129
|
+
`{input-substring: output}` map) bindings, plus `expect.error_prefix`. New cases:
|
|
130
|
+
`hook-before-prose`, `hook-false-falls-through`, `tool-state-output-deposit`,
|
|
131
|
+
`tool-unknown-halts`, `fanout-sample-index` (F3), `budget-fanout-charging` (F4).
|
|
132
|
+
Suite: 15 → 21 cases.
|
|
133
|
+
- **SPEC §7** now states the fan-out step-charging rule (`max(1, len(branches))`)
|
|
134
|
+
explicitly, with the map-reduce budget-sizing implication and a worked example;
|
|
135
|
+
`docs/patterns.md` and ROADMAP note the possible v0.3 `budget`/`branch_budget`
|
|
136
|
+
split.
|
|
137
|
+
|
|
138
|
+
> **Deferred from Remediation 002, delivered in 0.5.3:** author-facing scripted
|
|
139
|
+
> testing (`mklang test`), budget-feasibility check, dotted-segment lint,
|
|
140
|
+
> schema-identity test, ADR 0010.
|
|
141
|
+
|
|
142
|
+
## [0.5.1] — 2026-07-16
|
|
143
|
+
|
|
144
|
+
### Fixed
|
|
145
|
+
|
|
146
|
+
- **Judge silent clamp** — out-of-range or 0-based `{"choice": k}` replies are
|
|
147
|
+
no longer clamped to a valid gate. `parse_choice` returns `None` for OOR;
|
|
148
|
+
adapters raise `JudgeUnparseable`; the engine does not re-clamp. Soft-fallback
|
|
149
|
+
to `otherwise` (or hard-halt `judge-unparseable`) is traced via
|
|
150
|
+
`judge_fallback` / `judge_raw` — never a mute misroute with only `gate_via: llm`.
|
|
151
|
+
- **Showcase honesty (`triage.mk`)** — `search_kb` and `send_reply` are real
|
|
152
|
+
`tool:` states (host stubs); generative states no longer claim tool use or
|
|
153
|
+
“confirm the send.” Same honesty pass on `research.mk` (no fake `web_search`).
|
|
154
|
+
- **README** — dropped “no host code required”; documented that tools/hooks are
|
|
155
|
+
host-supplied; centered conformance + portable spec as the differentiator.
|
|
156
|
+
|
|
157
|
+
### Added
|
|
158
|
+
|
|
159
|
+
- Builtins / entry points: `search_kb`, `send_reply` (deterministic stubs).
|
|
160
|
+
- **SPEC §5** — normative 1-based judge protocol; OOR = anomaly; documented
|
|
161
|
+
`JUDGE_CONTEXT_CHARS` (4000) truncation.
|
|
162
|
+
- **SPEC §11 Threat model (v0.2)** — injection surface, partial mitigations,
|
|
163
|
+
explicit non-goals (declare rather than deny).
|
|
164
|
+
- Reference produce temperature defaults documented (non-normative).
|
|
165
|
+
- **Gate-divergence experiment** — `scripts/gate_divergence.py` +
|
|
166
|
+
`docs/experiments/gate-divergence.md` (cross-provider agreement scaffold).
|
|
167
|
+
- ADR 0004 honesty note: prose-gate reliability is empirical.
|
|
168
|
+
|
|
169
|
+
### Changed
|
|
170
|
+
|
|
171
|
+
- Positioning: “writable by non-programmers” softened to prose-first / readable;
|
|
172
|
+
production needs developer judgment for tools, hooks, and untrusted inputs.
|
|
173
|
+
|
|
174
|
+
## [0.5.0] — 2026-07-16
|
|
175
|
+
|
|
176
|
+
### Added
|
|
177
|
+
|
|
178
|
+
- **Conformance suite** (ADR 0009) — `conformance/cases/*.yaml`:
|
|
179
|
+
implementation-neutral cases (machine + scripted LLM + expected outcome) that
|
|
180
|
+
pin the language semantics; any second interpreter must pass them with its own
|
|
181
|
+
runner. Reference runner in `tests/test_conformance.py` (15 cases).
|
|
182
|
+
- **`mklang lint`** — `check` plus static analysis: dead gates after
|
|
183
|
+
`otherwise`, repair-only states, outputs nobody reads (terminal/judged states
|
|
184
|
+
exempt), template roots nothing provides (`--strict` exits 1 on findings).
|
|
185
|
+
- **Provider adapters as entry-point plugins** — group `mklang.providers`
|
|
186
|
+
(factory `(ProviderConfig) -> LLM`); builtin `anthropic`, unknown names fall
|
|
187
|
+
back to the OpenAI-compatible adapter. Completes the plugin story
|
|
188
|
+
(tools/hooks/providers).
|
|
189
|
+
- **Gated live smoke tests** (`tests/test_live.py`) — opt-in via `MKLANG_LIVE=1`,
|
|
190
|
+
provider-agnostic: they run the config's `active` provider (override with
|
|
191
|
+
`MKLANG_LIVE_PROVIDER`) and skip when its key is missing. All providers,
|
|
192
|
+
Anthropic included, share the same path — no provider-specific test code.
|
|
193
|
+
- **CI + docs site** — extended GitHub Actions workflow (tests, ruff, schema +
|
|
194
|
+
semantic checks, lint, build, gated live smoke on main) and an mkdocs-material
|
|
195
|
+
site assembled from the repo's canonical markdown (`scripts/build-docs.sh`,
|
|
196
|
+
deployed to GitHub Pages).
|
|
197
|
+
|
|
198
|
+
### Changed
|
|
199
|
+
|
|
200
|
+
- SPEC termination paragraph: the END-reachability validator is enforced (it
|
|
201
|
+
already was — the prose still said "SHOULD").
|
|
202
|
+
- Packaging metadata: classifiers, corrected repository URLs, author contact.
|
|
203
|
+
|
|
204
|
+
## [0.4.0] — 2026-07-16
|
|
205
|
+
|
|
206
|
+
### Added
|
|
207
|
+
|
|
208
|
+
- **Human-in-the-loop** (ADR 0008) — with `--hitl` (requires `--checkpoint`), a
|
|
209
|
+
fired `escalate` gate **suspends** at the handler state instead of just routing:
|
|
210
|
+
the envelope records `reason: "escalated"` and `hitl: true`. Reply via
|
|
211
|
+
**`mklang resume ck.json --set human.reply="…"`** — values land in the innermost
|
|
212
|
+
frame's context so the handler can interpolate `{{human.reply}}`. Library API:
|
|
213
|
+
`run(..., escalate_suspend=True)`; mutate `frames[-1]["ctx"]` before resuming.
|
|
214
|
+
Default off — escalate-as-routing (tier cascades) is unaffected; escalate to
|
|
215
|
+
`END` and fan-out branches never suspend. Language unchanged (spec stays 0.2).
|
|
216
|
+
|
|
217
|
+
## [0.3.0] — 2026-07-16
|
|
218
|
+
|
|
219
|
+
### Added
|
|
220
|
+
|
|
221
|
+
- **Resumable runs / checkpoints** (ADR 0007) — opt-in: with `--checkpoint PATH`,
|
|
222
|
+
budget exhaustion (`budget-exhausted` / `cost-exhausted`) **suspends** instead of
|
|
223
|
+
halting and writes a JSON checkpoint (frames = blackboard + position + spend per
|
|
224
|
+
`call` level, machine sha256). New subcommand **`mklang resume <checkpoint>`**
|
|
225
|
+
(`--max-tokens`, `--machine`, `--force`) continues as if uninterrupted — golden
|
|
226
|
+
round-trip tested, nested `call` included. New `RunResult` status `"suspended"`
|
|
227
|
+
with `frames`; library API `run(..., suspendable=, resume=)`; CLI exit code 3.
|
|
228
|
+
Fan-out branches never suspend. Language unchanged (spec stays 0.2).
|
|
229
|
+
|
|
230
|
+
### Fixed
|
|
231
|
+
|
|
232
|
+
- Docs drift pass: SPEC pseudo-schema includes `hook` / `hooks:`; comparison table and
|
|
233
|
+
philosophy updated for host hooks; ADR 0002/0004 no longer refer to code-hooks as
|
|
234
|
+
future-only; ROADMAP test count aligned (~70).
|
|
235
|
+
- Stale `__version__` in `mklang/__init__.py` (was pinned at 0.2.0).
|
|
236
|
+
|
|
237
|
+
## [0.2.2] — 2026-07-16
|
|
238
|
+
|
|
239
|
+
### Added
|
|
240
|
+
|
|
241
|
+
- **Code-hook gates** — optional `hook: <name>` on a gate evaluates a host predicate
|
|
242
|
+
`(context, output) -> bool` without the LLM (ADR 0006). Top-level `hooks:`
|
|
243
|
+
declarations; CLI builtins (`auto_approve_ok`, …); example `hook_gates.mk`. Trace
|
|
244
|
+
records `gate_via: hook|llm|otherwise`.
|
|
245
|
+
- **Tool / hook plugin registry** — entry-point groups `mklang.tools` and
|
|
246
|
+
`mklang.hooks`; `load_tool_registry()` / `load_hook_registry()` merge builtins with
|
|
247
|
+
third-party plugins (later keys win). Documented in CONTRIBUTING / patterns.
|
|
248
|
+
|
|
249
|
+
### Changed
|
|
250
|
+
|
|
251
|
+
- Default runtime provider is **DeepSeek** (`active: deepseek` in
|
|
252
|
+
`config/runtime.example.yaml`); README quickstart and status aligned. Live smoke
|
|
253
|
+
re-verified on DeepSeek (`expense_approval.mk` → `done`).
|
|
254
|
+
|
|
255
|
+
## [0.2.1] — 2026-07-16
|
|
256
|
+
|
|
257
|
+
Correctness hardening and multi-provider polish on top of the v0.2 core.
|
|
258
|
+
|
|
259
|
+
### Fixed
|
|
260
|
+
|
|
261
|
+
- **Sub-machine halt propagation** — a `call` whose child halts (budget, fail, …)
|
|
262
|
+
now halts the parent as `call-failed: <child-error>` with nested `sub_trace`,
|
|
263
|
+
instead of continuing as `done` with `result=None`.
|
|
264
|
+
- **Judge sees reasoning** — when `reason: true`, the private chain-of-thought is
|
|
265
|
+
passed to `LLM.judge` (SPEC §4.5 / §6), not only recorded in the trace.
|
|
266
|
+
- **Anthropic adapter parity** — transient retry with backoff, wrap API failures as
|
|
267
|
+
`ProviderError`, apply `temperature` when thinking is off, structured JSON judge
|
|
268
|
+
(shared `parse_choice` with OpenAI-compat).
|
|
269
|
+
- **Pre-run tier validation** — `mklang run` rejects machines that need a tier missing
|
|
270
|
+
from the provider map; engine KeyError messages name the missing tier.
|
|
271
|
+
- **Shared `cost_budget` across `call`** — sub-machines inherit the remaining token
|
|
272
|
+
budget (no unbounded child spend while the parent still looks under budget).
|
|
273
|
+
- **Judge unparseable is no longer silent** — adapters raise `JudgeUnparseable`; the
|
|
274
|
+
engine soft-falls back only to an eligible `otherwise` (trace: `judge_fallback`),
|
|
275
|
+
else halts with `judge-unparseable`.
|
|
276
|
+
- **`over` missing path / wrong type** — hard error (empty list still OK per SPEC).
|
|
277
|
+
- **Fan-out branch `call` halt** — preserves `sub_trace` and child token usage.
|
|
278
|
+
- Engine exception-safety (clean `halt`, isolated fan-out branches); empty-`eligible`
|
|
279
|
+
no longer crashes; `--set` accepts JSON (lists for `over`); `load_registry` skips
|
|
280
|
+
malformed siblings; `.env` discovered from cwd.
|
|
281
|
+
|
|
282
|
+
### Added
|
|
283
|
+
|
|
284
|
+
- **Formal `tool` states** — a state can invoke a host-registered callable so tool
|
|
285
|
+
observations re-enter the context (real ReAct, not prose-simulated). Optional
|
|
286
|
+
top-level `tools:` block; built-in demo tools (`calc`, `search`).
|
|
287
|
+
- **Cost accounting** — per-step token usage in the trace, a run total, and an
|
|
288
|
+
optional `cost_budget` (halt `cost-exhausted`).
|
|
289
|
+
- **Error taxonomy** — `refusal`, `provider-error`, `call-failed`,
|
|
290
|
+
`judge-unparseable` halt reasons; typed adapter exceptions (`CallFailed`,
|
|
291
|
+
`JudgeUnparseable`, …).
|
|
292
|
+
- **Structured-output judge** — OpenAI-compatible and Anthropic adapters judge via
|
|
293
|
+
JSON `{"choice": N}` with a regex fallback path in `parse_choice`.
|
|
294
|
+
- Per-tier `params` applied to the model (effort / thinking / reasoning_effort),
|
|
295
|
+
best-effort with drop-on-error; pre-run validation in `mklang run`; richer
|
|
296
|
+
`mklang check` (dead states, unproduced `result`, catch-all warning, version
|
|
297
|
+
advisory).
|
|
298
|
+
- Schema bundled as package data; `mklang: "0.2"` field; transient-error retry.
|
|
299
|
+
- Apache-2.0 `LICENSE`, `CONTRIBUTING.md`, this changelog.
|
|
300
|
+
- Quality: golden-trace and cookbook-conformance tests; Anthropic adapter unit tests
|
|
301
|
+
(params / refusal / usage / retry / judge). **Live Anthropic e2e still deferred**
|
|
302
|
+
(no `ANTHROPIC_API_KEY` in the release environment). Live-tested path remains
|
|
303
|
+
DeepSeek.
|
|
304
|
+
- Docs: SPEC header aligned to **0.2**; ROADMAP shipped items synced.
|
|
305
|
+
|
|
306
|
+
## [0.2.0] — core v0.2 + reference interpreter
|
|
307
|
+
|
|
308
|
+
### Added
|
|
309
|
+
|
|
310
|
+
- **Language core v0.2**: `reason` (traced chain-of-thought), `accumulate`
|
|
311
|
+
(list-append), fan-out (`sample` / `over`), sub-machine `call`, top-level `result`.
|
|
312
|
+
- Patterns cookbook (`SPEC.md §10`) mapping CoT / ReAct / Reflexion / Self-consistency
|
|
313
|
+
/ Tree-of-Thought / Plan-Execute / Debate / Map-Reduce / Router / speculative cascade.
|
|
314
|
+
- **Reference interpreter** (`src/mklang/`): loader + validator, `{{}}` interpolation,
|
|
315
|
+
multi-provider LLM adapters (native Anthropic + generic OpenAI-compatible), run loop,
|
|
316
|
+
nested trace, `mklang run` / `mklang check` CLI. Live-tested on DeepSeek.
|
|
317
|
+
- JSON Schema `oneOf {generative | call}`; examples `self_consistency`, `map_reduce`
|
|
318
|
+
(+ `summarize_doc`), `react`.
|
|
319
|
+
|
|
320
|
+
## [0.1] — language definition
|
|
321
|
+
|
|
322
|
+
### Added
|
|
323
|
+
|
|
324
|
+
- The mklang concept: an LLM-driven state machine where each state has four faces
|
|
325
|
+
(`structure`, `prompt`, `execution`, `gates`), gates are transitions, and the LLM is
|
|
326
|
+
the runtime. Provider-agnostic capability tiers (`fast` / `balanced` / `reasoning`).
|
|
327
|
+
- `SPEC.md`, JSON Schema, multi-provider runtime config, examples `triage`, `research`,
|
|
328
|
+
`expense_approval`.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Contributing to mklang
|
|
2
|
+
|
|
3
|
+
Thanks for your interest. mklang is a small, opinionated project: a language spec
|
|
4
|
+
plus a reference interpreter. Keep changes coherent with the design in
|
|
5
|
+
[`SPEC.md`](./SPEC.md) and the decisions recorded in [`docs/adr/`](./docs/adr).
|
|
6
|
+
|
|
7
|
+
## Dev setup
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
uv run --extra dev pytest -q # unit + conformance (no network — MockLLM)
|
|
11
|
+
MKLANG_LIVE=1 uv run --extra dev pytest -q tests/test_live.py # opt-in live smoke (active provider; MKLANG_LIVE_PROVIDER=… to override)
|
|
12
|
+
uv run --extra dev ruff check src tests
|
|
13
|
+
uv run mklang check examples/*.mk # schema + semantic validation
|
|
14
|
+
uv run mklang lint --strict examples/*.mk # + static analysis
|
|
15
|
+
uv run mklang test examples/triage.mk --script examples/triage.test.yaml # scripted scenarios, no API keys
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`pytest` already runs the [conformance suite](./conformance/README.md)
|
|
19
|
+
(`tests/test_conformance.py` over `conformance/cases/*.yaml`). `mklang test` is
|
|
20
|
+
the same case format for **author-facing** scenario scripts next to a machine.
|
|
21
|
+
|
|
22
|
+
Secrets live in `.env` (gitignored); copy `.env.example` and add provider keys for
|
|
23
|
+
live runs. The example runtime defaults to **DeepSeek** (`DEEPSEEK_API_KEY` +
|
|
24
|
+
`active: deepseek`). Never commit a key.
|
|
25
|
+
|
|
26
|
+
### Plugin tools / hooks / providers
|
|
27
|
+
|
|
28
|
+
Third-party packages can register callables without patching core:
|
|
29
|
+
|
|
30
|
+
```toml
|
|
31
|
+
# in the plugin package's pyproject.toml
|
|
32
|
+
[project.entry-points."mklang.tools"]
|
|
33
|
+
my_search = "mypkg.tools:search"
|
|
34
|
+
|
|
35
|
+
[project.entry-points."mklang.hooks"]
|
|
36
|
+
amount_ok = "mypkg.hooks:amount_ok"
|
|
37
|
+
|
|
38
|
+
[project.entry-points."mklang.providers"]
|
|
39
|
+
my_vendor = "mypkg.providers:factory" # (ProviderConfig) -> LLM
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
- Tools: `(dict) -> str` (tool-state observations).
|
|
43
|
+
- Hooks: `(context: dict, output) -> bool` (gate predicates).
|
|
44
|
+
- Providers: factory `(ProviderConfig) -> LLM`; unknown names fall back to the
|
|
45
|
+
OpenAI-compatible adapter.
|
|
46
|
+
|
|
47
|
+
The CLI loads `load_tool_registry()` / `load_hook_registry()` / the provider
|
|
48
|
+
registry (builtins + entry points). Library users may still pass explicit
|
|
49
|
+
`tools=` / `hooks=` to `run()`.
|
|
50
|
+
|
|
51
|
+
## The change checklist
|
|
52
|
+
|
|
53
|
+
A change to the **language** must land as a coherent set — in this order:
|
|
54
|
+
|
|
55
|
+
1. **`SPEC.md`** — describe the behavior (the spec is the source of truth).
|
|
56
|
+
2. **`schema/mklang.schema.json`** — update the structural schema, then re-bundle the
|
|
57
|
+
package copy: `cp schema/mklang.schema.json src/mklang/data/mklang.schema.json`
|
|
58
|
+
(a test asserts the two stay identical).
|
|
59
|
+
3. **Interpreter** — `src/mklang/` (model, loader/validator, engine, adapters, CLI).
|
|
60
|
+
4. **Conformance** — if the change touches language semantics (SPEC §5–§7), add or
|
|
61
|
+
update a case under `conformance/cases/` (ADR 0009).
|
|
62
|
+
5. **Examples** — add/adjust a machine in `examples/` that exercises the feature;
|
|
63
|
+
where gate paths matter, a sibling `*.test.yaml` for `mklang test` is welcome.
|
|
64
|
+
6. **Tests** — deterministic coverage with `MockLLM` in `tests/`; keep `ruff` clean.
|
|
65
|
+
7. **Docs** — `README.md`, `docs/patterns.md`, `CHANGELOG.md`, and `ROADMAP.md`.
|
|
66
|
+
|
|
67
|
+
A change to the **interpreter only** (no language change) skips steps 1–2 and 4
|
|
68
|
+
unless the host tooling surface needs a new conformance-facing scripted binding.
|
|
69
|
+
|
|
70
|
+
## Design decisions (ADRs)
|
|
71
|
+
|
|
72
|
+
Non-trivial or contentious decisions get a short ADR in `docs/adr/NNNN-title.md`
|
|
73
|
+
(Context / Decision / Consequences). See the existing ADRs in `docs/adr/`
|
|
74
|
+
(0001–0011) for the format. Reference the ADR in your PR.
|
|
75
|
+
|
|
76
|
+
## Versioning
|
|
77
|
+
|
|
78
|
+
- **Spec version** (`mklang:` field) changes when the _language_ changes.
|
|
79
|
+
- **Package version** (`pyproject.toml`, SemVer) changes when the _interpreter/tooling_
|
|
80
|
+
changes. Record both in `CHANGELOG.md`.
|
|
81
|
+
|
|
82
|
+
## Releases
|
|
83
|
+
|
|
84
|
+
Releases are provenance-bound: update `pyproject.toml` and `mklang.__version__`
|
|
85
|
+
together, record the package release in `CHANGELOG.md`, and publish a GitHub
|
|
86
|
+
Release whose tag is exactly `v<package-version>`. The release workflow runs the
|
|
87
|
+
offline suite, strict docs/package checks, and the required live-provider gate;
|
|
88
|
+
only its previously tested artifacts reach PyPI through the protected `pypi`
|
|
89
|
+
environment and Trusted Publishing. Do not upload a locally rebuilt artifact for
|
|
90
|
+
an existing tag.
|
|
91
|
+
|
|
92
|
+
## Non-goals (don't propose these)
|
|
93
|
+
|
|
94
|
+
Pinning a concrete provider/model inside a `.mk` — machines route by capability tier
|
|
95
|
+
only (ADR 0003). See `SPEC.md §9` for the current non-goals.
|