chore: remove internal docs, fix bench, clean README header, add CI b… #5
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # ═══════════════════════════════════════════════════════════════════════════════ | |
| # INVARIANT — GitHub Actions CI | |
| # Deterministic Trust Infrastructure for Bittensor | |
| # by Orthonode Infrastructure Labs · orthonode.xyz | |
| # ═══════════════════════════════════════════════════════════════════════════════ | |
| name: INVARIANT CI | |
| on: | |
| push: | |
| branches: [main, develop, "feat/**", "fix/**"] | |
| pull_request: | |
| branches: [main, develop] | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # Job 1: Python test suite (no Rust required) | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| test-python: | |
| name: Python Tests (fallback backend) | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| python-version: ["3.10", "3.11", "3.12"] | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| cache: pip | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -r requirements.txt | |
| - name: Run local test suite (pixel-art UI, no-color mode) | |
| run: | | |
| python scripts/test_locally.py --no-color --quick | |
| - name: Run pytest (21 tests) | |
| run: | | |
| pytest invariant/invariant/tests/ -v --tb=short | |
| - name: Bridge self-test | |
| run: | | |
| python invariant/invariant/phase1_core/invariant_gates_bridge.py | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # Job 2: Rust gate engine build + tests | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| test-rust: | |
| name: Rust Gate Engine | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: rustfmt, clippy | |
| - name: Cache Rust build artifacts | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| invariant/invariant-gates/target | |
| key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo- | |
| - name: Check formatting | |
| working-directory: invariant/invariant-gates | |
| run: cargo fmt --check | |
| - name: Clippy (deny warnings) | |
| working-directory: invariant/invariant-gates | |
| run: cargo clippy -- -D warnings | |
| - name: Run Rust unit tests | |
| working-directory: invariant/invariant-gates | |
| run: cargo test --release | |
| - name: Build benchmarks (compile check only) | |
| working-directory: invariant/invariant-gates | |
| run: cargo bench --no-run | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # Job 3: Rust + Python integration (maturin build) | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| test-rust-python: | |
| name: Rust + Python Integration | |
| runs-on: ubuntu-latest | |
| needs: [test-python, test-rust] | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Set up Python 3.12 | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.12" | |
| cache: pip | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Cache Rust build artifacts | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| invariant/invariant-gates/target | |
| key: ${{ runner.os }}-cargo-maturin-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo-maturin- | |
| - name: Install Python dependencies + maturin | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -r requirements.txt | |
| pip install maturin | |
| - name: Build Rust extension with maturin | |
| working-directory: invariant/invariant-gates | |
| run: | | |
| maturin develop --features python-ext --release | |
| - name: Verify Rust backend is active | |
| run: | | |
| python -c " | |
| import sys | |
| sys.path.insert(0, 'invariant/invariant/phase1_core') | |
| from invariant_gates_bridge import using_rust | |
| assert using_rust(), 'Expected Rust backend after maturin develop' | |
| print('✅ Rust backend confirmed active') | |
| " | |
| - name: Run full test suite with Rust backend | |
| run: | | |
| python scripts/test_locally.py --no-color --quick | |
| - name: Run pytest with Rust backend | |
| run: | | |
| pytest invariant/invariant/tests/ -v --tb=short | |
| - name: Throughput assertion (Rust must be < 50µs/receipt) | |
| run: | | |
| python -c " | |
| import sys, time, tempfile | |
| from pathlib import Path | |
| sys.path.insert(0, 'invariant/invariant/phase1_core') | |
| from invariant_gates_bridge import ( | |
| Registry, Verifier, build_receipt, | |
| derive_software_agent_id, hash_model, using_rust, GateResult | |
| ) | |
| assert using_rust(), 'Rust backend required for this test' | |
| with tempfile.TemporaryDirectory() as tmp: | |
| mh = hash_model('ci-perf-model') | |
| ah = derive_software_agent_id('5CIHotkey', mh, 1) | |
| reg = Registry(f'{tmp}/reg.json') | |
| reg.register_agent(ah, '5CIHotkey') | |
| reg.approve_model(mh) | |
| v = Verifier(f'{tmp}/reg.json', f'{tmp}/state.json') | |
| N = 500 | |
| BASE = 90000 | |
| t0 = time.perf_counter() | |
| for i in range(N): | |
| r = build_receipt(ah, mh, f't{i}', f'o{i}', BASE+i, 100, time.time()) | |
| assert GateResult.is_pass(v.verify(r)['result']) | |
| elapsed = time.perf_counter() - t0 | |
| per_us = (elapsed / N) * 1e6 | |
| rate = int(N / elapsed) | |
| print(f'Rust throughput: {per_us:.1f} µs/receipt ({rate:,} receipts/sec)') | |
| assert per_us < 200, f'Rust too slow: {per_us:.1f} µs (limit 200 µs in CI)' | |
| print('✅ Throughput assertion passed') | |
| " | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # Job 4: Security checks | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| security: | |
| name: Security Checks | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Check no seed phrases committed | |
| run: | | |
| if find . -name "*.mnemonic" -o -name "*.seed" -o -name "mnemonic.txt" -o -name "seed.txt" | grep -q .; then | |
| echo "❌ Seed phrase files found in repository!" | |
| exit 1 | |
| fi | |
| echo "✅ No seed phrase files found" | |
| - name: Check no .bittensor wallets committed | |
| run: | | |
| if find . -path "./.bittensor/wallets" -type d | grep -q .; then | |
| echo "❌ Bittensor wallet directory committed!" | |
| exit 1 | |
| fi | |
| echo "✅ No wallet directories found" | |
| - name: Check no private key patterns in code | |
| run: | | |
| # Check for obvious private key patterns (64-char hex not in test fixtures) | |
| if grep -r --include="*.py" "private_key\s*=\s*['\"]0x[a-fA-F0-9]\{64\}" . | grep -v test | grep -v "# example"; then | |
| echo "❌ Potential private key in code!" | |
| exit 1 | |
| fi | |
| echo "✅ No private key patterns found" | |
| - name: Verify .gitignore covers sensitive files | |
| run: | | |
| python -c " | |
| with open('.gitignore') as f: | |
| content = f.read() | |
| required = ['.bittensor/', 'seed-phrase/', 'miner_data/', 'validator_data/', '.env'] | |
| for pattern in required: | |
| if pattern not in content: | |
| print(f'❌ Missing from .gitignore: {pattern}') | |
| exit(1) | |
| print('✅ .gitignore covers all sensitive patterns') | |
| " | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # Job 5: Documentation check | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| docs: | |
| name: Documentation Check | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Check all required docs exist | |
| run: | | |
| python -c " | |
| from pathlib import Path | |
| required = [ | |
| 'README.md', | |
| 'ROADMAP.md', | |
| 'THREAT_MODEL.md', | |
| 'ARCHITECTURE.md', | |
| 'CHANGELOG.md', | |
| 'CONTRIBUTING.md', | |
| 'SECURITY.md', | |
| 'LICENSE', | |
| '.gitignore', | |
| 'docs/PITCH.md', | |
| 'docs/WHITEPAPER.md', | |
| 'docs/DEPLOYMENT_GUIDE.md', | |
| 'docs/INCENTIVE_MECHANISM.md', | |
| 'docs/LOCAL_TESTING.md', | |
| ] | |
| missing = [f for f in required if not Path(f).exists()] | |
| if missing: | |
| print('❌ Missing docs:') | |
| for f in missing: | |
| print(f' {f}') | |
| exit(1) | |
| print(f'✅ All {len(required)} required documents present') | |
| " | |
| - name: Check README has key sections | |
| run: | | |
| python -c " | |
| with open('README.md') as f: | |
| content = f.read() | |
| required = ['## What is INVARIANT', '## The Four Gates', '## Architecture', | |
| '## Credentials', '## Testing', 'orthonode.xyz'] | |
| missing = [s for s in required if s not in content] | |
| if missing: | |
| print('❌ README missing sections:', missing) | |
| exit(1) | |
| print('✅ README contains all required sections') | |
| " | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # Final status gate | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| all-checks: | |
| name: All Checks Pass | |
| runs-on: ubuntu-latest | |
| needs: [test-python, test-rust, security, docs] | |
| if: always() | |
| steps: | |
| - name: Check all jobs succeeded | |
| run: | | |
| if [[ "${{ needs.test-python.result }}" != "success" ]]; then | |
| echo "❌ Python tests failed" | |
| exit 1 | |
| fi | |
| if [[ "${{ needs.test-rust.result }}" != "success" ]]; then | |
| echo "❌ Rust tests failed" | |
| exit 1 | |
| fi | |
| if [[ "${{ needs.security.result }}" != "success" ]]; then | |
| echo "❌ Security checks failed" | |
| exit 1 | |
| fi | |
| if [[ "${{ needs.docs.result }}" != "success" ]]; then | |
| echo "❌ Documentation checks failed" | |
| exit 1 | |
| fi | |
| echo "✅ All INVARIANT CI checks passed" | |
| echo "" | |
| echo "INVARIANT is ready for deployment." | |
| echo " SHA live: Arbitrum Sepolia 0xD661...4615" | |
| echo " TON-SHA live: TON Testnet kQBV...05OP" | |
| echo " Tests: 21/21 pytest + 5/5 local suite" |