Skip to content

Commit bd5bd9d

Browse files
author
Neo
committed
feat: production hardening round 2 — migrations, prometheus, restore, CI
Database - Versioned schema migrations replace the flat SCHEMA list. Each migration runs inside BEGIN IMMEDIATE and is recorded in a new schema_version table; partial failures roll back atomically. - Database.restore_from() closes the live connection, atomically swaps the database file via a *.restoring temp + os.replace, reopens, and re-runs migrations. BackupManager.restore_latest()/restore_from() expose the higher-level helpers. Observability - New GET /metrics/prom endpoint emits Prometheus text exposition (counters as _total, gauges, histograms as summaries with quantiles). - Service / oracle cache prune wired into the gateway cleanup loop via duck-typed prune_caches() chain (ToolDispatcher → ServiceDispatcher → ServiceRegistry → OracleGateway). Evictions reported as caches.evicted. - MatrixBridge balance lookups now go through a lazily constructed Web3Manager, falling back gracefully when the chain isn't configured. Packaging - pyproject.toml is now the source of truth for metadata, build config, optional dependency groups, and tool config (pytest/coverage/mypy/ interrogate). - requirements.txt is runtime-only with ~= pins. Dev tooling moved to requirements-dev.txt; optional Sentry monitoring extras live in requirements-monitoring.txt and the [opnmatrx[monitoring]] extra. CI / repo hygiene - New CI jobs: mypy, interrogate, pytest-cov, pip-audit, and a Docker smoke test that builds the image and curls /health. - New release workflow cuts a GitHub Release on v* tags, builds sdist + wheel, and pulls the matching CHANGELOG section as the release body. - .github/CODEOWNERS, .github/PULL_REQUEST_TEMPLATE.md, and docs/RUNBOOK.md added. - ROADMAP.md notes that MTRX iOS lives in a separate repo. Tests (216 total, all passing) - New test_db_migrations.py (12 tests): schema versioning, idempotency, additive migrations, rollback on failure. - New test_metrics.py (14 tests): counter/gauge/histogram + Prometheus format. - New test_websocket.py (8 tests): handle_websocket happy path, conversation persistence across frames, validation errors, graceful failure when the ReAct loop raises. - Extended test_backup.py (5 new tests) and test_graceful_degradation.py (1 new test) for restore flow and cache eviction.
1 parent 887ae47 commit bd5bd9d

25 files changed

Lines changed: 1774 additions & 116 deletions

.github/CODEOWNERS

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# 0pnMatrx codeowners
2+
#
3+
# Pull requests touching these paths automatically request a review from
4+
# the listed owners. Order matters: the LAST matching pattern wins.
5+
6+
# Catch-all
7+
* @ItsDardanRexhepi
8+
9+
# Gateway / runtime / hivemind core
10+
/gateway/ @ItsDardanRexhepi
11+
/runtime/ @ItsDardanRexhepi
12+
/hivemind/ @ItsDardanRexhepi
13+
14+
# Smart contracts and blockchain glue
15+
/contracts/ @ItsDardanRexhepi
16+
/runtime/blockchain/ @ItsDardanRexhepi
17+
18+
# CI / release tooling
19+
/.github/workflows/ @ItsDardanRexhepi
20+
/Dockerfile @ItsDardanRexhepi
21+
/pyproject.toml @ItsDardanRexhepi
22+
/requirements*.txt @ItsDardanRexhepi
23+
24+
# Docs / runbook
25+
/docs/ @ItsDardanRexhepi
26+
/README.md @ItsDardanRexhepi
27+
/CHANGELOG.md @ItsDardanRexhepi
28+
/ROADMAP.md @ItsDardanRexhepi

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
## Summary
2+
3+
<!-- One or two sentences. What does this PR change and why? -->
4+
5+
## Changes
6+
7+
<!-- Bulleted list of what's actually different. Include touched paths
8+
when it helps reviewers find their way around. -->
9+
10+
-
11+
-
12+
-
13+
14+
## Testing
15+
16+
<!-- Show what you ran. CI will run the suite again, but please give
17+
reviewers a clear local signal. -->
18+
19+
- [ ] `pytest tests/ -v`
20+
- [ ] `flake8 . --select=E9,F63,F7,F82`
21+
- [ ] Manual smoke test (describe below)
22+
23+
```
24+
25+
```
26+
27+
## Risk / rollout
28+
29+
<!-- Schema migrations, breaking API changes, config knobs, anything
30+
that affects the on-call playbook. If this needs an entry in
31+
docs/RUNBOOK.md, link to the section you updated. -->
32+
33+
- Schema migrations added: <!-- yes/no, version number -->
34+
- Config changes required: <!-- yes/no, key path -->
35+
- Backwards compatible: <!-- yes/no -->
36+
37+
## Checklist
38+
39+
- [ ] Tests added or updated for the behaviour change
40+
- [ ] CHANGELOG.md updated under the next version
41+
- [ ] Docs updated if behaviour or config changed
42+
- [ ] No secrets, credentials, or `.env` files committed

.github/workflows/ci.yml

Lines changed: 99 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,9 @@ jobs:
2020
- name: Install dependencies
2121
run: |
2222
python -m pip install --upgrade pip
23-
pip install -r requirements.txt
24-
pip install flake8
23+
pip install -r requirements.txt -r requirements-dev.txt
2524
26-
- name: Lint with flake8
25+
- name: Lint with flake8 (fatal errors only)
2726
run: |
2827
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics --exclude=.venv,migrations
2928
@@ -32,6 +31,48 @@ jobs:
3231
python -c "import gateway; print('gateway OK')" || true
3332
python -c "import runtime; print('runtime OK')" || true
3433
34+
typecheck:
35+
runs-on: ubuntu-latest
36+
needs: lint
37+
steps:
38+
- uses: actions/checkout@v4
39+
40+
- name: Set up Python
41+
uses: actions/setup-python@v5
42+
with:
43+
python-version: '3.11'
44+
45+
- name: Install dependencies
46+
run: |
47+
python -m pip install --upgrade pip
48+
pip install -r requirements.txt -r requirements-dev.txt
49+
50+
- name: Run mypy
51+
# Non-blocking for now: the codebase is being type-annotated
52+
# incrementally. Per-module strictness lives in pyproject.toml.
53+
run: |
54+
mypy --config-file pyproject.toml || true
55+
56+
docstrings:
57+
runs-on: ubuntu-latest
58+
needs: lint
59+
steps:
60+
- uses: actions/checkout@v4
61+
62+
- name: Set up Python
63+
uses: actions/setup-python@v5
64+
with:
65+
python-version: '3.11'
66+
67+
- name: Install dependencies
68+
run: |
69+
python -m pip install --upgrade pip
70+
pip install -r requirements-dev.txt
71+
72+
- name: Run interrogate (docstring coverage)
73+
run: |
74+
interrogate -c pyproject.toml runtime gateway hivemind || true
75+
3576
test:
3677
runs-on: ubuntu-latest
3778
needs: lint
@@ -46,15 +87,24 @@ jobs:
4687
- name: Install dependencies
4788
run: |
4889
python -m pip install --upgrade pip
49-
pip install -r requirements.txt
50-
pip install pytest pytest-asyncio pytest-cov aiohttp
90+
pip install -r requirements.txt -r requirements-dev.txt
5191
52-
- name: Run tests
92+
- name: Run tests with coverage
5393
run: |
54-
python -m pytest tests/ -v --tb=short -x
94+
python -m pytest tests/ -v --tb=short \
95+
--cov=runtime --cov=gateway --cov=hivemind \
96+
--cov-report=term-missing --cov-report=xml
5597
env:
5698
PYTHONPATH: .
5799

100+
- name: Upload coverage artifact
101+
if: always()
102+
uses: actions/upload-artifact@v4
103+
with:
104+
name: coverage-xml
105+
path: coverage.xml
106+
if-no-files-found: ignore
107+
58108
security:
59109
runs-on: ubuntu-latest
60110
needs: lint
@@ -69,13 +119,13 @@ jobs:
69119
- name: Install dependencies
70120
run: |
71121
python -m pip install --upgrade pip
72-
pip install -r requirements.txt
73-
pip install safety bandit
122+
pip install -r requirements.txt -r requirements-dev.txt
74123
75-
- name: Check dependencies for vulnerabilities
124+
- name: Audit dependencies with pip-audit
125+
# Non-blocking: the dependency tree includes upstream advisories
126+
# we cannot patch ourselves; this surfaces them in CI logs.
76127
run: |
77-
pip install safety
78-
safety check --output text || true
128+
pip-audit --strict || true
79129
80130
- name: Bandit security scan
81131
run: |
@@ -84,3 +134,40 @@ jobs:
84134
- name: Check for hardcoded secrets
85135
run: |
86136
grep -rn "YOUR_" --include="*.json" . | grep -v ".example" | grep -v "node_modules" && echo "WARNING: Possible non-example config with placeholder keys" || echo "No hardcoded secrets found"
137+
138+
docker:
139+
runs-on: ubuntu-latest
140+
needs: [lint, test]
141+
steps:
142+
- uses: actions/checkout@v4
143+
144+
- name: Set up Docker Buildx
145+
uses: docker/setup-buildx-action@v3
146+
147+
- name: Build image
148+
uses: docker/build-push-action@v6
149+
with:
150+
context: .
151+
push: false
152+
load: true
153+
tags: opnmatrx-gateway:ci
154+
cache-from: type=gha
155+
cache-to: type=gha,mode=max
156+
157+
- name: Smoke test container
158+
# Boot the gateway, hit /health, and make sure it returns 200.
159+
run: |
160+
set -e
161+
cid=$(docker run -d --rm -p 18790:18790 opnmatrx-gateway:ci)
162+
trap "docker kill $cid >/dev/null 2>&1 || true" EXIT
163+
# Give the gateway a few seconds to bind.
164+
for i in 1 2 3 4 5 6 7 8 9 10; do
165+
if curl -fsS http://localhost:18790/health >/dev/null; then
166+
echo "Gateway responded on attempt $i"
167+
exit 0
168+
fi
169+
sleep 2
170+
done
171+
echo "Gateway did not respond to /health within timeout"
172+
docker logs $cid || true
173+
exit 1

.github/workflows/release.yml

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
name: Release
2+
3+
# Cuts a GitHub Release whenever a ``v*`` tag is pushed (e.g. ``v0.4.0``).
4+
# The job:
5+
# 1. checks out the tag
6+
# 2. builds the sdist + wheel from pyproject.toml
7+
# 3. extracts the matching section of CHANGELOG.md as the release body
8+
# 4. creates the GitHub Release with the build artefacts attached
9+
#
10+
# Tagging convention: lightweight or annotated tags both work as long as
11+
# they match ``v[0-9]+.[0-9]+.[0-9]+``. The CHANGELOG section header must
12+
# be ``## [X.Y.Z] - YYYY-MM-DD`` for the extractor to find it.
13+
14+
on:
15+
push:
16+
tags:
17+
- 'v[0-9]+.[0-9]+.[0-9]+'
18+
- 'v[0-9]+.[0-9]+.[0-9]+-*'
19+
20+
permissions:
21+
contents: write
22+
23+
jobs:
24+
release:
25+
runs-on: ubuntu-latest
26+
steps:
27+
- name: Checkout
28+
uses: actions/checkout@v4
29+
with:
30+
fetch-depth: 0
31+
32+
- name: Set up Python
33+
uses: actions/setup-python@v5
34+
with:
35+
python-version: '3.11'
36+
37+
- name: Install build tooling
38+
run: |
39+
python -m pip install --upgrade pip
40+
pip install build
41+
42+
- name: Build sdist and wheel
43+
run: |
44+
python -m build
45+
46+
- name: Extract version from tag
47+
id: version
48+
run: |
49+
tag="${GITHUB_REF#refs/tags/}"
50+
echo "tag=$tag" >> "$GITHUB_OUTPUT"
51+
echo "version=${tag#v}" >> "$GITHUB_OUTPUT"
52+
53+
- name: Extract changelog section
54+
id: changelog
55+
run: |
56+
version="${{ steps.version.outputs.version }}"
57+
# Pull the section between ``## [X.Y.Z]`` and the next ``## [``.
58+
awk -v ver="$version" '
59+
$0 ~ "^## \\[" ver "\\]" { capture=1; next }
60+
capture && $0 ~ "^## \\[" { exit }
61+
capture { print }
62+
' CHANGELOG.md > release_notes.md
63+
if [ ! -s release_notes.md ]; then
64+
echo "No CHANGELOG entry for $version — falling back to tag message" >&2
65+
git tag -l --format='%(contents)' "${{ steps.version.outputs.tag }}" > release_notes.md
66+
fi
67+
# Always show what we're about to publish for log auditability.
68+
echo "----- release_notes.md -----"
69+
cat release_notes.md
70+
echo "----- end -----"
71+
72+
- name: Create GitHub Release
73+
uses: softprops/action-gh-release@v2
74+
with:
75+
tag_name: ${{ steps.version.outputs.tag }}
76+
name: ${{ steps.version.outputs.tag }}
77+
body_path: release_notes.md
78+
draft: false
79+
prerelease: ${{ contains(steps.version.outputs.tag, '-') }}
80+
files: |
81+
dist/*.whl
82+
dist/*.tar.gz

CHANGELOG.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,77 @@ All notable changes to 0pnMatrx are documented here.
44

55
---
66

7+
## [0.4.0] — 2026-04-09
8+
9+
### Production hardening — round 2
10+
11+
#### Database
12+
13+
- Versioned schema migrations replace the flat `SCHEMA` list. Each
14+
migration runs inside `BEGIN IMMEDIATE` and is recorded in a new
15+
`schema_version` table; partial failures roll back atomically.
16+
- `Database.restore_from()` closes the live connection, atomically
17+
swaps the database file, reopens it, and re-runs migrations to bring
18+
the snapshot up to the current version.
19+
- `BackupManager.restore_latest()` and `restore_from()` higher-level
20+
helpers; full restore procedure documented in `docs/RUNBOOK.md`.
21+
22+
#### Observability
23+
24+
- New `GET /metrics/prom` endpoint exposes counters, gauges, and
25+
histograms in Prometheus text exposition format (counters as
26+
`_total`, histograms as summaries with 0.5 / 0.95 / 0.99 quantiles).
27+
- Service / oracle cache prune is now wired into the gateway cleanup
28+
loop via a duck-typed `prune_caches()` chain
29+
(`ToolDispatcher → ServiceDispatcher → ServiceRegistry →
30+
OracleGateway`). Evictions are reported via `caches.evicted`.
31+
- `MatrixBridge` now performs real on-chain balance lookups via a
32+
lazily constructed `Web3Manager`, falling back gracefully when the
33+
chain is not configured.
34+
35+
#### Packaging
36+
37+
- `pyproject.toml` is now the source of truth for metadata, build
38+
config, optional dependency groups, and tool configuration (pytest,
39+
coverage, mypy, interrogate).
40+
- `requirements.txt` is now runtime-only with `~=` ("compatible
41+
release") pins. Development tooling moved to `requirements-dev.txt`,
42+
optional Sentry monitoring extras to `requirements-monitoring.txt`
43+
and the `[opnmatrx[monitoring]]` extra.
44+
45+
#### CI / repo hygiene
46+
47+
- New CI jobs: mypy type-check, interrogate docstring coverage,
48+
pytest-cov coverage reporting, pip-audit dependency audit, and a
49+
Docker smoke-test job that builds the image and curls `/health`.
50+
- New release workflow (`.github/workflows/release.yml`) cuts a
51+
GitHub Release when a `v*` tag is pushed, builds sdist + wheel, and
52+
pulls the matching CHANGELOG section as the release body.
53+
- `.github/CODEOWNERS` and `.github/PULL_REQUEST_TEMPLATE.md` added.
54+
- `docs/RUNBOOK.md` added with the full on-call playbook.
55+
56+
#### Tests
57+
58+
- New: `tests/test_db_migrations.py` (12 tests) covering schema
59+
versioning, idempotency, additive migrations, and rollback on
60+
failure.
61+
- New: `tests/test_metrics.py` (14 tests) covering counter / gauge /
62+
histogram collection plus Prometheus formatting.
63+
- New: `tests/test_websocket.py` (8 tests) covering the previously
64+
uncovered `handle_websocket` endpoint: happy path, conversation
65+
persistence, validation errors, and graceful failure when the ReAct
66+
loop raises.
67+
- Extended: `tests/test_backup.py` and `tests/test_graceful_degradation.py`
68+
with restore-flow and cache-eviction coverage.
69+
70+
#### Notes
71+
72+
- The MTRX iOS app remains intentionally out of scope for this
73+
repository — see `ROADMAP.md` for what belongs in the separate
74+
`MTRX-iOS` repo (Swift code, APNs, TestFlight CI, StoreKit).
75+
76+
---
77+
778
## [0.3.0] — 2026-04-08
879

980
### Managed Agent Orchestration

0 commit comments

Comments
 (0)