Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added
- `integrations/graphify_import.py` (#80, PR #81): draft `status: inferred`
contracts from a [graphify](https://github.com/Graphify-Labs/graphify)
knowledge graph — the brownfield entry point. Signatures come from the
Python source via `ast`; deps only from EXTRACTED edges between selected
units. Repo-only tooling: no new MCP tools or CLI commands. The repo's own
extracted graph is committed at `graphify-out/graph.json`.

## [0.4.0] - 2026-07-09

**Renamed: heddle → hashloom.** Two unrelated "heddle" MCP servers already
Expand Down
10 changes: 6 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ documents "the entire surface"; if a change would add to it, stop and confirm.

The repo is itself a hashloom project: `contracts/` holds contracts for the
stable seams (the five `api.py` functions, the `contract.py` hashing trio,
`impl_hash`, `verification_key`, `HashloomError`, the `Store` Protocol). The
`impl_hash`, `verification_key`, `HashloomError`, the `Store` Protocol, and
the `LanguageAdapter` seam behind the per-extension adapters). The
workflow from [docs/getting-started.md](docs/getting-started.md) applies here:

- **The hashloom MCP server is project-configured** in [.mcp.json](.mcp.json)
Expand All @@ -47,9 +48,10 @@ workflow from [docs/getting-started.md](docs/getting-started.md) applies here:
`status: inferred`; the user flips it to `confirmed` on review. `hashloom
status` lists the review queue.
- **Do not contract churning interiors.** `remote.py`, `cache_server.py`, and
`shared.py` are deliberately uncontracted while the v0.3 hosted-store work
reshapes them; helpers (`tokens.py`, `project.py`) are weft. Pinning
interiors is the failure mode the README warns about.
`shared.py` are deliberately uncontracted while the hosted-store work (the
v0.5 theme, renumbered from v0.4 in the 0.4.0 rename) reshapes them; helpers
(`tokens.py`, `project.py`) are weft. Pinning interiors is the failure mode
the README warns about.
- **The hashloom gate layers on the DoD — it never replaces it.** Full
`uv run pytest` and the benchmark below remain the definition of done;
hashloom's cached verify must not be the only thing vouching for hashloom.
Expand Down
6 changes: 3 additions & 3 deletions ISSUES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
Per the v0.1 spec: if a task isn't on a milestone, it's an issue. The repo now
has a remote, so new follow-ups are filed on the GitHub tracker (the
verification-model sharpening lives there as
[#18](https://github.com/davet47/heddle/issues/18) through
[#20](https://github.com/davet47/heddle/issues/20), prioritised in
[#18](https://github.com/davet47/hashloom/issues/18) through
[#20](https://github.com/davet47/hashloom/issues/20), prioritised in
[ROADMAP.md](ROADMAP.md)). This file keeps the original spec non-goals and the
launch follow-ups, with status as of the 0.1.0 release (shipped 2026-06-23).

Expand All @@ -19,7 +19,7 @@ launch follow-ups, with status as of the 0.1.0 release (shipped 2026-06-23).

## Known limitations / follow-ups

7. **PyPI release**: ✓ **Resolved in 0.1.0.** Published as `heddle-mcp` (the bare `heddle` name is held by a third-party placeholder) via GitHub Actions Trusted Publishing. `pip install heddle-mcp`; the import name and CLI stay `heddle`. See [RELEASING.md](RELEASING.md).
7. **PyPI release**: ✓ **Resolved in 0.1.0.** Published as `heddle-mcp` (the bare `heddle` name was held by a third-party placeholder) via GitHub Actions Trusted Publishing; the import name and CLI stayed `heddle` through 0.3.3. Since the 0.4.0 rename the distribution, import name, and CLI are all `hashloom` — see item 10. See [RELEASING.md](RELEASING.md).
8. **README gif**: ✓ **Resolved in 0.1.0.** Recorded and embedded under the CI badge; storyboard in [docs/demo.md](docs/demo.md).
9. **Pre-existing stale bytecode**: partially mitigated. The verification runner passes `-B` / `PYTHONDONTWRITEBYTECODE` so its own runs never cache bytecode, but with the default `pycache_trust: true` a stale user-written `__pycache__` (same size, same mtime second) could still be loaded. Mitigation shipped: set `pycache_trust: false` (or `--no-pycache-trust`) to clear `__pycache__` before each verify run. Making that the default remains a possible future change.
10. **Name finalization**: ✓ **Resolved**, then revised. "heddle" was the locked project name through 0.3.3 (PyPI: `heddle-mcp`); renamed to **hashloom** in 0.4.0 after two unrelated "heddle" MCP servers surfaced — see the CHANGELOG's 0.4.0 entry. The bare `hashloom` PyPI name is ours.
Expand Down
33 changes: 18 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,21 +185,24 @@ cd examples/sales
hashloom init && hashloom index && hashloom serve # then point your agent at it
```

20 contracts, 25 tests, three dependency layers deep. Every example directory
ships its own README with per-language run instructions.

There are counterpart examples in Go at `examples/go-ledger` (8 contracts over a small
double-entry ledger, same loop: `hashloom init && hashloom index`, then
`hashloom verify --radius Entry` gates the blast radius with `go test` under the
hood — needs a Go toolchain), in TypeScript at `examples/ts-cart`
(8 contracts over a shopping cart; `npm install` first for its `typescript`,
then the same loop — verification runs on Node's built-in `node:test`, Node
>= 22.6), and in Java at `examples/java-payroll` (11 contracts over a weekly
payroll run, three layers deep — records, `Class.method` quals, a parameterized
bracket table, and a `@Nested` test class with dotted node ids; the shape of a
Spring service layer with zero framework dependencies. Same loop —
`hashloom verify --radius TimeSheet` runs Maven under the hood; needs a JDK >= 17
and Maven).
20 contracts, 25 tests, dependency chains up to six contracts deep. Every
example directory ships its own README with per-language run instructions.

There are counterpart examples in three other languages, each using the same
`init && index` loop:

- **Go** — `examples/go-ledger`: 8 contracts over a small double-entry ledger.
`hashloom verify --radius Entry` gates the blast radius with `go test` under
the hood. Needs a Go toolchain.
- **TypeScript** — `examples/ts-cart`: 8 contracts over a shopping cart. Run
`npm install` first for its `typescript`; verification runs on Node's
built-in `node:test`. Needs Node >= 22.6.
- **Java** — `examples/java-payroll`: 11 contracts over a weekly payroll run,
three layers deep — records, `Class.method` quals, a parameterized bracket
table, and a `@Nested` test class with dotted node ids; the shape of a
Spring service layer with zero framework dependencies.
`hashloom verify --radius TimeSheet` runs Maven under the hood. Needs a
JDK >= 17 and Maven.

## Development

Expand Down
3 changes: 2 additions & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

**Shipped so far** (full detail in the [CHANGELOG](CHANGELOG.md)):

- **v0.1** (0.1.0 → PyPI as `hashloom`, 2026-06-23) — the engine:
- **v0.1** (0.1.0 → PyPI as `heddle-mcp`, 2026-06-23; the project renamed to
`hashloom` in 0.4.0) — the engine:
content-addressed contracts, a hash-keyed verification cache, and a
blast-radius query, over MCP. Single-process and Python-only by design.
- **v0.2** (0.2.0 → PyPI, 2026-07-04) — solo → team: a shared verification
Expand Down
12 changes: 7 additions & 5 deletions docs/benchmarks.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ representative regeneration tasks, one per dependency layer. The **full sweeps**
barely benefit — so a sweep always averages lower than the gate. That is not a
regression; it is the honest cost of counting everything.

Raw-side counts reproduce exactly; hashloom-side totals jitter by ~1 token per
unit between runs (verify statuses flip `pass` → `cached-pass`), so per-unit
last decimals move while the ratios hold.
Hashloom-side counts reproduce exactly (both scripts warm the cache before
measuring, so every measured verify is a `cached-pass`); raw-side totals can
drift by ~1 token per unit across environments, because the raw count includes
live test-runner output and its incidental formatting. Per-unit last decimals
move while the ratios hold.

## What one number hides: the distribution

Expand All @@ -44,7 +46,7 @@ the more a ~300-token packet replaces:
mode prints nothing on a green suite, so the raw baseline gets *zero*
suite-output tokens here — the Java row is the most conservative of the four.)

The examples are deliberately small (8–20 contracts, 2–3 layers), so their
The examples are deliberately small (8–20 contracts, chains two to six deep), so their
sweeps sit in the 3–4× range. Deeper projects score higher, not lower: every
additional dependency layer widens the gap between reading a closure and
reading a packet. The DoD gate's 5.4× on three mid-to-deep units shows the
Expand All @@ -53,7 +55,7 @@ same effect inside one project.
## Cache economics (from a real store, not a benchmark)

Verification caching is the other half of the payoff. This repo dogfoods
hashloom (12 contracts over its own stable seams — see `contracts/`), and its
hashloom (13 contracts over its own stable seams — see `contracts/`), and its
store counters after day one:

| store | verify requests | served from cache | test runs avoided | hit rate |
Expand Down
4 changes: 2 additions & 2 deletions docs/demo.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ rm -rf .hashloom && hashloom init && hashloom index
| 2 | benchmark table | `uv run python bench/benchmark.py` | 5×+ fewer tokens per regeneration | 5 |
| 3 | the ask | Claude Code: "re-implement `revenue_by_region`" | the agent asks hashloom, not the filesystem | 3 |
| 4 | `get_contract` | MCP `get_contract("revenue_by_region")` → packet | one ~300-token packet: spec + dep signatures + callers | 5 |
| 5 | the weave | agent edits `src/revenue.py::revenue_by_region` | it weaves the weft | 4 |
| 5 | the weave | agent edits `src/metrics.py::revenue_by_region` | it weaves the weft | 4 |
| 6 | `verify` (miss) | MCP `verify(["revenue_by_region"])` → `pass` | hash-keyed verification — pytest only on a miss | 5 |
| 7 | `verify` (hit) | `verify` again → `cached-pass` | second run: cached-pass, no pytest | 3 |
| 8 | `status` | MCP `status()` → token counters | the whole loop, in a few hundred tokens | 4 |
Expand All @@ -54,7 +54,7 @@ reports `tokens: 0`: the token counters are incremented only by the MCP server

## Recording tips
- Scene 2's benchmark rebuilds and warms `examples/sales/.hashloom`, so don't wipe
the store after it. Scene 5's edit to `src/revenue.py::revenue_by_region` busts
the store after it. Scene 5's edit to `src/metrics.py::revenue_by_region` busts
only that unit's impl hash, so scene 6 verifies it cold (a real pytest `pass`),
scene 7 reuses the cache, and scene 8's `status` stays clean (`dirty: 0`).
Re-indexing the whole store instead leaves every other contract `dirty` and
Expand Down
13 changes: 9 additions & 4 deletions docs/hosted-store.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,11 @@ Behind the Protocol it can later be a remote backend with no change to callers.
`tests/test_shared_store.py` shows client A's green served to client B without
B running pytest, and confirms failures are not published.

What the MVP does NOT do yet: it is not wired into the CLI/MCP (no surface
change), and the shared store is local-file only. Wiring is below.
When the MVP first shipped it was not yet wired into the CLI/MCP and the
shared store was local-file only; both have since shipped — `build_store()`
wires the layered store into `index`/`status`/`verify` and the MCP server,
and `RemoteStore` makes the shared side an HTTP backend (item 1 below and
"Running the cache server").

## From MVP to hosted (the hard parts)

Expand Down Expand Up @@ -62,8 +65,10 @@ change), and the shared store is local-file only. Wiring is below.
5. **Cross-graph invalidation.** When a shared contract changes, dependents'
shared verdicts must be invalidated for the whole team, not just locally.
`mark_stale` needs a shared analogue keyed off the dependency graph.
6. **Clocks.** `ran_at` is client-generated today; a shared store should stamp
server-side to order writes without trusting client clocks.
6. ✓ **Clocks (shipped for the shared path).** The cache server's own
`SqliteStore` stamps `ran_at` server-side — the client's publish request
carries no timestamp — so write ordering never trusts client clocks.
Local stores still stamp locally, where ordering is single-writer anyway.

## Running the cache server

Expand Down
2 changes: 1 addition & 1 deletion examples/sales/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ how incomplete sales are excluded, say — then:
hashloom verify --radius included_sales
```

Exactly one unit re-runs; its twelve dependents stay `cached-pass`, because
Exactly one unit re-runs; its fourteen dependents stay `cached-pass`, because
they lean on `included_sales`'s *contract*, which didn't change. Now edit a
contract instead — add a field to [contracts/Sale.yaml](contracts/Sale.yaml)'s
signature and `hashloom index` — and `verify --radius Sale` re-verifies
Expand Down
Loading