diff --git a/README.md b/README.md index 216ff5e..58cd254 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ the path containing your ADR files relative to the `mkdocs.yml` file. {{ adr_summary(adr_path="docs/adr", adr_style="nygard") }} ``` -`adr_style` can be `nygard` or `MADR3` +`adr_style` can be `nygard`, `MADR2`, `MADR3`, or `MADR4` ## More customization @@ -82,30 +82,32 @@ class ADRDocument: There are some differences in what metadata is available when using different formats: -| | Nygard | MADR3 | MADR2 | -|-----------|--------|-------|-------| -| file_path | ✅︎ | ✅︎ | ✅︎ | -| title | ✅︎ | ✅︎ | ✅︎ | -| date | ✅︎ | ✅︎ | ✅︎ | -| status | ⚠ | ✅︎ | ✅︎ | -| statuses | ✅︎ | ⚠ | ⚠ | -| deciders | ❌ | ✅︎ | ✅︎ | -| consulted | ❌ | ✅︎ | ❌ | -| informed | ❌ | ✅︎ | ❌ | +| | Nygard | MADR4 | MADR3 | MADR2 | +|-----------|--------|-------|-------|-------| +| file_path | ✅︎ | ✅︎ | ✅︎ | ✅︎ | +| title | ✅︎ | ✅︎ | ✅︎ | ✅︎ | +| date | ✅︎ | ✅︎ | ✅︎ | ✅︎ | +| status | ⚠ | ✅︎ | ✅︎ | ✅︎ | +| statuses | ✅︎ | ⚠ | ⚠ | ⚠ | +| deciders | ❌ | ✅︎ | ✅︎ | ✅︎ | +| consulted | ❌ | ✅︎ | ✅︎ | ❌ | +| informed | ❌ | ✅︎ | ✅︎ | ❌ | * **Nygard format** * `status` is the last item `statuses`. (I don't believe we should use multiple statuses, however `adr-tools` allows it) * `deciders`, `consulted` and `informed` are not supported by the format -* **MADR2** and **MADR3** +* **MADR2**, **MADR3**, and **MADR4** * I wasn't able to find an automated tool supporting superseding documents. By looking at the template it looks like there's a single status. `statuses` will return a list with a single status. + * MADR4 uses `decision-makers` instead of `deciders` in the YAML frontmatter, but the parser maps it to the `deciders` field in the document model ## Supported ADR formats The supported ADR formats are: * `nygard` format, it is recommended to use [adr-tools](https://github.com/npryce/adr-tools) to manage the directory +* `MADR` [version 4](https://github.com/adr/madr/blob/4.0.0/template/adr-template.md) * `MADR` [version 3](https://github.com/adr/madr/blob/3.0.0/template/adr-template.md) * `MADR` [version 2](https://github.com/adr/madr/blob/2.1.2/template/template.md) diff --git a/docs/adr_madr4_example/.markdownlint b/docs/adr_madr4_example/.markdownlint new file mode 100644 index 0000000..29a8529 --- /dev/null +++ b/docs/adr_madr4_example/.markdownlint @@ -0,0 +1,20 @@ +# source: https://github.com/adr/madr/blob/3.0.0/template/.markdownlint.yml +default: true + +# Allow arbitrary line length +# +# Reason: We apply the one-sentence-per-line rule. A sentence may get longer than 80 characters, especially if links are contained. +# +# Details: https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md013---line-length +MD013: false + +# Allow duplicate headings +# +# Reasons: +# +# - The chosen option is considerably often used as title of the ADR (e.g., ADR-0015). Thus, that title repeats. +# - We use "Examples" multiple times (e.g., ADR-0010). +# - Markdown lint should support the user and not annoy them. +# +# Details: https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md024---multiple-headings-with-the-same-content +MD024: false diff --git a/docs/adr_madr4_example/.pages b/docs/adr_madr4_example/.pages new file mode 100644 index 0000000..c378349 --- /dev/null +++ b/docs/adr_madr4_example/.pages @@ -0,0 +1,4 @@ +nav: + - Summary: summary.md + - ... | regex=^\d{4}- + diff --git a/docs/adr_madr4_example/0001-record-architecture-decisions.md b/docs/adr_madr4_example/0001-record-architecture-decisions.md new file mode 100644 index 0000000..cf84c73 --- /dev/null +++ b/docs/adr_madr4_example/0001-record-architecture-decisions.md @@ -0,0 +1,37 @@ +--- +# source: https://github.com/adr/madr/blob/4.0.0/template/adr-template.md +# These are optional elements. Feel free to remove any of them. +status: superseded by [ADR-0002](0002-change-mind.md) +date: 2024-01-24 +decision-makers: Nick Fury, Tony Stark +# status: {proposed | rejected | accepted | deprecated | … | superseded by [ADR-0005](0005-example.md)} +# date: {YYYY-MM-DD when the decision was last updated} +# decision-makers: {list everyone involved in the decision} +# consulted: {list everyone whose opinions are sought (typically subject-matter experts); and with whom there is a two-way communication} +# informed: {list everyone who is kept up-to-date on progress; and with whom there is a one-way communication} +--- +# Use Markdown Any Decision Records + +## Context and Problem Statement + +We want to record any decisions made in this project independent whether decisions concern the architecture ("architectural decision record"), the code, or other fields. +Which format and structure should these records follow? + +## Considered Options + +* [MADR](https://adr.github.io/madr/) 4.0.0 – The Markdown Any Decision Records +* [Michael Nygard's template](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions) – The first incarnation of the term "ADR" +* Other templates listed at +* Formless – No conventions for file format and structure + +## Decision Outcome + +Chosen option: "MADR 4.0.0", because + +* Implicit assumptions should be made explicit. + Design documentation is important to enable people understanding the decisions later on. + See also [A rational design process: How and why to fake it](https://doi.org/10.1109/TSE.1986.6312940). +* MADR allows for structured capturing of any decision. +* The MADR format is lean and fits our development style. +* The MADR structure is comprehensible and facilitates usage & maintenance. +* The MADR project is vivid. diff --git a/docs/adr_madr4_example/0002-change-mind.md b/docs/adr_madr4_example/0002-change-mind.md new file mode 100644 index 0000000..a7a6f32 --- /dev/null +++ b/docs/adr_madr4_example/0002-change-mind.md @@ -0,0 +1,12 @@ +--- +# source: https://github.com/adr/madr/blob/4.0.0/template/adr-template.md +# These are optional elements. Feel free to remove any of them. +status: accepted +date: 2024-01-25 +decision-makers: Nick Fury, Tony Stark +--- +# Changed my mind about ADR format + +## Context and Problem Statement + +For some reason we don't like MADR anymore. diff --git a/docs/adr_madr4_example/adr-template.md b/docs/adr_madr4_example/adr-template.md new file mode 100644 index 0000000..f95ff0a --- /dev/null +++ b/docs/adr_madr4_example/adr-template.md @@ -0,0 +1,80 @@ +--- +# source: https://github.com/adr/madr/blob/4.0.0/template/adr-template.md +# These are optional elements. Feel free to remove any of them. +status: {proposed | rejected | accepted | deprecated | … | superseded by [ADR-0005](0005-example.md)} +date: {YYYY-MM-DD when the decision was last updated} +decision-makers: {list everyone involved in the decision} +consulted: {list everyone whose opinions are sought (typically subject-matter experts); and with whom there is a two-way communication} +informed: {list everyone who is kept up-to-date on progress; and with whom there is a one-way communication} +--- +# {short title of solved problem and solution} + +## Context and Problem Statement + +{Describe the context and problem statement, e.g., in free form using two to three sentences or in the form of an illustrative story. + You may want to articulate the problem in form of a question and add links to collaboration boards or issue management systems.} + + +## Decision Drivers + +* {decision driver 1, e.g., a force, facing concern, …} +* {decision driver 2, e.g., a force, facing concern, …} +* … + +## Considered Options + +* {title of option 1} +* {title of option 2} +* {title of option 3} +* … + +## Decision Outcome + +Chosen option: "{title of option 1}", because +{justification. e.g., only option, which meets k.o. criterion decision driver | which resolves force {force} | … | comes out best (see below)}. + + +### Consequences + +* Good, because {positive consequence, e.g., improvement of one or more desired qualities, …} +* Bad, because {negative consequence, e.g., compromising one or more desired qualities, …} +* … + + +## Validation + +{describe how the implementation of/compliance with the ADR is validated. E.g., by a review or an ArchUnit test} + + +## Pros and Cons of the Options + +### {title of option 1} + + +{example | description | pointer to more information | …} + +* Good, because {argument a} +* Good, because {argument b} + +* Neutral, because {argument c} +* Bad, because {argument d} +* … + +### {title of other option} + +{example | description | pointer to more information | …} + +* Good, because {argument a} +* Good, because {argument b} +* Neutral, because {argument c} +* Bad, because {argument d} +* … + + +## More Information + +{You might want to provide additional evidence/confidence for the decision outcome here and/or + document the team agreement on the decision and/or + define when this decision when and how the decision should be realized and if/when it should be re-visited and/or + how the decision is validated. + Links to other decisions and resources might here appear as well.} diff --git a/docs/adr_madr4_example/summary.md b/docs/adr_madr4_example/summary.md new file mode 100644 index 0000000..156ba09 --- /dev/null +++ b/docs/adr_madr4_example/summary.md @@ -0,0 +1,3 @@ +# ADR Summary + +{{ adr_summary(adr_path="docs/adr_madr4_example", adr_style="MADR4") }} diff --git a/mkdocs_macros_adr_summary/factory.py b/mkdocs_macros_adr_summary/factory.py index 605402b..b4c46e9 100644 --- a/mkdocs_macros_adr_summary/factory.py +++ b/mkdocs_macros_adr_summary/factory.py @@ -21,10 +21,11 @@ from typing import Dict, Type from .interfaces import ADRParser, TYPE_ADRStyle -from .parser import MADR2Parser, MADR3Parser, NygardParser +from .parser import MADR2Parser, MADR3Parser, MADR4Parser, NygardParser parser_registry: Dict[TYPE_ADRStyle, Type[ADRParser]] = { "nygard": NygardParser, + "MADR4": MADR4Parser, "MADR3": MADR3Parser, "MADR2": MADR2Parser, } diff --git a/mkdocs_macros_adr_summary/interfaces.py b/mkdocs_macros_adr_summary/interfaces.py index 74c9d6f..1233e4e 100644 --- a/mkdocs_macros_adr_summary/interfaces.py +++ b/mkdocs_macros_adr_summary/interfaces.py @@ -24,7 +24,7 @@ from pathlib import Path from typing import Literal, Optional, Sequence -TYPE_ADRStyle = Literal["MADR2", "MADR3", "nygard"] +TYPE_ADRStyle = Literal["MADR2", "MADR3", "MADR4", "nygard"] @dataclass diff --git a/mkdocs_macros_adr_summary/parser/__init__.py b/mkdocs_macros_adr_summary/parser/__init__.py index 89a0d64..548df85 100644 --- a/mkdocs_macros_adr_summary/parser/__init__.py +++ b/mkdocs_macros_adr_summary/parser/__init__.py @@ -29,4 +29,5 @@ # from .madr2 import MADR2Parser from .madr3 import MADR3Parser +from .madr4 import MADR4Parser from .nygard import NygardParser diff --git a/mkdocs_macros_adr_summary/parser/madr4.py b/mkdocs_macros_adr_summary/parser/madr4.py new file mode 100644 index 0000000..5e65839 --- /dev/null +++ b/mkdocs_macros_adr_summary/parser/madr4.py @@ -0,0 +1,30 @@ +# Copyright (c) 2024 Federico Busetti <729029+febus982@users.noreply.github.com> +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. + +from typing import Optional + +from .madr3 import MADR3Parser +from .types import TYPE_AST + + +class MADR4Parser(MADR3Parser): + @classmethod + def _get_deciders(cls, metadata: dict, ast: TYPE_AST) -> Optional[str]: + return metadata.get("decision-makers") diff --git a/tests/adr_docs/madr4/0001-valid_with_metadata.md b/tests/adr_docs/madr4/0001-valid_with_metadata.md new file mode 100644 index 0000000..86d7008 --- /dev/null +++ b/tests/adr_docs/madr4/0001-valid_with_metadata.md @@ -0,0 +1,34 @@ +--- +#source: https://github.com/adr/madr/blob/4.0.0/template/adr-template.md +#These are optional metadata elements. Feel free to remove any of them. +status: accepted +date: 2024-01-20 +decision-makers: Nick Fury +consulted: Anthony Stark +informed: Thor Odinson +--- +# Use Markdown Any Decision Records + +## Context and Problem Statement + +We want to record any decisions made in this project independent whether decisions concern the architecture ("architectural decision record"), the code, or other fields. +Which format and structure should these records follow? + +## Considered Options + +* [MADR](https://adr.github.io/madr/) 4.0.0 – The Markdown Any Decision Records +* [Michael Nygard's template](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions) – The first incarnation of the term "ADR" +* Other templates listed at +* Formless – No conventions for file format and structure + +## Decision Outcome + +Chosen option: "MADR 4.0.0", because + +* Implicit assumptions should be made explicit. + Design documentation is important to enable people understanding the decisions later on. + See also [A rational design process: How and why to fake it](https://doi.org/10.1109/TSE.1986.6312940). +* MADR allows for structured capturing of any decision. +* The MADR format is lean and fits our development style. +* The MADR structure is comprehensible and facilitates usage & maintenance. +* The MADR project is vivid. diff --git a/tests/adr_docs/madr4/invalid_blank_document.md b/tests/adr_docs/madr4/invalid_blank_document.md new file mode 100644 index 0000000..e69de29 diff --git a/tests/adr_docs/madr4/invalid_headers.md b/tests/adr_docs/madr4/invalid_headers.md new file mode 100644 index 0000000..7d9c7a1 --- /dev/null +++ b/tests/adr_docs/madr4/invalid_headers.md @@ -0,0 +1,37 @@ +--- +#source: https://github.com/adr/madr/blob/3.0.0/template/adr-template.md +#These are optional elements. Feel free to remove any of them. +status: accepted +date: 2024-01-20 +Someinvalidstringhere +#status: {proposed | rejected | accepted | deprecated | … | superseded by [ADR-0005](0005-example.md)} +#date: {YYYY-MM-DD when the decision was last updated} +#deciders: {list everyone involved in the decision} +#consulted: {list everyone whose opinions are sought (typically subject-matter experts); and with whom there is a two-way communication} +#informed: {list everyone who is kept up-to-date on progress; and with whom there is a one-way communication} +--- +Use Markdown Any Decision Records + +## Context and Problem Statement + +We want to record any decisions made in this project independent whether decisions concern the architecture ("architectural decision record"), the code, or other fields. +Which format and structure should these records follow? + +## Considered Options + +* [MADR](https://adr.github.io/madr/) 3.0.0 – The Markdown Any Decision Records +* [Michael Nygard's template](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions) – The first incarnation of the term "ADR" +* Other templates listed at +* Formless – No conventions for file format and structure + +## Decision Outcome + +Chosen option: "MADR 3.0.0", because + +* Implicit assumptions should be made explicit. + Design documentation is important to enable people understanding the decisions later on. + See also [A rational design process: How and why to fake it](https://doi.org/10.1109/TSE.1986.6312940). +* MADR allows for structured capturing of any decision. +* The MADR format is lean and fits our development style. +* The MADR structure is comprehensible and facilitates usage & maintenance. +* The MADR project is vivid. diff --git a/tests/adr_docs/madr4/invalid_title_h3.md b/tests/adr_docs/madr4/invalid_title_h3.md new file mode 100644 index 0000000..07f2ebb --- /dev/null +++ b/tests/adr_docs/madr4/invalid_title_h3.md @@ -0,0 +1,36 @@ +--- +#source: https://github.com/adr/madr/blob/3.0.0/template/adr-template.md +#These are optional elements. Feel free to remove any of them. +status: accepted +date: 2024-01-20 +#status: {proposed | rejected | accepted | deprecated | … | superseded by [ADR-0005](0005-example.md)} +#date: {YYYY-MM-DD when the decision was last updated} +#deciders: {list everyone involved in the decision} +#consulted: {list everyone whose opinions are sought (typically subject-matter experts); and with whom there is a two-way communication} +#informed: {list everyone who is kept up-to-date on progress; and with whom there is a one-way communication} +--- +### Use Markdown Any Decision Records + +## Context and Problem Statement + +We want to record any decisions made in this project independent whether decisions concern the architecture ("architectural decision record"), the code, or other fields. +Which format and structure should these records follow? + +## Considered Options + +* [MADR](https://adr.github.io/madr/) 3.0.0 – The Markdown Any Decision Records +* [Michael Nygard's template](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions) – The first incarnation of the term "ADR" +* Other templates listed at +* Formless – No conventions for file format and structure + +## Decision Outcome + +Chosen option: "MADR 3.0.0", because + +* Implicit assumptions should be made explicit. + Design documentation is important to enable people understanding the decisions later on. + See also [A rational design process: How and why to fake it](https://doi.org/10.1109/TSE.1986.6312940). +* MADR allows for structured capturing of any decision. +* The MADR format is lean and fits our development style. +* The MADR structure is comprehensible and facilitates usage & maintenance. +* The MADR project is vivid. diff --git a/tests/adr_docs/madr4/invalid_title_p.md b/tests/adr_docs/madr4/invalid_title_p.md new file mode 100644 index 0000000..9a6e7c5 --- /dev/null +++ b/tests/adr_docs/madr4/invalid_title_p.md @@ -0,0 +1,36 @@ +--- +#source: https://github.com/adr/madr/blob/3.0.0/template/adr-template.md +#These are optional elements. Feel free to remove any of them. +status: accepted +date: 2024-01-20 +#status: {proposed | rejected | accepted | deprecated | … | superseded by [ADR-0005](0005-example.md)} +#date: {YYYY-MM-DD when the decision was last updated} +#deciders: {list everyone involved in the decision} +#consulted: {list everyone whose opinions are sought (typically subject-matter experts); and with whom there is a two-way communication} +#informed: {list everyone who is kept up-to-date on progress; and with whom there is a one-way communication} +--- +Use Markdown Any Decision Records + +## Context and Problem Statement + +We want to record any decisions made in this project independent whether decisions concern the architecture ("architectural decision record"), the code, or other fields. +Which format and structure should these records follow? + +## Considered Options + +* [MADR](https://adr.github.io/madr/) 3.0.0 – The Markdown Any Decision Records +* [Michael Nygard's template](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions) – The first incarnation of the term "ADR" +* Other templates listed at +* Formless – No conventions for file format and structure + +## Decision Outcome + +Chosen option: "MADR 3.0.0", because + +* Implicit assumptions should be made explicit. + Design documentation is important to enable people understanding the decisions later on. + See also [A rational design process: How and why to fake it](https://doi.org/10.1109/TSE.1986.6312940). +* MADR allows for structured capturing of any decision. +* The MADR format is lean and fits our development style. +* The MADR structure is comprehensible and facilitates usage & maintenance. +* The MADR project is vivid. diff --git a/tests/adr_docs/madr4/valid_without_metadata_and_id.md b/tests/adr_docs/madr4/valid_without_metadata_and_id.md new file mode 100644 index 0000000..a5a2e29 --- /dev/null +++ b/tests/adr_docs/madr4/valid_without_metadata_and_id.md @@ -0,0 +1,36 @@ +--- +#source: https://github.com/adr/madr/blob/4.0.0/template/adr-template.md +#These are optional metadata elements. Feel free to remove any of them. +#status: accepted +#date: 2024-01-20 +#status: {proposed | rejected | accepted | deprecated | … | superseded by ADR-0123} +#date: {YYYY-MM-DD when the decision was last updated} +#decision-makers: {list everyone involved in the decision} +#consulted: {list everyone whose opinions are sought (typically subject-matter experts); and with whom there is a two-way communication} +#informed: {list everyone who is kept up-to-date on progress; and with whom there is a one-way communication} +--- +# Use Markdown Any Decision Records + +## Context and Problem Statement + +We want to record any decisions made in this project independent whether decisions concern the architecture ("architectural decision record"), the code, or other fields. +Which format and structure should these records follow? + +## Considered Options + +* [MADR](https://adr.github.io/madr/) 4.0.0 – The Markdown Any Decision Records +* [Michael Nygard's template](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions) – The first incarnation of the term "ADR" +* Other templates listed at +* Formless – No conventions for file format and structure + +## Decision Outcome + +Chosen option: "MADR 4.0.0", because + +* Implicit assumptions should be made explicit. + Design documentation is important to enable people understanding the decisions later on. + See also [A rational design process: How and why to fake it](https://doi.org/10.1109/TSE.1986.6312940). +* MADR allows for structured capturing of any decision. +* The MADR format is lean and fits our development style. +* The MADR structure is comprehensible and facilitates usage & maintenance. +* The MADR project is vivid. diff --git a/tests/test_madr4_parser.py b/tests/test_madr4_parser.py new file mode 100644 index 0000000..b086aae --- /dev/null +++ b/tests/test_madr4_parser.py @@ -0,0 +1,77 @@ +from datetime import datetime +from pathlib import Path + +import pytest + +from mkdocs_macros_adr_summary.parser.exceptions import InvalidFileError +from mkdocs_macros_adr_summary.parser.madr4 import MADR4Parser + + +@pytest.mark.parametrize( + ["filename", "expected_metadata"], + [ + ( + "0001-valid_with_metadata.md", + { + "document_id": 1, + "status": "Accepted", + "statuses": tuple(["Accepted"]), + "date": datetime.fromisoformat("2024-01-20").date(), + "deciders": "Nick Fury", + "consulted": "Anthony Stark", + "informed": "Thor Odinson", + }, + ), + ( + "valid_without_metadata_and_id.md", + { + "document_id": None, + "status": None, + "statuses": tuple(), + "date": None, + "deciders": None, + "consulted": None, + "informed": None, + }, + ), + ], +) +def test_parse_valid_document( + filename: str, expected_metadata: dict, adr_document_factory +): + assert MADR4Parser.parse( + Path(__file__).parent.joinpath(f"adr_docs/madr4/{filename}"), + base_path=Path(__file__).parent, + ) == adr_document_factory( + file_path=f"../adr_docs/madr4/{filename}", + title="Use Markdown Any Decision Records", + **expected_metadata, + ) + + +def test_parse_invalid_blank_document(): + with pytest.raises(InvalidFileError): + MADR4Parser.parse( + Path(__file__).parent.joinpath("adr_docs/madr4/invalid_blank_document.md"), + base_path=Path(__file__).parent, + ) + + +@pytest.mark.parametrize( + ["filename"], + [("invalid_title_h3.md",), ("invalid_title_p.md",)], +) +def test_parse_invalid_title(filename: str, adr_document_factory): + document = MADR4Parser.parse( + Path(__file__).parent.joinpath(f"adr_docs/madr4/{filename}"), + base_path=Path(__file__).parent, + ) + assert document.title == adr_document_factory().title + + +def test_parse_invalid_headers(): + with pytest.raises(InvalidFileError): + MADR4Parser.parse( + Path(__file__).parent.joinpath("adr_docs/madr4/invalid_headers.md"), + base_path=Path(__file__).parent, + )