iil-adrfw 0.2.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 (56) hide show
  1. iil_adrfw-0.2.0/.github/workflows/ci.yml +32 -0
  2. iil_adrfw-0.2.0/.github/workflows/publish.yml +40 -0
  3. iil_adrfw-0.2.0/.gitignore +5 -0
  4. iil_adrfw-0.2.0/.windsurf/rules/project-facts.md +63 -0
  5. iil_adrfw-0.2.0/PKG-INFO +141 -0
  6. iil_adrfw-0.2.0/README.md +120 -0
  7. iil_adrfw-0.2.0/SCHEMA_V3_CHANGELOG.md +146 -0
  8. iil_adrfw-0.2.0/SCHEMA_V3_SPEC.md +473 -0
  9. iil_adrfw-0.2.0/SPRINT_PLAN.md +171 -0
  10. iil_adrfw-0.2.0/docs/ADR_SHADOW_PR_DEFERRED.md +67 -0
  11. iil_adrfw-0.2.0/docs/CASCADE_PIPELINE.md +172 -0
  12. iil_adrfw-0.2.0/examples/ADR-099-multi-tenancy.md +121 -0
  13. iil_adrfw-0.2.0/examples/ADR-099-multi-tenancy.rules.yaml +165 -0
  14. iil_adrfw-0.2.0/examples/ADR-188-unified-vector-store.md +310 -0
  15. iil_adrfw-0.2.0/examples/ADR-188-unified-vector-store.rules.yaml +245 -0
  16. iil_adrfw-0.2.0/examples/ADR-9088-hypothetical-v10.md +28 -0
  17. iil_adrfw-0.2.0/examples/ADR-9088-hypothetical-v10.rules.yaml +30 -0
  18. iil_adrfw-0.2.0/examples/_test_adrs_v3/ADR-580.md +21 -0
  19. iil_adrfw-0.2.0/examples/_test_diff_left/ADR-099-multi-tenancy.md +121 -0
  20. iil_adrfw-0.2.0/examples/_test_diff_left/ADR-099-multi-tenancy.rules.yaml +165 -0
  21. iil_adrfw-0.2.0/examples/_test_diff_right/ADR-099-multi-tenancy.md +121 -0
  22. iil_adrfw-0.2.0/examples/_test_diff_right/ADR-099-multi-tenancy.rules.yaml +165 -0
  23. iil_adrfw-0.2.0/examples/django_polyrepo/apps/billing/models.py +29 -0
  24. iil_adrfw-0.2.0/examples/polyrepo_workspace/bfagent/apps/stories/models.py +13 -0
  25. iil_adrfw-0.2.0/examples/polyrepo_workspace/mcp-hub/rag_mcp/db/schema.sql +33 -0
  26. iil_adrfw-0.2.0/examples/polyrepo_workspace/meiki-hub/apps/cases/models.py +14 -0
  27. iil_adrfw-0.2.0/examples/test_e2e.py +122 -0
  28. iil_adrfw-0.2.0/examples/test_e2e_cli.py +210 -0
  29. iil_adrfw-0.2.0/examples/test_e2e_cross_repo.py +174 -0
  30. iil_adrfw-0.2.0/examples/test_e2e_diff.py +231 -0
  31. iil_adrfw-0.2.0/examples/test_e2e_narrate.py +250 -0
  32. iil_adrfw-0.2.0/examples/test_e2e_propose.py +328 -0
  33. iil_adrfw-0.2.0/examples/test_e2e_query_audit.py +254 -0
  34. iil_adrfw-0.2.0/examples/test_e2e_regression.py +250 -0
  35. iil_adrfw-0.2.0/examples/test_e2e_schema_v3.py +466 -0
  36. iil_adrfw-0.2.0/examples/test_e2e_v11.py +206 -0
  37. iil_adrfw-0.2.0/pyproject.toml +47 -0
  38. iil_adrfw-0.2.0/schemas/adr_frontmatter.schema.json +489 -0
  39. iil_adrfw-0.2.0/schemas/adr_rules.schema.json +284 -0
  40. iil_adrfw-0.2.0/schemas/constitution.schema.json +181 -0
  41. iil_adrfw-0.2.0/src/iil_adrfw/__init__.py +0 -0
  42. iil_adrfw-0.2.0/src/iil_adrfw/audit/__init__.py +431 -0
  43. iil_adrfw-0.2.0/src/iil_adrfw/checkers/__init__.py +43 -0
  44. iil_adrfw-0.2.0/src/iil_adrfw/checkers/python_ast.py +185 -0
  45. iil_adrfw-0.2.0/src/iil_adrfw/cli.py +421 -0
  46. iil_adrfw-0.2.0/src/iil_adrfw/cross_repo/__init__.py +369 -0
  47. iil_adrfw-0.2.0/src/iil_adrfw/diff/__init__.py +315 -0
  48. iil_adrfw-0.2.0/src/iil_adrfw/domain/__init__.py +254 -0
  49. iil_adrfw-0.2.0/src/iil_adrfw/domain/cross_repo.py +88 -0
  50. iil_adrfw-0.2.0/src/iil_adrfw/graph/__init__.py +254 -0
  51. iil_adrfw-0.2.0/src/iil_adrfw/narrate/__init__.py +309 -0
  52. iil_adrfw-0.2.0/src/iil_adrfw/persistence/__init__.py +614 -0
  53. iil_adrfw-0.2.0/src/iil_adrfw/propose/__init__.py +440 -0
  54. iil_adrfw-0.2.0/src/iil_adrfw/server.py +915 -0
  55. iil_adrfw-0.2.0/validate_example.py +103 -0
  56. iil_adrfw-0.2.0/validate_schemas.py +14 -0
@@ -0,0 +1,32 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ lint:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ - uses: actions/setup-python@v5
15
+ with:
16
+ python-version: "3.12"
17
+ - run: pip install ruff
18
+ - run: ruff check src/
19
+
20
+ test:
21
+ runs-on: ubuntu-latest
22
+ needs: lint
23
+ steps:
24
+ - uses: actions/checkout@v4
25
+ - uses: actions/setup-python@v5
26
+ with:
27
+ python-version: "3.12"
28
+ - run: pip install -e ".[dev]"
29
+ - run: python examples/test_e2e_schema_v3.py
30
+ - run: python examples/test_e2e.py
31
+ - run: python examples/test_e2e_v11.py
32
+ - run: python examples/test_e2e_regression.py
@@ -0,0 +1,40 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ dry_run:
7
+ description: "Dry run (skip upload)"
8
+ required: false
9
+ default: "false"
10
+ type: choice
11
+ options:
12
+ - "true"
13
+ - "false"
14
+
15
+ jobs:
16
+ publish:
17
+ runs-on: ubuntu-latest
18
+ permissions:
19
+ contents: read
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+ - uses: actions/setup-python@v5
23
+ with:
24
+ python-version: "3.12"
25
+
26
+ - name: Install build tools
27
+ run: pip install hatchling twine
28
+
29
+ - name: Build
30
+ run: python -m hatchling build
31
+
32
+ - name: Check dist
33
+ run: twine check dist/*
34
+
35
+ - name: Upload to PyPI
36
+ if: inputs.dry_run == 'false'
37
+ env:
38
+ TWINE_USERNAME: __token__
39
+ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
40
+ run: twine upload dist/*
@@ -0,0 +1,5 @@
1
+ __pycache__/
2
+ *.pyc
3
+ *.egg-info/
4
+ dist/
5
+ .eggs/
@@ -0,0 +1,63 @@
1
+ # Project Facts: iil-adrfw
2
+
3
+ ## Meta
4
+
5
+ - **Type**: `python-library`
6
+ - **GitHub**: `https://github.com/achimdehnert/iil-adrfw`
7
+ - **Branch**: `main`
8
+ - **PyPI**: `iil-adrfw`
9
+ - **Build**: `hatchling` (`python3 -m hatchling build`)
10
+
11
+ ## Package
12
+
13
+ | Field | Value |
14
+ |-------|-------|
15
+ | **Name** | `iil-adrfw` |
16
+ | **Version** | `0.2.0` |
17
+ | **Python** | `>=3.12` |
18
+ | **Entry points** | `iil-adrfw` (CLI), `iil-adrfw-mcp` (FastMCP server) |
19
+ | **Source layout** | `src/iil_adrfw/` |
20
+
21
+ ## Key modules
22
+
23
+ | Module | Purpose |
24
+ |--------|---------|
25
+ | `persistence/` | Loader: normalize → validate → construct ADR objects |
26
+ | `domain/` | ADR dataclass, Status enum, cross-repo models |
27
+ | `graph/` | Constitution graph (supersession, dependencies) |
28
+ | `audit/` | Staleness, implementation evidence checks |
29
+ | `checkers/` | AST-based Python code checkers |
30
+ | `cli.py` | CLI entry point |
31
+ | `server.py` | FastMCP MCP server |
32
+
33
+ ## Schemas
34
+
35
+ - `schemas/adr_frontmatter.schema.json` — ADR frontmatter (Schema v3)
36
+ - `schemas/adr_rules.schema.json` — ADR executable rules
37
+ - `schemas/constitution.schema.json` — Constitution graph
38
+
39
+ ## Tests
40
+
41
+ ```bash
42
+ python3 examples/test_e2e_schema_v3.py # 22 cases — Schema v3
43
+ python3 examples/test_e2e.py # Core tests
44
+ python3 examples/test_e2e_v11.py # v1.1 format tests
45
+ python3 examples/test_e2e_regression.py # Regression tests
46
+ ```
47
+
48
+ ## CI/CD
49
+
50
+ - `.github/workflows/ci.yml` — ruff lint + test suites on push/PR
51
+ - `.github/workflows/publish.yml` — hatchling build + twine upload (workflow_dispatch)
52
+
53
+ ## MCP Prefix (WSL Standard)
54
+
55
+ | Prefix | Server |
56
+ |--------|--------|
57
+ | `mcp0_` | deployment-mcp |
58
+ | `mcp1_` | github |
59
+ | `mcp2_` | orchestrator |
60
+ | `mcp3_` | outline-knowledge |
61
+ | `mcp4_` | paperless-docs |
62
+ | `mcp5_` | platform-context |
63
+ | `mcp6_` | playwright |
@@ -0,0 +1,141 @@
1
+ Metadata-Version: 2.4
2
+ Name: iil-adrfw
3
+ Version: 0.2.0
4
+ Summary: Architectural Decision Record framework with bi-temporal validity, AST checkers, and FastMCP integration
5
+ Project-URL: Homepage, https://github.com/achimdehnert/iil-adrfw
6
+ Project-URL: Repository, https://github.com/achimdehnert/iil-adrfw
7
+ Author-email: Achim Dehnert <achim@iil.gmbh>
8
+ Requires-Python: >=3.12
9
+ Requires-Dist: fastmcp>=3.0
10
+ Requires-Dist: jsonschema>=4.23
11
+ Requires-Dist: libcst>=1.5
12
+ Requires-Dist: pydantic>=2.8
13
+ Requires-Dist: pyyaml>=6.0
14
+ Requires-Dist: referencing>=0.35
15
+ Provides-Extra: dev
16
+ Requires-Dist: mypy>=1.11; extra == 'dev'
17
+ Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
18
+ Requires-Dist: pytest>=8.0; extra == 'dev'
19
+ Requires-Dist: ruff>=0.7; extra == 'dev'
20
+ Description-Content-Type: text/markdown
21
+
22
+ # iil-adrfw
23
+
24
+ **ADR Framework for the IIL Platform** — schema validation, loader normalization, constitution graph, audit tooling.
25
+
26
+ [![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/downloads/)
27
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
28
+
29
+ ## What it does
30
+
31
+ - **Schema v3** — strict JSON Schema for ADR frontmatter with `additionalProperties: false`
32
+ - **Phase 1 Normalizer** — 15+ field aliases, status normalization, type coercion, reference extraction
33
+ - **Phase 2 Validator** — jsonschema validation against `adr_frontmatter.schema.json`
34
+ - **Phase 3 Domain** — typed `ADR` dataclass with `Status` enum, temporal fields, relations
35
+ - **Constitution Graph** — cross-ADR dependency/supersession graph with cycle detection
36
+ - **Audit** — staleness checks, implementation evidence verification, drift detection
37
+ - **CLI + MCP Server** — `iil-adrfw` CLI and `iil-adrfw-mcp` FastMCP server
38
+
39
+ ## Real-world validation
40
+
41
+ Tested against **156 real platform ADRs**:
42
+
43
+ | Mode | Result |
44
+ |---|---|
45
+ | Strict load (validate=True) | **152/156 (97.4%)** |
46
+ | Unit tests | **22/22 green** |
47
+ | Remaining failures | 4x broken YAML (unfixable) |
48
+
49
+ ## Installation
50
+
51
+ ```bash
52
+ pip install -e .
53
+
54
+ # With dev dependencies:
55
+ pip install -e ".[dev]"
56
+ ```
57
+
58
+ ## Usage
59
+
60
+ ### Python API
61
+
62
+ ```python
63
+ from pathlib import Path
64
+ from iil_adrfw.persistence import load_adr, load_adrs
65
+
66
+ # Load single ADR
67
+ adr = load_adr(Path("docs/adr/ADR-099.md"), Path("schemas/"))
68
+
69
+ # Load all ADRs in directory
70
+ adrs = load_adrs(Path("docs/adr/"), Path("schemas/"))
71
+
72
+ # Diagnosis mode (skip normalization)
73
+ raw_adr = load_adr(path, schemas, raw=True)
74
+ ```
75
+
76
+ ### CLI
77
+
78
+ ```bash
79
+ # Validate ADRs
80
+ iil-adrfw validate docs/adr/ --schemas schemas/
81
+
82
+ # Audit for staleness, missing evidence
83
+ iil-adrfw audit docs/adr/ --schemas schemas/
84
+ ```
85
+
86
+ ### MCP Server
87
+
88
+ ```bash
89
+ iil-adrfw-mcp
90
+ ```
91
+
92
+ ## Schema v3 highlights
93
+
94
+ - **5 new fields**: `updated`, `version`, `review_status`, `owner`, `implementation_done_when`
95
+ - **3 removed fields**: `glossary`, `review_cadence`, `next_review_date`
96
+ - **Status enum**: `{draft, proposed, accepted, deprecated, superseded, rejected, experimental}`
97
+ - **Implementation status**: `{none, planned, in_progress, partial, implemented, complete, verified, rolled_back}`
98
+
99
+ ### Loader normalizations (Phase 1)
100
+
101
+ | Step | What |
102
+ |---|---|
103
+ | C.1 | 12 field aliases (`date`→`decision_date`, `author`→`owner`, etc.) |
104
+ | C.2 | Status normalization (case, suffixes) |
105
+ | C.3 | Scalar-to-list auto-wrapping |
106
+ | C.4 | Reference field normalization (ADR-NNN extraction) |
107
+ | C.5 | ID inference, title inference, domains default, deciders default |
108
+ | C.6 | Amended-format normalization |
109
+ | C.7 | `implemented` field → `implementation_status` mapping |
110
+ | C.8 | Strip unknown properties |
111
+
112
+ See [SCHEMA_V3_SPEC.md](SCHEMA_V3_SPEC.md) for full specification.
113
+
114
+ ## Running tests
115
+
116
+ ```bash
117
+ python examples/test_e2e_schema_v3.py # Schema v3 tests (22 cases)
118
+ python examples/test_e2e.py # Core tests
119
+ python examples/test_e2e_v11.py # v1.1 format tests
120
+ ```
121
+
122
+ ## Project structure
123
+
124
+ ```
125
+ iil-adrfw/
126
+ ├── schemas/ # JSON Schema files
127
+ │ ├── adr_frontmatter.schema.json
128
+ │ ├── adr_rules.schema.json
129
+ │ └── constitution.schema.json
130
+ ├── src/iil_adrfw/
131
+ │ ├── persistence/ # Loader (normalize + validate + construct)
132
+ │ ├── domain/ # ADR dataclass, Status enum
133
+ │ ├── graph/ # Constitution graph
134
+ │ ├── audit/ # Staleness, evidence checks
135
+ │ ├── checkers/ # AST-based code checkers
136
+ │ ├── cli.py # CLI entry point
137
+ │ └── server.py # FastMCP server
138
+ ├── examples/ # Example ADRs + test suites
139
+ ├── SCHEMA_V3_SPEC.md # Full v3 specification
140
+ └── SCHEMA_V3_CHANGELOG.md # Changelog for downstream consumers
141
+ ```
@@ -0,0 +1,120 @@
1
+ # iil-adrfw
2
+
3
+ **ADR Framework for the IIL Platform** — schema validation, loader normalization, constitution graph, audit tooling.
4
+
5
+ [![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/downloads/)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
7
+
8
+ ## What it does
9
+
10
+ - **Schema v3** — strict JSON Schema for ADR frontmatter with `additionalProperties: false`
11
+ - **Phase 1 Normalizer** — 15+ field aliases, status normalization, type coercion, reference extraction
12
+ - **Phase 2 Validator** — jsonschema validation against `adr_frontmatter.schema.json`
13
+ - **Phase 3 Domain** — typed `ADR` dataclass with `Status` enum, temporal fields, relations
14
+ - **Constitution Graph** — cross-ADR dependency/supersession graph with cycle detection
15
+ - **Audit** — staleness checks, implementation evidence verification, drift detection
16
+ - **CLI + MCP Server** — `iil-adrfw` CLI and `iil-adrfw-mcp` FastMCP server
17
+
18
+ ## Real-world validation
19
+
20
+ Tested against **156 real platform ADRs**:
21
+
22
+ | Mode | Result |
23
+ |---|---|
24
+ | Strict load (validate=True) | **152/156 (97.4%)** |
25
+ | Unit tests | **22/22 green** |
26
+ | Remaining failures | 4x broken YAML (unfixable) |
27
+
28
+ ## Installation
29
+
30
+ ```bash
31
+ pip install -e .
32
+
33
+ # With dev dependencies:
34
+ pip install -e ".[dev]"
35
+ ```
36
+
37
+ ## Usage
38
+
39
+ ### Python API
40
+
41
+ ```python
42
+ from pathlib import Path
43
+ from iil_adrfw.persistence import load_adr, load_adrs
44
+
45
+ # Load single ADR
46
+ adr = load_adr(Path("docs/adr/ADR-099.md"), Path("schemas/"))
47
+
48
+ # Load all ADRs in directory
49
+ adrs = load_adrs(Path("docs/adr/"), Path("schemas/"))
50
+
51
+ # Diagnosis mode (skip normalization)
52
+ raw_adr = load_adr(path, schemas, raw=True)
53
+ ```
54
+
55
+ ### CLI
56
+
57
+ ```bash
58
+ # Validate ADRs
59
+ iil-adrfw validate docs/adr/ --schemas schemas/
60
+
61
+ # Audit for staleness, missing evidence
62
+ iil-adrfw audit docs/adr/ --schemas schemas/
63
+ ```
64
+
65
+ ### MCP Server
66
+
67
+ ```bash
68
+ iil-adrfw-mcp
69
+ ```
70
+
71
+ ## Schema v3 highlights
72
+
73
+ - **5 new fields**: `updated`, `version`, `review_status`, `owner`, `implementation_done_when`
74
+ - **3 removed fields**: `glossary`, `review_cadence`, `next_review_date`
75
+ - **Status enum**: `{draft, proposed, accepted, deprecated, superseded, rejected, experimental}`
76
+ - **Implementation status**: `{none, planned, in_progress, partial, implemented, complete, verified, rolled_back}`
77
+
78
+ ### Loader normalizations (Phase 1)
79
+
80
+ | Step | What |
81
+ |---|---|
82
+ | C.1 | 12 field aliases (`date`→`decision_date`, `author`→`owner`, etc.) |
83
+ | C.2 | Status normalization (case, suffixes) |
84
+ | C.3 | Scalar-to-list auto-wrapping |
85
+ | C.4 | Reference field normalization (ADR-NNN extraction) |
86
+ | C.5 | ID inference, title inference, domains default, deciders default |
87
+ | C.6 | Amended-format normalization |
88
+ | C.7 | `implemented` field → `implementation_status` mapping |
89
+ | C.8 | Strip unknown properties |
90
+
91
+ See [SCHEMA_V3_SPEC.md](SCHEMA_V3_SPEC.md) for full specification.
92
+
93
+ ## Running tests
94
+
95
+ ```bash
96
+ python examples/test_e2e_schema_v3.py # Schema v3 tests (22 cases)
97
+ python examples/test_e2e.py # Core tests
98
+ python examples/test_e2e_v11.py # v1.1 format tests
99
+ ```
100
+
101
+ ## Project structure
102
+
103
+ ```
104
+ iil-adrfw/
105
+ ├── schemas/ # JSON Schema files
106
+ │ ├── adr_frontmatter.schema.json
107
+ │ ├── adr_rules.schema.json
108
+ │ └── constitution.schema.json
109
+ ├── src/iil_adrfw/
110
+ │ ├── persistence/ # Loader (normalize + validate + construct)
111
+ │ ├── domain/ # ADR dataclass, Status enum
112
+ │ ├── graph/ # Constitution graph
113
+ │ ├── audit/ # Staleness, evidence checks
114
+ │ ├── checkers/ # AST-based code checkers
115
+ │ ├── cli.py # CLI entry point
116
+ │ └── server.py # FastMCP server
117
+ ├── examples/ # Example ADRs + test suites
118
+ ├── SCHEMA_V3_SPEC.md # Full v3 specification
119
+ └── SCHEMA_V3_CHANGELOG.md # Changelog for downstream consumers
120
+ ```
@@ -0,0 +1,146 @@
1
+ # Schema v3 — Changelog
2
+
3
+ **Date:** 2026-05-08
4
+ **Basis:** Real-world data on 156 platform ADRs (Cascade run)
5
+ **Goal:** Lift strict-load rate from 65% (102/156) to ≥97% via schema additions
6
+ and Phase 1 normalization.
7
+
8
+ ---
9
+
10
+ ## Schema additions (5 fields truly new, all optional)
11
+
12
+ | Field | Type | Real-world frequency |
13
+ |---|---|---|
14
+ | `updated` | string (date) | 5% |
15
+ | `version` | integer ≥1 | 3% |
16
+ | `review_status` | enum (`pending`, `in_review`, `approved`, `rejected`, `stale`) | 3% |
17
+ | `owner` | string | 1% direct + receives `author` alias mappings |
18
+ | `implementation_done_when` | string | 1% |
19
+
20
+ Note: `implementation_evidence`, `related`, `amends` were already in v2 schema.
21
+
22
+ ## Schema removals (3 fields, 0% real-world use)
23
+
24
+ - `glossary` — content belongs in markdown body (see ADR-188 v1.1 example)
25
+ - `review_cadence` — replaced functionally by `staleness_months`
26
+ - `next_review_date` — was a derived field, never populated
27
+
28
+ ## Schema unchanged (critical for backwards compatibility)
29
+
30
+ - **Status enum** — `{draft, proposed, accepted, deprecated, superseded, rejected, experimental}` — `draft` used by 2 ADRs
31
+ - **`implementation_status` enum** — `{none, planned, in_progress, partial, implemented, complete, verified, rolled_back}` — `implemented` used by 81 ADRs, `verified` by 1
32
+ - **`additionalProperties: false`** — strict mode retained for governance
33
+ - **All v1.1 strategic fields** — `decision_drivers`, `open_questions`, `consumers`, `deprecation_timeline`, `spof_mitigation`, `per_repo_status` — kept as the v1.1 reference format
34
+
35
+ ---
36
+
37
+ ## Loader behavior (Phase 1 normalization)
38
+
39
+ All Phase 1 transforms run by default. To bypass them for diagnostic purposes:
40
+ `load_adr(path, schemas, raw=True)`.
41
+
42
+ ### Field aliases (target wins if both present)
43
+
44
+ | Source | Target |
45
+ |---|---|
46
+ | `decision-makers` | `deciders` |
47
+ | `superseded-by` | `superseded_by` |
48
+ | `depends-on` | `depends_on` |
49
+ | `conflicts-with` | `conflicts_with` |
50
+ | `relates_to`, `relates-to`, `related_adrs` | `related` |
51
+ | `last_verified` | `last_reviewed` |
52
+ | `adr_id` | `id` |
53
+ | `review` | `review_status` |
54
+ | `author` | `owner` |
55
+
56
+ ### Stripped fields (low-frequency tool-noise)
57
+
58
+ `reviewed-by`, `reviewed`, `repos`, `nav_order`, `parent`, `commit`,
59
+ `product_name`, `supersedes_check`, `superseded_by_planned`
60
+
61
+ ### Status normalization
62
+
63
+ Applied to the `status` value:
64
+ - Case-insensitive: `Accepted` → `accepted`
65
+ - Strip quotes: `'"accepted"'` → `accepted`
66
+ - Strip version suffix: `accepted (v2)` → `accepted`
67
+ - Strip revision suffix: `Accepted (Revision v1.1)` → `accepted`
68
+
69
+ Unknown values pass through unchanged so the schema enum check produces a
70
+ clear error message.
71
+
72
+ ### Auto-wrap scalar-to-list (only on observed-as-scalar fields)
73
+
74
+ `deciders`, `consulted`, `informed`, `domains`, `amends` — string value gets
75
+ wrapped to `[value]`. Sentinel values `–`, `-`, `—`, `n/a`, `none`, `""` →
76
+ empty list `[]`.
77
+
78
+ ### Reference-field normalization
79
+
80
+ For `supersedes`, `superseded_by`, `depends_on`, `consolidates`,
81
+ `conflicts_with`, `informs`, `related`, `amends`: strings have all
82
+ `ADR-NNN` tokens extracted into a list.
83
+
84
+ ### `implemented` field handling
85
+
86
+ | Input | Action |
87
+ |---|---|
88
+ | `implemented: true` | set `implementation_status: implemented` |
89
+ | `implemented: "2026-03-15"` (date) | set `implementation_status: implemented` AND `updated: "2026-03-15"` |
90
+ | `implemented: false` | leave `implementation_status` unchanged |
91
+
92
+ The field is always removed from the frontmatter after processing.
93
+
94
+ ### Amended-format normalization
95
+
96
+ Legacy form `amended: "2026-05-08"` (plain date) is recognized:
97
+ - The date is moved to `last_reviewed` (if not already set)
98
+ - The malformed `amended` field is dropped
99
+
100
+ Proper `amended` lists (with `version`, `at`, `by`, `summary`) pass through unchanged.
101
+
102
+ ### Title inference
103
+
104
+ If `title` is missing or empty:
105
+ 1. Try first H1 in body: `# ADR-NNN — <title>` or `# ADR-NNN: <title>`
106
+ 2. Fallback: filename slug `ADR-098-multi-tenant-cron.md` → `"multi tenant cron"`
107
+ 3. Final fallback: `"(untitled ADR)"`
108
+
109
+ ---
110
+
111
+ ## Migration impact
112
+
113
+ - **Existing ADR files:** No file rewrites needed. Phase 1 normalization handles
114
+ legacy formats transparently.
115
+ - **Existing tests:** All 41 prior tests remain green.
116
+ - **adr-doctor compatibility:** unchanged — both tools share normalization
117
+ semantics but operate independently.
118
+
119
+ ## API change: `raw` parameter
120
+
121
+ ```python
122
+ # Before v3 (still supported):
123
+ load_adr(path, schemas, validate=True)
124
+ load_adr(path, schemas, validate=False)
125
+
126
+ # New in v3 — diagnostic mode:
127
+ load_adr(path, schemas, validate=False, raw=True) # see frontmatter as-written
128
+ ```
129
+
130
+ `raw=True` skips Phase 1 entirely. Useful for finding ADRs that depend on
131
+ tolerance and could be migrated to canonical form.
132
+
133
+ ## Test additions
134
+
135
+ - `test_e2e_schema_v3.py` — 22 tests covering all C.1–C.8 transforms,
136
+ field renames, schema additions/removals, and combined real-world scenarios.
137
+
138
+ ## Out of scope for v3
139
+
140
+ | Topic | Why deferred |
141
+ |---|---|
142
+ | `nav_order`, `parent`, `commit`, `product_name` as schema fields | <2% use, stripped instead |
143
+ | `reviewed-by` as own field | 1% use, semantically distinct from `consulted`, stripped |
144
+ | Audit auditors using new fields (`implementation_evidence_check`, `circular_dependency`, `orphaned_adr`) | Iter 3 — separate increment |
145
+ | Schema `$id` version bump | Cosmetic, deferred to first breaking change |
146
+ | Status aliases (`done`, `active`, `wip`) | Antizipativ, nicht in echten Daten beobachtet — log-warning instead |