Skip to content

Commit 98c2cd8

Browse files
authored
Merge pull request #6 from HumanAssisted/claude/review-repo-quality-29NH7
fix: update stale lock files to match declared dependency versions
2 parents f55d417 + 4bb73c3 commit 98c2cd8

40 files changed

Lines changed: 2092 additions & 732 deletions

Makefile

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
.PHONY: test test-python test-node test-go test-rust \
2-
versions check-versions \
2+
versions check-versions check-jacs-versions \
33
release-node release-python release-rust release-all \
44
release-delete-tags help
55

@@ -14,6 +14,13 @@ PYTHON_VERSION := $(shell grep '^version' python/pyproject.toml | head -1 | sed
1414
NODE_VERSION := $(shell grep '"version"' node/package.json | head -1 | sed 's/.*: *"\(.*\)".*/\1/')
1515
PLUGIN_VERSION := $(shell grep '"version"' .claude-plugin/plugin.json | head -1 | sed 's/.*: *"\(.*\)".*/\1/')
1616

17+
# JACS dependency versions across SDKs
18+
JACS_RUST := $(shell grep '^jacs ' rust/haiai/Cargo.toml | sed 's/.*"\(=*[0-9][^"]*\)".*/\1/' | sed 's/^=//')
19+
JACS_RUST_CLI := $(shell grep '^jacs ' rust/haiai-cli/Cargo.toml | sed 's/.*"\(=*[0-9][^"]*\)".*/\1/' | sed 's/^=//')
20+
JACS_RUST_MCP := $(shell grep '^jacs ' rust/hai-mcp/Cargo.toml | sed 's/.*"\(=*[0-9][^"]*\)".*/\1/' | sed 's/^=//')
21+
JACS_PYTHON := $(shell grep 'jacs==' python/pyproject.toml | sed 's/.*jacs==\([^"]*\)".*/\1/')
22+
JACS_NODE := $(shell grep '@hai.ai/jacs' node/package.json | head -1 | sed 's/.*: *"\(.*\)".*/\1/')
23+
1724
# ============================================================================
1825
# TEST
1926
# ============================================================================
@@ -70,6 +77,23 @@ check-versions:
7077
echo "ERROR: haiai ($(RUST_VERSION)) != plugin ($(PLUGIN_VERSION))"; exit 1; fi
7178
@echo "All versions match: $(RUST_VERSION)"
7279

80+
check-jacs-versions:
81+
@echo "JACS dependency versions:"
82+
@echo " rust/haiai $(JACS_RUST)"
83+
@echo " rust/haiai-cli $(JACS_RUST_CLI)"
84+
@echo " rust/hai-mcp $(JACS_RUST_MCP)"
85+
@echo " python $(JACS_PYTHON)"
86+
@echo " node $(JACS_NODE)"
87+
@if [ "$(JACS_RUST)" != "$(JACS_RUST_CLI)" ]; then \
88+
echo "ERROR: jacs in haiai ($(JACS_RUST)) != haiai-cli ($(JACS_RUST_CLI))"; exit 1; fi
89+
@if [ "$(JACS_RUST)" != "$(JACS_RUST_MCP)" ]; then \
90+
echo "ERROR: jacs in haiai ($(JACS_RUST)) != hai-mcp ($(JACS_RUST_MCP))"; exit 1; fi
91+
@if [ "$(JACS_RUST)" != "$(JACS_PYTHON)" ]; then \
92+
echo "ERROR: jacs in haiai ($(JACS_RUST)) != python ($(JACS_PYTHON))"; exit 1; fi
93+
@if [ "$(JACS_RUST)" != "$(JACS_NODE)" ]; then \
94+
echo "ERROR: jacs in haiai ($(JACS_RUST)) != node ($(JACS_NODE))"; exit 1; fi
95+
@echo "All JACS versions match: $(JACS_RUST)"
96+
7397
# ============================================================================
7498
# GITHUB CI RELEASE (via git tags)
7599
# ============================================================================
@@ -107,7 +131,7 @@ release-node:
107131
git push origin node/v$(NODE_VERSION)
108132
@echo "Tagged node/v$(NODE_VERSION) - CI will publish to npm"
109133

110-
release-all: check-versions release-rust
134+
release-all: check-versions check-jacs-versions release-rust
111135
@echo "Waiting 30s for Rust CI to start building CLI binaries..."
112136
@sleep 30
113137
$(MAKE) release-python release-node
@@ -129,7 +153,8 @@ help:
129153
@echo ""
130154
@echo "VERSION INFO:"
131155
@echo " make versions Show all detected versions"
132-
@echo " make check-versions Verify all package versions match"
156+
@echo " make check-versions Verify all package versions match"
157+
@echo " make check-jacs-versions Verify JACS dep versions match across SDKs"
133158
@echo ""
134159
@echo "TEST:"
135160
@echo " make test Run all tests"

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Give your AI agent an email address. Register, get a `@hai.ai` address, and build a reputation.
44

5-
Official SDKs for the [HAI.AI](https://hai.ai) platform -- cryptographic agent identity, signed email, and conflict-resolution benchmarking for AI agents.
5+
Official SDKs for the [HAI.AI](https://hai.ai) platform -- build helpful, trustworthy AI agents with cryptographic identity, signed email, and verified benchmarks.
66

77
## Install
88

@@ -26,7 +26,7 @@ This gives you the `haiai` binary with built-in MCP server.
2626

2727
```toml
2828
[dependencies]
29-
haiai = "0.1.4"
29+
haiai = "0.1.5"
3030
```
3131

3232
### Python

agents.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Agents — Lessons Learned
2+
3+
Quality audit of the HAIAI SDK (Python, Node, Go, Rust) — March 2026.
4+
5+
## Critical Findings
6+
7+
### Wrong endpoints (Go)
8+
- `ProRun()` called `/api/benchmark/subscribe` instead of `/api/benchmark/purchase`.
9+
- `GetAgentAttestation()` used singular `/attestation` instead of plural `/attestations`.
10+
- Key lookup functions (`FetchKeyByEmail`, etc.) defaulted to a separate `keys.hai.ai` host instead of the main HAI endpoint.
11+
- **Lesson:** Endpoint paths drift silently across SDKs. The `fixtures/contract_endpoints.json` fixture now exists to catch this — keep it updated when adding new endpoints.
12+
13+
### Missing payload fields (Python, Go)
14+
- Python `hello_world()` omitted `agent_id` from the POST body (present in the other 3 SDKs).
15+
- `benchmark()` in Python and Go omitted `"transport": "sse"`.
16+
- **Lesson:** When one SDK adds a field, grep all SDKs for the same call and confirm parity.
17+
18+
## High-Priority Patterns
19+
20+
### No retry / no backoff
21+
- Go and Rust had zero retry logic. Node threw immediately on 429.
22+
- **Fix:** All SDKs now retry on `[429, 500, 502, 503, 504]` with exponential backoff (default 3 attempts). The status-code list is validated by a cross-SDK contract test (`test_retryable_status_codes_match_python`).
23+
- **Lesson:** Retry policy is a cross-cutting concern — treat it like a shared spec, not per-SDK discretion.
24+
25+
### Unbounded reads (Go)
26+
- `io.ReadAll()` without a size cap could exhaust memory on malicious responses.
27+
- **Fix:** `limitedReadAll()` helper caps at 10 MB.
28+
- **Lesson:** Any `ReadAll`/`.text()`/`.read()` on an HTTP response should have a size limit.
29+
30+
## Medium-Priority Patterns
31+
32+
### Private key exposure (Node)
33+
- Private key was stored as an enumerable property, visible in `JSON.stringify()` and property iteration.
34+
- **Fix:** Moved to a module-level `WeakMap`, non-enumerable and non-serializable.
35+
- **Lesson:** Secrets should never be plain object properties. Use `WeakMap`, closures, or `Object.defineProperty` with `enumerable: false`.
36+
37+
### Hardcoded passphrase (Node)
38+
- `register()` used `"register-temp"` as a temp key passphrase.
39+
- **Fix:** `crypto.randomBytes()`.
40+
- **Lesson:** Grep for string literals that look like passphrases/tokens — they're easy to miss in review.
41+
42+
### Base URL validation
43+
- Python, Node, and Rust accepted arbitrary URL schemes (`ftp://`, `file://`, `javascript://`).
44+
- **Fix:** All SDKs now reject non-`http(s)://` schemes at construction time.
45+
- **Lesson:** Validate inputs at the boundary, not deep in the call chain.
46+
47+
### Unbounded reconnection (Node, Rust)
48+
- SSE/WebSocket reconnection loops had no cap — could retry forever.
49+
- **Fix:** `maxReconnectAttempts` (default 10) with exponential backoff.
50+
- **Lesson:** Every retry loop needs a ceiling.
51+
52+
### Missing SSE timeout (Go)
53+
- SSE HTTP client had no `ResponseHeaderTimeout` — could hang indefinitely.
54+
- **Fix:** 30-second timeout on response headers.
55+
56+
### Race conditions (Go)
57+
- Mutable fields (`haiAgentID`, `agentEmail`) accessed without synchronization.
58+
- **Fix:** `sync.RWMutex` around mutable state.
59+
- **Lesson:** Any field written after construction needs a mutex in Go.
60+
61+
### Missing query parameters (Python, Go, Node)
62+
- `list_messages()` and `search_messages()` were missing `has_attachments`, `since`, `until` filters that Rust already had.
63+
- **Lesson:** When Rust (the reference implementation) has a feature, all SDKs should too.
64+
65+
### Reply endpoint undocumented (Rust)
66+
- Rust uses a server-side `/email/reply` endpoint; other SDKs compose client-side. Neither approach was documented.
67+
- **Fix:** Added to `fixtures/contract_endpoints.json` with a migration note.
68+
- **Lesson:** Architectural divergences between SDKs need explicit documentation, not just code.
69+
70+
## Process Takeaways
71+
72+
1. **Contract fixtures prevent drift.** `fixtures/contract_endpoints.json` is the source of truth for endpoints. Add new entries there first.
73+
2. **Cross-SDK grep is mandatory.** Before merging a behavior change in one SDK, grep the same function/field name across all four.
74+
3. **Test the sad path.** Most bugs were in error handling, retry logic, and edge cases — not the happy path.
75+
4. **Security is a default, not a feature.** Key exposure, hardcoded secrets, and unbounded reads were all "it works" code that was silently dangerous.
76+
5. **Rust is the reference.** When in doubt about what a function should do, check the Rust implementation first — it tends to be the most complete.

fixtures/contract_endpoints.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,11 @@
1414
"method": "POST",
1515
"path": "/api/v1/agents/jobs/{job_id}/response",
1616
"auth_required": true
17+
},
18+
"reply": {
19+
"method": "POST",
20+
"path": "/api/agents/{agent_id}/email/reply",
21+
"auth_required": true,
22+
"note": "Server-side reply endpoint. Rust SDK uses this directly; Python/Node/Go currently fetch the original client-side and compose locally. Other SDKs should migrate to this endpoint for consistency."
1723
}
1824
}

go/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# haiai-go -- Go SDK
22

3-
Give your AI agent an email address. Go SDK for the [HAI.AI](https://hai.ai) platform -- cryptographic agent identity, signed email, and conflict-resolution benchmarking.
3+
Give your AI agent an email address. Go SDK for the [HAI.AI](https://hai.ai) platform -- build helpful, trustworthy AI agents with cryptographic identity, signed email, and verified benchmarks.
44

55
## Install
66

0 commit comments

Comments
 (0)