hashloom 0.4.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 (185) hide show
  1. hashloom-0.4.0/.dockerignore +6 -0
  2. hashloom-0.4.0/.github/ISSUE_TEMPLATE/bug_report.md +23 -0
  3. hashloom-0.4.0/.github/ISSUE_TEMPLATE/feature_request.md +17 -0
  4. hashloom-0.4.0/.github/PULL_REQUEST_TEMPLATE.md +15 -0
  5. hashloom-0.4.0/.github/workflows/ci.yml +58 -0
  6. hashloom-0.4.0/.github/workflows/release.yml +84 -0
  7. hashloom-0.4.0/.gitignore +10 -0
  8. hashloom-0.4.0/.mcp.json +8 -0
  9. hashloom-0.4.0/CHANGELOG.md +245 -0
  10. hashloom-0.4.0/CLAUDE.md +116 -0
  11. hashloom-0.4.0/CONTRIBUTING.md +54 -0
  12. hashloom-0.4.0/Dockerfile +8 -0
  13. hashloom-0.4.0/ISSUES.md +29 -0
  14. hashloom-0.4.0/LICENSE +202 -0
  15. hashloom-0.4.0/PKG-INFO +246 -0
  16. hashloom-0.4.0/README.md +218 -0
  17. hashloom-0.4.0/RELEASING.md +61 -0
  18. hashloom-0.4.0/ROADMAP.md +100 -0
  19. hashloom-0.4.0/bench/benchmark.py +107 -0
  20. hashloom-0.4.0/bench/sweep.py +140 -0
  21. hashloom-0.4.0/contracts/HashloomError.yaml +9 -0
  22. hashloom-0.4.0/contracts/LanguageAdapter.yaml +15 -0
  23. hashloom-0.4.0/contracts/Store.yaml +13 -0
  24. hashloom-0.4.0/contracts/contract_hash.yaml +20 -0
  25. hashloom-0.4.0/contracts/diff_contracts.yaml +15 -0
  26. hashloom-0.4.0/contracts/get_contract.yaml +13 -0
  27. hashloom-0.4.0/contracts/get_dependents.yaml +12 -0
  28. hashloom-0.4.0/contracts/impl_hash.yaml +18 -0
  29. hashloom-0.4.0/contracts/parse_contract.yaml +16 -0
  30. hashloom-0.4.0/contracts/put_contract.yaml +17 -0
  31. hashloom-0.4.0/contracts/status.yaml +14 -0
  32. hashloom-0.4.0/contracts/verification_key.yaml +12 -0
  33. hashloom-0.4.0/contracts/verify.yaml +19 -0
  34. hashloom-0.4.0/docs/benchmarks.md +118 -0
  35. hashloom-0.4.0/docs/demo.gif +0 -0
  36. hashloom-0.4.0/docs/demo.md +63 -0
  37. hashloom-0.4.0/docs/getting-started.md +133 -0
  38. hashloom-0.4.0/docs/hosted-store.md +92 -0
  39. hashloom-0.4.0/examples/go-ledger/README.md +53 -0
  40. hashloom-0.4.0/examples/go-ledger/contracts/Account.yaml +9 -0
  41. hashloom-0.4.0/examples/go-ledger/contracts/BalanceByAccount.yaml +11 -0
  42. hashloom-0.4.0/examples/go-ledger/contracts/Balanced.yaml +13 -0
  43. hashloom-0.4.0/examples/go-ledger/contracts/Entry.yaml +12 -0
  44. hashloom-0.4.0/examples/go-ledger/contracts/FormatCents.yaml +12 -0
  45. hashloom-0.4.0/examples/go-ledger/contracts/PostedEntries.yaml +12 -0
  46. hashloom-0.4.0/examples/go-ledger/contracts/TotalCredits.yaml +11 -0
  47. hashloom-0.4.0/examples/go-ledger/contracts/TotalDebits.yaml +11 -0
  48. hashloom-0.4.0/examples/go-ledger/go.mod +3 -0
  49. hashloom-0.4.0/examples/go-ledger/ledger/filters.go +13 -0
  50. hashloom-0.4.0/examples/go-ledger/ledger/filters_test.go +21 -0
  51. hashloom-0.4.0/examples/go-ledger/ledger/report.go +19 -0
  52. hashloom-0.4.0/examples/go-ledger/ledger/report_test.go +26 -0
  53. hashloom-0.4.0/examples/go-ledger/ledger/totals.go +37 -0
  54. hashloom-0.4.0/examples/go-ledger/ledger/totals_test.go +33 -0
  55. hashloom-0.4.0/examples/go-ledger/ledger/types.go +14 -0
  56. hashloom-0.4.0/examples/go-ledger/ledger/types_test.go +21 -0
  57. hashloom-0.4.0/examples/java-payroll/README.md +67 -0
  58. hashloom-0.4.0/examples/java-payroll/contracts/Employee.yaml +10 -0
  59. hashloom-0.4.0/examples/java-payroll/contracts/PaySlip.yaml +10 -0
  60. hashloom-0.4.0/examples/java-payroll/contracts/TimeSheet.yaml +10 -0
  61. hashloom-0.4.0/examples/java-payroll/contracts/formatCents.yaml +12 -0
  62. hashloom-0.4.0/examples/java-payroll/contracts/grossCents.yaml +12 -0
  63. hashloom-0.4.0/examples/java-payroll/contracts/netCents.yaml +11 -0
  64. hashloom-0.4.0/examples/java-payroll/contracts/overtimeHours.yaml +11 -0
  65. hashloom-0.4.0/examples/java-payroll/contracts/render.yaml +11 -0
  66. hashloom-0.4.0/examples/java-payroll/contracts/slipFor.yaml +11 -0
  67. hashloom-0.4.0/examples/java-payroll/contracts/totalNetCents.yaml +11 -0
  68. hashloom-0.4.0/examples/java-payroll/contracts/withholdingCents.yaml +11 -0
  69. hashloom-0.4.0/examples/java-payroll/pom.xml +30 -0
  70. hashloom-0.4.0/examples/java-payroll/src/main/java/payroll/Employee.java +4 -0
  71. hashloom-0.4.0/examples/java-payroll/src/main/java/payroll/Pay.java +22 -0
  72. hashloom-0.4.0/examples/java-payroll/src/main/java/payroll/PaySlip.java +4 -0
  73. hashloom-0.4.0/examples/java-payroll/src/main/java/payroll/Payroll.java +37 -0
  74. hashloom-0.4.0/examples/java-payroll/src/main/java/payroll/Tax.java +29 -0
  75. hashloom-0.4.0/examples/java-payroll/src/main/java/payroll/TimeSheet.java +4 -0
  76. hashloom-0.4.0/examples/java-payroll/src/test/java/payroll/PayTest.java +28 -0
  77. hashloom-0.4.0/examples/java-payroll/src/test/java/payroll/PayrollTest.java +47 -0
  78. hashloom-0.4.0/examples/java-payroll/src/test/java/payroll/TaxTest.java +28 -0
  79. hashloom-0.4.0/examples/java-payroll/src/test/java/payroll/TypesTest.java +29 -0
  80. hashloom-0.4.0/examples/sales/README.md +57 -0
  81. hashloom-0.4.0/examples/sales/conftest.py +4 -0
  82. hashloom-0.4.0/examples/sales/contracts/Customer.yaml +10 -0
  83. hashloom-0.4.0/examples/sales/contracts/Product.yaml +10 -0
  84. hashloom-0.4.0/examples/sales/contracts/Region.yaml +8 -0
  85. hashloom-0.4.0/examples/sales/contracts/Sale.yaml +12 -0
  86. hashloom-0.4.0/examples/sales/contracts/average_sale.yaml +16 -0
  87. hashloom-0.4.0/examples/sales/contracts/customers_by_segment.yaml +12 -0
  88. hashloom-0.4.0/examples/sales/contracts/included_sales.yaml +15 -0
  89. hashloom-0.4.0/examples/sales/contracts/revenue_by_category.yaml +12 -0
  90. hashloom-0.4.0/examples/sales/contracts/revenue_by_customer.yaml +12 -0
  91. hashloom-0.4.0/examples/sales/contracts/revenue_by_product.yaml +12 -0
  92. hashloom-0.4.0/examples/sales/contracts/revenue_by_region.yaml +13 -0
  93. hashloom-0.4.0/examples/sales/contracts/revenue_by_segment.yaml +13 -0
  94. hashloom-0.4.0/examples/sales/contracts/revenue_share_by_region.yaml +16 -0
  95. hashloom-0.4.0/examples/sales/contracts/sale_count_by_region.yaml +12 -0
  96. hashloom-0.4.0/examples/sales/contracts/sales_in_region.yaml +12 -0
  97. hashloom-0.4.0/examples/sales/contracts/sales_over.yaml +12 -0
  98. hashloom-0.4.0/examples/sales/contracts/segment_revenue_share.yaml +16 -0
  99. hashloom-0.4.0/examples/sales/contracts/top_customers.yaml +14 -0
  100. hashloom-0.4.0/examples/sales/contracts/top_products.yaml +14 -0
  101. hashloom-0.4.0/examples/sales/contracts/total_revenue.yaml +12 -0
  102. hashloom-0.4.0/examples/sales/src/__init__.py +0 -0
  103. hashloom-0.4.0/examples/sales/src/customers.py +29 -0
  104. hashloom-0.4.0/examples/sales/src/filters.py +15 -0
  105. hashloom-0.4.0/examples/sales/src/metrics.py +49 -0
  106. hashloom-0.4.0/examples/sales/src/products.py +28 -0
  107. hashloom-0.4.0/examples/sales/src/types.py +28 -0
  108. hashloom-0.4.0/examples/sales/tests/__init__.py +0 -0
  109. hashloom-0.4.0/examples/sales/tests/test_customers.py +36 -0
  110. hashloom-0.4.0/examples/sales/tests/test_filters.py +39 -0
  111. hashloom-0.4.0/examples/sales/tests/test_metrics.py +71 -0
  112. hashloom-0.4.0/examples/sales/tests/test_products.py +37 -0
  113. hashloom-0.4.0/examples/sales/tests/test_types.py +20 -0
  114. hashloom-0.4.0/examples/ts-cart/README.md +58 -0
  115. hashloom-0.4.0/examples/ts-cart/cart/filters.test.ts +15 -0
  116. hashloom-0.4.0/examples/ts-cart/cart/filters.ts +7 -0
  117. hashloom-0.4.0/examples/ts-cart/cart/pricing.test.ts +26 -0
  118. hashloom-0.4.0/examples/ts-cart/cart/pricing.ts +24 -0
  119. hashloom-0.4.0/examples/ts-cart/cart/totals.test.ts +20 -0
  120. hashloom-0.4.0/examples/ts-cart/cart/totals.ts +12 -0
  121. hashloom-0.4.0/examples/ts-cart/cart/types.test.ts +15 -0
  122. hashloom-0.4.0/examples/ts-cart/cart/types.ts +11 -0
  123. hashloom-0.4.0/examples/ts-cart/contracts/LineItem.yaml +11 -0
  124. hashloom-0.4.0/examples/ts-cart/contracts/Sku.yaml +9 -0
  125. hashloom-0.4.0/examples/ts-cart/contracts/activeItems.yaml +11 -0
  126. hashloom-0.4.0/examples/ts-cart/contracts/discountCents.yaml +9 -0
  127. hashloom-0.4.0/examples/ts-cart/contracts/formatPrice.yaml +12 -0
  128. hashloom-0.4.0/examples/ts-cart/contracts/itemCount.yaml +10 -0
  129. hashloom-0.4.0/examples/ts-cart/contracts/subtotalCents.yaml +11 -0
  130. hashloom-0.4.0/examples/ts-cart/contracts/totalCents.yaml +10 -0
  131. hashloom-0.4.0/examples/ts-cart/package-lock.json +27 -0
  132. hashloom-0.4.0/examples/ts-cart/package.json +9 -0
  133. hashloom-0.4.0/glama.json +6 -0
  134. hashloom-0.4.0/package-lock.json +27 -0
  135. hashloom-0.4.0/package.json +8 -0
  136. hashloom-0.4.0/pyproject.toml +53 -0
  137. hashloom-0.4.0/server.json +29 -0
  138. hashloom-0.4.0/src/hashloom/__init__.py +8 -0
  139. hashloom-0.4.0/src/hashloom/api.py +274 -0
  140. hashloom-0.4.0/src/hashloom/cache_server.py +154 -0
  141. hashloom-0.4.0/src/hashloom/cli.py +108 -0
  142. hashloom-0.4.0/src/hashloom/config.py +135 -0
  143. hashloom-0.4.0/src/hashloom/contract.py +212 -0
  144. hashloom-0.4.0/src/hashloom/errors.py +26 -0
  145. hashloom-0.4.0/src/hashloom/implhash.py +100 -0
  146. hashloom-0.4.0/src/hashloom/indexer.py +96 -0
  147. hashloom-0.4.0/src/hashloom/langs/__init__.py +65 -0
  148. hashloom-0.4.0/src/hashloom/langs/go.py +173 -0
  149. hashloom-0.4.0/src/hashloom/langs/gohash/go.mod +3 -0
  150. hashloom-0.4.0/src/hashloom/langs/gohash/main.go +108 -0
  151. hashloom-0.4.0/src/hashloom/langs/hashloom-init.gradle +9 -0
  152. hashloom-0.4.0/src/hashloom/langs/java.py +294 -0
  153. hashloom-0.4.0/src/hashloom/langs/javahash/JavaHash.java +162 -0
  154. hashloom-0.4.0/src/hashloom/langs/python.py +56 -0
  155. hashloom-0.4.0/src/hashloom/langs/tshash/main.js +189 -0
  156. hashloom-0.4.0/src/hashloom/langs/typescript.py +269 -0
  157. hashloom-0.4.0/src/hashloom/project.py +144 -0
  158. hashloom-0.4.0/src/hashloom/py.typed +0 -0
  159. hashloom-0.4.0/src/hashloom/remote.py +104 -0
  160. hashloom-0.4.0/src/hashloom/server.py +91 -0
  161. hashloom-0.4.0/src/hashloom/shared.py +65 -0
  162. hashloom-0.4.0/src/hashloom/store.py +277 -0
  163. hashloom-0.4.0/src/hashloom/tokens.py +36 -0
  164. hashloom-0.4.0/src/hashloom/verify.py +149 -0
  165. hashloom-0.4.0/tests/__init__.py +0 -0
  166. hashloom-0.4.0/tests/conftest.py +83 -0
  167. hashloom-0.4.0/tests/test_cli.py +73 -0
  168. hashloom-0.4.0/tests/test_concurrency.py +63 -0
  169. hashloom-0.4.0/tests/test_contract_hash.py +135 -0
  170. hashloom-0.4.0/tests/test_go_adapter.py +107 -0
  171. hashloom-0.4.0/tests/test_implhash.py +131 -0
  172. hashloom-0.4.0/tests/test_interpreter.py +128 -0
  173. hashloom-0.4.0/tests/test_java_adapter.py +329 -0
  174. hashloom-0.4.0/tests/test_langs_seam.py +47 -0
  175. hashloom-0.4.0/tests/test_mcp_e2e.py +117 -0
  176. hashloom-0.4.0/tests/test_namespacing.py +212 -0
  177. hashloom-0.4.0/tests/test_pycache.py +96 -0
  178. hashloom-0.4.0/tests/test_remote_store.py +181 -0
  179. hashloom-0.4.0/tests/test_semantic_diff.py +134 -0
  180. hashloom-0.4.0/tests/test_shared_store.py +79 -0
  181. hashloom-0.4.0/tests/test_store_and_api.py +233 -0
  182. hashloom-0.4.0/tests/test_toolchain_key.py +78 -0
  183. hashloom-0.4.0/tests/test_typescript_adapter.py +150 -0
  184. hashloom-0.4.0/tests/test_verify.py +233 -0
  185. hashloom-0.4.0/uv.lock +1395 -0
@@ -0,0 +1,6 @@
1
+ .git
2
+ .venv
3
+ .hashloom
4
+ node_modules
5
+ dist
6
+ __pycache__
@@ -0,0 +1,23 @@
1
+ ---
2
+ name: Bug report
3
+ about: Something doesn't work as documented
4
+ title: ""
5
+ labels: bug
6
+ ---
7
+
8
+ **What happened**
9
+ <!-- The behaviour you saw. -->
10
+
11
+ **What you expected**
12
+
13
+ **Repro**
14
+ <!-- Minimal steps. A small contract YAML plus the command or MCP call is ideal. -->
15
+
16
+ **Environment**
17
+ - hashloom version (`hashloom --version`):
18
+ - Python version:
19
+ - OS:
20
+
21
+ **Anything else**
22
+ <!-- status / verify output, the structured error code and message if there is
23
+ one (hashloom never leaks a stack trace over MCP). -->
@@ -0,0 +1,17 @@
1
+ ---
2
+ name: Feature request
3
+ about: Propose a change or addition
4
+ title: ""
5
+ labels: enhancement
6
+ ---
7
+
8
+ **The problem**
9
+ <!-- What are you trying to do that hashloom makes hard today? -->
10
+
11
+ **Proposed direction**
12
+
13
+ **Does it fit the surface?**
14
+ <!-- hashloom is deliberately small: 5 MCP tools, 5 CLI commands, and the README
15
+ documents the entire surface. If this adds to that surface, say why it earns a
16
+ place (the failure mode we guard against is "scope creep toward Loom"). Where the
17
+ project is already headed lives in ROADMAP.md. -->
@@ -0,0 +1,15 @@
1
+ ## What and why
2
+
3
+ <!-- What does this change do, and why? Link any issue it closes. -->
4
+
5
+ ## Checklist
6
+
7
+ - [ ] `uv run pytest` passes (full suite).
8
+ - [ ] Touches `contract.py` or `implhash.py`? The hash-stability tests
9
+ (`tests/test_contract_hash.py`, `tests/test_implhash.py`) still pass, and
10
+ any change to what busts a hash is intentional.
11
+ - [ ] Touches the context packets or hashing? `uv run python bench/benchmark.py`
12
+ still reports >5x.
13
+ - [ ] Scope: this does not expand the 5-MCP-tool / 5-CLI-command surface, or it
14
+ was discussed in an issue first.
15
+ - [ ] Tool errors stay structured (`HashloomError`); no stack traces over MCP.
@@ -0,0 +1,58 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ # cancel superseded runs on the same ref (e.g. rapid pushes)
8
+ concurrency:
9
+ group: ${{ github.workflow }}-${{ github.ref }}
10
+ cancel-in-progress: true
11
+
12
+ permissions:
13
+ contents: read
14
+
15
+ jobs:
16
+ test:
17
+ name: test (py${{ matrix.python-version }})
18
+ runs-on: ubuntu-latest
19
+ strategy:
20
+ fail-fast: false
21
+ matrix:
22
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
23
+ steps:
24
+ - uses: actions/checkout@v4
25
+ - uses: astral-sh/setup-uv@v6
26
+ with:
27
+ enable-cache: true
28
+ - uses: actions/setup-go@v5 # for the Go adapter tests (else they skip)
29
+ with:
30
+ go-version: 'stable'
31
+ - uses: actions/setup-node@v4 # for the TypeScript adapter tests (else they skip)
32
+ with:
33
+ node-version: '22' # >= 22.6 for .ts type-stripping in node:test
34
+ - uses: actions/setup-java@v4 # for the Java adapter tests (else they skip; mvn ships on the runner)
35
+ with:
36
+ distribution: 'temurin'
37
+ java-version: '21'
38
+ - name: Install the TypeScript toolchain
39
+ run: npm ci # provides the `typescript` the tshash helper resolves
40
+ - name: Sync deps
41
+ run: uv sync --frozen --python ${{ matrix.python-version }}
42
+ - name: Run tests
43
+ # hash-stability suites are load-bearing; e2e spawns the real stdio server
44
+ run: uv run --python ${{ matrix.python-version }} pytest -q
45
+
46
+ benchmark:
47
+ name: benchmark (>5x DoD guard)
48
+ runs-on: ubuntu-latest
49
+ steps:
50
+ - uses: actions/checkout@v4
51
+ - uses: astral-sh/setup-uv@v6
52
+ with:
53
+ enable-cache: true
54
+ - name: Sync deps
55
+ run: uv sync --frozen
56
+ - name: Token-reduction benchmark
57
+ # exits nonzero below the 5x definition-of-done line
58
+ run: uv run python bench/benchmark.py
@@ -0,0 +1,84 @@
1
+ name: Release
2
+
3
+ # Publish to PyPI via Trusted Publishing (OIDC) when a version tag is pushed.
4
+ # No API token is stored: the publish job mints a short-lived OIDC token that
5
+ # PyPI trusts because this repo + workflow + environment are registered there.
6
+ #
7
+ # The published version comes from src/hashloom/__init__.py (hatchling reads it
8
+ # as the dynamic version); the tag is only the trigger, so keep the tag and
9
+ # __version__ in sync, e.g. __version__ = "0.1.0" published by tag v0.1.0.
10
+
11
+ on:
12
+ push:
13
+ tags:
14
+ - "v*"
15
+
16
+ permissions:
17
+ contents: read
18
+
19
+ jobs:
20
+ build:
21
+ name: Build distribution
22
+ runs-on: ubuntu-latest
23
+ steps:
24
+ - uses: actions/checkout@v4
25
+ - name: Check the tag matches __version__
26
+ # The whole chain assumes it: PyPI publishes __version__ (hatchling
27
+ # dynamic version) while the mcp-registry job stamps server.json from
28
+ # the tag. Fail fast instead of publishing two different versions.
29
+ run: |
30
+ TAG=${GITHUB_REF#refs/tags/v}
31
+ PKG=$(sed -n 's/^__version__ = "\(.*\)"$/\1/p' src/hashloom/__init__.py)
32
+ [ "$TAG" = "$PKG" ] || { echo "tag v$TAG != __version__ $PKG" >&2; exit 1; }
33
+ - uses: astral-sh/setup-uv@v6
34
+ with:
35
+ enable-cache: true
36
+ - name: Build sdist and wheel
37
+ run: uv build
38
+ - name: Store the distribution
39
+ uses: actions/upload-artifact@v4
40
+ with:
41
+ name: dist
42
+ path: dist/
43
+
44
+ publish:
45
+ name: Publish to PyPI
46
+ needs: build
47
+ runs-on: ubuntu-latest
48
+ # `name` must match the environment in the PyPI pending-publisher form.
49
+ environment:
50
+ name: pypi
51
+ url: https://pypi.org/p/hashloom
52
+ permissions:
53
+ id-token: write # required for Trusted Publishing; no stored secret
54
+ steps:
55
+ - name: Download the distribution
56
+ uses: actions/download-artifact@v4
57
+ with:
58
+ name: dist
59
+ path: dist/
60
+ - name: Publish to PyPI
61
+ uses: pypa/gh-action-pypi-publish@release/v1
62
+
63
+ mcp-registry:
64
+ name: Publish to MCP Registry
65
+ # needs the PyPI publish: the registry validates that the listed version
66
+ # exists on PyPI and that its README carries the mcp-name marker.
67
+ needs: publish
68
+ runs-on: ubuntu-latest
69
+ permissions:
70
+ id-token: write # GitHub OIDC is the registry login; no stored secret
71
+ contents: read
72
+ steps:
73
+ - uses: actions/checkout@v4
74
+ - name: Install mcp-publisher
75
+ run: |
76
+ curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz" | tar xz mcp-publisher
77
+ - name: Sync server.json versions to the tag
78
+ run: |
79
+ VERSION=${GITHUB_REF#refs/tags/v}
80
+ jq --arg v "$VERSION" '.version = $v | .packages[].version = $v' server.json > server.tmp && mv server.tmp server.json
81
+ - name: Authenticate to MCP Registry
82
+ run: ./mcp-publisher login github-oidc
83
+ - name: Publish to MCP Registry
84
+ run: ./mcp-publisher publish
@@ -0,0 +1,10 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .venv/
4
+ dist/
5
+ build/
6
+ *.egg-info/
7
+ .hashloom/
8
+ .pytest_cache/
9
+ node_modules/
10
+ target/
@@ -0,0 +1,8 @@
1
+ {
2
+ "mcpServers": {
3
+ "hashloom": {
4
+ "command": "uv",
5
+ "args": ["run", "hashloom", "serve"]
6
+ }
7
+ }
8
+ }
@@ -0,0 +1,245 @@
1
+ # Changelog
2
+
3
+ All notable changes are documented here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project aims to
5
+ follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.4.0] - 2026-07-09
10
+
11
+ **Renamed: heddle → hashloom.** Two unrelated "heddle" MCP servers already
12
+ existed (one predating this project by three months), and the bare PyPI name
13
+ was squatted — the loom-part namespace turned out to be crowded ground. The
14
+ coinage `hashloom` says what the tool is (a loom keyed by hashes), is
15
+ unclaimed everywhere, and takes the *bare* PyPI name: `pip install hashloom`.
16
+
17
+ ### Changed
18
+ - Distribution `heddle-mcp` → `hashloom`; import and CLI `heddle` → `hashloom`;
19
+ MCP registry id `io.github.davet47/heddle` → `io.github.davet47/hashloom`
20
+ (registry launches now use `uvx hashloom serve` via `packageArguments`);
21
+ error class `HeddleError` → `HashloomError`; cache-server token env var
22
+ `HEDDLE_CACHE_TOKEN` → `HASHLOOM_CACHE_TOKEN`; project marker directory
23
+ `.heddle/` → `.hashloom/`.
24
+ - **Migration** (pre-0.4.0 projects): the store is derived — run
25
+ `hashloom init && hashloom index` in each project, move any
26
+ `.heddle/config.json` to `.hashloom/config.json` by hand, and delete the old
27
+ `.heddle/` directory. Contract files are untouched.
28
+ - The roadmap's hosted-store theme renumbers v0.4 → v0.5 (this release took
29
+ the 0.4.0 slot).
30
+ - The engine is otherwise unchanged; `heddle-mcp` 0.3.3 is the tombstone.
31
+
32
+ ## [0.3.3] - 2026-07-09
33
+
34
+ The tombstone release: **the project has been renamed to `hashloom`** (the
35
+ loom-part namespace turned out to be crowded — two unrelated "heddle" MCP
36
+ servers already exist). This is the final release as `heddle-mcp`; the engine
37
+ continues unchanged as [`hashloom`](https://pypi.org/project/hashloom/) from
38
+ 0.4.0 on. No code changes.
39
+
40
+ ## [0.3.2] - 2026-07-09
41
+
42
+ Theme: Java, end to end — a fourth language adapter, its reference example,
43
+ and the adapter seam itself brought under contract. Plus run instructions for
44
+ every example, and the repo dogfooding its own MCP server.
45
+
46
+ ### Added
47
+ - **Java adapter** — a `.java` impl routes to Java: hashing via a single-file
48
+ `javac`-tree helper (`langs/javahash/JavaHash.java`, JDK-only, zero
49
+ dependencies) so formatting, comments, and javadoc never bust the cache;
50
+ tests via the project's own Maven or Gradle, auto-detected from the build
51
+ manifest (`pom.xml` → `mvn`, `build.gradle` → `gradle`, committed
52
+ `mvnw`/`gradlew` wrappers preferred); toolchain from `.heddle/config.json`
53
+ (`{"java": "..."}`), else `java` on PATH. Needs a JDK >= 11. The contract
54
+ syntax and the 5-tool / 5-CLI surface are unchanged.
55
+ - **The adapter seam under contract** — `contracts/LanguageAdapter.yaml`
56
+ (status: inferred, pending review) covers the six-method per-language
57
+ Protocol and `adapter_for` routing, with toolchain-free seam tests in
58
+ `tests/test_langs_seam.py`. Heddle-on-heddle: the seam that gained a fourth
59
+ implementation is now itself a contracted unit.
60
+ - **`examples/java-payroll`** — the Java counterpart example: 11 contracts over
61
+ a weekly payroll run (types → pay/tax arithmetic → payslip rendering, three
62
+ layers deep), the shape of a Spring service layer with zero framework
63
+ dependencies (JUnit 5 only, via Maven). Exercises records, `Class.method`
64
+ quals, a parameterized bracket table, and a `@Nested` test class with dotted
65
+ node ids. `bench/sweep.py` learns the `.java` suite runner, and the
66
+ [benchmark scorecard](docs/benchmarks.md) gains the measured row (3.2× over
67
+ all 11 units, the most conservative baseline of the four).
68
+ - **Per-example run instructions** — each of the four example projects ships
69
+ its own README: prerequisites, running the tests directly, the heddle loop,
70
+ and a blast-radius walkthrough whose radius sizes and re-run sets were
71
+ executed and measured before landing, not asserted.
72
+ - **Dogfood config** — `.mcp.json` registers the repo's own MCP server at
73
+ project scope, and CLAUDE.md now directs agents to prefer `get_contract`
74
+ packets over reading contracted seams' source files.
75
+ - **Glama listing** — a claim file plus an introspectable Dockerfile, the next
76
+ step in the post-registry discoverability sequence (#64).
77
+
78
+ ## [0.3.1] - 2026-07-05
79
+
80
+ Theme: discoverability. An official MCP Registry listing, and the CI that
81
+ keeps it from going stale. No engine changes.
82
+
83
+ ### Added
84
+ - An MCP Registry listing, `io.github.davet47/heddle`: `server.json` at the
85
+ repo root describes the server, the README carries the `mcp-name`
86
+ ownership marker (the registry verifies PyPI packages by finding it in
87
+ the package description), and a `heddle-mcp` console script boots the
88
+ stdio server directly — the registry's launch convention is
89
+ `uvx <distribution>`, and uvx runs the script named after the
90
+ distribution. A launch shim for `heddle serve`, not a sixth CLI command.
91
+ - Registry publishing in CI: an `mcp-registry` job in the release workflow
92
+ authenticates with GitHub OIDC (no stored secret), syncs the versions in
93
+ `server.json` from the tag, and publishes to
94
+ registry.modelcontextprotocol.io after the PyPI publish succeeds — every
95
+ tagged release updates the listing.
96
+ - A benchmark scorecard, `docs/benchmarks.md`, and a full-sweep benchmark,
97
+ `bench/sweep.py`: the scorecard holds every published token-reduction
98
+ number — the sales DoD gate (5.4×) plus full sweeps of all three example
99
+ projects (sales 4.1×, go-ledger 3.0×, ts-cart 3.1× — sweeps count the leaf
100
+ units the gate doesn't, so they average lower by construction), the
101
+ dogfood store's cache counters, and the concessions any public claim
102
+ should carry (closure knowledge granted free to the baseline;
103
+ first-try-green assumed for heddle; per-change estimate, not a session
104
+ total; initial build costs more). `sweep.py` runs the same accounting over
105
+ every verifiable unit of any heddle project, language-aware only in the
106
+ suite runner (pytest / `go test` / `node --test`). Every scorecard row is
107
+ reproducible from this repo alone; ratios are deterministic tiktoken
108
+ counts and independent of what drives the session. The README's "The
109
+ number" section links to it.
110
+
111
+ ## [0.3.0] - 2026-07-04
112
+
113
+ Theme: adoption. Everything a first-time visitor needs that the engine
114
+ releases didn't ship: a getting-started walkthrough for the contract-first
115
+ agent workflow, sample projects in all three supported languages, and the
116
+ strongest possible existence proof — heddle developing heddle, its own stable
117
+ seams under contract (written `inferred` by the agent, since reviewed and
118
+ explicitly confirmed by a human, at zero re-verification cost). No code
119
+ changes: the engine is 0.2.0's; the 5 MCP tools / 5 CLI commands surface and
120
+ all response shapes are unchanged.
121
+
122
+ ### Added
123
+ - Heddle develops on heddle: the repo is now itself a heddle project —
124
+ `contracts/` holds 12 contracts over the stable seams (the five `api.py`
125
+ tool functions, the hashing trio, `impl_hash`, `verification_key`,
126
+ `HeddleError`, the `Store` Protocol), bound to the existing test suite and
127
+ born `status: inferred` pending human review. `heddle verify --radius` is
128
+ the inner-loop gate for changes to contracted seams; the full pytest suite
129
+ and the benchmark remain the definition of done.
130
+ - A getting-started walkthrough, `docs/getting-started.md`: how a human and an
131
+ agent build a package contract-first — setup, the CLAUDE.md working rules to
132
+ give the agent, the inferred→confirmed review loop, and the `verify --radius`
133
+ gate as the definition of done.
134
+ - A TypeScript sample project, `examples/ts-cart`: 8 contracts over a shopping
135
+ cart (interface/type-alias contracts included), exercising the TypeScript
136
+ adapter end to end — canonical-AST impl hashing via the project's own
137
+ `typescript`, `node:test` verification under type stripping, and the
138
+ `verify --radius` gate.
139
+ - A Go sample project, `examples/go-ledger`: 8 contracts over a small
140
+ double-entry ledger (struct/type contracts included), exercising the Go
141
+ adapter end to end — AST-stable impl hashing, `go test -json` verification,
142
+ and the `verify --radius` gate.
143
+
144
+ ## [0.2.0] - 2026-07-04
145
+
146
+ Theme: solo → team. A team can now share verification greens — one teammate or
147
+ CI verifies a unit once and everyone gets `cached-pass` — soundly, keyed by
148
+ toolchain. Plus two new language adapters, contract provenance, and a
149
+ CI-gateable verify. Everything is additive: the 5 MCP tools / 5 CLI commands
150
+ surface and the contract format are unchanged, and 0.1.0 projects work as-is.
151
+ One-time cost on upgrade: existing cached greens re-verify once (the
152
+ verification key now includes the toolchain).
153
+
154
+ ### Added
155
+ - Gate-shaped verification: `verify` responses carry a top-level `ok` — true iff
156
+ every unit is `pass`/`cached-pass` (failures, unknown names, and unverifiable
157
+ units all gate) — and a `radius` option (`heddle verify --radius NAME`, MCP
158
+ `verify(names, radius=true)`) widens each name to itself plus every transitive
159
+ dependent, spec-only units dropped. One call = a hard pass/fail for a change's
160
+ whole blast radius; the CLI exit code mirrors `ok`, so a CI step or agent loop
161
+ can block on it. No new tools/commands.
162
+ - Contract provenance: an optional `status: inferred | confirmed` field on
163
+ contracts. `inferred` marks a contract reverse-engineered from code and not
164
+ yet human-reviewed; tools warn — never refuse — when a decision rests on one
165
+ (`inferred: true` on `get_dependents`/`get_contract` dep entries, `inferred`
166
+ and `invalidated_inferred` on `put_contract`, an `inferred` list on `verify`
167
+ results, and a review-queue list in `status`). Absent = `confirmed`, so
168
+ existing contracts are unaffected, and the field is excluded from the contract
169
+ hash, so confirming an inferred contract after review invalidates nothing and
170
+ busts no cached green. The 5-tool / 5-CLI surface is unchanged; every new
171
+ response key appears only when an inferred contract is actually involved.
172
+ - Remote shared verification cache (transport): a `RemoteStore` client and a
173
+ stdlib `python -m heddle.cache_server` (a bearer-token JSON HTTP service over a
174
+ `SqliteStore`) let a team share verdicts and impl blobs across machines —
175
+ configured per project via `.heddle/config.json` `{"shared": {"url","token"}}`,
176
+ with **no change to the 5 MCP tools / 5 CLI commands** (the cache server is an
177
+ operational process, not a subcommand). A shared-store outage degrades silently
178
+ to local verify. Cross-machine greens are made sound by the toolchain-in-key
179
+ change below.
180
+ - Toolchain in the verification key: the key now folds in a per-language toolchain
181
+ identity (`python 3.11.7` / `go 1.21.5` / `node <v> ts <v>`), so a shared or
182
+ cross-machine green is trusted only when the toolchain version matches —
183
+ otherwise it re-runs. Version-only (no OS/arch), so a CI(Linux) green still
184
+ serves a Mac/Windows dev. One-time: existing greens re-verify once on upgrade.
185
+ - Semantic diff in `put_contract`: the response carries a `diff` of what changed
186
+ versus the prior contract (signature, deps, invariants, examples, impl/tests).
187
+ - Test source in the verification key (#18): editing a test's body now forces a
188
+ re-run; reformatting, comments, and docstrings in a test stay cached. Conftest
189
+ fixtures and helpers a test calls are not yet covered.
190
+ - Content-addressed impl-source store: `heddle index` stores each impl file's
191
+ source as a deduped blob, so the store can serve weft, not only verdicts.
192
+ - A backend-agnostic `Store` interface (a Protocol) with `SqliteStore` as the
193
+ local implementation: the seam for a shared/remote cache.
194
+ - Shared verification cache MVP (`LayeredStore`): a local store fronted by a
195
+ shared one (read-through, write-through for greens), so one client's verified
196
+ green serves another. Design for the hosted service in docs/hosted-store.md.
197
+ - Multi-language adapters: contracts whose `impl` is a `.go` file verify with a
198
+ Go adapter (a stdlib `go/ast` hash helper plus `go test -json`), chosen by the
199
+ impl's extension. Python is unchanged and the default; the contract syntax and
200
+ the 5-tool / 5-CLI surface are unchanged.
201
+ - TypeScript adapter: contracts whose `impl` is a `.ts`/`.tsx`/`.mts`/`.cts` file
202
+ verify with a TypeScript adapter — a hand-written canonical AST hash via the TS
203
+ Compiler API (resolved from the project's own `typescript`, since TS has no
204
+ `ast.dump`), plus an auto-detected test runner (vitest / jest if declared in
205
+ `package.json`, else Node's built-in `node:test`). Needs Node (>= 22.6 for `.ts`
206
+ type-stripping under `node:test`). Python and Go are unchanged; the contract
207
+ syntax and the 5-tool / 5-CLI surface are unchanged.
208
+ - A `rechecks` block in `status` (#20): re-verifications triggered by a contract
209
+ change, and how many changed no verdict (`wasted_rate`).
210
+ - Invariants out of the contract hash (#19): rewording or reordering an invariant
211
+ no longer changes the hash or cascades a re-verify. Invariants are documentation;
212
+ the machine check is the tests (their source is in the key via #18). One-time:
213
+ contracts with invariants re-hash once on upgrade.
214
+
215
+ ## [0.1.0] - 2026-06-23
216
+
217
+ First public release, published to PyPI as `heddle-mcp` (the import name and CLI
218
+ stay `heddle`).
219
+
220
+ ### Added
221
+ - Content-addressed contracts: one hashable YAML file per software unit
222
+ (signature, invariants, examples, dependencies), with subdirectory namespaces.
223
+ - Hash-keyed verification cache: a green test result keyed on the contract,
224
+ implementation, and transitive dependency hashes. pytest runs only on a miss;
225
+ failures are never served from cache.
226
+ - Mechanical blast radius: `get_dependents` reports the exact set of invalidated
227
+ dependents, direct or transitive, by hash.
228
+ - Five MCP tools (`get_contract`, `put_contract`, `get_dependents`, `verify`,
229
+ `status`) and five CLI commands (`init`, `index`, `serve`, `status`, `verify`).
230
+ - Configurable verify interpreter and timeout via `heddle serve --python`,
231
+ `.heddle/config.json`, or an auto-detected project `.venv`.
232
+ - `--no-pycache-trust` / `pycache_trust` to clear stale `__pycache__` before a
233
+ verify run.
234
+ - Structured errors over MCP (no stack traces).
235
+ - CI (tests on Python 3.10 through 3.13 plus the >5x benchmark guard) and PyPI
236
+ Trusted Publishing on version tags.
237
+
238
+ [Unreleased]: https://github.com/davet47/hashloom/compare/v0.4.0...HEAD
239
+ [0.4.0]: https://github.com/davet47/hashloom/compare/v0.3.3...v0.4.0
240
+ [0.3.3]: https://github.com/davet47/hashloom/compare/v0.3.2...v0.3.3
241
+ [0.3.2]: https://github.com/davet47/hashloom/compare/v0.3.1...v0.3.2
242
+ [0.3.1]: https://github.com/davet47/hashloom/compare/v0.3.0...v0.3.1
243
+ [0.3.0]: https://github.com/davet47/hashloom/compare/v0.2.0...v0.3.0
244
+ [0.2.0]: https://github.com/davet47/hashloom/compare/v0.1.0...v0.2.0
245
+ [0.1.0]: https://github.com/davet47/hashloom/releases/tag/v0.1.0
@@ -0,0 +1,116 @@
1
+ # Working rules for hashloom
2
+
3
+ Hashloom is a hash-keyed verification cache + content-addressed contract store,
4
+ exposed over MCP. Contracts are warp (durable), code is weft (regenerable). These
5
+ rules override default behavior — follow them.
6
+
7
+ ## Git is the user's
8
+
9
+ **Commit and push only when the user explicitly asks** — never on your own, and
10
+ write **no `Co-Authored-By` trailer** when you do. The user owns the PR/merge flow
11
+ and brings local back to a clean, synced `main` before each new piece of work.
12
+
13
+ **A fresh branch per change.** The first action on any new feature/change is
14
+ `git switch -c <scoped-name>` off `main` — never commit to `main`, and never reuse
15
+ a previous feature branch for new work. One piece = one branch = one PR.
16
+
17
+ **Never git worktrees** — too much machinery for this project; the user dislikes
18
+ them. Don't reach for worktree isolation.
19
+
20
+ ## Scope discipline
21
+
22
+ Anything not on the current milestone is an entry in [ISSUES.md](ISSUES.md) — file
23
+ it there, don't write the code. The named failure mode is "scope creep toward
24
+ Loom." Keep the surface minimal: **5 MCP tools, 5 CLI commands.** The README
25
+ documents "the entire surface"; if a change would add to it, stop and confirm.
26
+ (The shared-cache backend `python -m hashloom.cache_server` is a deliberate
27
+ *operational* process, not a 6th CLI command — the client surface stays 5/5.)
28
+
29
+ ## Hashloom develops on hashloom
30
+
31
+ The repo is itself a hashloom project: `contracts/` holds contracts for the
32
+ stable seams (the five `api.py` functions, the `contract.py` hashing trio,
33
+ `impl_hash`, `verification_key`, `HashloomError`, the `Store` Protocol). The
34
+ workflow from [docs/getting-started.md](docs/getting-started.md) applies here:
35
+
36
+ - **The hashloom MCP server is project-configured** in [.mcp.json](.mcp.json)
37
+ (`uv run hashloom serve`). When its tools are present, **prefer `get_contract`
38
+ packets and `get_dependents` over reading a contracted seam's source file** —
39
+ the ~300-token packet is the token-frugal path this project exists to prove;
40
+ fall back to file reads only for uncontracted code.
41
+ - **Before changing a contracted seam**, check the blast radius (the
42
+ `get_dependents` MCP tool when connected — it is not a CLI command);
43
+ **after touching one**, `uv run hashloom verify --radius <name>` must return
44
+ `ok: true` — that is the inner-loop gate.
45
+ - **A new stable seam gets a contract before its implementation.** If you (the
46
+ agent) derived the contract rather than the user specifying it, mark it
47
+ `status: inferred`; the user flips it to `confirmed` on review. `hashloom
48
+ status` lists the review queue.
49
+ - **Do not contract churning interiors.** `remote.py`, `cache_server.py`, and
50
+ `shared.py` are deliberately uncontracted while the v0.3 hosted-store work
51
+ reshapes them; helpers (`tokens.py`, `project.py`) are weft. Pinning
52
+ interiors is the failure mode the README warns about.
53
+ - **The hashloom gate layers on the DoD — it never replaces it.** Full
54
+ `uv run pytest` and the benchmark below remain the definition of done;
55
+ hashloom's cached verify must not be the only thing vouching for hashloom.
56
+
57
+ ## Definition of done: >5x token reduction
58
+
59
+ `bench/benchmark.py` is the DoD guard — it exits nonzero below 5x (currently
60
+ 5x+). Run it for anything that could touch the context packets or hashing.
61
+ **Never regress it.**
62
+
63
+ ## Hash stability is load-bearing
64
+
65
+ `tests/test_contract_hash.py` and `tests/test_implhash.py` are the spec. Cosmetic
66
+ input changes — whitespace, key order, comments, docstrings, file relocation —
67
+ must never change a hash; meaning changes (signature, invariant/example order)
68
+ must. If you touch `contract.py` or `implhash.py`, these tests are the contract.
69
+ Run `uv run pytest` (full suite) before declaring anything done.
70
+
71
+ ## The store is derived
72
+
73
+ `.hashloom/store.db` is rebuildable from `contracts/` via `hashloom index` — never
74
+ hand-edit it. `contracts/*.yaml` is the source of truth.
75
+
76
+ ## Errors are structured
77
+
78
+ Nothing leaks a stack trace over MCP. `_respond` in
79
+ [server.py](src/hashloom/server.py) wraps every tool; raise `HashloomError(code,
80
+ message)` for anything an agent should see. Keep it that way.
81
+
82
+ ## The verify interpreter
83
+
84
+ `verify` shells pytest out to a resolved interpreter (see
85
+ [config.py](src/hashloom/config.py)), in precedence order:
86
+
87
+ 1. `hashloom serve --python PATH`
88
+ 2. `.hashloom/config.json` → `{"python": "..."}`
89
+ 3. auto-detected project venv (`<root>/.venv/bin/python`, …)
90
+ 4. `sys.executable`
91
+
92
+ So hashloom can verify a target project against *its own* venv without being
93
+ installed into it. `hashloom status` reports the resolved interpreter.
94
+
95
+ Non-Python impls resolve their own toolchain by the same precedence, keyed by the
96
+ impl extension: a `.go` impl uses `go` (`.hashloom/config.json` → `"go"`); a
97
+ `.ts`/`.tsx` impl uses `node` (→ `"node"`) plus the project's *own* `typescript`,
98
+ and auto-detects the test runner (vitest / jest, else Node's `node:test`); a
99
+ `.java` impl uses `java` (→ `"java"`, JDK >= 11) and auto-detects the runner from
100
+ the build manifest (`pom.xml` → Maven, `build.gradle` → Gradle, committed
101
+ `mvnw`/`gradlew` wrappers preferred).
102
+
103
+ ## How to run
104
+
105
+ ```bash
106
+ uv run pytest # full suite — hash stability is load-bearing
107
+ uv run python bench/benchmark.py # the DoD number
108
+ ```
109
+
110
+ CI (`.github/workflows/ci.yml`) runs both on every push and PR, so the DoD and
111
+ hash-stability rules are enforced, not just documented. Keep it green.
112
+
113
+ Python >=3.10. Deps: mcp, pyyaml, tiktoken, pytest. TypeScript contracts also
114
+ need Node >=22.6 and the project's own `typescript` (CI installs a repo-local one
115
+ via `npm ci`; `node_modules/` is gitignored). Java contracts need a JDK >=11 plus
116
+ Maven or Gradle (CI installs Temurin; Maven ships on the runner).
@@ -0,0 +1,54 @@
1
+ # Contributing to hashloom
2
+
3
+ Thanks for your interest. hashloom is a hash-keyed verification cache and a
4
+ content-addressed contract store, exposed over MCP. Contracts are the durable
5
+ warp; code is the regenerable weft. A few rules keep it that way.
6
+
7
+ ## Scope: the surface is fixed on purpose
8
+
9
+ hashloom is deliberately small: **5 MCP tools and 5 CLI commands**, and the README
10
+ documents the entire surface. The named failure mode is "scope creep toward
11
+ Loom." Bug fixes and docs are welcome any time. A change that would add to that
12
+ surface needs a conversation first, so please open an issue before writing it.
13
+
14
+ ## Development
15
+
16
+ Requires Python >= 3.10 and [uv](https://docs.astral.sh/uv/).
17
+
18
+ ```bash
19
+ uv sync
20
+ uv run pytest # full suite
21
+ uv run python bench/benchmark.py # the definition-of-done number
22
+ ```
23
+
24
+ ## Two things are load-bearing
25
+
26
+ 1. **Hash stability.** `tests/test_contract_hash.py` and `tests/test_implhash.py`
27
+ are the spec. Cosmetic input changes (whitespace, key order, comments,
28
+ docstrings, file relocation) must never change a hash; meaning changes
29
+ (signature, invariant or example order) must. If you touch `contract.py` or
30
+ `implhash.py`, those tests are the contract.
31
+ 2. **The >5x token reduction.** `bench/benchmark.py` is the definition-of-done
32
+ guard and exits nonzero below 5x. Run it for anything that could touch the
33
+ context packets or hashing, and never regress it.
34
+
35
+ Two more invariants worth knowing: nothing leaks a stack trace over MCP (tool
36
+ errors are structured `HashloomError(code, message)` values, see `server.py`), and
37
+ `.hashloom/store.db` is derived state rebuildable from `contracts/` via `hashloom
38
+ index`, so never hand-edit it. `contracts/*.yaml` is the source of truth.
39
+
40
+ ## Pull requests
41
+
42
+ - Branch off `main`, one focused change per branch.
43
+ - `main` is protected: every PR must pass CI (pytest on Python 3.10 through 3.13,
44
+ plus the benchmark guard) before it can merge. Run both locally first.
45
+ - Keep the diff scoped to one thing. If you spot an unrelated issue, file it.
46
+ - Match the surrounding code: its naming, comment density, and idioms.
47
+
48
+ Where the project is headed lives in [ROADMAP.md](ROADMAP.md); deferred non-goals
49
+ are in [ISSUES.md](ISSUES.md).
50
+
51
+ ## Releases
52
+
53
+ Maintainer-only, via PyPI Trusted Publishing on a version tag. See
54
+ [RELEASING.md](RELEASING.md).
@@ -0,0 +1,8 @@
1
+ # Runs the MCP server against a baked-in empty project so directory sandboxes
2
+ # (e.g. Glama) can start it and introspect the tools. To serve a real project,
3
+ # mount it over /app: docker run -i --rm -v "$PWD":/app <image>
4
+ FROM python:3.13-slim
5
+ WORKDIR /app
6
+ COPY . /src
7
+ RUN pip install --no-cache-dir /src && hashloom init
8
+ ENTRYPOINT ["hashloom-mcp"]