svarupa 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 (150) hide show
  1. svarupa-0.1.0/.github/workflows/ci.yml +128 -0
  2. svarupa-0.1.0/.github/workflows/release.yml +38 -0
  3. svarupa-0.1.0/.github/workflows/svarupa.yml +61 -0
  4. svarupa-0.1.0/.gitignore +18 -0
  5. svarupa-0.1.0/.svarupa/architecture.lock +38 -0
  6. svarupa-0.1.0/LICENSE +21 -0
  7. svarupa-0.1.0/PKG-INFO +185 -0
  8. svarupa-0.1.0/README.md +152 -0
  9. svarupa-0.1.0/docs/images/demo-architecture.png +0 -0
  10. svarupa-0.1.0/docs/images/demo-deploy-topology.png +0 -0
  11. svarupa-0.1.0/docs/lockfile-grammar.md +248 -0
  12. svarupa-0.1.0/docs/research/2026-09-06-archify-teardown.md +79 -0
  13. svarupa-0.1.0/docs/research/2026-09-06-graphify-teardown.md +126 -0
  14. svarupa-0.1.0/docs/reviews/2026-08-30-p1-0-review.md +89 -0
  15. svarupa-0.1.0/docs/reviews/2026-08-30-spike-0-results.md +149 -0
  16. svarupa-0.1.0/docs/reviews/2026-08-30-spike-0-review.md +119 -0
  17. svarupa-0.1.0/docs/reviews/2026-08-30-spike-0b-call-resolution.md +139 -0
  18. svarupa-0.1.0/docs/reviews/2026-08-30-spike-0c-typescript.md +121 -0
  19. svarupa-0.1.0/docs/reviews/2026-08-31-p1-1-detect-review.md +79 -0
  20. svarupa-0.1.0/docs/reviews/2026-08-31-p1-2-wave1-review.md +74 -0
  21. svarupa-0.1.0/docs/reviews/2026-09-01-p1-2-wave2-review.md +76 -0
  22. svarupa-0.1.0/docs/reviews/2026-09-01-p1-3-p1-4-review.md +84 -0
  23. svarupa-0.1.0/docs/reviews/2026-09-02-p1-5-derive-review.md +101 -0
  24. svarupa-0.1.0/docs/reviews/2026-09-02-p1-6-wave1-layout-review.md +134 -0
  25. svarupa-0.1.0/docs/reviews/2026-09-03-p1-6-wave2-emit-review.md +133 -0
  26. svarupa-0.1.0/docs/reviews/2026-09-04-p1-7-lockfile-review.md +128 -0
  27. svarupa-0.1.0/docs/reviews/2026-09-04-replay-corpus.md +134 -0
  28. svarupa-0.1.0/docs/reviews/2026-09-05-wave11-review.md +122 -0
  29. svarupa-0.1.0/docs/reviews/2026-09-06-express-mounts-review.md +72 -0
  30. svarupa-0.1.0/docs/reviews/2026-09-06-p1-8-setup-review.md +103 -0
  31. svarupa-0.1.0/docs/reviews/2026-09-06-semantics-review.md +76 -0
  32. svarupa-0.1.0/docs/reviews/2026-09-06-ts-routes-review.md +65 -0
  33. svarupa-0.1.0/docs/reviews/2026-09-06-wave-a-review.md +72 -0
  34. svarupa-0.1.0/docs/reviews/2026-09-06-wave-b-review.md +89 -0
  35. svarupa-0.1.0/docs/reviews/2026-09-07-wave-c-review.md +77 -0
  36. svarupa-0.1.0/docs/reviews/2026-09-07-wave-de-review.md +92 -0
  37. svarupa-0.1.0/docs/reviews/2026-09-08-review-20-product-readiness.md +202 -0
  38. svarupa-0.1.0/docs/reviews/2026-09-08-review-21-rereview.md +200 -0
  39. svarupa-0.1.0/docs/reviews/2026-09-09-review-22-ship-gate.md +85 -0
  40. svarupa-0.1.0/docs/reviews/2026-09-09-review-23-ship-gate-rerun.md +239 -0
  41. svarupa-0.1.0/docs/reviews/2026-09-09-review-24-confirmation.md +134 -0
  42. svarupa-0.1.0/docs/reviews/2026-09-20-review-25-environment-distinction.md +65 -0
  43. svarupa-0.1.0/docs/reviews/2026-09-24-review-26-scale-and-performance.md +161 -0
  44. svarupa-0.1.0/docs/reviews/TEMPLATE.md +100 -0
  45. svarupa-0.1.0/docs/superpowers/specs/2026-08-30-svarupa-design.md +835 -0
  46. svarupa-0.1.0/docs/superpowers/specs/2026-09-06-conceptual-diagrams-design.md +277 -0
  47. svarupa-0.1.0/docs/superpowers/specs/2026-09-19-distribution-onboarding-design.md +73 -0
  48. svarupa-0.1.0/docs/superpowers/specs/2026-09-19-environment-distinction-design.md +89 -0
  49. svarupa-0.1.0/pyproject.toml +97 -0
  50. svarupa-0.1.0/scripts/mutate_conceptual.py +287 -0
  51. svarupa-0.1.0/scripts/mutate_dataflow.py +341 -0
  52. svarupa-0.1.0/scripts/mutate_emit.py +167 -0
  53. svarupa-0.1.0/scripts/mutate_environments.py +152 -0
  54. svarupa-0.1.0/scripts/mutate_lock.py +218 -0
  55. svarupa-0.1.0/scripts/mutate_query.py +354 -0
  56. svarupa-0.1.0/scripts/mutate_review20.py +394 -0
  57. svarupa-0.1.0/scripts/mutate_scale.py +140 -0
  58. svarupa-0.1.0/scripts/mutate_semantics.py +424 -0
  59. svarupa-0.1.0/scripts/mutate_setup.py +193 -0
  60. svarupa-0.1.0/scripts/mutate_wave11.py +157 -0
  61. svarupa-0.1.0/scripts/mutate_waved.py +263 -0
  62. svarupa-0.1.0/scripts/render_fixture_lock.py +100 -0
  63. svarupa-0.1.0/scripts/replay_history.py +206 -0
  64. svarupa-0.1.0/skills/svarupa/SKILL.md +144 -0
  65. svarupa-0.1.0/svarupa/__init__.py +8 -0
  66. svarupa-0.1.0/svarupa/__main__.py +9 -0
  67. svarupa-0.1.0/svarupa/build.py +852 -0
  68. svarupa-0.1.0/svarupa/cli.py +430 -0
  69. svarupa-0.1.0/svarupa/cluster.py +366 -0
  70. svarupa-0.1.0/svarupa/derive/__init__.py +101 -0
  71. svarupa-0.1.0/svarupa/derive/architecture.py +1101 -0
  72. svarupa-0.1.0/svarupa/derive/base.py +460 -0
  73. svarupa-0.1.0/svarupa/derive/components.py +294 -0
  74. svarupa-0.1.0/svarupa/derive/dataflow.py +750 -0
  75. svarupa-0.1.0/svarupa/derive/erd.py +54 -0
  76. svarupa-0.1.0/svarupa/derive/system.py +382 -0
  77. svarupa-0.1.0/svarupa/detect.py +793 -0
  78. svarupa-0.1.0/svarupa/diagnostics.py +177 -0
  79. svarupa-0.1.0/svarupa/emit/__init__.py +277 -0
  80. svarupa-0.1.0/svarupa/emit/cards.py +291 -0
  81. svarupa-0.1.0/svarupa/emit/data.py +531 -0
  82. svarupa-0.1.0/svarupa/emit/markup.py +126 -0
  83. svarupa-0.1.0/svarupa/emit/report.py +337 -0
  84. svarupa-0.1.0/svarupa/emit/svg.py +597 -0
  85. svarupa-0.1.0/svarupa/emit/viewer.py +1706 -0
  86. svarupa-0.1.0/svarupa/extract/__init__.py +282 -0
  87. svarupa-0.1.0/svarupa/extract/base.py +516 -0
  88. svarupa-0.1.0/svarupa/extract/compose.py +408 -0
  89. svarupa-0.1.0/svarupa/extract/environments.py +567 -0
  90. svarupa-0.1.0/svarupa/extract/python.py +460 -0
  91. svarupa-0.1.0/svarupa/extract/rationale.py +206 -0
  92. svarupa-0.1.0/svarupa/extract/resolve.py +1113 -0
  93. svarupa-0.1.0/svarupa/extract/semantics.py +679 -0
  94. svarupa-0.1.0/svarupa/extract/typescript.py +787 -0
  95. svarupa-0.1.0/svarupa/extract/vocabulary.py +259 -0
  96. svarupa-0.1.0/svarupa/identity.py +94 -0
  97. svarupa-0.1.0/svarupa/layout/__init__.py +199 -0
  98. svarupa-0.1.0/svarupa/layout/compose.py +99 -0
  99. svarupa-0.1.0/svarupa/layout/engines.py +1699 -0
  100. svarupa-0.1.0/svarupa/layout/geometry.py +283 -0
  101. svarupa-0.1.0/svarupa/layout/sugiyama.py +200 -0
  102. svarupa-0.1.0/svarupa/layout/text.py +161 -0
  103. svarupa-0.1.0/svarupa/layout/validate.py +584 -0
  104. svarupa-0.1.0/svarupa/lock/__init__.py +54 -0
  105. svarupa-0.1.0/svarupa/lock/build.py +306 -0
  106. svarupa-0.1.0/svarupa/lock/diff.py +220 -0
  107. svarupa-0.1.0/svarupa/lock/grammar.py +375 -0
  108. svarupa-0.1.0/svarupa/model.py +283 -0
  109. svarupa-0.1.0/svarupa/py.typed +0 -0
  110. svarupa-0.1.0/svarupa/query/__init__.py +464 -0
  111. svarupa-0.1.0/svarupa/query/cli.py +185 -0
  112. svarupa-0.1.0/svarupa/query/mcp_server.py +127 -0
  113. svarupa-0.1.0/svarupa/setup/__init__.py +162 -0
  114. svarupa-0.1.0/svarupa/setup/base.py +28 -0
  115. svarupa-0.1.0/svarupa/setup/ci_github.py +119 -0
  116. svarupa-0.1.0/svarupa/setup/ci_gitlab.py +101 -0
  117. svarupa-0.1.0/svarupa/setup/skill.py +176 -0
  118. svarupa-0.1.0/svarupa/tsconfig.py +201 -0
  119. svarupa-0.1.0/tests/__init__.py +0 -0
  120. svarupa-0.1.0/tests/fixtures/louvain_seed_sensitive.json +414 -0
  121. svarupa-0.1.0/tests/js/package-lock.json +815 -0
  122. svarupa-0.1.0/tests/js/package.json +6 -0
  123. svarupa-0.1.0/tests/js/viewer_harness.js +390 -0
  124. svarupa-0.1.0/tests/test_build.py +626 -0
  125. svarupa-0.1.0/tests/test_cards.py +249 -0
  126. svarupa-0.1.0/tests/test_cluster.py +286 -0
  127. svarupa-0.1.0/tests/test_compose.py +184 -0
  128. svarupa-0.1.0/tests/test_conceptual.py +526 -0
  129. svarupa-0.1.0/tests/test_conceptual_review16.py +369 -0
  130. svarupa-0.1.0/tests/test_dataflow.py +667 -0
  131. svarupa-0.1.0/tests/test_derive.py +1010 -0
  132. svarupa-0.1.0/tests/test_detect.py +671 -0
  133. svarupa-0.1.0/tests/test_determinism.py +326 -0
  134. svarupa-0.1.0/tests/test_diagnostics.py +390 -0
  135. svarupa-0.1.0/tests/test_emit.py +1551 -0
  136. svarupa-0.1.0/tests/test_env_diagram.py +266 -0
  137. svarupa-0.1.0/tests/test_environments.py +606 -0
  138. svarupa-0.1.0/tests/test_extract.py +780 -0
  139. svarupa-0.1.0/tests/test_extract_typescript.py +565 -0
  140. svarupa-0.1.0/tests/test_graph_query.py +627 -0
  141. svarupa-0.1.0/tests/test_layout.py +1621 -0
  142. svarupa-0.1.0/tests/test_lock.py +1142 -0
  143. svarupa-0.1.0/tests/test_lock_grammar.py +368 -0
  144. svarupa-0.1.0/tests/test_markup_types.py +146 -0
  145. svarupa-0.1.0/tests/test_model.py +217 -0
  146. svarupa-0.1.0/tests/test_semantics.py +1244 -0
  147. svarupa-0.1.0/tests/test_setup.py +446 -0
  148. svarupa-0.1.0/tests/test_viewer_js.py +79 -0
  149. svarupa-0.1.0/tests/test_waved.py +493 -0
  150. svarupa-0.1.0/uv.lock +1510 -0
@@ -0,0 +1,128 @@
1
+ name: ci
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ concurrency:
9
+ group: ${{ github.workflow }}-${{ github.ref }}
10
+ cancel-in-progress: true
11
+
12
+ env:
13
+ FORCE_COLOR: "1"
14
+
15
+ jobs:
16
+ lint:
17
+ runs-on: ubuntu-latest
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+ - uses: astral-sh/setup-uv@v5
21
+ with:
22
+ enable-cache: true
23
+ - run: uv sync --all-extras --dev
24
+ - name: ruff
25
+ run: uv run ruff check svarupa tests
26
+ - name: ruff format check
27
+ run: uv run ruff format --check svarupa tests
28
+ - name: pyright
29
+ run: uv run pyright svarupa
30
+
31
+ test:
32
+ name: test ${{ matrix.os }} py${{ matrix.python }}
33
+ runs-on: ${{ matrix.os }}
34
+ strategy:
35
+ fail-fast: false
36
+ matrix:
37
+ os: [ubuntu-latest, macos-latest]
38
+ python: ["3.10", "3.11", "3.12", "3.13"]
39
+ steps:
40
+ - uses: actions/checkout@v4
41
+ - uses: astral-sh/setup-uv@v5
42
+ with:
43
+ enable-cache: true
44
+ - run: uv sync --dev --python ${{ matrix.python }}
45
+ - name: pytest
46
+ run: uv run pytest -q
47
+
48
+ # The determinism suite is the highest-value test in the project. If it
49
+ # regresses, every PR shows architecture changes that did not happen, teams
50
+ # learn to ignore the check, and the governance pillar is worthless.
51
+ determinism:
52
+ name: determinism ${{ matrix.os }} py${{ matrix.python }} seed=${{ matrix.hashseed }}
53
+ runs-on: ${{ matrix.os }}
54
+ strategy:
55
+ fail-fast: false
56
+ matrix:
57
+ os: [ubuntu-latest, macos-latest]
58
+ python: ["3.10", "3.13"]
59
+ hashseed: ["0", "12345", "random"]
60
+ env:
61
+ PYTHONHASHSEED: ${{ matrix.hashseed }}
62
+ LC_ALL: C
63
+ LANG: C
64
+ steps:
65
+ - uses: actions/checkout@v4
66
+ - uses: astral-sh/setup-uv@v5
67
+ with:
68
+ enable-cache: true
69
+ - run: uv sync --dev --python ${{ matrix.python }}
70
+ - name: determinism suite
71
+ run: uv run pytest -q -m determinism
72
+
73
+ # Gate 2 from Spike 0, deferred at the time because a Docker daemon was not
74
+ # available locally. Renders the same corpus on Linux and macOS and asserts
75
+ # the bytes match. A hash mismatch here means the lockfile is not portable,
76
+ # which breaks per-PR diffing at the root.
77
+ cross-platform-bytes:
78
+ name: byte identity (${{ matrix.os }})
79
+ runs-on: ${{ matrix.os }}
80
+ strategy:
81
+ fail-fast: false
82
+ matrix:
83
+ os: [ubuntu-latest, macos-latest]
84
+ env:
85
+ LC_ALL: C
86
+ LANG: C
87
+ steps:
88
+ - uses: actions/checkout@v4
89
+ - uses: astral-sh/setup-uv@v5
90
+ with:
91
+ enable-cache: true
92
+ - run: uv sync --dev
93
+ # Builds from a real fixture tree on disk, not an in-memory string.
94
+ # An in-memory corpus is pure str manipulation and can essentially never
95
+ # differ across platforms, so it verified the serializer's determinism
96
+ # rather than the product's. NFD paths, dirent ordering and
97
+ # case-insensitive volumes only enter through an actual filesystem walk.
98
+ - name: build from a real fixture tree
99
+ run: uv run python scripts/render_fixture_lock.py lockfile.out
100
+ - name: hash
101
+ run: |
102
+ python3 -c "
103
+ import hashlib, pathlib
104
+ b = pathlib.Path('lockfile.out').read_bytes()
105
+ print(f'sha256={hashlib.sha256(b).hexdigest()} bytes={len(b)}')
106
+ "
107
+ - uses: actions/upload-artifact@v4
108
+ with:
109
+ name: lockfile-${{ matrix.os }}
110
+ path: lockfile.out
111
+
112
+ compare-bytes:
113
+ needs: cross-platform-bytes
114
+ runs-on: ubuntu-latest
115
+ steps:
116
+ - uses: actions/download-artifact@v4
117
+ with:
118
+ path: artifacts
119
+ - name: assert Linux and macOS bytes are identical
120
+ run: |
121
+ a=artifacts/lockfile-ubuntu-latest/lockfile.out
122
+ b=artifacts/lockfile-macos-latest/lockfile.out
123
+ if ! cmp -s "$a" "$b"; then
124
+ echo "::error::lockfile bytes differ between Linux and macOS"
125
+ diff <(cat -A "$a") <(cat -A "$b") | head -40
126
+ exit 1
127
+ fi
128
+ echo "byte identity confirmed: $(sha256sum "$a" | cut -d' ' -f1)"
@@ -0,0 +1,38 @@
1
+ name: release
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*"]
6
+
7
+ permissions:
8
+ # id-token is what PyPI trusted publishing (OIDC) exchanges for a short-lived
9
+ # upload credential; no API token is stored anywhere.
10
+ id-token: write
11
+ contents: read
12
+
13
+ jobs:
14
+ publish:
15
+ runs-on: ubuntu-latest
16
+ environment: pypi
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - uses: astral-sh/setup-uv@v5
21
+
22
+ - name: check the tag matches the package version
23
+ shell: bash
24
+ run: |
25
+ TAG="${GITHUB_REF_NAME#v}"
26
+ PKG=$(python3 -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
27
+ test "$TAG" = "$PKG" || { echo "tag v$TAG != pyproject version $PKG"; exit 1; }
28
+
29
+ - name: run the test suite
30
+ run: |
31
+ uv sync --frozen
32
+ uv run pytest
33
+
34
+ - name: build
35
+ run: uv build
36
+
37
+ - name: publish to PyPI
38
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,61 @@
1
+ name: architecture
2
+
3
+ on:
4
+ pull_request:
5
+
6
+ permissions:
7
+ contents: read
8
+
9
+ jobs:
10
+ diff:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ with:
15
+ # The base branch's tree and its committed lockfile are both needed,
16
+ # so the fetch cannot be depth 1.
17
+ fetch-depth: 0
18
+
19
+ - uses: astral-sh/setup-uv@v5
20
+
21
+ - name: install svarupa
22
+ run: uv tool install 'svarupa==0.1.0'
23
+
24
+ - name: regenerate the base lockfile from base-branch code
25
+ shell: bash
26
+ run: |
27
+ git worktree add /tmp/base-src "${{ github.event.pull_request.base.sha }}"
28
+ svarupa /tmp/base-src --lock --out /tmp/base-artifact
29
+
30
+ # `shell: bash` turns pipefail on, so nothing here hides a failure.
31
+ # The delta is written to the job summary before the exit code is
32
+ # re-raised: a failing run is the one whose delta must be readable.
33
+ - name: architecture delta
34
+ shell: bash
35
+ run: |
36
+ BASE_SHA="${{ github.event.pull_request.base.sha }}"
37
+ # A base commit git cannot read is a job failure, never a silent
38
+ # fallback: falling back would drop the drift guard exactly when
39
+ # the checkout is wrong.
40
+ git cat-file -e "$BASE_SHA^{commit}"
41
+ if git cat-file -e "$BASE_SHA:.svarupa/architecture.lock" 2>/dev/null; then
42
+ git show "$BASE_SHA:.svarupa/architecture.lock" > /tmp/committed-base.lock
43
+ svarupa . --lock \
44
+ --diff /tmp/committed-base.lock \
45
+ --drift-base /tmp/base-artifact/architecture.lock \
46
+ > /tmp/delta.txt 2>&1 && code=0 || code=$?
47
+ else
48
+ # First adoption: the base branch has no committed lockfile yet, so
49
+ # the delta is taken against the regenerated base directly.
50
+ svarupa . --lock \
51
+ --diff /tmp/base-artifact/architecture.lock \
52
+ > /tmp/delta.txt 2>&1 && code=0 || code=$?
53
+ fi
54
+ cat /tmp/delta.txt
55
+ {
56
+ echo '## Architecture delta'
57
+ echo '````'
58
+ cat /tmp/delta.txt
59
+ echo '````'
60
+ } >> "$GITHUB_STEP_SUMMARY"
61
+ exit "$code"
@@ -0,0 +1,18 @@
1
+ # The artifact directory is regenerated on every run, with one exception:
2
+ # `architecture.lock` is committed on purpose. It is the diffable fingerprint
3
+ # of the architecture, and a reviewer is meant to see `dep api -> auth` appear
4
+ # as a green line in the pull request before CI even runs. Ignoring the whole
5
+ # directory would ignore the one file the design says to commit.
6
+ .svarupa/*
7
+ !.svarupa/architecture.lock
8
+
9
+ *.egg-info/
10
+ *.pyc
11
+ .pytest_cache/
12
+ .ruff_cache/
13
+ .venv/
14
+ .venv2/
15
+ __pycache__/
16
+ build/
17
+ dist/
18
+ tests/js/node_modules/
@@ -0,0 +1,38 @@
1
+ # svarupa 0.1.0
2
+ # schema 1.7
3
+ # grammars python@0.25.0
4
+ dep scripts svarupa
5
+ dep scripts svarupa/extract
6
+ dep scripts svarupa/lock
7
+ dep svarupa svarupa/derive
8
+ dep svarupa svarupa/emit
9
+ dep svarupa svarupa/extract
10
+ dep svarupa svarupa/lock
11
+ dep svarupa svarupa/query
12
+ dep svarupa svarupa/setup
13
+ dep svarupa/derive svarupa
14
+ dep svarupa/derive svarupa/extract
15
+ dep svarupa/emit svarupa
16
+ dep svarupa/emit svarupa/derive
17
+ dep svarupa/emit svarupa/extract
18
+ dep svarupa/emit svarupa/layout
19
+ dep svarupa/emit svarupa/lock
20
+ dep svarupa/extract svarupa
21
+ dep svarupa/layout svarupa
22
+ dep svarupa/layout svarupa/derive
23
+ dep svarupa/lock svarupa
24
+ dep svarupa/lock svarupa/extract
25
+ dep svarupa/query svarupa
26
+ dep svarupa/setup svarupa
27
+ entrypoint svarupa svarupa
28
+ environment pypi
29
+ module scripts
30
+ module svarupa
31
+ module svarupa/derive
32
+ module svarupa/emit
33
+ module svarupa/extract
34
+ module svarupa/layout
35
+ module svarupa/lock
36
+ module svarupa/query
37
+ module svarupa/setup
38
+ role svarupa cli
svarupa-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 svarupa contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
svarupa-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,185 @@
1
+ Metadata-Version: 2.5
2
+ Name: svarupa
3
+ Version: 0.1.0
4
+ Summary: Verified architecture diagrams and a queryable knowledge graph, derived from your codebase. Every box points at a line of code.
5
+ Project-URL: Homepage, https://github.com/aiwithvd/svarupa
6
+ Project-URL: Repository, https://github.com/aiwithvd/svarupa
7
+ Project-URL: Issues, https://github.com/aiwithvd/svarupa/issues
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: architecture,diagrams,knowledge-graph,static-analysis,tree-sitter
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Software Development :: Documentation
19
+ Requires-Python: >=3.10
20
+ Requires-Dist: networkx<4,>=3.2
21
+ Requires-Dist: pathspec<2,>=0.12
22
+ Requires-Dist: pyyaml<7,>=6
23
+ Requires-Dist: tomli>=2; python_version < '3.11'
24
+ Requires-Dist: tree-sitter-python==0.25.0
25
+ Requires-Dist: tree-sitter-typescript==0.23.2
26
+ Requires-Dist: tree-sitter<0.27,>=0.26
27
+ Provides-Extra: leiden
28
+ Requires-Dist: leidenalg>=0.10; extra == 'leiden'
29
+ Requires-Dist: python-igraph>=0.11; extra == 'leiden'
30
+ Provides-Extra: mcp
31
+ Requires-Dist: mcp>=2; extra == 'mcp'
32
+ Description-Content-Type: text/markdown
33
+
34
+ # Svarupa
35
+
36
+ **स्वरूप** — *"its own true form."*
37
+
38
+ Svarupa reads a codebase and produces a **verified** map of it: a queryable
39
+ knowledge graph plus six types of architecture diagram (architecture, module
40
+ dependencies, data flow, request flow, deploy topology, ERD), delivered as one
41
+ interactive HTML artifact. A type the evidence cannot produce is named as
42
+ absent, never fabricated.
43
+
44
+ The defining constraint: **every node and every edge in every diagram carries
45
+ `file:line` evidence, or it does not render.** Not a heuristic guess, not a
46
+ model's plausible story. A claim you can click through to the source line that
47
+ proves it.
48
+
49
+ The name states the thesis. Svarupa is a thing's actual form, not its intended
50
+ one. Architecture documents describe what someone meant to build. Svarupa
51
+ renders what exists.
52
+
53
+ > **Status: alpha (v0.1.0).** The evidence contract is stable; flags and output
54
+ > formats may still change between minor versions.
55
+
56
+ ---
57
+
58
+ ## Quickstart
59
+
60
+ ```bash
61
+ uv tool install svarupa # or: pipx install svarupa / pip install svarupa
62
+ svarupa . # scans the repo, writes .svarupa/
63
+ open .svarupa/index.html
64
+ ```
65
+
66
+ That is the whole setup — no configuration file, no daemon, no account. The
67
+ first run of a typical repository takes seconds.
68
+
69
+ `python -m svarupa` is the same command; `--out ./map` writes the artifact
70
+ somewhere other than `<repo>/.svarupa/`. The artifact holds:
71
+
72
+ - `index.html`: every diagram type as a tab. Click a box for its passport
73
+ (kind, connections, reach, cited source lines); double-click a drillable box,
74
+ or click its chevron, to open it in place down to component and code level.
75
+ - `graph.json`: the knowledge graph. `svarupa query <dir> get_node <label>`,
76
+ `get_neighbors`, `shortest_path`, `affected`, `god_nodes`, `graph_stats`,
77
+ `query_graph`; `svarupa mcp <dir>` serves the same over MCP.
78
+ - `REPORT.md`: the resolution scorecard and every finding, with its code.
79
+ - Declared environments (production, staging, qa, development) are extracted
80
+ with `file:line` evidence and surface in the CLI summary, a REPORT.md
81
+ section, `graph.json` nodes, and a cited strip on the deploy topology.
82
+ - `--lock` writes the committed architecture lockfile; `--diff` prints the
83
+ architecture delta against a base lockfile. `svarupa setup ci_github`
84
+ (or `ci_gitlab`) installs the per-pull-request workflow.
85
+
86
+ ![The architecture tab of the demo repository](docs/images/demo-architecture.png)
87
+
88
+ ![The deploy topology tab, with the declared-environments strip on top](docs/images/demo-deploy-topology.png)
89
+
90
+ Declared environments — production, staging, qa, development — are extracted
91
+ with `file:line` evidence and shown as a cited strip above the topology.
92
+
93
+ ---
94
+
95
+ ## Why
96
+
97
+ Every tool in this space sits on one side of a divide and cannot cross it.
98
+
99
+ | Tool | Analyzes code | Draws formal diagrams |
100
+ |---|---|---|
101
+ | Graphify | Deeply, 25+ languages | No — force-directed node-link graphs only |
102
+ | Understand-Anything | Yes, AST + LLM | No — force layouts only |
103
+ | Archify | No — reads nothing | Yes, five types, excellent quality |
104
+ | Cocoon-AI | No — prompt-to-diagram | Yes, architecture only |
105
+
106
+ Nobody closes the loop `code → AST → derived formal diagram`. The analyzers
107
+ produce blobs; the drawers produce unverifiable pictures.
108
+
109
+ ### The larger thesis: continuous architecture governance
110
+
111
+ Graphify and Understand-Anything are *onboarding* tools. You run one when you
112
+ join a project, look at the graph, and never open it again.
113
+
114
+ Because Svarupa's output is deterministic and evidence-backed, it runs in CI on
115
+ every pull request: diff the architecture, comment the impact, fail the build
116
+ on policy violations. **Cross-language architecture linting is a category
117
+ nobody occupies.**
118
+
119
+ ```
120
+ Architecture impact of #482
121
+
122
+ + 2 components billing.refunds, billing.webhooks
123
+ + 1 datastore redis (compose.yml:31)
124
+ ! NEW DEPENDENCY billing -> auth.internal
125
+ via billing/refunds.py:44
126
+ + 4 endpoints POST /refunds, GET /refunds/{id}, ...
127
+ ```
128
+
129
+ ---
130
+
131
+ ## Design principles
132
+
133
+ **Fail-closed on evidence.** An element without a source location is not
134
+ emitted. When Python's dynamic dispatch means a call target cannot be pinned,
135
+ no edge is written and the gap is counted in the report, rather than papered
136
+ over with a plausible one.
137
+
138
+ **Three-bin honesty.** The resolution scorecard reports *resolved*,
139
+ *known-external*, and **unresolved-unknown**. Counting every failure as
140
+ "external" would launder resolver bugs into a number that looks like honesty.
141
+
142
+ **Deterministic by contract.** Same commit, same bytes, on any machine.
143
+ Canonical ordering, `==`-pinned grammars, NFC-normalized paths, no timestamps,
144
+ no locale-dependent formatting. Verified in CI across Linux and macOS on Python
145
+ 3.10 through 3.13 with `PYTHONHASHSEED` varied.
146
+
147
+ **Identity comes from structure, not from clustering.** Lockfile modules are
148
+ directories, packages, and workspace members. Community detection is
149
+ chaotically sensitive to input perturbation — measured: one added import flips
150
+ 25-38% of community assignments — so it is used for visual grouping only and
151
+ never reaches anything committed.
152
+
153
+ **Measured, not assumed.** Function-level sequence diagrams were cut after
154
+ measurement showed call resolution at ~20% on service code with median chain
155
+ depth 0-1. Replaying this repository's own real history (15 commits of actual
156
+ development: license swaps, viewer work, layout fixes) produced a median of
157
+ **0 lockfile lines per commit** — the diff moves only when the architecture
158
+ moves, not when code churns. See `docs/reviews/` for the numbers and the
159
+ reasoning.
160
+
161
+ ---
162
+
163
+ ## Development
164
+
165
+ ```bash
166
+ uv sync
167
+ uv run pytest
168
+ uv run ruff check svarupa
169
+ uv run pyright svarupa
170
+ ```
171
+
172
+ Design and review history:
173
+
174
+ - `docs/superpowers/specs/` — the design, with a decision log carrying the
175
+ reasoning behind each choice
176
+ - `docs/reviews/` — adversarial reviews, raw and triaged. Every finding is
177
+ marked Accepted, Rejected, or Deferred **with a recorded why**, because a
178
+ rejected finding needs its reasoning kept just as much as an accepted one
179
+
180
+ ---
181
+
182
+ ## License
183
+
184
+ MIT. See `LICENSE`.
185
+
@@ -0,0 +1,152 @@
1
+ # Svarupa
2
+
3
+ **स्वरूप** — *"its own true form."*
4
+
5
+ Svarupa reads a codebase and produces a **verified** map of it: a queryable
6
+ knowledge graph plus six types of architecture diagram (architecture, module
7
+ dependencies, data flow, request flow, deploy topology, ERD), delivered as one
8
+ interactive HTML artifact. A type the evidence cannot produce is named as
9
+ absent, never fabricated.
10
+
11
+ The defining constraint: **every node and every edge in every diagram carries
12
+ `file:line` evidence, or it does not render.** Not a heuristic guess, not a
13
+ model's plausible story. A claim you can click through to the source line that
14
+ proves it.
15
+
16
+ The name states the thesis. Svarupa is a thing's actual form, not its intended
17
+ one. Architecture documents describe what someone meant to build. Svarupa
18
+ renders what exists.
19
+
20
+ > **Status: alpha (v0.1.0).** The evidence contract is stable; flags and output
21
+ > formats may still change between minor versions.
22
+
23
+ ---
24
+
25
+ ## Quickstart
26
+
27
+ ```bash
28
+ uv tool install svarupa # or: pipx install svarupa / pip install svarupa
29
+ svarupa . # scans the repo, writes .svarupa/
30
+ open .svarupa/index.html
31
+ ```
32
+
33
+ That is the whole setup — no configuration file, no daemon, no account. The
34
+ first run of a typical repository takes seconds.
35
+
36
+ `python -m svarupa` is the same command; `--out ./map` writes the artifact
37
+ somewhere other than `<repo>/.svarupa/`. The artifact holds:
38
+
39
+ - `index.html`: every diagram type as a tab. Click a box for its passport
40
+ (kind, connections, reach, cited source lines); double-click a drillable box,
41
+ or click its chevron, to open it in place down to component and code level.
42
+ - `graph.json`: the knowledge graph. `svarupa query <dir> get_node <label>`,
43
+ `get_neighbors`, `shortest_path`, `affected`, `god_nodes`, `graph_stats`,
44
+ `query_graph`; `svarupa mcp <dir>` serves the same over MCP.
45
+ - `REPORT.md`: the resolution scorecard and every finding, with its code.
46
+ - Declared environments (production, staging, qa, development) are extracted
47
+ with `file:line` evidence and surface in the CLI summary, a REPORT.md
48
+ section, `graph.json` nodes, and a cited strip on the deploy topology.
49
+ - `--lock` writes the committed architecture lockfile; `--diff` prints the
50
+ architecture delta against a base lockfile. `svarupa setup ci_github`
51
+ (or `ci_gitlab`) installs the per-pull-request workflow.
52
+
53
+ ![The architecture tab of the demo repository](docs/images/demo-architecture.png)
54
+
55
+ ![The deploy topology tab, with the declared-environments strip on top](docs/images/demo-deploy-topology.png)
56
+
57
+ Declared environments — production, staging, qa, development — are extracted
58
+ with `file:line` evidence and shown as a cited strip above the topology.
59
+
60
+ ---
61
+
62
+ ## Why
63
+
64
+ Every tool in this space sits on one side of a divide and cannot cross it.
65
+
66
+ | Tool | Analyzes code | Draws formal diagrams |
67
+ |---|---|---|
68
+ | Graphify | Deeply, 25+ languages | No — force-directed node-link graphs only |
69
+ | Understand-Anything | Yes, AST + LLM | No — force layouts only |
70
+ | Archify | No — reads nothing | Yes, five types, excellent quality |
71
+ | Cocoon-AI | No — prompt-to-diagram | Yes, architecture only |
72
+
73
+ Nobody closes the loop `code → AST → derived formal diagram`. The analyzers
74
+ produce blobs; the drawers produce unverifiable pictures.
75
+
76
+ ### The larger thesis: continuous architecture governance
77
+
78
+ Graphify and Understand-Anything are *onboarding* tools. You run one when you
79
+ join a project, look at the graph, and never open it again.
80
+
81
+ Because Svarupa's output is deterministic and evidence-backed, it runs in CI on
82
+ every pull request: diff the architecture, comment the impact, fail the build
83
+ on policy violations. **Cross-language architecture linting is a category
84
+ nobody occupies.**
85
+
86
+ ```
87
+ Architecture impact of #482
88
+
89
+ + 2 components billing.refunds, billing.webhooks
90
+ + 1 datastore redis (compose.yml:31)
91
+ ! NEW DEPENDENCY billing -> auth.internal
92
+ via billing/refunds.py:44
93
+ + 4 endpoints POST /refunds, GET /refunds/{id}, ...
94
+ ```
95
+
96
+ ---
97
+
98
+ ## Design principles
99
+
100
+ **Fail-closed on evidence.** An element without a source location is not
101
+ emitted. When Python's dynamic dispatch means a call target cannot be pinned,
102
+ no edge is written and the gap is counted in the report, rather than papered
103
+ over with a plausible one.
104
+
105
+ **Three-bin honesty.** The resolution scorecard reports *resolved*,
106
+ *known-external*, and **unresolved-unknown**. Counting every failure as
107
+ "external" would launder resolver bugs into a number that looks like honesty.
108
+
109
+ **Deterministic by contract.** Same commit, same bytes, on any machine.
110
+ Canonical ordering, `==`-pinned grammars, NFC-normalized paths, no timestamps,
111
+ no locale-dependent formatting. Verified in CI across Linux and macOS on Python
112
+ 3.10 through 3.13 with `PYTHONHASHSEED` varied.
113
+
114
+ **Identity comes from structure, not from clustering.** Lockfile modules are
115
+ directories, packages, and workspace members. Community detection is
116
+ chaotically sensitive to input perturbation — measured: one added import flips
117
+ 25-38% of community assignments — so it is used for visual grouping only and
118
+ never reaches anything committed.
119
+
120
+ **Measured, not assumed.** Function-level sequence diagrams were cut after
121
+ measurement showed call resolution at ~20% on service code with median chain
122
+ depth 0-1. Replaying this repository's own real history (15 commits of actual
123
+ development: license swaps, viewer work, layout fixes) produced a median of
124
+ **0 lockfile lines per commit** — the diff moves only when the architecture
125
+ moves, not when code churns. See `docs/reviews/` for the numbers and the
126
+ reasoning.
127
+
128
+ ---
129
+
130
+ ## Development
131
+
132
+ ```bash
133
+ uv sync
134
+ uv run pytest
135
+ uv run ruff check svarupa
136
+ uv run pyright svarupa
137
+ ```
138
+
139
+ Design and review history:
140
+
141
+ - `docs/superpowers/specs/` — the design, with a decision log carrying the
142
+ reasoning behind each choice
143
+ - `docs/reviews/` — adversarial reviews, raw and triaged. Every finding is
144
+ marked Accepted, Rejected, or Deferred **with a recorded why**, because a
145
+ rejected finding needs its reasoning kept just as much as an accepted one
146
+
147
+ ---
148
+
149
+ ## License
150
+
151
+ MIT. See `LICENSE`.
152
+