slartiarch 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 (67) hide show
  1. slartiarch-0.1.0/.adr-dir +1 -0
  2. slartiarch-0.1.0/.github/scripts/check_radon.sh +33 -0
  3. slartiarch-0.1.0/.github/workflows/ci.yml +51 -0
  4. slartiarch-0.1.0/.gitignore +14 -0
  5. slartiarch-0.1.0/.nvimrc +23 -0
  6. slartiarch-0.1.0/.pre-commit-config.yaml +17 -0
  7. slartiarch-0.1.0/AGENTS.md +92 -0
  8. slartiarch-0.1.0/Makefile +41 -0
  9. slartiarch-0.1.0/PKG-INFO +114 -0
  10. slartiarch-0.1.0/README.md +97 -0
  11. slartiarch-0.1.0/docs/adr/0001-python-project.md +31 -0
  12. slartiarch-0.1.0/docs/adr/0002-findings-are-the-interface.md +45 -0
  13. slartiarch-0.1.0/docs/adr/0003-python-interfaces-are-a-linkml-projection.md +54 -0
  14. slartiarch-0.1.0/docs/architecture.md +285 -0
  15. slartiarch-0.1.0/docs/diagrams/containers.mmd +44 -0
  16. slartiarch-0.1.0/docs/diagrams/index.mmd +19 -0
  17. slartiarch-0.1.0/docs/slarti/constraints.yaml +102 -0
  18. slartiarch-0.1.0/docs/slarti/data/invalid/D1.ttl +10 -0
  19. slartiarch-0.1.0/docs/slarti/data/invalid/D2.ttl +8 -0
  20. slartiarch-0.1.0/docs/slarti/data/invalid/D3.ttl +7 -0
  21. slartiarch-0.1.0/docs/slarti/data/valid/report.ttl +17 -0
  22. slartiarch-0.1.0/docs/slarti/likec4/slarti.c4 +129 -0
  23. slartiarch-0.1.0/docs/slarti/linkml/slarti.yaml +247 -0
  24. slartiarch-0.1.0/docs/slarti/shacl/invariants.ttl +36 -0
  25. slartiarch-0.1.0/docs/specs/slarti-project.md +373 -0
  26. slartiarch-0.1.0/package-lock.json +2112 -0
  27. slartiarch-0.1.0/package.json +13 -0
  28. slartiarch-0.1.0/pyproject.toml +95 -0
  29. slartiarch-0.1.0/slarti/__init__.py +1 -0
  30. slartiarch-0.1.0/slarti/checks/__init__.py +0 -0
  31. slartiarch-0.1.0/slarti/checks/delegated.py +47 -0
  32. slartiarch-0.1.0/slarti/checks/doc_checks.py +139 -0
  33. slartiarch-0.1.0/slarti/checks/ownership.py +125 -0
  34. slartiarch-0.1.0/slarti/checks/registry_checks.py +201 -0
  35. slartiarch-0.1.0/slarti/cli.py +167 -0
  36. slartiarch-0.1.0/slarti/config.py +118 -0
  37. slartiarch-0.1.0/slarti/docs.py +57 -0
  38. slartiarch-0.1.0/slarti/domain.py +286 -0
  39. slartiarch-0.1.0/slarti/env.py +130 -0
  40. slartiarch-0.1.0/slarti/findings.py +86 -0
  41. slartiarch-0.1.0/slarti/fixtures.py +124 -0
  42. slartiarch-0.1.0/slarti/generate.py +105 -0
  43. slartiarch-0.1.0/slarti/inject.py +53 -0
  44. slartiarch-0.1.0/slarti/models.py +145 -0
  45. slartiarch-0.1.0/slarti/proc.py +47 -0
  46. slartiarch-0.1.0/slarti/registry.py +99 -0
  47. slartiarch-0.1.0/slarti/report.py +348 -0
  48. slartiarch-0.1.0/slarti/resolvers.py +74 -0
  49. slartiarch-0.1.0/slarti/runner.py +48 -0
  50. slartiarch-0.1.0/slarti/scaffold.py +126 -0
  51. slartiarch-0.1.0/tests/__init__.py +0 -0
  52. slartiarch-0.1.0/tests/conftest.py +60 -0
  53. slartiarch-0.1.0/tests/test_cli.py +102 -0
  54. slartiarch-0.1.0/tests/test_config.py +63 -0
  55. slartiarch-0.1.0/tests/test_doc_checks.py +76 -0
  56. slartiarch-0.1.0/tests/test_dogfood.py +71 -0
  57. slartiarch-0.1.0/tests/test_domain.py +60 -0
  58. slartiarch-0.1.0/tests/test_env.py +53 -0
  59. slartiarch-0.1.0/tests/test_findings.py +46 -0
  60. slartiarch-0.1.0/tests/test_fixtures.py +58 -0
  61. slartiarch-0.1.0/tests/test_generate.py +61 -0
  62. slartiarch-0.1.0/tests/test_inject.py +51 -0
  63. slartiarch-0.1.0/tests/test_models.py +65 -0
  64. slartiarch-0.1.0/tests/test_ownership.py +54 -0
  65. slartiarch-0.1.0/tests/test_registry.py +119 -0
  66. slartiarch-0.1.0/tests/test_scaffold.py +46 -0
  67. slartiarch-0.1.0/uv.lock +2981 -0
@@ -0,0 +1 @@
1
+ docs/adr
@@ -0,0 +1,33 @@
1
+ #!/bin/bash
2
+
3
+ # nounset: undefined variable outputs error message, and forces an exit
4
+ set -u
5
+ # errexit: abort script at first error
6
+ set -e
7
+
8
+ echo "Radon Cyclomatic Complexity below grade A:"
9
+ PACKAGE="slarti"
10
+ CC_COMMAND="uv run radon cc -n B ${PACKAGE}"
11
+ CC=$($CC_COMMAND)
12
+ $CC_COMMAND
13
+
14
+ echo "Radon Maintainability Index below grade A:"
15
+ MI_COMMAND="uv run radon mi -n B ${PACKAGE}"
16
+ MI=$($MI_COMMAND)
17
+ $MI_COMMAND
18
+
19
+ echo "Lines of Code over 80 lines:"
20
+ L=$(uv run radon cc --json "${PACKAGE}" | jq -r 'to_entries[] |
21
+ .key as $file |
22
+ .value[] |
23
+ select(.type == "method" or .type == "function") |
24
+ (.endline - .lineno + 1) as $length |
25
+ select($length > 80) |
26
+ "\($file):\(.lineno) - \(.name) (\($length) lines)"
27
+ ')
28
+ echo "$L"
29
+
30
+
31
+ if [[ ${#CC} -gt 0 || ${#MI} -gt 0 || ${#L} -gt 0 ]]; then
32
+ exit 1
33
+ fi
@@ -0,0 +1,51 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ pull_request:
8
+ branches:
9
+ - main
10
+
11
+
12
+ jobs:
13
+ lint-and-test:
14
+ runs-on: ubuntu-latest
15
+
16
+ steps:
17
+ - name: Checkout repository
18
+ uses: actions/checkout@v5
19
+
20
+ - name: Set up python
21
+ uses: actions/setup-python@v6
22
+ with:
23
+ python-version: "3.11"
24
+
25
+ - name: Set up node
26
+ uses: actions/setup-node@v6
27
+ with:
28
+ node-version: "20"
29
+
30
+ - name: Install the pinned LikeC4
31
+ run: npm ci
32
+
33
+ - name: Install uv
34
+ run: pip install uv
35
+
36
+ - name: Sync deps (dev)
37
+ run: uv sync --group dev
38
+
39
+ - name: CI
40
+ run: make ci
41
+
42
+ - name: Architecture (slarti dogfoods itself)
43
+ run: make arch
44
+
45
+ - name: Upload code coverage
46
+ if: always()
47
+ uses: actions/upload-artifact@v5
48
+ with:
49
+ name: coverage-html
50
+ path: htmlcov/
51
+ if-no-files-found: ignore
@@ -0,0 +1,14 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+
4
+ # Coverage / test
5
+ *.coverage
6
+ .coverage*
7
+ .htmlcov/
8
+ .pytest_cache/
9
+
10
+ # Node: the user's pinned LikeC4
11
+ node_modules/
12
+
13
+ # slarti: transient output
14
+ build/
@@ -0,0 +1,23 @@
1
+ " lua << EOF
2
+ " require('lazy-loader')()
3
+ " EOF
4
+
5
+ let g:gutentags_ctags_exclude += ['*/.venv/*']
6
+ let g:projectionist_heuristics = {
7
+ \ 'pyproject.toml': {
8
+ \ 'slarti/*.py': {
9
+ \ 'type': 'function',
10
+ \ 'alternate': [
11
+ \ 'tests/{dirname}test_{basename}.py',
12
+ \ 'tests/{dirname}/test_{basename}.py',
13
+ \ ]
14
+ \ },
15
+ \ 'tests/**/test_*.py': {
16
+ \ 'type': 'test',
17
+ \ 'alternate': [
18
+ \ 'slarti/{dirname}{basename}.py',
19
+ \ 'slarti/{dirname}/{basename}.py',
20
+ \ ]
21
+ \ },
22
+ \ },
23
+ \ }
@@ -0,0 +1,17 @@
1
+ repos:
2
+ - repo: https://github.com/astral-sh/ruff-pre-commit
3
+ rev: v0.16.0
4
+ hooks:
5
+ - id: ruff-check
6
+ - id: ruff-format
7
+ - repo: https://github.com/pre-commit/mirrors-mypy
8
+ rev: v2.3.0
9
+ hooks:
10
+ - id: mypy
11
+ additional_dependencies: []
12
+ - repo: https://github.com/pre-commit/pre-commit-hooks
13
+ rev: v6.0.0
14
+ hooks:
15
+ - id: check-merge-conflict
16
+ - id: end-of-file-fixer
17
+ - id: trailing-whitespace
@@ -0,0 +1,92 @@
1
+ # Agent Guidelines
2
+
3
+ This file provides guidance when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ See this projects README.md for an overview of the project, and ADR documents in docs/adr/ for architectural decisions.
8
+
9
+ ## Development Commands
10
+
11
+ **IMPORTANT**: Always use the Makefile commands for development tasks.
12
+
13
+ ### Setup
14
+ ```bash
15
+ make setup # Set up venv and sync dependencies with uv
16
+ ```
17
+
18
+ ### Testing
19
+ ```bash
20
+ make test # Run all tests with pytest and coverage
21
+ uv run pytest tests/test_foo.py # Run specific test file
22
+ uv run pytest tests/test_foo.py::test_name # Run specific test
23
+ uv run pytest -v # Verbose output
24
+ ```
25
+
26
+ ### Code Quality
27
+ ```bash
28
+ make lint # Check code with ruff
29
+ make fix # Auto-fix ruff issues
30
+ make type # Type check with mypy (strict mode)
31
+ make radon # Check cyclomatic complexity and maintainability
32
+ make ci # Run all CI checks (lint + type + test + radon)
33
+ ```
34
+
35
+ **Before completing any feature**, run `make ci` to ensure all checks pass.
36
+
37
+ ## Code Quality Requirements
38
+
39
+ Writing guidance:
40
+
41
+ - Only include a brief description of the thing in docstrings (no arguments or return types)
42
+ - Do not use docstrings at the beginning of files.
43
+
44
+ ## Project Structure
45
+
46
+ ```
47
+ slarti/ # Main package source code
48
+ tests/ # Test files (mirror slarti/ structure)
49
+ model/ # The architecture model slarti checks itself against
50
+ docs/adr/ # Architecture Decision Records
51
+ docs/architecture.md # Generated in part by slarti itself
52
+ ```
53
+
54
+ ## The architecture model (slarti dogfoods itself)
55
+
56
+ This repository is checked by its own tool. Before committing a change that
57
+ touches structure or domain entities, run:
58
+
59
+ ```bash
60
+ slartiarch check # the seams: OWN-*, REG-*, DOC-*
61
+ slartiarch docs # regenerate the diagrams and tables
62
+ slartiarch docs --check # the drift gate CI runs
63
+ ```
64
+
65
+ ### Files you may edit
66
+
67
+ - `model/arch/*.c4` — the LikeC4 model of slarti's own containers
68
+ - `model/schema/slarti.yaml` — the LinkML schema of slarti's domain entities
69
+ - `model/shapes/*.ttl` — hand-written SHACL
70
+ - `model/constraints.yaml` — the rule registry
71
+ - `model/data/valid/*`, `model/data/invalid/*` — fixtures, one per rule
72
+ - prose **outside** generated regions in `docs/architecture.md`
73
+
74
+ ### Files you must never edit
75
+
76
+ - anything under `docs/diagrams/`
77
+ - anything between `<!-- slarti:begin ... -->` and `<!-- slarti:end ... -->`
78
+
79
+ Those regions are regenerated by `slartiarch docs`; hand edits are reported as
80
+ `DOC-1` / `DOC-2` and block the merge.
81
+
82
+ ### Rules
83
+
84
+ - A new domain class needs an `owner` annotation naming a LikeC4 element, and
85
+ that element must list the class in its `owns` metadata (`OWN-1`..`OWN-5`).
86
+ - A new SHACL shape needs a constraint in `model/constraints.yaml` referencing
87
+ it, and a fixture under `model/data/invalid/` that makes it fire
88
+ (`REG-2`, `REG-6`, `REG-7`).
89
+ - A rule that cannot be mechanically enforced is written as `enforced_by: none`
90
+ with a `reason`. That is honest, not a failure.
91
+ - `slartiarch report --json` describes every rule, shape, owned class and check ID,
92
+ each check with its remedy.
@@ -0,0 +1,41 @@
1
+ .PHONY: setup test lint type adr new coverage vulture fix radon treepeat ci arch docs domain
2
+
3
+ setup:
4
+ uv venv
5
+ uv sync --python .venv/bin/python
6
+
7
+ test:
8
+ uv run pytest
9
+
10
+ lint:
11
+ uv run ruff check .
12
+
13
+ vulture:
14
+ uv run vulture --min-confidence 55 slarti
15
+
16
+ fix:
17
+ uv run ruff check . --fix
18
+
19
+ type:
20
+ uv run mypy
21
+
22
+ radon:
23
+ uv run .github/scripts/check_radon.sh
24
+
25
+ treepeat:
26
+ uv run treepeat detect . -i '**/node_modules/**,**/.venv/**,**/build/**'
27
+
28
+ ci: test lint type radon treepeat vulture
29
+
30
+ # The Python interfaces are a LinkML projection, not hand-written (see README).
31
+ domain:
32
+ uv run gen-pydantic docs/slarti/linkml/slarti.yaml > slarti/domain.py
33
+
34
+ # slarti dogfoods itself (I13): the architecture document is generated by the tool.
35
+ arch:
36
+ uv run slartiarch doctor
37
+ uv run slartiarch check
38
+ uv run slartiarch docs --check
39
+
40
+ docs:
41
+ uv run slartiarch docs
@@ -0,0 +1,114 @@
1
+ Metadata-Version: 2.4
2
+ Name: slartiarch
3
+ Version: 0.1.0
4
+ Summary: A coordination CLI for validated architecture documentation
5
+ Author-email: Dane Summers <dsummersl@gmail.com>
6
+ License-Expression: Apache-2.0
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Typing :: Typed
9
+ Requires-Python: >=3.11
10
+ Requires-Dist: linkml-runtime>=1.11.1
11
+ Requires-Dist: pydantic>=2.11
12
+ Requires-Dist: pyyaml>=6.0.3
13
+ Requires-Dist: rdflib>=7.6
14
+ Requires-Dist: tomli-w>=1.2
15
+ Requires-Dist: typer>=0.27
16
+ Description-Content-Type: text/markdown
17
+
18
+ # slartiarch
19
+
20
+ <div align="center">
21
+ <img src="https://static.wikia.nocookie.net/hitchhikers/images/f/f9/Slartibartfast_comics.png/revision/latest?cb=20230703161559" width="200" alt="Slartibartfast">
22
+ </div>
23
+
24
+ Slartibartfast: the architect. From *The Hitchhiker's Guide to the Galaxy*, the curmudgeonly designer of Norway's fjords — and the namesake of this tool.
25
+
26
+ slartiarch is a coordination CLI that checks consistency between [LikeC4](https://likec4.dev) architecture
27
+ diagrams, [LinkML](https://linkml.io) schemas, and [SHACL](https://www.w3.org/TR/shacl/) shapes.
28
+ It also regenerates the diagrams in your architecture document so the prose never drifts.
29
+
30
+ Each tool is useful on its own with no slartiarch involved. slartiarch adds the seams — rules that span
31
+ two tools, and checks that the answers still agree.
32
+
33
+ ## Install
34
+
35
+ ```bash
36
+ uv tool install slartiarch
37
+ npm install likec4
38
+ pip install linkml pyshacl
39
+ ```
40
+
41
+ ## Use
42
+
43
+ ```bash
44
+ slartiarch init # scaffold docs/slarti/ and a config
45
+ slartiarch doctor # resolved paths and tool versions
46
+ slartiarch check # run all validations (CI command)
47
+ slartiarch check --json # structured output for automation
48
+ slartiarch docs # regenerate diagrams and tables into your docs
49
+ slartiarch docs --check # fail if committed output is stale
50
+ slartiarch report # every rule, shape, and check ID
51
+ slartiarch report --json # fully detailed, for agents
52
+ ```
53
+
54
+ Run `slartiarch` with no arguments for a full reference. `slartiarch report --json` describes every
55
+ rule, enforcer kind, check ID, and remedy.
56
+
57
+ ## Layout
58
+
59
+ ```
60
+ docs/
61
+ architecture.md prose with generated diagram and table regions
62
+ diagrams/ GENERATED — never hand-edited
63
+ slarti/
64
+ likec4/*.c4 LikeC4 architecture model
65
+ linkml/*.yaml LinkML domain schemas
66
+ shacl/*.ttl hand-written SHACL shapes
67
+ constraints.yaml rule registry
68
+ data/valid/* fixtures that must pass validation
69
+ data/invalid/* one fixture per SHACL rule; each must fail
70
+ ```
71
+
72
+ ## How the tools work together
73
+
74
+ LikeC4 draws the system. LinkML defines the entities. SHACL validates instances. Each
75
+ works standalone:
76
+
77
+ ```bash
78
+ likec4 start docs/slarti/likec4 # live architecture browser
79
+ gen-pydantic docs/slarti/linkml/slarti.yaml # Python interfaces from schema
80
+ pyshacl -s docs/slarti/shacl/invariants.ttl ... # validate RDF against shapes
81
+ ```
82
+
83
+ A constraint in the registry ties them together:
84
+
85
+ ```yaml
86
+ # docs/slarti/constraints.yaml
87
+ - id: D8
88
+ statement: A subtask belongs to the same list as its parent.
89
+ enforced_by:
90
+ kind: shacl_shape
91
+ ref: "todo:SubtaskSharesListWithParent"
92
+ fixture: docs/slarti/data/invalid/D8.yaml
93
+ ```
94
+
95
+ Running `slartiarch check` walks the seams: it verifies the shape exists, the fixture
96
+ fails (proving the shape fires), both halves of the ownership claim agree, and the
97
+ constraint table in your document still matches. `slartiarch docs` regenerates the Mermaid
98
+ diagrams and tables so the architecture document never goes stale.
99
+
100
+ ## Dogfooding
101
+
102
+ This repository's [docs/architecture.md](docs/architecture.md) is generated by slartiarch from
103
+ `docs/slarti/`. CI runs `slartiarch check` and `slartiarch docs --check`.
104
+
105
+ ## Dev
106
+
107
+ ```bash
108
+ make setup # uv venv + sync
109
+ npm ci # pinned LikeC4
110
+ make domain # regenerate slarti/domain.py from the LinkML schema
111
+ make ci # tests, lint, types, complexity, duplication, dead code
112
+ ```
113
+
114
+ Architecture decisions live in `docs/adr/`.
@@ -0,0 +1,97 @@
1
+ # slartiarch
2
+
3
+ <div align="center">
4
+ <img src="https://static.wikia.nocookie.net/hitchhikers/images/f/f9/Slartibartfast_comics.png/revision/latest?cb=20230703161559" width="200" alt="Slartibartfast">
5
+ </div>
6
+
7
+ Slartibartfast: the architect. From *The Hitchhiker's Guide to the Galaxy*, the curmudgeonly designer of Norway's fjords — and the namesake of this tool.
8
+
9
+ slartiarch is a coordination CLI that checks consistency between [LikeC4](https://likec4.dev) architecture
10
+ diagrams, [LinkML](https://linkml.io) schemas, and [SHACL](https://www.w3.org/TR/shacl/) shapes.
11
+ It also regenerates the diagrams in your architecture document so the prose never drifts.
12
+
13
+ Each tool is useful on its own with no slartiarch involved. slartiarch adds the seams — rules that span
14
+ two tools, and checks that the answers still agree.
15
+
16
+ ## Install
17
+
18
+ ```bash
19
+ uv tool install slartiarch
20
+ npm install likec4
21
+ pip install linkml pyshacl
22
+ ```
23
+
24
+ ## Use
25
+
26
+ ```bash
27
+ slartiarch init # scaffold docs/slarti/ and a config
28
+ slartiarch doctor # resolved paths and tool versions
29
+ slartiarch check # run all validations (CI command)
30
+ slartiarch check --json # structured output for automation
31
+ slartiarch docs # regenerate diagrams and tables into your docs
32
+ slartiarch docs --check # fail if committed output is stale
33
+ slartiarch report # every rule, shape, and check ID
34
+ slartiarch report --json # fully detailed, for agents
35
+ ```
36
+
37
+ Run `slartiarch` with no arguments for a full reference. `slartiarch report --json` describes every
38
+ rule, enforcer kind, check ID, and remedy.
39
+
40
+ ## Layout
41
+
42
+ ```
43
+ docs/
44
+ architecture.md prose with generated diagram and table regions
45
+ diagrams/ GENERATED — never hand-edited
46
+ slarti/
47
+ likec4/*.c4 LikeC4 architecture model
48
+ linkml/*.yaml LinkML domain schemas
49
+ shacl/*.ttl hand-written SHACL shapes
50
+ constraints.yaml rule registry
51
+ data/valid/* fixtures that must pass validation
52
+ data/invalid/* one fixture per SHACL rule; each must fail
53
+ ```
54
+
55
+ ## How the tools work together
56
+
57
+ LikeC4 draws the system. LinkML defines the entities. SHACL validates instances. Each
58
+ works standalone:
59
+
60
+ ```bash
61
+ likec4 start docs/slarti/likec4 # live architecture browser
62
+ gen-pydantic docs/slarti/linkml/slarti.yaml # Python interfaces from schema
63
+ pyshacl -s docs/slarti/shacl/invariants.ttl ... # validate RDF against shapes
64
+ ```
65
+
66
+ A constraint in the registry ties them together:
67
+
68
+ ```yaml
69
+ # docs/slarti/constraints.yaml
70
+ - id: D8
71
+ statement: A subtask belongs to the same list as its parent.
72
+ enforced_by:
73
+ kind: shacl_shape
74
+ ref: "todo:SubtaskSharesListWithParent"
75
+ fixture: docs/slarti/data/invalid/D8.yaml
76
+ ```
77
+
78
+ Running `slartiarch check` walks the seams: it verifies the shape exists, the fixture
79
+ fails (proving the shape fires), both halves of the ownership claim agree, and the
80
+ constraint table in your document still matches. `slartiarch docs` regenerates the Mermaid
81
+ diagrams and tables so the architecture document never goes stale.
82
+
83
+ ## Dogfooding
84
+
85
+ This repository's [docs/architecture.md](docs/architecture.md) is generated by slartiarch from
86
+ `docs/slarti/`. CI runs `slartiarch check` and `slartiarch docs --check`.
87
+
88
+ ## Dev
89
+
90
+ ```bash
91
+ make setup # uv venv + sync
92
+ npm ci # pinned LikeC4
93
+ make domain # regenerate slarti/domain.py from the LinkML schema
94
+ make ci # tests, lint, types, complexity, duplication, dead code
95
+ ```
96
+
97
+ Architecture decisions live in `docs/adr/`.
@@ -0,0 +1,31 @@
1
+ # 1. Python project
2
+
3
+ Date: 2025-09-05
4
+
5
+ ## Status
6
+
7
+ Accepted
8
+
9
+ ## Context
10
+
11
+ A new project is being started that will be implemented in Python.
12
+
13
+ ## Decision
14
+
15
+ Use a standard layout for the project with the following details:
16
+ - Use [uv](https://docs.astral.sh/uv/) as the package manager.
17
+ - Use [pytest](https://docs.pytest.org/en/stable/) as the test framework (with coverage).
18
+ - Use [adr-tools](https://github.com/npryce/adr-tools) to document architectural decisions.
19
+
20
+ Use the following layout for the python project:
21
+
22
+ ```plaintext
23
+ project-root/
24
+ ├── slarti/
25
+ ├── tests/
26
+ └── docs/
27
+ ```
28
+
29
+ ## Consequences
30
+
31
+ What becomes easier or more difficult to do and any risks introduced by the change that will need to be mitigated.
@@ -0,0 +1,45 @@
1
+ # 2. Findings are the interface
2
+
3
+ Date: 2026-07-25
4
+
5
+ ## Status
6
+
7
+ Accepted
8
+
9
+ ## Context
10
+
11
+ `slarti` has two consumers with different needs. A human wants to read what is
12
+ wrong; an LLM agent — which has no memory of the repository between sessions —
13
+ needs a stable identifier to reason about and a remedy it can act on. Exit codes
14
+ alone carry neither.
15
+
16
+ The tool also delegates every real validation to LikeC4, LinkML and pyshacl. Its
17
+ own output must therefore be clearly distinguishable from theirs, and phrased in
18
+ domain terms rather than tool terms: "class `Finding` has no owner", not
19
+ `KeyError: 'owner'`.
20
+
21
+ ## Decision
22
+
23
+ Findings, not exit codes, are the interface. Every finding carries a stable rule
24
+ ID (`OWN-1`, `REG-2`, `DOC-1`, `ENV-1`), a file, a subject named in domain terms,
25
+ a message and a remedy sentence. The same findings render as text or as JSON;
26
+ `--json` changes the encoding, never the content. IDs are permanent — retired
27
+ checks are never reused — and `slarti report --json` describes all of them.
28
+
29
+ Because the findings are the contract, the entities they are about are modelled
30
+ first-class: `Finding`, `Report`, `Constraint` and `Enforcer` are classes in
31
+ `model/schema/slarti.yaml`, with SHACL shapes over them (`D1`, `D2`, `D3`) and a
32
+ negative fixture per shape.
33
+
34
+ Diagnostics from the delegated tools are passed through unaltered and never
35
+ reformatted as findings.
36
+
37
+ ## Consequences
38
+
39
+ - Agents can converge: run `slarti check --json`, apply the remedy, re-run.
40
+ - Message wording is part of the contract, and is covered by tests.
41
+ - Adding a check means adding an ID, a remedy sentence and a catalogue entry —
42
+ slightly more work than raising an exception, deliberately.
43
+ - Rules that no model can enforce are recorded as `enforced_by: none` with a
44
+ reason and surface in the unverified-invariants table rather than being silently
45
+ absent.
@@ -0,0 +1,54 @@
1
+ # 3. The Python interfaces are a LinkML projection
2
+
3
+ Date: 2026-07-25
4
+
5
+ ## Status
6
+
7
+ Accepted
8
+
9
+ ## Context
10
+
11
+ ADR-0002 made the findings the interface and modelled the entities they are about
12
+ — `Finding`, `Report`, `Constraint`, `Enforcer` — as classes in
13
+ `model/schema/slarti.yaml`. The implementation nevertheless carried its own
14
+ hand-written dataclasses with the same names and roughly the same fields.
15
+
16
+ Two definitions of one entity is exactly the drift `slarti` exists to catch. The
17
+ schema could gain a slot the code never reads, or the code could gain a field the
18
+ shapes never see, and nothing would fail. The seam checks could not help: they
19
+ join LikeC4 to LinkML and the registry to the models, not the schema to the
20
+ Python that claims to implement it.
21
+
22
+ LinkML is a schema language whose point is projection: one definition, generated
23
+ into whatever shape a consumer needs — SHACL for pyshacl, JSON Schema for an
24
+ agent, Pydantic or TypeScript for an implementation. Hand-writing the Python was
25
+ declining the one thing LinkML is best at.
26
+
27
+ ## Decision
28
+
29
+ `slarti/domain.py` is generated from `model/schema/slarti.yaml` by `gen-pydantic`
30
+ (`make domain`) and committed. It is the only definition of the domain entities;
31
+ `findings.py`, `registry.py`, `models.py`, `inject.py` and `env.py` import from it
32
+ rather than declaring their own.
33
+
34
+ Behaviour stays hand-written. The generated classes carry shape — fields, ranges,
35
+ cardinality, permissible values — and modules add the verbs around them
36
+ (`Report.as_schema`, `registry.is_unenforced`, `Likec4Model.owners_of`). The
37
+ serialised report is built as the generated `Report`, so `slarti check --json`
38
+ emits the shape the schema declares by construction.
39
+
40
+ Drift between the schema and the committed projection is a test failure
41
+ (`tests/test_domain.py`), the same gate `slarti docs --check` applies to the
42
+ document.
43
+
44
+ ## Consequences
45
+
46
+ - A slot added to the schema is a field in the implementation after `make domain`;
47
+ a field the implementation wants must be argued for in the schema first.
48
+ - An enforcer kind that is not in the `EnforcerKind` enum is now rejected when the
49
+ registry is read, with the permissible values named.
50
+ - The generated module is excluded from ruff, mypy and vulture: the schema is
51
+ reviewed, its projection is not.
52
+ - `pydantic` becomes a runtime dependency.
53
+ - The same schema still generates SHACL, JSON Schema and TypeScript for other
54
+ consumers — the projection to Python is one target among several, not a fork.