guardspine-local-council 2.0.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.
- guardspine_local_council-2.0.0/.github/CLA.md +41 -0
- guardspine_local_council-2.0.0/.github/workflows/ci.yml +26 -0
- guardspine_local_council-2.0.0/.github/workflows/cla.yml +34 -0
- guardspine_local_council-2.0.0/.github/workflows/codeguard.yml +26 -0
- guardspine_local_council-2.0.0/.gitignore +10 -0
- guardspine_local_council-2.0.0/LICENSE +100 -0
- guardspine_local_council-2.0.0/NOTICE +16 -0
- guardspine_local_council-2.0.0/PKG-INFO +295 -0
- guardspine_local_council-2.0.0/README.md +281 -0
- guardspine_local_council-2.0.0/audit-all-repos.py +314 -0
- guardspine_local_council-2.0.0/evidence-packs/audit-summary.json +46 -0
- guardspine_local_council-2.0.0/evidence-packs/audit.log +146 -0
- guardspine_local_council-2.0.0/evidence-packs/council-audit-2026-01-31-run2.json +1122 -0
- guardspine_local_council-2.0.0/evidence-packs/guardspine-adapter-webhook-evidence.json +798 -0
- guardspine_local_council-2.0.0/evidence-packs/guardspine-kernel-evidence.json +364 -0
- guardspine_local_council-2.0.0/evidence-packs/guardspine-local-council-evidence.json +1122 -0
- guardspine_local_council-2.0.0/evidence-packs/guardspine-spec-evidence.json +586 -0
- guardspine_local_council-2.0.0/evidence-packs/guardspine-verify-evidence.json +1046 -0
- guardspine_local_council-2.0.0/evidence-packs/n8n-nodes-guardspine-evidence.json +887 -0
- guardspine_local_council-2.0.0/evidence-packs/rlm-docsync-evidence.json +1021 -0
- guardspine_local_council-2.0.0/evidence-test.py +201 -0
- guardspine_local_council-2.0.0/examples/basic_review.py +45 -0
- guardspine_local_council-2.0.0/lib/pii-shield.wasm +0 -0
- guardspine_local_council-2.0.0/pyproject.toml +19 -0
- guardspine_local_council-2.0.0/src/guardspine_local_council/__init__.py +39 -0
- guardspine_local_council-2.0.0/src/guardspine_local_council/adapters/pii_wasm_client.py +129 -0
- guardspine_local_council-2.0.0/src/guardspine_local_council/aggregator.py +48 -0
- guardspine_local_council-2.0.0/src/guardspine_local_council/council.py +827 -0
- guardspine_local_council-2.0.0/src/guardspine_local_council/providers/__init__.py +24 -0
- guardspine_local_council-2.0.0/src/guardspine_local_council/providers/anthropic.py +111 -0
- guardspine_local_council-2.0.0/src/guardspine_local_council/providers/hooks.py +252 -0
- guardspine_local_council-2.0.0/src/guardspine_local_council/providers/mcp_client.py +149 -0
- guardspine_local_council-2.0.0/src/guardspine_local_council/providers/ollama.py +102 -0
- guardspine_local_council-2.0.0/src/guardspine_local_council/providers/openai.py +96 -0
- guardspine_local_council-2.0.0/src/guardspine_local_council/providers/openrouter.py +109 -0
- guardspine_local_council-2.0.0/src/guardspine_local_council/types.py +175 -0
- guardspine_local_council-2.0.0/tests/__init__.py +0 -0
- guardspine_local_council-2.0.0/tests/test_aggregator.py +67 -0
- guardspine_local_council-2.0.0/tests/test_council.py +234 -0
- guardspine_local_council-2.0.0/tests/test_council_mock.py +128 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# GuardSpine Individual Contributor License Agreement (DRAFT - counsel review)
|
|
2
|
+
|
|
3
|
+
Purpose: every contribution after this point must grant GuardSpine the right to
|
|
4
|
+
relicense, so the no-CLA gap (the root cause of the aragossa problem) never
|
|
5
|
+
recurs. Based on the Apache ICLA with an explicit relicense/sublicense grant.
|
|
6
|
+
Wire a CLA-assistant bot (e.g., CLA Assistant) to enforce on new PRs.
|
|
7
|
+
|
|
8
|
+
------------------------------------------------------------------------------
|
|
9
|
+
GUARDSPINE INDIVIDUAL CONTRIBUTOR LICENSE AGREEMENT (v1)
|
|
10
|
+
------------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
Thank you for contributing to GuardSpine, Inc. ("Company"). This Agreement sets
|
|
13
|
+
the terms under which You contribute. "Contribution" means any work of authorship
|
|
14
|
+
You intentionally submit to a Company project.
|
|
15
|
+
|
|
16
|
+
1. Copyright license. You grant Company and recipients of software distributed by
|
|
17
|
+
Company a perpetual, worldwide, non-exclusive, royalty-free, irrevocable
|
|
18
|
+
copyright license to reproduce, prepare derivative works of, publicly display,
|
|
19
|
+
sublicense, distribute, and RELICENSE Your Contributions and derivative works,
|
|
20
|
+
under ANY license terms Company selects, including non-open-source and
|
|
21
|
+
commercial terms.
|
|
22
|
+
|
|
23
|
+
2. Patent license. You grant Company and recipients a perpetual, worldwide,
|
|
24
|
+
non-exclusive, royalty-free, irrevocable (except as below) patent license to
|
|
25
|
+
make, use, sell, offer to sell, import, and otherwise transfer Your
|
|
26
|
+
Contributions, for patent claims You can license that are necessarily
|
|
27
|
+
infringed by Your Contribution alone or combined with the project. If any
|
|
28
|
+
entity brings patent litigation alleging the Contribution infringes, the
|
|
29
|
+
patent licenses You granted for that Contribution terminate.
|
|
30
|
+
|
|
31
|
+
3. Originality and rights. You represent each Contribution is Your original
|
|
32
|
+
creation and You are legally entitled to grant the above licenses. If Your
|
|
33
|
+
employer has rights to work You create, You represent You have permission to
|
|
34
|
+
contribute, or Your employer has waived such rights.
|
|
35
|
+
|
|
36
|
+
4. Third-party material. If You submit work that is not Your original creation,
|
|
37
|
+
You will identify it and its license and any restrictions.
|
|
38
|
+
|
|
39
|
+
5. No warranty. Contributions are provided "as is", without warranties.
|
|
40
|
+
|
|
41
|
+
You: ____________________ GitHub user: ____________ Date: ______
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: ${{ matrix.python-version }}
|
|
20
|
+
- name: Install dependencies
|
|
21
|
+
run: |
|
|
22
|
+
pip install guardspine-kernel
|
|
23
|
+
pip install -e .
|
|
24
|
+
pip install pytest pytest-asyncio pytest-cov
|
|
25
|
+
- name: Run tests
|
|
26
|
+
run: pytest tests/ -v --tb=short
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# CLA Assistant - enforces .github/CLA.md on every PR so the no-CLA gap
|
|
2
|
+
# (root cause of the aragossa/PII-Shield ambiguity) cannot recur.
|
|
3
|
+
#
|
|
4
|
+
# BEFORE THIS WORKS, David must (repo settings, one-time):
|
|
5
|
+
# 1. Create a fine-grained PAT with contents+PRs write, add as secret CLA_PAT.
|
|
6
|
+
# 2. The action stores signatures in-repo at signatures/cla.json on main.
|
|
7
|
+
# path-to-document auto-resolves to this repo via github.repository, so it keeps
|
|
8
|
+
# working after the codeguard-action -> guardspine-code-action rename.
|
|
9
|
+
name: CLA Assistant
|
|
10
|
+
on:
|
|
11
|
+
issue_comment:
|
|
12
|
+
types: [created]
|
|
13
|
+
pull_request_target:
|
|
14
|
+
types: [opened, closed, synchronize]
|
|
15
|
+
permissions:
|
|
16
|
+
actions: write
|
|
17
|
+
contents: write
|
|
18
|
+
pull-requests: write
|
|
19
|
+
statuses: write
|
|
20
|
+
jobs:
|
|
21
|
+
cla:
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
steps:
|
|
24
|
+
- name: CLA Assistant
|
|
25
|
+
if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target'
|
|
26
|
+
uses: contributor-assistant/github-action@v2.6.1
|
|
27
|
+
env:
|
|
28
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
29
|
+
PERSONAL_ACCESS_TOKEN: ${{ secrets.CLA_PAT }}
|
|
30
|
+
with:
|
|
31
|
+
path-to-signatures: 'signatures/cla.json'
|
|
32
|
+
path-to-document: '${{ github.server_url }}/${{ github.repository }}/blob/main/.github/CLA.md'
|
|
33
|
+
branch: 'main'
|
|
34
|
+
allowlist: DNYoussef,m1el,aragossa,dependabot[bot],*[bot]
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: CodeGuard
|
|
2
|
+
on:
|
|
3
|
+
pull_request:
|
|
4
|
+
types: [opened, synchronize]
|
|
5
|
+
|
|
6
|
+
permissions:
|
|
7
|
+
contents: read
|
|
8
|
+
pull-requests: write
|
|
9
|
+
issues: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
analyze:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: DNYoussef/codeguard-action@v1
|
|
17
|
+
id: codeguard
|
|
18
|
+
with:
|
|
19
|
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
20
|
+
openrouter_api_key: ${{ secrets.OPENROUTER_API_KEY }}
|
|
21
|
+
risk_threshold: L3
|
|
22
|
+
- uses: actions/upload-artifact@v4
|
|
23
|
+
if: always()
|
|
24
|
+
with:
|
|
25
|
+
name: evidence-bundle
|
|
26
|
+
path: .guardspine/bundles/
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
Business Source License 1.1
|
|
2
|
+
|
|
3
|
+
License text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved.
|
|
4
|
+
"Business Source License" is a trademark of MariaDB Corporation Ab.
|
|
5
|
+
|
|
6
|
+
-----------------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
Parameters
|
|
9
|
+
|
|
10
|
+
Licensor: GuardSpine, Inc.
|
|
11
|
+
|
|
12
|
+
Licensed Work: GuardSpine Local Council version 2.0.0.
|
|
13
|
+
The Licensed Work is (c) 2026 GuardSpine, Inc.
|
|
14
|
+
|
|
15
|
+
Additional Use Grant: You may make production use of the Licensed Work if your
|
|
16
|
+
use is non-commercial, or is for evaluation or internal
|
|
17
|
+
testing, or is by an organization with annual gross revenue
|
|
18
|
+
below USD 1,000,000. Any other production use, and any use
|
|
19
|
+
that offers the Licensed Work (or a service whose value
|
|
20
|
+
derives primarily from it) to third parties for a fee,
|
|
21
|
+
requires a commercial license from GuardSpine, Inc.
|
|
22
|
+
|
|
23
|
+
Change Date: Four years from the date each version of the Licensed Work
|
|
24
|
+
is first published.
|
|
25
|
+
|
|
26
|
+
Change License: Apache License, Version 2.0
|
|
27
|
+
|
|
28
|
+
For information about alternative licensing arrangements for the Licensed Work,
|
|
29
|
+
please contact legal@guardspine.ai
|
|
30
|
+
|
|
31
|
+
-----------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
Notice
|
|
34
|
+
|
|
35
|
+
Business Source License 1.1
|
|
36
|
+
|
|
37
|
+
Terms
|
|
38
|
+
|
|
39
|
+
The Licensor hereby grants you the right to copy, modify, create derivative
|
|
40
|
+
works, redistribute, and make non-production use of the Licensed Work. The
|
|
41
|
+
Licensor may make an Additional Use Grant, above, permitting limited production
|
|
42
|
+
use.
|
|
43
|
+
|
|
44
|
+
Effective on the Change Date, or the fourth anniversary of the first publicly
|
|
45
|
+
available distribution of a specific version of the Licensed Work under this
|
|
46
|
+
License, whichever comes first, the Licensor hereby grants you rights under the
|
|
47
|
+
terms of the Change License, and the rights granted in the paragraph above
|
|
48
|
+
terminate.
|
|
49
|
+
|
|
50
|
+
If your use of the Licensed Work does not comply with the requirements currently
|
|
51
|
+
in effect as described in this License, you must purchase a commercial license
|
|
52
|
+
from the Licensor, its affiliated entities, or authorized resellers, or you must
|
|
53
|
+
refrain from using the Licensed Work.
|
|
54
|
+
|
|
55
|
+
All copies of the original and modified Licensed Work, and derivative works of
|
|
56
|
+
the Licensed Work, are subject to this License. This License applies separately
|
|
57
|
+
for each version of the Licensed Work and the Change Date may vary for each
|
|
58
|
+
version of the Licensed Work released by Licensor.
|
|
59
|
+
|
|
60
|
+
You must conspicuously display this License on each original or modified copy of
|
|
61
|
+
the Licensed Work. If you receive the Licensed Work in original or modified form
|
|
62
|
+
from a third party, the terms and conditions set forth in this License apply to
|
|
63
|
+
your use of that work.
|
|
64
|
+
|
|
65
|
+
Any use of the Licensed Work in violation of this License will automatically
|
|
66
|
+
terminate your rights under this License for the current and all other versions
|
|
67
|
+
of the Licensed Work.
|
|
68
|
+
|
|
69
|
+
This License does not grant you any right in any trademark or logo of Licensor or
|
|
70
|
+
its affiliates (provided that you may use a trademark or logo of Licensor as
|
|
71
|
+
expressly required by this License).
|
|
72
|
+
|
|
73
|
+
TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON AN
|
|
74
|
+
"AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, EXPRESS
|
|
75
|
+
OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
76
|
+
FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND TITLE.
|
|
77
|
+
|
|
78
|
+
MariaDB hereby grants you permission to use this License's text to license your
|
|
79
|
+
works, and to refer to it using the trademark "Business Source License", as long
|
|
80
|
+
as you comply with the Covenants of Licensor below.
|
|
81
|
+
|
|
82
|
+
Covenants of Licensor
|
|
83
|
+
|
|
84
|
+
In consideration of the right to use this License's text and the "Business Source
|
|
85
|
+
License" name and trademark, Licensor covenants to MariaDB, and to all other
|
|
86
|
+
recipients of the licensed work to be provided by Licensor:
|
|
87
|
+
|
|
88
|
+
1. To specify as the Change License the GPL Version 2.0 or any later version, or
|
|
89
|
+
a license that is compatible with GPL Version 2.0 or a later version, where
|
|
90
|
+
"compatible" means that software provided under the Change License can be
|
|
91
|
+
included in a program with software provided under GPL Version 2.0 or a later
|
|
92
|
+
version. Licensor may specify additional Change Licenses without limitation.
|
|
93
|
+
|
|
94
|
+
2. To either: (a) specify an additional grant of rights to use that does not
|
|
95
|
+
impose any additional restriction on the right granted in this License, as the
|
|
96
|
+
Additional Use Grant; or (b) insert the text "None".
|
|
97
|
+
|
|
98
|
+
3. To specify a Change Date.
|
|
99
|
+
|
|
100
|
+
4. Not to modify this License in any other way.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
NOTICE
|
|
2
|
+
|
|
3
|
+
GuardSpine Local Council (c) 2026 GuardSpine, Inc.
|
|
4
|
+
Licensed under the Business Source License 1.1. See LICENSE.
|
|
5
|
+
|
|
6
|
+
This product includes PII-Shield (lib/pii-shield.wasm and integration code),
|
|
7
|
+
authored by aragossa. PII-Shield was contributed under Apache License 2.0.
|
|
8
|
+
A written confirmation that the PII-Shield code and the pii-shield.wasm binary
|
|
9
|
+
may be redistributed under the Business Source License 1.1 is being obtained;
|
|
10
|
+
until then those portions remain available under Apache License 2.0.
|
|
11
|
+
|
|
12
|
+
This product depends on third-party Python packages installed via pip and used
|
|
13
|
+
unmodified, each under its own license, including: PyGithub (LGPL-3.0),
|
|
14
|
+
requests (Apache-2.0), PyYAML (MIT), unidiff (MIT), cryptography (Apache-2.0 /
|
|
15
|
+
BSD-3-Clause), openai (Apache-2.0), anthropic (MIT), wasmtime (Apache-2.0),
|
|
16
|
+
toml (MIT).
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: guardspine-local-council
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: Local AI council review using Ollama -- no cloud API keys required
|
|
5
|
+
Project-URL: Homepage, https://github.com/DNYoussef/guardspine-local-council
|
|
6
|
+
License-Expression: BUSL-1.1
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
License-File: NOTICE
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Requires-Dist: guardspine-kernel>=0.2.0
|
|
11
|
+
Requires-Dist: httpx>=0.24.0
|
|
12
|
+
Requires-Dist: wasmtime>=16.0.0
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# guardspine-local-council
|
|
16
|
+
|
|
17
|
+
**Multi-model AI code review council -- local-first, cloud-optional.**
|
|
18
|
+
|
|
19
|
+
Run multi-model code review councils on your machine using [Ollama](https://ollama.com), or connect to cloud providers (OpenAI, Anthropic, OpenRouter). Reviews produce cryptographically chained evidence bundles compatible with the GuardSpine ecosystem.
|
|
20
|
+
|
|
21
|
+
## Requirements
|
|
22
|
+
|
|
23
|
+
- Python 3.10+
|
|
24
|
+
- [Ollama](https://ollama.com) running locally (`ollama serve`) for local-only use
|
|
25
|
+
- At least one model pulled (e.g. `ollama pull llama3.1`)
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install guardspine-local-council
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Or from source:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
git clone https://github.com/DNYoussef/guardspine-local-council.git
|
|
37
|
+
cd guardspine-local-council
|
|
38
|
+
pip install -e .
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Dependencies
|
|
42
|
+
|
|
43
|
+
- **guardspine-kernel** (>=0.2.0) -- Canonical JSON hashing (RFC 8785) and content hash computation. Ensures cross-language parity with the TypeScript `@guardspine/kernel`.
|
|
44
|
+
- **httpx** (>=0.24.0) -- Async HTTP client for Ollama and cloud API calls.
|
|
45
|
+
- **wasmtime** (>=16.0.0) -- WASM runtime for the built-in PII-Shield sanitizer.
|
|
46
|
+
|
|
47
|
+
## Quick Start
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
import asyncio
|
|
51
|
+
from guardspine_local_council import LocalCouncil, OllamaProvider, ReviewRequest
|
|
52
|
+
|
|
53
|
+
async def main():
|
|
54
|
+
providers = [
|
|
55
|
+
OllamaProvider(model="llama3.1", reviewer_id="reviewer-a"),
|
|
56
|
+
OllamaProvider(model="llama3.1", reviewer_id="reviewer-b"),
|
|
57
|
+
OllamaProvider(model="llama3.1", reviewer_id="reviewer-c"),
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
council = LocalCouncil(providers, quorum=2, consensus_threshold=0.66)
|
|
61
|
+
|
|
62
|
+
request = ReviewRequest(
|
|
63
|
+
artifact_id="my-function",
|
|
64
|
+
artifact_type="python-function",
|
|
65
|
+
content="def add(a, b): return a + b",
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
result = await council.review(request)
|
|
69
|
+
print(f"Decision: {result.consensus_decision} ({result.consensus_confidence})")
|
|
70
|
+
|
|
71
|
+
asyncio.run(main())
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## How It Works
|
|
75
|
+
|
|
76
|
+
1. You create provider instances (Ollama, OpenAI, Anthropic, or OpenRouter).
|
|
77
|
+
2. `LocalCouncil` sanitizes the review prompt through the built-in PII-Shield WASM module.
|
|
78
|
+
3. The sanitized prompt is sent to all providers in parallel.
|
|
79
|
+
4. Each provider returns a structured vote (approve/reject/abstain + confidence + findings).
|
|
80
|
+
5. `SimpleAggregator` computes a confidence-weighted majority decision.
|
|
81
|
+
6. Quorum and consensus threshold checks determine the final result.
|
|
82
|
+
7. An evidence bundle is produced with a SHA-256 hash chain for tamper detection.
|
|
83
|
+
|
|
84
|
+
## Providers
|
|
85
|
+
|
|
86
|
+
Four providers are included. All implement the `ReviewProvider` protocol and return `ReviewVote` objects.
|
|
87
|
+
|
|
88
|
+
### OllamaProvider (local, no API key)
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from guardspine_local_council import OllamaProvider
|
|
92
|
+
|
|
93
|
+
provider = OllamaProvider(
|
|
94
|
+
model="llama3.1", # Any Ollama model
|
|
95
|
+
base_url="http://localhost:11434", # Ollama API endpoint
|
|
96
|
+
reviewer_id="local-1",
|
|
97
|
+
)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Uses Ollama's `/api/generate` endpoint with JSON format mode. Falls back to abstain on parse failure.
|
|
101
|
+
|
|
102
|
+
### OpenAIProvider
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
from guardspine_local_council import OpenAIProvider
|
|
106
|
+
|
|
107
|
+
provider = OpenAIProvider(
|
|
108
|
+
model="gpt-4o",
|
|
109
|
+
api_key="sk-...", # or set OPENAI_API_KEY env var
|
|
110
|
+
)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### AnthropicProvider
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
from guardspine_local_council import AnthropicProvider
|
|
117
|
+
|
|
118
|
+
provider = AnthropicProvider(
|
|
119
|
+
model="claude-sonnet-4-5-20250929",
|
|
120
|
+
api_key="sk-ant-...", # or set ANTHROPIC_API_KEY env var
|
|
121
|
+
)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### OpenRouterProvider
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
from guardspine_local_council import OpenRouterProvider
|
|
128
|
+
|
|
129
|
+
provider = OpenRouterProvider(
|
|
130
|
+
model="openrouter/auto", # or any model on OpenRouter
|
|
131
|
+
api_key="sk-or-...", # or set OPENROUTER_API_KEY env var
|
|
132
|
+
)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
You can mix providers freely. A council of 1 Ollama + 1 OpenAI + 1 Anthropic model works.
|
|
136
|
+
|
|
137
|
+
## Council Configuration
|
|
138
|
+
|
|
139
|
+
| Parameter | Default | Description |
|
|
140
|
+
|-----------|---------|-------------|
|
|
141
|
+
| `providers` | (required) | List of `ReviewProvider` instances |
|
|
142
|
+
| `hooks` | `[]` | Optional list of `ReviewHook` instances for pre/post processing |
|
|
143
|
+
| `sanitizer` | `None` | Optional external sanitizer (in addition to built-in WASM) |
|
|
144
|
+
| `quorum` | `3` | Minimum non-abstain votes required |
|
|
145
|
+
| `consensus_threshold` | `0.66` | Minimum weighted confidence for a decision |
|
|
146
|
+
| `sanitization_salt_fingerprint` | `sha256:00000000` | Non-secret salt fingerprint for sanitization attestations |
|
|
147
|
+
|
|
148
|
+
## Review Modes
|
|
149
|
+
|
|
150
|
+
### Single Review
|
|
151
|
+
|
|
152
|
+
`council.review(request)` sends one prompt to all providers in parallel and returns a `CouncilResult` with the aggregated decision.
|
|
153
|
+
|
|
154
|
+
### Rubric Review
|
|
155
|
+
|
|
156
|
+
`council.rubric_review(request, rubric)` reviews code against a specific rubric. Providers run sequentially (VRAM constraint for local models). Returns a list of `ReviewVote` objects.
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
from guardspine_local_council import RubricContext
|
|
160
|
+
|
|
161
|
+
rubric = RubricContext(
|
|
162
|
+
rubric_name="input-validation",
|
|
163
|
+
description="All user input must be validated before use",
|
|
164
|
+
violations=[ # from a deterministic scanner
|
|
165
|
+
{"severity": "high", "rule_id": "IV-001", "file": "api.py", "line_number": 42,
|
|
166
|
+
"description": "Unvalidated query parameter"}
|
|
167
|
+
],
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
votes = await council.rubric_review(request, rubric)
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Full Audit
|
|
174
|
+
|
|
175
|
+
`council.full_audit(request, rubrics)` runs all providers against all rubrics and aggregates into an `AuditResult`. Each rubric gets a pass/fail/needs-review verdict via 2-of-3 majority. The overall decision rejects if any rubric with critical findings fails.
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
result = await council.full_audit(request, rubrics)
|
|
179
|
+
print(result.overall_decision) # "approve" | "reject" | "needs-review"
|
|
180
|
+
print(result.summary)
|
|
181
|
+
|
|
182
|
+
# Pivot findings from rubric-oriented to file-oriented
|
|
183
|
+
for filename, report in result.by_file().items():
|
|
184
|
+
print(f"{filename}: {report.critical_count} critical findings")
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Hooks
|
|
188
|
+
|
|
189
|
+
Hooks run deterministically around review calls. The models never call MCP tools themselves -- hooks enrich prompts before the model sees them and validate output after.
|
|
190
|
+
|
|
191
|
+
### SequentialThinkingHook
|
|
192
|
+
|
|
193
|
+
Connects to `@modelcontextprotocol/server-sequential-thinking` via stdio. Decomposes each rubric into 5 structured reasoning steps and prepends a chain-of-thought scaffold to the prompt.
|
|
194
|
+
|
|
195
|
+
```python
|
|
196
|
+
from guardspine_local_council import SequentialThinkingHook
|
|
197
|
+
|
|
198
|
+
hook = SequentialThinkingHook(num_steps=5)
|
|
199
|
+
|
|
200
|
+
council = LocalCouncil(providers, hooks=[hook])
|
|
201
|
+
await council.start_hooks()
|
|
202
|
+
result = await council.full_audit(request, rubrics)
|
|
203
|
+
await council.close_hooks()
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### MCPClientHook
|
|
207
|
+
|
|
208
|
+
Generic hook that calls any MCP server's tool to enrich prompts. Useful for injecting library docs, past findings, or external context.
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
from guardspine_local_council import MCPClientHook
|
|
212
|
+
|
|
213
|
+
hook = MCPClientHook(
|
|
214
|
+
name="memory",
|
|
215
|
+
server_command=["python", "-m", "memory_mcp"],
|
|
216
|
+
tool_name="recall",
|
|
217
|
+
)
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## PII-Shield Integration
|
|
221
|
+
|
|
222
|
+
All review prompts pass through a built-in PII-Shield WASM module (`lib/pii-shield.wasm`) before reaching any model. This strips API keys, credentials, and PII from code submitted for review.
|
|
223
|
+
|
|
224
|
+
The WASM module runs via `wasmtime` using temporary files for stdin/stdout. The Engine and Module are cached as a singleton; only the Store is recreated per call.
|
|
225
|
+
|
|
226
|
+
**Fail-closed by default**: if the WASM module fails, the review raises `RuntimeError` rather than sending unsanitized content. Set `GUARDSPINE_PII_FAIL_OPEN=1` to override (not recommended for production).
|
|
227
|
+
|
|
228
|
+
You can also provide an additional external sanitizer via the `sanitizer` parameter on `LocalCouncil`. Both stages are tracked in the evidence bundle's `sanitization` attestation block.
|
|
229
|
+
|
|
230
|
+
## Evidence Bundle Output
|
|
231
|
+
|
|
232
|
+
Council reviews produce v0.2.x evidence bundles containing:
|
|
233
|
+
|
|
234
|
+
- Individual reviewer votes with confidence scores
|
|
235
|
+
- Consensus decision and rationale
|
|
236
|
+
- SHA-256 hash chain with immutability proof (using `guardspine-kernel` for canonical JSON hashing)
|
|
237
|
+
- Optional `sanitization` attestation metadata (v0.2.1 format when sanitization occurred)
|
|
238
|
+
|
|
239
|
+
```python
|
|
240
|
+
result = await council.review(request)
|
|
241
|
+
bundle = result.evidence_bundle
|
|
242
|
+
|
|
243
|
+
print(f"Bundle ID: {bundle.bundle_id}")
|
|
244
|
+
print(f"Version: {bundle.version}") # "0.2.0" or "0.2.1"
|
|
245
|
+
print(f"Root hash: {bundle.immutability_proof.root_hash}")
|
|
246
|
+
|
|
247
|
+
# Verify with guardspine-verify
|
|
248
|
+
import json
|
|
249
|
+
with open("council-evidence.json", "w") as f:
|
|
250
|
+
json.dump(bundle.__dict__, f, default=str)
|
|
251
|
+
# $ guardspine-verify council-evidence.json
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Bundles are unsigned by default. For signed bundles, use GuardSpine Enterprise or provide a signing key via configuration.
|
|
255
|
+
|
|
256
|
+
## Ollama Setup
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
# Check Ollama is running
|
|
260
|
+
curl http://localhost:11434/api/tags
|
|
261
|
+
|
|
262
|
+
# Pull models
|
|
263
|
+
ollama pull llama3.1
|
|
264
|
+
ollama pull codellama
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
The council returns abstain votes if a model is unavailable.
|
|
268
|
+
|
|
269
|
+
## Data Types
|
|
270
|
+
|
|
271
|
+
| Type | Purpose |
|
|
272
|
+
|------|---------|
|
|
273
|
+
| `ReviewRequest` | Input: artifact ID, type, content, optional context and risk tier hint |
|
|
274
|
+
| `ReviewVote` | One reviewer's decision (approve/reject/abstain), confidence, rationale, findings |
|
|
275
|
+
| `CouncilResult` | Aggregated result with votes, consensus, dissent, quorum status, evidence bundle |
|
|
276
|
+
| `RubricContext` | Scanner-produced rubric with name, description, and violations |
|
|
277
|
+
| `RubricVerdict` | Per-rubric result: pass/fail/needs-review with critical findings |
|
|
278
|
+
| `AuditResult` | Full audit result across all rubrics with overall decision |
|
|
279
|
+
| `FileFinding` | Single finding attributed to a specific file (for `by_file()` pivot) |
|
|
280
|
+
| `FileReport` | All findings for one file, with `critical_count` property |
|
|
281
|
+
| `EvidenceBundle` | v0.2.x bundle with items, hash chain, and optional sanitization |
|
|
282
|
+
|
|
283
|
+
## Related Projects
|
|
284
|
+
|
|
285
|
+
| Project | Description |
|
|
286
|
+
|---------|-------------|
|
|
287
|
+
| [guardspine-kernel-py](https://github.com/DNYoussef/guardspine-kernel-py) | Python kernel: canonical hashing, content hash (required dependency) |
|
|
288
|
+
| [@guardspine/kernel](https://github.com/DNYoussef/guardspine-kernel) | TypeScript kernel (cross-language parity) |
|
|
289
|
+
| [guardspine-verify](https://github.com/DNYoussef/guardspine-verify) | Verify council evidence bundles offline |
|
|
290
|
+
| [guardspine-spec](https://github.com/DNYoussef/guardspine-spec) | Bundle specification (v0.2.1) |
|
|
291
|
+
| [codeguard-action](https://github.com/DNYoussef/codeguard-action) | GitHub Action for automated code review |
|
|
292
|
+
|
|
293
|
+
## License
|
|
294
|
+
|
|
295
|
+
Business Source License 1.1 (source-available) -- see [LICENSE](LICENSE). Free for non-commercial, evaluation, and small-organization use (annual revenue under USD 1,000,000); other production use requires a commercial license from GuardSpine, Inc. Each version converts to Apache-2.0 four years after its release.
|