clinicsentry 0.3.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.
- clinicsentry-0.3.0/.dockerignore +25 -0
- clinicsentry-0.3.0/.github/ISSUE_TEMPLATE/bug_report.md +29 -0
- clinicsentry-0.3.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
- clinicsentry-0.3.0/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- clinicsentry-0.3.0/.github/pull_request_template.md +29 -0
- clinicsentry-0.3.0/.github/workflows/ci.yml +248 -0
- clinicsentry-0.3.0/.github/workflows/docs.yml +27 -0
- clinicsentry-0.3.0/.github/workflows/release.yml +109 -0
- clinicsentry-0.3.0/.gitignore +43 -0
- clinicsentry-0.3.0/.pre-commit-config.yaml +36 -0
- clinicsentry-0.3.0/CHANGELOG.md +136 -0
- clinicsentry-0.3.0/CODE_OF_CONDUCT.md +40 -0
- clinicsentry-0.3.0/CONTRIBUTING.md +72 -0
- clinicsentry-0.3.0/CONVENTIONS.md +37 -0
- clinicsentry-0.3.0/Dockerfile +65 -0
- clinicsentry-0.3.0/LICENSE +201 -0
- clinicsentry-0.3.0/NOTICE +7 -0
- clinicsentry-0.3.0/PKG-INFO +303 -0
- clinicsentry-0.3.0/README.md +185 -0
- clinicsentry-0.3.0/RESPONSIBLE_USE.md +40 -0
- clinicsentry-0.3.0/SECURITY.md +48 -0
- clinicsentry-0.3.0/STATUS.md +16 -0
- clinicsentry-0.3.0/THREAT_MODEL.md +43 -0
- clinicsentry-0.3.0/deploy/compose/docker-compose.yml +111 -0
- clinicsentry-0.3.0/deploy/compose/otel-collector.yaml +37 -0
- clinicsentry-0.3.0/deploy/compose/prometheus.yml +11 -0
- clinicsentry-0.3.0/deploy/grafana/dashboards/clinicsentry.json +57 -0
- clinicsentry-0.3.0/deploy/grafana/provisioning/dashboards/clinicsentry.yaml +12 -0
- clinicsentry-0.3.0/deploy/grafana/provisioning/datasources/prometheus.yaml +9 -0
- clinicsentry-0.3.0/deploy/helm/clinicsentry/Chart.yaml +16 -0
- clinicsentry-0.3.0/deploy/helm/clinicsentry/templates/_helpers.tpl +30 -0
- clinicsentry-0.3.0/deploy/helm/clinicsentry/templates/deployment.yaml +58 -0
- clinicsentry-0.3.0/deploy/helm/clinicsentry/templates/hpa.yaml +20 -0
- clinicsentry-0.3.0/deploy/helm/clinicsentry/templates/networkpolicy.yaml +23 -0
- clinicsentry-0.3.0/deploy/helm/clinicsentry/templates/service.yaml +13 -0
- clinicsentry-0.3.0/deploy/helm/clinicsentry/values.yaml +61 -0
- clinicsentry-0.3.0/deploy/terraform/aws/main.tf +257 -0
- clinicsentry-0.3.0/deploy/terraform/azure/main.tf +115 -0
- clinicsentry-0.3.0/deploy/terraform/gcp/main.tf +121 -0
- clinicsentry-0.3.0/docs/adapters.md +28 -0
- clinicsentry-0.3.0/docs/adr/0000-template.md +30 -0
- clinicsentry-0.3.0/docs/adr/0001-module-boundaries.md +41 -0
- clinicsentry-0.3.0/docs/adr/0002-adapter-abc-contract.md +35 -0
- clinicsentry-0.3.0/docs/adr/0003-audit-chain-semantics.md +33 -0
- clinicsentry-0.3.0/docs/adr/0004-propagation-graph-schema.md +33 -0
- clinicsentry-0.3.0/docs/adr/0005-key-management.md +36 -0
- clinicsentry-0.3.0/docs/adr/0006-iec62304-class-enforcement.md +43 -0
- clinicsentry-0.3.0/docs/adr/0007-compliance-rule-language.md +40 -0
- clinicsentry-0.3.0/docs/adr/0008-exception-taxonomy.md +48 -0
- clinicsentry-0.3.0/docs/adr/0009-async-sync-boundary.md +29 -0
- clinicsentry-0.3.0/docs/adr/0010-test-conventions.md +49 -0
- clinicsentry-0.3.0/docs/adr/0011-di-patterns.md +29 -0
- clinicsentry-0.3.0/docs/adr/0012-configuration-loading.md +27 -0
- clinicsentry-0.3.0/docs/adr/0013-observability-hooks.md +28 -0
- clinicsentry-0.3.0/docs/adr/0014-threat-model-scope.md +41 -0
- clinicsentry-0.3.0/docs/adr/0015-redaction-mode-selection.md +28 -0
- clinicsentry-0.3.0/docs/adr/0016-adversarial-normalization-scan-path.md +80 -0
- clinicsentry-0.3.0/docs/adr/README.md +31 -0
- clinicsentry-0.3.0/docs/api.md +71 -0
- clinicsentry-0.3.0/docs/changelog.md +1 -0
- clinicsentry-0.3.0/docs/concepts/audit-trail.md +39 -0
- clinicsentry-0.3.0/docs/concepts/escalation-router.md +35 -0
- clinicsentry-0.3.0/docs/concepts/meddevice-mode.md +31 -0
- clinicsentry-0.3.0/docs/concepts/phi-firewall.md +32 -0
- clinicsentry-0.3.0/docs/index.md +23 -0
- clinicsentry-0.3.0/docs/policy.md +64 -0
- clinicsentry-0.3.0/docs/quickstart.md +62 -0
- clinicsentry-0.3.0/docs/regulatory-mapping.md +48 -0
- clinicsentry-0.3.0/docs/responsible-use.md +1 -0
- clinicsentry-0.3.0/docs/threat-model.md +1 -0
- clinicsentry-0.3.0/docs/tutorials/a2a.md +5 -0
- clinicsentry-0.3.0/docs/tutorials/adk.md +5 -0
- clinicsentry-0.3.0/docs/tutorials/claude.md +5 -0
- clinicsentry-0.3.0/docs/tutorials/crewai.md +5 -0
- clinicsentry-0.3.0/docs/tutorials/langgraph.md +5 -0
- clinicsentry-0.3.0/docs/tutorials/mcp.md +5 -0
- clinicsentry-0.3.0/docs/tutorials/openai-agents.md +5 -0
- clinicsentry-0.3.0/docs/tutorials/production.md +5 -0
- clinicsentry-0.3.0/examples/clinical_summarizer.py +58 -0
- clinicsentry-0.3.0/examples/policy.yaml +34 -0
- clinicsentry-0.3.0/mkdocs.yml +80 -0
- clinicsentry-0.3.0/pyproject.toml +248 -0
- clinicsentry-0.3.0/src/clinicsentry/__init__.py +32 -0
- clinicsentry-0.3.0/src/clinicsentry/adapters/__init__.py +27 -0
- clinicsentry-0.3.0/src/clinicsentry/adapters/a2a.py +60 -0
- clinicsentry-0.3.0/src/clinicsentry/adapters/base.py +158 -0
- clinicsentry-0.3.0/src/clinicsentry/adapters/claude_sdk.py +71 -0
- clinicsentry-0.3.0/src/clinicsentry/adapters/crewai.py +89 -0
- clinicsentry-0.3.0/src/clinicsentry/adapters/google_adk.py +100 -0
- clinicsentry-0.3.0/src/clinicsentry/adapters/langgraph.py +66 -0
- clinicsentry-0.3.0/src/clinicsentry/adapters/mcp_proxy.py +92 -0
- clinicsentry-0.3.0/src/clinicsentry/adapters/openai_agents.py +92 -0
- clinicsentry-0.3.0/src/clinicsentry/audit/__init__.py +24 -0
- clinicsentry-0.3.0/src/clinicsentry/audit/backend.py +220 -0
- clinicsentry-0.3.0/src/clinicsentry/audit/chain.py +137 -0
- clinicsentry-0.3.0/src/clinicsentry/audit/migrations/__init__.py +16 -0
- clinicsentry-0.3.0/src/clinicsentry/audit/migrations/alembic.ini +38 -0
- clinicsentry-0.3.0/src/clinicsentry/audit/migrations/env.py +54 -0
- clinicsentry-0.3.0/src/clinicsentry/audit/migrations/runner.py +43 -0
- clinicsentry-0.3.0/src/clinicsentry/audit/migrations/script.py.mako +24 -0
- clinicsentry-0.3.0/src/clinicsentry/audit/migrations/versions/0001_initial_audit_events.py +66 -0
- clinicsentry-0.3.0/src/clinicsentry/audit/otel.py +74 -0
- clinicsentry-0.3.0/src/clinicsentry/audit/pdf_report.py +127 -0
- clinicsentry-0.3.0/src/clinicsentry/audit/postgres.py +188 -0
- clinicsentry-0.3.0/src/clinicsentry/audit/report.py +191 -0
- clinicsentry-0.3.0/src/clinicsentry/audit/s3.py +169 -0
- clinicsentry-0.3.0/src/clinicsentry/cli.py +303 -0
- clinicsentry-0.3.0/src/clinicsentry/compliance/__init__.py +34 -0
- clinicsentry-0.3.0/src/clinicsentry/compliance/engine.py +442 -0
- clinicsentry-0.3.0/src/clinicsentry/compliance/rules/eu_ai_act.yaml +21 -0
- clinicsentry-0.3.0/src/clinicsentry/compliance/rules/fda_tplc.yaml +21 -0
- clinicsentry-0.3.0/src/clinicsentry/compliance/rules/hipaa.yaml +33 -0
- clinicsentry-0.3.0/src/clinicsentry/compliance/rules/iec62304.yaml +21 -0
- clinicsentry-0.3.0/src/clinicsentry/dashboard/__init__.py +21 -0
- clinicsentry-0.3.0/src/clinicsentry/dashboard/app.py +242 -0
- clinicsentry-0.3.0/src/clinicsentry/errors.py +176 -0
- clinicsentry-0.3.0/src/clinicsentry/escalation/__init__.py +36 -0
- clinicsentry-0.3.0/src/clinicsentry/escalation/channels.py +228 -0
- clinicsentry-0.3.0/src/clinicsentry/escalation/confidence.py +178 -0
- clinicsentry-0.3.0/src/clinicsentry/escalation/extra_signals.py +216 -0
- clinicsentry-0.3.0/src/clinicsentry/escalation/router.py +222 -0
- clinicsentry-0.3.0/src/clinicsentry/escalation/temperature_scaling.py +209 -0
- clinicsentry-0.3.0/src/clinicsentry/guard.py +279 -0
- clinicsentry-0.3.0/src/clinicsentry/meddevice/__init__.py +51 -0
- clinicsentry-0.3.0/src/clinicsentry/meddevice/cia.py +125 -0
- clinicsentry-0.3.0/src/clinicsentry/meddevice/clinician_auth.py +115 -0
- clinicsentry-0.3.0/src/clinicsentry/meddevice/cloud_kms.py +216 -0
- clinicsentry-0.3.0/src/clinicsentry/meddevice/http_kms.py +144 -0
- clinicsentry-0.3.0/src/clinicsentry/meddevice/iec62304_v2.py +64 -0
- clinicsentry-0.3.0/src/clinicsentry/meddevice/keys.py +152 -0
- clinicsentry-0.3.0/src/clinicsentry/meddevice/mode.py +208 -0
- clinicsentry-0.3.0/src/clinicsentry/observability/__init__.py +16 -0
- clinicsentry-0.3.0/src/clinicsentry/observability/logging.py +68 -0
- clinicsentry-0.3.0/src/clinicsentry/observability/metrics.py +82 -0
- clinicsentry-0.3.0/src/clinicsentry/observability/tracing.py +30 -0
- clinicsentry-0.3.0/src/clinicsentry/performance.py +130 -0
- clinicsentry-0.3.0/src/clinicsentry/phi/__init__.py +34 -0
- clinicsentry-0.3.0/src/clinicsentry/phi/adversarial.py +220 -0
- clinicsentry-0.3.0/src/clinicsentry/phi/detectors.py +198 -0
- clinicsentry-0.3.0/src/clinicsentry/phi/firewall.py +296 -0
- clinicsentry-0.3.0/src/clinicsentry/phi/medical_ner.py +110 -0
- clinicsentry-0.3.0/src/clinicsentry/phi/minimum_necessary.py +100 -0
- clinicsentry-0.3.0/src/clinicsentry/phi/multilingual.py +100 -0
- clinicsentry-0.3.0/src/clinicsentry/phi/ocr.py +58 -0
- clinicsentry-0.3.0/src/clinicsentry/phi/parsers.py +149 -0
- clinicsentry-0.3.0/src/clinicsentry/phi/pipeline.py +93 -0
- clinicsentry-0.3.0/src/clinicsentry/phi/propagation.py +51 -0
- clinicsentry-0.3.0/src/clinicsentry/phi/redaction.py +105 -0
- clinicsentry-0.3.0/src/clinicsentry/policy.py +366 -0
- clinicsentry-0.3.0/src/clinicsentry/py.typed +0 -0
- clinicsentry-0.3.0/src/clinicsentry/types.py +203 -0
- clinicsentry-0.3.0/tests/__init__.py +0 -0
- clinicsentry-0.3.0/tests/_docker_guard.py +43 -0
- clinicsentry-0.3.0/tests/adapters/__init__.py +8 -0
- clinicsentry-0.3.0/tests/adapters/test_claude_sdk_integration.py +26 -0
- clinicsentry-0.3.0/tests/adapters/test_crewai_integration.py +26 -0
- clinicsentry-0.3.0/tests/adapters/test_google_adk_integration.py +26 -0
- clinicsentry-0.3.0/tests/adapters/test_langgraph_integration.py +48 -0
- clinicsentry-0.3.0/tests/adapters/test_mcp_proxy_integration.py +26 -0
- clinicsentry-0.3.0/tests/adapters/test_openai_agents_integration.py +26 -0
- clinicsentry-0.3.0/tests/fixtures/synthetic_phi/__init__.py +4 -0
- clinicsentry-0.3.0/tests/fixtures/synthetic_phi/corpus.json +2352 -0
- clinicsentry-0.3.0/tests/fixtures/synthetic_phi/generator.py +235 -0
- clinicsentry-0.3.0/tests/test_adapters.py +234 -0
- clinicsentry-0.3.0/tests/test_adversarial_robustness.py +300 -0
- clinicsentry-0.3.0/tests/test_alembic_migrations.py +107 -0
- clinicsentry-0.3.0/tests/test_audit.py +171 -0
- clinicsentry-0.3.0/tests/test_audit_backends.py +162 -0
- clinicsentry-0.3.0/tests/test_audit_chain_mutation.py +161 -0
- clinicsentry-0.3.0/tests/test_cli_summary.py +77 -0
- clinicsentry-0.3.0/tests/test_cloud_kms_aws.py +111 -0
- clinicsentry-0.3.0/tests/test_cloud_kms_mocked.py +199 -0
- clinicsentry-0.3.0/tests/test_compliance.py +281 -0
- clinicsentry-0.3.0/tests/test_confidence_mutation.py +317 -0
- clinicsentry-0.3.0/tests/test_escalation.py +120 -0
- clinicsentry-0.3.0/tests/test_escalation_extra.py +135 -0
- clinicsentry-0.3.0/tests/test_guard_integration.py +122 -0
- clinicsentry-0.3.0/tests/test_http_kms.py +140 -0
- clinicsentry-0.3.0/tests/test_layering.py +93 -0
- clinicsentry-0.3.0/tests/test_meddevice.py +94 -0
- clinicsentry-0.3.0/tests/test_meddevice_extra.py +102 -0
- clinicsentry-0.3.0/tests/test_meddevice_mutation.py +369 -0
- clinicsentry-0.3.0/tests/test_pdf_report.py +51 -0
- clinicsentry-0.3.0/tests/test_performance.py +63 -0
- clinicsentry-0.3.0/tests/test_phi_firewall.py +155 -0
- clinicsentry-0.3.0/tests/test_phi_heavy_deps.py +164 -0
- clinicsentry-0.3.0/tests/test_phi_pipeline.py +63 -0
- clinicsentry-0.3.0/tests/test_policy_validation.py +183 -0
- clinicsentry-0.3.0/tests/test_postgres_integration.py +194 -0
- clinicsentry-0.3.0/tests/test_propagation.py +95 -0
- clinicsentry-0.3.0/tests/test_softhsm_integration.py +272 -0
- clinicsentry-0.3.0/tests/test_synthetic_phi.py +139 -0
- clinicsentry-0.3.0/tests/test_temperature_scaling.py +124 -0
- clinicsentry-0.3.0/tests/test_untrusted_input.py +114 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
.git
|
|
2
|
+
.github
|
|
3
|
+
.venv
|
|
4
|
+
__pycache__
|
|
5
|
+
*.pyc
|
|
6
|
+
*.pyo
|
|
7
|
+
*.pyd
|
|
8
|
+
.pytest_cache
|
|
9
|
+
.mypy_cache
|
|
10
|
+
.ruff_cache
|
|
11
|
+
htmlcov
|
|
12
|
+
coverage.xml
|
|
13
|
+
.coverage
|
|
14
|
+
.coverage.*
|
|
15
|
+
.DS_Store
|
|
16
|
+
_tmp
|
|
17
|
+
node_modules
|
|
18
|
+
*.log
|
|
19
|
+
*.sqlite
|
|
20
|
+
*.db
|
|
21
|
+
.idea
|
|
22
|
+
.vscode
|
|
23
|
+
deploy/grafana/dashboards-generated
|
|
24
|
+
docs/_build
|
|
25
|
+
site
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug report
|
|
3
|
+
about: Something behaves incorrectly (for PHI-detection bypasses, see SECURITY.md instead)
|
|
4
|
+
labels: bug
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
**⚠️ If this is a PHI-detection bypass or any security issue, do NOT file it
|
|
8
|
+
here — follow [SECURITY.md](../../SECURITY.md) for private reporting.**
|
|
9
|
+
|
|
10
|
+
## What happened
|
|
11
|
+
|
|
12
|
+
A clear description of the bug.
|
|
13
|
+
|
|
14
|
+
## Minimal reproduction
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
# Smallest snippet that reproduces the issue.
|
|
18
|
+
# NEVER paste real PHI — use synthetic identifiers (e.g. SSN 123-45-6789).
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Expected behavior
|
|
22
|
+
|
|
23
|
+
## Environment
|
|
24
|
+
|
|
25
|
+
- clinicsentry version:
|
|
26
|
+
- Python version:
|
|
27
|
+
- OS:
|
|
28
|
+
- Installed extras (e.g. `[phi]`, `[postgres]`):
|
|
29
|
+
- Agent framework + version (if adapter-related):
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Feature request
|
|
3
|
+
about: Propose a new capability or improvement
|
|
4
|
+
labels: enhancement
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Problem
|
|
8
|
+
|
|
9
|
+
What clinical-AI compliance problem does this solve? Reference the relevant
|
|
10
|
+
regulation clause (HIPAA / FDA TPLC / IEC 62304 / EU AI Act) if applicable.
|
|
11
|
+
|
|
12
|
+
## Proposed solution
|
|
13
|
+
|
|
14
|
+
## Alternatives considered
|
|
15
|
+
|
|
16
|
+
## Constraints
|
|
17
|
+
|
|
18
|
+
ClinicSentry is latency-budgeted middleware (≤50 ms p95 total overhead, no
|
|
19
|
+
additional LLM calls on the hot path). Note any expected latency or
|
|
20
|
+
dependency impact.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
## Summary
|
|
2
|
+
|
|
3
|
+
What and why (one paragraph).
|
|
4
|
+
|
|
5
|
+
## ADR reference
|
|
6
|
+
|
|
7
|
+
ADR-NNNN (link to `docs/adr/`).
|
|
8
|
+
|
|
9
|
+
## Changes
|
|
10
|
+
|
|
11
|
+
- Bullet list of concrete changes.
|
|
12
|
+
|
|
13
|
+
## Testing
|
|
14
|
+
|
|
15
|
+
- `pytest -q tests/test_<module>.py`
|
|
16
|
+
- Manual verification steps (if applicable).
|
|
17
|
+
|
|
18
|
+
## Risk / Compliance
|
|
19
|
+
|
|
20
|
+
Any HIPAA / FDA / IEC 62304 implications? Reference the rule.
|
|
21
|
+
|
|
22
|
+
## Checklist
|
|
23
|
+
|
|
24
|
+
- [ ] Tests added / updated.
|
|
25
|
+
- [ ] Coverage ≥ 85%.
|
|
26
|
+
- [ ] `ruff check`, `ruff format --check`, `mypy --strict` clean.
|
|
27
|
+
- [ ] `CHANGELOG.md` updated under `## [Unreleased]`.
|
|
28
|
+
- [ ] `CHANGELOG.md` updated if this advances a feature.
|
|
29
|
+
- [ ] Any breaking change documented in PR title with `[breaking]`.
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
concurrency:
|
|
10
|
+
group: ci-${{ github.ref }}
|
|
11
|
+
cancel-in-progress: true
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
lint:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: "3.11"
|
|
21
|
+
cache: "pip"
|
|
22
|
+
- run: pip install -e '.[dev]'
|
|
23
|
+
- run: ruff format --check .
|
|
24
|
+
- run: ruff check .
|
|
25
|
+
|
|
26
|
+
typecheck:
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/checkout@v4
|
|
30
|
+
- uses: actions/setup-python@v5
|
|
31
|
+
with:
|
|
32
|
+
python-version: "3.11"
|
|
33
|
+
cache: "pip"
|
|
34
|
+
- run: pip install -e '.[dev]'
|
|
35
|
+
- run: mypy
|
|
36
|
+
|
|
37
|
+
test:
|
|
38
|
+
runs-on: ubuntu-latest
|
|
39
|
+
strategy:
|
|
40
|
+
matrix:
|
|
41
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
42
|
+
steps:
|
|
43
|
+
- uses: actions/checkout@v4
|
|
44
|
+
- uses: actions/setup-python@v5
|
|
45
|
+
with:
|
|
46
|
+
python-version: ${{ matrix.python-version }}
|
|
47
|
+
cache: "pip"
|
|
48
|
+
- run: pip install -e '.[dev,all]'
|
|
49
|
+
- run: pytest --cov=clinicsentry --cov-report=xml --cov-report=term -q
|
|
50
|
+
- name: Upload coverage
|
|
51
|
+
if: matrix.python-version == '3.11'
|
|
52
|
+
uses: actions/upload-artifact@v4
|
|
53
|
+
with:
|
|
54
|
+
name: coverage
|
|
55
|
+
path: coverage.xml
|
|
56
|
+
|
|
57
|
+
import-safety:
|
|
58
|
+
runs-on: ubuntu-latest
|
|
59
|
+
steps:
|
|
60
|
+
- uses: actions/checkout@v4
|
|
61
|
+
- uses: actions/setup-python@v5
|
|
62
|
+
with:
|
|
63
|
+
python-version: "3.11"
|
|
64
|
+
- run: pip install -e .
|
|
65
|
+
- name: Import every public module in a subprocess
|
|
66
|
+
run: |
|
|
67
|
+
python - <<'PY'
|
|
68
|
+
import importlib
|
|
69
|
+
import pkgutil
|
|
70
|
+
import sys
|
|
71
|
+
import clinicsentry
|
|
72
|
+
|
|
73
|
+
failed = []
|
|
74
|
+
for mod in pkgutil.walk_packages(clinicsentry.__path__, prefix="clinicsentry."):
|
|
75
|
+
if "dashboard" in mod.name or "cli" in mod.name:
|
|
76
|
+
continue
|
|
77
|
+
try:
|
|
78
|
+
importlib.import_module(mod.name)
|
|
79
|
+
except Exception as e:
|
|
80
|
+
failed.append((mod.name, repr(e)))
|
|
81
|
+
|
|
82
|
+
for name, err in failed:
|
|
83
|
+
print(f"FAIL: {name}: {err}", file=sys.stderr)
|
|
84
|
+
sys.exit(1 if failed else 0)
|
|
85
|
+
PY
|
|
86
|
+
|
|
87
|
+
license-scan:
|
|
88
|
+
runs-on: ubuntu-latest
|
|
89
|
+
steps:
|
|
90
|
+
- uses: actions/checkout@v4
|
|
91
|
+
- uses: actions/setup-python@v5
|
|
92
|
+
with:
|
|
93
|
+
python-version: "3.11"
|
|
94
|
+
- run: pip install -e '.[dev]' pip-licenses
|
|
95
|
+
- run: |
|
|
96
|
+
pip-licenses --format=markdown --order=license \
|
|
97
|
+
--allow-only="Apache Software License;Apache License 2.0;Apache-2.0;Apache-2.0 OR BSD-2-Clause;Apache-2.0 OR BSD-3-Clause;MIT License;MIT;BSD License;BSD 3-Clause;BSD 2-Clause;BSD-3-Clause;BSD-2-Clause;ISC;ISC License (ISCL);Mozilla Public License 2.0 (MPL 2.0);MPL-2.0;Python Software Foundation License;PSF-2.0;Freely Distributable;The Unlicense (Unlicense);Public Domain"
|
|
98
|
+
|
|
99
|
+
security-scan:
|
|
100
|
+
runs-on: ubuntu-latest
|
|
101
|
+
steps:
|
|
102
|
+
- uses: actions/checkout@v4
|
|
103
|
+
- uses: actions/setup-python@v5
|
|
104
|
+
with:
|
|
105
|
+
python-version: "3.11"
|
|
106
|
+
- run: pip install -e '.[dev]'
|
|
107
|
+
- run: pip-audit --strict || true # advisory until baseline established
|
|
108
|
+
- run: bandit -r src/clinicsentry -ll -ii
|
|
109
|
+
|
|
110
|
+
api-stability:
|
|
111
|
+
runs-on: ubuntu-latest
|
|
112
|
+
steps:
|
|
113
|
+
- uses: actions/checkout@v4
|
|
114
|
+
- uses: actions/setup-python@v5
|
|
115
|
+
with:
|
|
116
|
+
python-version: "3.11"
|
|
117
|
+
- run: pip install -e .
|
|
118
|
+
- name: Check public __all__ surface is non-empty
|
|
119
|
+
run: |
|
|
120
|
+
python - <<'PY'
|
|
121
|
+
import clinicsentry
|
|
122
|
+
assert clinicsentry.__all__, "clinicsentry.__all__ must not be empty"
|
|
123
|
+
assert "ClinicSentry" in clinicsentry.__all__
|
|
124
|
+
print("public surface:", sorted(clinicsentry.__all__))
|
|
125
|
+
PY
|
|
126
|
+
|
|
127
|
+
package-data:
|
|
128
|
+
# Verifies the compliance YAML rule files are included in both wheel and
|
|
129
|
+
# sdist artifacts. Without this, `load_default_rulesets()` silently returns
|
|
130
|
+
# zero rules at runtime, producing empty regulatory reports.
|
|
131
|
+
runs-on: ubuntu-latest
|
|
132
|
+
steps:
|
|
133
|
+
- uses: actions/checkout@v4
|
|
134
|
+
- uses: actions/setup-python@v5
|
|
135
|
+
with:
|
|
136
|
+
python-version: "3.11"
|
|
137
|
+
cache: "pip"
|
|
138
|
+
- run: pip install build
|
|
139
|
+
- run: python -m build
|
|
140
|
+
- name: Verify rules YAML in wheel
|
|
141
|
+
run: |
|
|
142
|
+
python -m zipfile -l dist/*.whl | grep "compliance/rules/.*\.yaml" | tee /tmp/wheel-yaml.txt
|
|
143
|
+
test $(wc -l < /tmp/wheel-yaml.txt) -ge 4
|
|
144
|
+
- name: Verify rules YAML in sdist
|
|
145
|
+
run: |
|
|
146
|
+
tar -tzf dist/*.tar.gz | grep "compliance/rules/.*\.yaml" | tee /tmp/sdist-yaml.txt
|
|
147
|
+
test $(wc -l < /tmp/sdist-yaml.txt) -ge 4
|
|
148
|
+
- name: Verify default rulesets load from installed wheel
|
|
149
|
+
run: |
|
|
150
|
+
python -m venv /tmp/wheelvenv
|
|
151
|
+
/tmp/wheelvenv/bin/pip install dist/*.whl
|
|
152
|
+
/tmp/wheelvenv/bin/python -c "
|
|
153
|
+
from clinicsentry.compliance import load_default_rulesets
|
|
154
|
+
sets = load_default_rulesets()
|
|
155
|
+
assert len(sets) == 4, sets
|
|
156
|
+
assert sum(len(s.rules) for s in sets) >= 14
|
|
157
|
+
print('OK:', len(sets), 'rulesets')
|
|
158
|
+
"
|
|
159
|
+
|
|
160
|
+
postgres-integration:
|
|
161
|
+
# Postgres audit backend integration tests via testcontainers. The
|
|
162
|
+
# ubuntu-latest runner ships a running Docker daemon, so testcontainers
|
|
163
|
+
# talks to it over the default socket — no docker-in-docker service needed.
|
|
164
|
+
runs-on: ubuntu-latest
|
|
165
|
+
steps:
|
|
166
|
+
- uses: actions/checkout@v4
|
|
167
|
+
- uses: actions/setup-python@v5
|
|
168
|
+
with:
|
|
169
|
+
python-version: "3.11"
|
|
170
|
+
cache: "pip"
|
|
171
|
+
- run: pip install -e '.[dev,postgres]' 'testcontainers[postgres]>=4.0' 'psycopg[binary]>=3.1'
|
|
172
|
+
- run: pytest tests/test_postgres_integration.py -q
|
|
173
|
+
|
|
174
|
+
adapter-sdks:
|
|
175
|
+
# Real-SDK compatibility tests for each adapter. Each adapter test file
|
|
176
|
+
# auto-skips when its SDK is not installed, so a fan-out matrix lets each
|
|
177
|
+
# SDK be installed in its own clean job and tested independently.
|
|
178
|
+
runs-on: ubuntu-latest
|
|
179
|
+
strategy:
|
|
180
|
+
fail-fast: false
|
|
181
|
+
matrix:
|
|
182
|
+
include:
|
|
183
|
+
- extra: "langgraph"
|
|
184
|
+
test: "tests/adapters/test_langgraph_integration.py"
|
|
185
|
+
- extra: "crewai"
|
|
186
|
+
test: "tests/adapters/test_crewai_integration.py"
|
|
187
|
+
- extra: "adk"
|
|
188
|
+
test: "tests/adapters/test_google_adk_integration.py"
|
|
189
|
+
- extra: "openai-agents"
|
|
190
|
+
test: "tests/adapters/test_openai_agents_integration.py"
|
|
191
|
+
- extra: "claude"
|
|
192
|
+
test: "tests/adapters/test_claude_sdk_integration.py"
|
|
193
|
+
- extra: "mcp"
|
|
194
|
+
test: "tests/adapters/test_mcp_proxy_integration.py"
|
|
195
|
+
steps:
|
|
196
|
+
- uses: actions/checkout@v4
|
|
197
|
+
- uses: actions/setup-python@v5
|
|
198
|
+
with:
|
|
199
|
+
python-version: "3.11"
|
|
200
|
+
cache: "pip"
|
|
201
|
+
- run: pip install -e ".[dev,${{ matrix.extra }}]"
|
|
202
|
+
- run: pytest ${{ matrix.test }} -q
|
|
203
|
+
|
|
204
|
+
heavy-deps:
|
|
205
|
+
# Heavy PHI detection happy-path runs. Manual / on-demand only because
|
|
206
|
+
# spaCy model wheels and Tesseract binary inflate runtime significantly
|
|
207
|
+
# and we don't want them on every PR.
|
|
208
|
+
if: ${{ contains(github.event.head_commit.message, '[heavy-deps]') || github.event_name == 'workflow_dispatch' }}
|
|
209
|
+
runs-on: ubuntu-latest
|
|
210
|
+
steps:
|
|
211
|
+
- uses: actions/checkout@v4
|
|
212
|
+
- uses: actions/setup-python@v5
|
|
213
|
+
with:
|
|
214
|
+
python-version: "3.11"
|
|
215
|
+
cache: "pip"
|
|
216
|
+
- name: Install Tesseract
|
|
217
|
+
run: sudo apt-get update && sudo apt-get install -y tesseract-ocr
|
|
218
|
+
- run: pip install -e '.[dev,phi,nlp-medical,ocr]'
|
|
219
|
+
- name: Install spaCy clinical model
|
|
220
|
+
run: |
|
|
221
|
+
pip install https://s3-us-west-2.amazonaws.com/ai2-s2-scispacy/releases/v0.5.4/en_core_sci_sm-0.5.4.tar.gz || echo "scispacy model unavailable"
|
|
222
|
+
- run: pytest tests/test_phi_heavy_deps.py -q
|
|
223
|
+
|
|
224
|
+
mutation:
|
|
225
|
+
# Mutation testing against the four novelty modules. Manually triggered
|
|
226
|
+
# only — full runs cost ~10 minutes and aren't useful on every PR.
|
|
227
|
+
if: ${{ contains(github.event.head_commit.message, '[mutation]') || github.event_name == 'workflow_dispatch' }}
|
|
228
|
+
runs-on: ubuntu-latest
|
|
229
|
+
steps:
|
|
230
|
+
- uses: actions/checkout@v4
|
|
231
|
+
- uses: actions/setup-python@v5
|
|
232
|
+
with:
|
|
233
|
+
python-version: "3.11"
|
|
234
|
+
cache: "pip"
|
|
235
|
+
- run: pip install -e '.[dev]'
|
|
236
|
+
- name: Run mutmut on each novelty module
|
|
237
|
+
run: |
|
|
238
|
+
set -e
|
|
239
|
+
for module in \
|
|
240
|
+
src/clinicsentry/phi/propagation.py \
|
|
241
|
+
src/clinicsentry/audit/chain.py \
|
|
242
|
+
src/clinicsentry/escalation/confidence.py \
|
|
243
|
+
src/clinicsentry/meddevice/mode.py; do
|
|
244
|
+
echo "=== mutmut: $module ==="
|
|
245
|
+
rm -rf .mutmut-cache
|
|
246
|
+
mutmut run --paths-to-mutate="$module" --simple-output || true
|
|
247
|
+
mutmut results
|
|
248
|
+
done
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
name: Docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
docstring-check:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: actions/setup-python@v5
|
|
14
|
+
with:
|
|
15
|
+
python-version: "3.11"
|
|
16
|
+
- run: pip install -e '.[dev]'
|
|
17
|
+
- run: ruff check --select D --extend-ignore D203,D213,D104,D105,D107 src/clinicsentry
|
|
18
|
+
|
|
19
|
+
build-mkdocs:
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
|
+
- uses: actions/setup-python@v5
|
|
24
|
+
with:
|
|
25
|
+
python-version: "3.11"
|
|
26
|
+
- run: pip install -e '.[docs]'
|
|
27
|
+
- run: mkdocs build --strict
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*.*.*"
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write
|
|
10
|
+
id-token: write # cosign keyless signing + PyPI trusted publishing
|
|
11
|
+
packages: write
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
build-and-sign:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: "3.11"
|
|
22
|
+
cache: "pip"
|
|
23
|
+
|
|
24
|
+
- name: Install build tooling
|
|
25
|
+
run: pip install --upgrade build twine
|
|
26
|
+
|
|
27
|
+
- name: Verify tag matches package version
|
|
28
|
+
run: |
|
|
29
|
+
PKG_VERSION=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
|
|
30
|
+
TAG_VERSION="${GITHUB_REF_NAME#v}"
|
|
31
|
+
if [ "$PKG_VERSION" != "$TAG_VERSION" ]; then
|
|
32
|
+
echo "Tag $GITHUB_REF_NAME does not match pyproject version $PKG_VERSION" >&2
|
|
33
|
+
exit 1
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
- name: Build wheel + sdist
|
|
37
|
+
run: python -m build
|
|
38
|
+
|
|
39
|
+
- name: Validate distribution metadata
|
|
40
|
+
run: twine check --strict dist/*
|
|
41
|
+
|
|
42
|
+
- name: Smoke-test wheel in a clean venv
|
|
43
|
+
run: |
|
|
44
|
+
python -m venv /tmp/smoke
|
|
45
|
+
/tmp/smoke/bin/pip install dist/*.whl
|
|
46
|
+
/tmp/smoke/bin/python -c "import clinicsentry; print(clinicsentry.__version__)"
|
|
47
|
+
/tmp/smoke/bin/clinicsentry scan "SSN 123-45-6789" | grep -q "REDACTED"
|
|
48
|
+
|
|
49
|
+
- name: Install Syft (SBOM)
|
|
50
|
+
uses: anchore/sbom-action/download-syft@v0.17.7
|
|
51
|
+
|
|
52
|
+
- name: Generate CycloneDX SBOM
|
|
53
|
+
run: |
|
|
54
|
+
syft dir:. -o cyclonedx-json=sbom.cyclonedx.json
|
|
55
|
+
syft dir:. -o spdx-json=sbom.spdx.json
|
|
56
|
+
|
|
57
|
+
- name: Install Cosign
|
|
58
|
+
uses: sigstore/cosign-installer@v3.7.0
|
|
59
|
+
|
|
60
|
+
- name: Sign SBOM (keyless)
|
|
61
|
+
env:
|
|
62
|
+
COSIGN_EXPERIMENTAL: "1"
|
|
63
|
+
run: cosign sign-blob --yes --output-signature sbom.cyclonedx.json.sig sbom.cyclonedx.json
|
|
64
|
+
|
|
65
|
+
- name: Build container image
|
|
66
|
+
run: docker build -t clinicsentry:${{ github.ref_name }} .
|
|
67
|
+
|
|
68
|
+
- name: Sign container image (keyless)
|
|
69
|
+
env:
|
|
70
|
+
COSIGN_EXPERIMENTAL: "1"
|
|
71
|
+
run: |
|
|
72
|
+
docker save clinicsentry:${{ github.ref_name }} -o image.tar
|
|
73
|
+
cosign sign-blob --yes --output-signature image.tar.sig image.tar
|
|
74
|
+
|
|
75
|
+
- name: Upload release artifacts
|
|
76
|
+
uses: softprops/action-gh-release@v2
|
|
77
|
+
with:
|
|
78
|
+
files: |
|
|
79
|
+
dist/*
|
|
80
|
+
sbom.cyclonedx.json
|
|
81
|
+
sbom.spdx.json
|
|
82
|
+
sbom.cyclonedx.json.sig
|
|
83
|
+
image.tar.sig
|
|
84
|
+
|
|
85
|
+
- name: Store dists for publish job
|
|
86
|
+
uses: actions/upload-artifact@v4
|
|
87
|
+
with:
|
|
88
|
+
name: python-dists
|
|
89
|
+
path: dist/
|
|
90
|
+
|
|
91
|
+
publish-pypi:
|
|
92
|
+
# Requires a PyPI "trusted publisher" configured for this repo + workflow.
|
|
93
|
+
# The `pypi` environment gates the publish behind any protection rules you
|
|
94
|
+
# configure (e.g. required reviewers).
|
|
95
|
+
needs: build-and-sign
|
|
96
|
+
runs-on: ubuntu-latest
|
|
97
|
+
environment:
|
|
98
|
+
name: pypi
|
|
99
|
+
permissions:
|
|
100
|
+
id-token: write
|
|
101
|
+
steps:
|
|
102
|
+
- name: Download dists
|
|
103
|
+
uses: actions/download-artifact@v4
|
|
104
|
+
with:
|
|
105
|
+
name: python-dists
|
|
106
|
+
path: dist/
|
|
107
|
+
|
|
108
|
+
- name: Publish to PyPI (trusted publishing)
|
|
109
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# Distribution / packaging
|
|
7
|
+
build/
|
|
8
|
+
dist/
|
|
9
|
+
*.egg-info/
|
|
10
|
+
*.egg
|
|
11
|
+
|
|
12
|
+
# Virtual environments
|
|
13
|
+
.venv/
|
|
14
|
+
venv/
|
|
15
|
+
env/
|
|
16
|
+
|
|
17
|
+
# IDE
|
|
18
|
+
.idea/
|
|
19
|
+
.vscode/
|
|
20
|
+
*.swp
|
|
21
|
+
*.swo
|
|
22
|
+
|
|
23
|
+
# OS
|
|
24
|
+
.DS_Store
|
|
25
|
+
Thumbs.db
|
|
26
|
+
|
|
27
|
+
# Testing / coverage
|
|
28
|
+
.pytest_cache/
|
|
29
|
+
.coverage
|
|
30
|
+
htmlcov/
|
|
31
|
+
.mypy_cache/
|
|
32
|
+
.ruff_cache/
|
|
33
|
+
|
|
34
|
+
# Temp / scratch
|
|
35
|
+
_tmp/
|
|
36
|
+
|
|
37
|
+
# Audit files created by demos
|
|
38
|
+
*.sqlite
|
|
39
|
+
*.sqlite3
|
|
40
|
+
clinicsentry_audit.log
|
|
41
|
+
|
|
42
|
+
# Local agent settings
|
|
43
|
+
.claude/
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
+
rev: v4.6.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: trailing-whitespace
|
|
6
|
+
- id: end-of-file-fixer
|
|
7
|
+
- id: check-yaml
|
|
8
|
+
exclude: ^deploy/helm/.*/templates/
|
|
9
|
+
- id: check-toml
|
|
10
|
+
- id: check-merge-conflict
|
|
11
|
+
- id: detect-private-key
|
|
12
|
+
- id: mixed-line-ending
|
|
13
|
+
|
|
14
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
15
|
+
rev: v0.4.10
|
|
16
|
+
hooks:
|
|
17
|
+
- id: ruff
|
|
18
|
+
args: [--fix, --exit-non-zero-on-fix]
|
|
19
|
+
- id: ruff-format
|
|
20
|
+
|
|
21
|
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
22
|
+
rev: v1.10.0
|
|
23
|
+
hooks:
|
|
24
|
+
- id: mypy
|
|
25
|
+
additional_dependencies:
|
|
26
|
+
- pydantic>=2.0
|
|
27
|
+
- types-PyYAML
|
|
28
|
+
args: [--config-file=pyproject.toml]
|
|
29
|
+
files: ^src/clinicsentry/
|
|
30
|
+
|
|
31
|
+
- repo: https://github.com/PyCQA/bandit
|
|
32
|
+
rev: 1.7.9
|
|
33
|
+
hooks:
|
|
34
|
+
- id: bandit
|
|
35
|
+
args: [-ll, -ii, -r]
|
|
36
|
+
files: ^src/clinicsentry/
|