rapier-runtime 0.5.0__tar.gz → 0.5.2__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.
- rapier_runtime-0.5.2/.githooks/pre-commit +82 -0
- rapier_runtime-0.5.2/.github/workflows/secret-scan.yml +45 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/.gitignore +3 -0
- rapier_runtime-0.5.2/.gitleaks.toml +23 -0
- rapier_runtime-0.5.2/.loom/audits/smuggled-virtue.done +3 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/.loom/project.json +7 -1
- rapier_runtime-0.5.2/CHANGELOG.md +46 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/PKG-INFO +1 -1
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/docs/STATUS.md +38 -1
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/pyproject.toml +1 -1
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/cli.py +26 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/models.py +13 -6
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/stages/resolver/citation_gate.py +2 -1
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/stages/resolver/compose.py +56 -11
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/stages/resolver/cross_review.py +18 -1
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/stages/resolver/definitiveness_gate.py +2 -1
- rapier_runtime-0.5.2/src/rapier/verify/rider_record.py +190 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/tests/test_resolver_stages.py +2 -1
- rapier_runtime-0.5.2/tests/test_rider_record.py +140 -0
- rapier_runtime-0.5.2/tests/test_single_vendor_report.py +81 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/.claude/scripts/loom-deploy-nonprod.sh +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/.claude/scripts/loom-deploy-prod.sh +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/.claude/scripts/loom-env-check.sh +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/.claude/scripts/loom-state-docs.sh +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/LICENSE +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/README.md +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/SECURITY.md +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/docs/chatlogs/.gitkeep +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/docs/chatlogs/202607301413.checkpoint.handoff-cut-0-5-0-to-pypi.md +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/docs/chatlogs/20260802-explainability-reminder.md +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/docs/mcp-server-scope.md +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/docs/threat-model.md +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/manifests/echo.yaml +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/manifests/sparring.frame.yaml +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/manifests/sparring.full.yaml +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/manifests/sparring.proposer.yaml +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/manifests/sparring.spar.yaml +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/site/README.md +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/site/coming-soon.html +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/site/index.html +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/site/rapier-logo.png +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/__init__.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/_json.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/convergence.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/envelope.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/ledger.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/manifest.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/mcp/__init__.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/mcp/server.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/mcp/tools.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/onboarding.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/pipeline.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/presets.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/secrets.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/stage.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/stages/__init__.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/stages/echo.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/stages/frame.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/stages/proposer/__init__.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/stages/proposer/phase_stages.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/stages/proposer/phases.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/stages/proposer/traceability_gate.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/stages/reconcile.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/stages/resolver/__init__.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/stages/resolver/_binding.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/stages/resolver/_extract.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/stages/resolver/anchored_fix.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/stages/resolver/author.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/verify/__init__.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/verify/_bootstrap.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/verify/_llm_shim.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/verify/_vendor/PROVENANCE.md +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/verify/_vendor/cite_check.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/verify/_vendor/lib_llm.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/verify/_vendor/spar_cross_review.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/verify/_vendor/spar_definitiveness_gate.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/verify/_vendor/spar_verify_gate.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/verify/_vendor/verify_grounding.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/verify/service.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/tests/test_cite_check_evidence_union.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/tests/test_cli.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/tests/test_convergence.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/tests/test_envelope.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/tests/test_extract_json.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/tests/test_frame_stage.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/tests/test_full_ceremony.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/tests/test_manifest.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/tests/test_mcp.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/tests/test_models_vendors.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/tests/test_onboarding.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/tests/test_persistence_default.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/tests/test_pipeline.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/tests/test_policy.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/tests/test_proposer_g2.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/tests/test_proposer_stages.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/tests/test_proposer_traceability_gate.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/tests/test_reconcile.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/tests/test_secrets.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/tests/test_vendor_policy_v4.py +0 -0
- {rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/tests/test_verify_service.py +0 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Loom pre-commit secret-scan gate (project template).
|
|
3
|
+
#
|
|
4
|
+
# Lives in .githooks/ so it is git-tracked and shared across all clones.
|
|
5
|
+
# Activated per-clone with: git config core.hooksPath .githooks
|
|
6
|
+
# (projlaunch wires new worktrees automatically; /sync-now sets it on existing
|
|
7
|
+
# clones -- see loom-secret-scan-check.)
|
|
8
|
+
#
|
|
9
|
+
# Why this exists: the 2026-06-30 .env-leak incident (CR-0004). A `.env` backup
|
|
10
|
+
# with live keys was committed via `git add -A` and auto-pushed to origin within
|
|
11
|
+
# seconds -- no human gate. .gitignore is one typo from failing, and it did.
|
|
12
|
+
# This is the local backstop: it blocks the commit BEFORE any auto-push fires.
|
|
13
|
+
# The server-side scan (.github/workflows/secret-scan.yml) is the guarantee;
|
|
14
|
+
# this hook is defense-in-depth that catches a secret before it leaves the box.
|
|
15
|
+
#
|
|
16
|
+
# Three gates:
|
|
17
|
+
# 1. PATH gate -- refuses to stage any .env secret file (templates/examples
|
|
18
|
+
# excepted). This alone would have stopped the CR-0004 incident.
|
|
19
|
+
# 2. CONTENT gate -- scans added lines for high-signal credential signatures,
|
|
20
|
+
# including in chatlogs and ordinary markdown (the inline-paste vector).
|
|
21
|
+
# Skips only dirs that legitimately carry the pattern literals / key-format
|
|
22
|
+
# teaching prose: .githooks/, docs/security/, docs/onboarding/.
|
|
23
|
+
# 3. gitleaks -- run if installed, as broader defense in depth (uses this
|
|
24
|
+
# repo's .gitleaks.toml automatically).
|
|
25
|
+
#
|
|
26
|
+
# Bypass (intentional, rare -- e.g. a verified false positive):
|
|
27
|
+
# git commit --no-verify
|
|
28
|
+
# Prefer fixing the staged content over bypassing.
|
|
29
|
+
|
|
30
|
+
set -uo pipefail
|
|
31
|
+
|
|
32
|
+
staged=$(git diff --cached --name-only --diff-filter=AM)
|
|
33
|
+
[ -z "$staged" ] && exit 0
|
|
34
|
+
|
|
35
|
+
fail=0
|
|
36
|
+
|
|
37
|
+
# --- Gate 1: env-secret file PATHS (the CR-0004 incident class) -------------
|
|
38
|
+
while IFS= read -r f; do
|
|
39
|
+
[ -z "$f" ] && continue
|
|
40
|
+
base=$(basename "$f")
|
|
41
|
+
case "$base" in
|
|
42
|
+
*.example|*.template|*.sample) continue ;;
|
|
43
|
+
esac
|
|
44
|
+
if printf '%s\n' "$base" | grep -qE '^\.env($|[.-])'; then
|
|
45
|
+
echo "[pre-commit] BLOCKED: staged env-secret file: $f" >&2
|
|
46
|
+
fail=1
|
|
47
|
+
fi
|
|
48
|
+
done <<< "$staged"
|
|
49
|
+
|
|
50
|
+
# --- Gate 2: credential SIGNATURES in added lines --------------------------
|
|
51
|
+
# High-signal, prefix-anchored signatures (near-zero false positives). Generic
|
|
52
|
+
# high-entropy / base64 detection is deliberately left to gitleaks (Gate 3),
|
|
53
|
+
# where the false positives live.
|
|
54
|
+
patterns='sk-ant-[A-Za-z0-9_-]{16}|sk-proj-[A-Za-z0-9_-]{16}|sk-[A-Za-z0-9]{20}|xai-[A-Za-z0-9]{16}|SG\.[A-Za-z0-9_.-]{16}|AC[a-z0-9]{30}|(sk|rk|pk)_(live|test)_[A-Za-z0-9]{16}|whsec_[A-Za-z0-9]{16}|AKIA[0-9A-Z]{16}|gh[opsu]_[A-Za-z0-9]{36}|github_pat_[A-Za-z0-9_]{20}|xox[baprs]-[A-Za-z0-9-]{10}|xapp-[A-Za-z0-9-]{10}|AIza[0-9A-Za-z_-]{35}|GOCSPX-[A-Za-z0-9_-]{20}|-----BEGIN [A-Z ]*PRIVATE KEY-----'
|
|
55
|
+
while IFS= read -r f; do
|
|
56
|
+
[ -z "$f" ] && continue
|
|
57
|
+
case "$f" in
|
|
58
|
+
.githooks/*|docs/security/*|docs/onboarding/*) continue ;;
|
|
59
|
+
esac
|
|
60
|
+
added=$(git diff --cached -U0 -- "$f" | grep '^+' | grep -v '^+++' || true)
|
|
61
|
+
if printf '%s\n' "$added" | grep -qE "$patterns"; then
|
|
62
|
+
echo "[pre-commit] BLOCKED: credential-like signature in staged change: $f" >&2
|
|
63
|
+
fail=1
|
|
64
|
+
fi
|
|
65
|
+
done <<< "$staged"
|
|
66
|
+
|
|
67
|
+
# --- Gate 3: gitleaks if available (defense in depth) ----------------------
|
|
68
|
+
if command -v gitleaks >/dev/null 2>&1; then
|
|
69
|
+
if ! gitleaks protect --staged --no-banner >/dev/null 2>&1; then
|
|
70
|
+
echo "[pre-commit] BLOCKED: gitleaks flagged staged content." >&2
|
|
71
|
+
fail=1
|
|
72
|
+
fi
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
if [ "$fail" -ne 0 ]; then
|
|
76
|
+
echo "[pre-commit] -------------------------------------------------------------" >&2
|
|
77
|
+
echo "[pre-commit] Commit blocked by the secret-scan gate. Remove the secret from" >&2
|
|
78
|
+
echo "[pre-commit] the staged set -- env backups belong OUTSIDE the repo tree." >&2
|
|
79
|
+
echo "[pre-commit] If this is a verified false positive: git commit --no-verify" >&2
|
|
80
|
+
exit 1
|
|
81
|
+
fi
|
|
82
|
+
exit 0
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
name: Secret scan
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
gitleaks:
|
|
13
|
+
name: gitleaks
|
|
14
|
+
runs-on: ubuntu-24.04
|
|
15
|
+
steps:
|
|
16
|
+
- name: Checkout (full history for delta scan)
|
|
17
|
+
uses: actions/checkout@v4
|
|
18
|
+
with:
|
|
19
|
+
fetch-depth: 0
|
|
20
|
+
|
|
21
|
+
- name: Install gitleaks
|
|
22
|
+
run: |
|
|
23
|
+
set -euo pipefail
|
|
24
|
+
GITLEAKS_VERSION="8.21.2"
|
|
25
|
+
curl -sSfL -o /tmp/gitleaks.tar.gz \
|
|
26
|
+
"https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz"
|
|
27
|
+
tar -xzf /tmp/gitleaks.tar.gz -C /tmp gitleaks
|
|
28
|
+
sudo mv /tmp/gitleaks /usr/local/bin/gitleaks
|
|
29
|
+
gitleaks version
|
|
30
|
+
|
|
31
|
+
- name: Scan changes only (push diff or PR diff)
|
|
32
|
+
env:
|
|
33
|
+
BASE_SHA: ${{ github.event.before || github.event.pull_request.base.sha || '' }}
|
|
34
|
+
HEAD_SHA: ${{ github.sha }}
|
|
35
|
+
run: |
|
|
36
|
+
set -euo pipefail
|
|
37
|
+
if [ -z "$BASE_SHA" ] || [ "$BASE_SHA" = "0000000000000000000000000000000000000000" ]; then
|
|
38
|
+
echo "[gitleaks] no base SHA; scanning HEAD commit"
|
|
39
|
+
gitleaks detect --redact --verbose --no-banner --exit-code 1 \
|
|
40
|
+
--log-opts="-1 HEAD"
|
|
41
|
+
else
|
|
42
|
+
echo "[gitleaks] scanning diff $BASE_SHA..$HEAD_SHA"
|
|
43
|
+
gitleaks detect --redact --verbose --no-banner --exit-code 1 \
|
|
44
|
+
--log-opts="$BASE_SHA..$HEAD_SHA"
|
|
45
|
+
fi
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# gitleaks configuration for this repo.
|
|
2
|
+
#
|
|
3
|
+
# What this is: a small settings file (TOML format -- plain "key = value" lines
|
|
4
|
+
# grouped under [section] headers) that tells the secret scanner two things:
|
|
5
|
+
# which detection rules to use, and which files to deliberately skip so we don't
|
|
6
|
+
# raise false alarms. Read by BOTH the server-side scan
|
|
7
|
+
# (.github/workflows/secret-scan.yml) and the optional local pre-commit hook.
|
|
8
|
+
|
|
9
|
+
[extend]
|
|
10
|
+
# Use gitleaks' built-in rule set (Anthropic, OpenAI, AWS, Stripe, GitHub, ...).
|
|
11
|
+
useDefault = true
|
|
12
|
+
|
|
13
|
+
[allowlist]
|
|
14
|
+
description = "Known false-positive surfaces (safe to skip)"
|
|
15
|
+
paths = [
|
|
16
|
+
# Dependency lock files: integrity hashes, never real credentials.
|
|
17
|
+
'''(^|/)(package-lock\.json|composer\.lock|yarn\.lock|pnpm-lock\.yaml)$''',
|
|
18
|
+
# Redaction tests feed fake key-shaped strings (sk-..., sk-ant-...) on purpose.
|
|
19
|
+
'''^tests/test_secrets\.py$''',
|
|
20
|
+
]
|
|
21
|
+
# Add project-specific entries above as real false positives appear, e.g.:
|
|
22
|
+
# '''database/migrations/.*\.sql$''' # bulk data with high-entropy strings
|
|
23
|
+
# '''(^|/)\.env\.example$''' # ONLY if its placeholders trip the scanner
|
|
@@ -4,5 +4,11 @@
|
|
|
4
4
|
"tier": null,
|
|
5
5
|
"source": "declared",
|
|
6
6
|
"loom_commit": "c956f9c151c7303a190ec8d8c43b5ad6ca8a1e58",
|
|
7
|
-
"stamped": "2026-07-30"
|
|
7
|
+
"stamped": "2026-07-30",
|
|
8
|
+
"pm": {
|
|
9
|
+
"exempt": "Public repo; Bart ruled exempt 2026-10-02"
|
|
10
|
+
},
|
|
11
|
+
"ownership": {
|
|
12
|
+
"owner": "bart"
|
|
13
|
+
}
|
|
8
14
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Notable changes to `rapier-runtime`. Earlier releases are summarized in
|
|
4
|
+
[`docs/STATUS.md`](docs/STATUS.md) and on the
|
|
5
|
+
[Releases](https://github.com/muddyone/rapier-runtime/releases) page.
|
|
6
|
+
|
|
7
|
+
## 0.5.2 — 2026-10-16
|
|
8
|
+
|
|
9
|
+
### Fixed: the report no longer calls a same-vendor review independent
|
|
10
|
+
|
|
11
|
+
Found on a live run with a single vendor's key, where the reviewer and the
|
|
12
|
+
author were the same model:
|
|
13
|
+
|
|
14
|
+
- **The report contradicted itself.** "Already fixed under challenge" said
|
|
15
|
+
"an independent, different-vendor reviewer raised each of these," and the
|
|
16
|
+
registry section pointed to "the cross-vendor challenge," while "How much to
|
|
17
|
+
trust this" correctly said the review was not independent. Every section
|
|
18
|
+
now says what actually happened; a cross-vendor run reads as before.
|
|
19
|
+
- **The progress warning blamed the wrong key.** It said "OPENAI_API_KEY
|
|
20
|
+
absent; falling back to a Claude reviewer" when the OpenAI key was set and
|
|
21
|
+
no Claude model was used. It now names the vendor that ran the review.
|
|
22
|
+
- **Arithmetic printed as raw LaTeX.** Math the model wrote as `\( H \times
|
|
23
|
+
0.60 \)` now reads `H × 0.60` in the report. Only text inside `\( \)` or
|
|
24
|
+
`\[ \]` is rewritten.
|
|
25
|
+
|
|
26
|
+
## 0.5.1 — 2026-10-16
|
|
27
|
+
|
|
28
|
+
### Changed: default models for five OpenAI-compatible vendors
|
|
29
|
+
|
|
30
|
+
Five built-in defaults named models that a vendor has retired or is retiring. If
|
|
31
|
+
you relied on a vendor's default (no `model:` in your manifest and no
|
|
32
|
+
`RAPIER_MODEL_<VENDOR>` set), **your runs now use a different model.** To keep
|
|
33
|
+
the old one while it still works, name it in your manifest or set
|
|
34
|
+
`RAPIER_MODEL_<VENDOR>`.
|
|
35
|
+
|
|
36
|
+
| Vendor | Was | Now | Why |
|
|
37
|
+
|---|---|---|---|
|
|
38
|
+
| `openrouter` | `openai/gpt-4o` | `openai/gpt-6-sol` | gpt-4o stops working 2027-01-14. gpt-6-sol is OpenAI's current flagship and costs slightly less. |
|
|
39
|
+
| `deepseek` | `deepseek-chat` | `deepseek-flash` | `deepseek-chat` was retired 2026-07-24, so the old default already fails. |
|
|
40
|
+
| `groq` | `llama-3.3-70b-versatile` | `openai/gpt-oss-120b` | Shut down 2026-08-16, so the old default already fails. Groq names gpt-oss-120b as the replacement. |
|
|
41
|
+
| `together` | `meta-llama/Llama-3.3-70B-Instruct-Turbo` | `openai/gpt-oss-120b` | Leaves Together's serverless tier 2026-10-22. |
|
|
42
|
+
| `gemini` | `gemini-2.5-flash` | `gemini-3.5-flash-lite` | 2.5 Flash is closed to new API users. Google recommends 3.5 Flash-Lite for new projects, at the same price. |
|
|
43
|
+
|
|
44
|
+
`xai` (`grok-4.3`), `mistral` (`mistral-large-latest`) and `ollama` (`llama3.1`)
|
|
45
|
+
are unchanged. Each was checked against its vendor's deprecation page on
|
|
46
|
+
2026-10-15.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rapier-runtime
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.2
|
|
4
4
|
Summary: A code-orchestrated engine that runs the SPARRING method: grounded, cross-vendor adversarial review for AI-in-the-loop decisions.
|
|
5
5
|
Project-URL: Homepage, https://rapierruntime.com
|
|
6
6
|
Project-URL: Repository, https://github.com/muddyone/rapier-runtime
|
|
@@ -1,9 +1,46 @@
|
|
|
1
1
|
# Rapier — dev status
|
|
2
2
|
|
|
3
|
-
_Last updated: 2026-
|
|
3
|
+
_Last updated: 2026-10-16 (tag v0.5.1, 230 tests)._ A running "where we are /
|
|
4
4
|
what's next" so a new session can continue without reconstructing from git log.
|
|
5
5
|
|
|
6
6
|
## Done
|
|
7
|
+
- **RELEASED (2026-10-16) — `rapier-runtime` 0.5.1 on PyPI. Default models for
|
|
8
|
+
five vendors replaced.** Git tag `v0.5.1`;
|
|
9
|
+
https://pypi.org/project/rapier-runtime/0.5.1/. OpenRouter's default
|
|
10
|
+
`openai/gpt-4o` (stops working 2027-01-14, Loom Plan 26) is now
|
|
11
|
+
`openai/gpt-6-sol` (Bart's ruling). Checking every OpenAI-compatible default
|
|
12
|
+
against its vendor's deprecation page found two already failing (DeepSeek
|
|
13
|
+
`deepseek-chat`, Groq `llama-3.3-70b-versatile`), one leaving 2026-10-22
|
|
14
|
+
(Together's Llama 3.3 Turbo) and one closed to new users (`gemini-2.5-flash`).
|
|
15
|
+
The full table is in `CHANGELOG.md`, which starts with this release. None of
|
|
16
|
+
the new defaults has been tried on a live run yet; that needs vendor keys.
|
|
17
|
+
- **RELEASED (2026-08-28) — `rapier-runtime` 0.5.0 on PyPI. The verifier stops
|
|
18
|
+
calling real citations fabricated.** Git tag `v0.5.0`;
|
|
19
|
+
https://pypi.org/project/rapier-runtime/0.5.0/. Eleven commits, and the
|
|
20
|
+
headline is a correctness fix in the grounding verifier:
|
|
21
|
+
`backend_literature` ran only a Crossref *bibliographic search* and gated every
|
|
22
|
+
candidate on a cited-surname match — but a bare identifier carries no surname,
|
|
23
|
+
so it could never match and fell through to `GROUNDED_REFUTED / "fabricated"`.
|
|
24
|
+
A bare DOI and a bare arXiv id, the two most authoritative citation forms
|
|
25
|
+
there are, were the two that always failed. `cite_check` had exposed
|
|
26
|
+
`lookup_crossref_doi` and `lookup_arxiv` the whole time; the backend called
|
|
27
|
+
neither. A verifier that flags real citations as fake is worse than none — it
|
|
28
|
+
trains the reader to distrust the gate.
|
|
29
|
+
Also in: identifier-strength resolution (DOI → arXiv → search, decisive both
|
|
30
|
+
ways); new `law` (CourtListener) and `patent` (Google Patents) backends;
|
|
31
|
+
evidence unioned across every identifier a reference supplies; a Proposer-side
|
|
32
|
+
`traceability_gate` (RM §10.1/§13.1) so the framing step's own citations are
|
|
33
|
+
grounded — verification was Resolver-only, so a Proposer could cite a
|
|
34
|
+
nonexistent source and the run reported clean; the reconcile gate now runs as
|
|
35
|
+
a standing check in `/spar`; and `RAPIER_MODEL_<VENDOR>` overrides the default
|
|
36
|
+
model per vendor. 223 tests green.
|
|
37
|
+
**Build note:** `pyproject.toml` pins `hatchling<1.30`. 1.30 emits
|
|
38
|
+
Metadata-Version 2.5 (PEP 794), which the current publishing toolchain rejects
|
|
39
|
+
(`twine check`: "'2.5' is not a valid metadata version") — that blocked this
|
|
40
|
+
release at the gate until pinned. Lift only when `twine check` passes a 2.5 wheel.
|
|
41
|
+
**Site:** no deploy needed. The full landing has been live since 2026-07-16 and
|
|
42
|
+
carries no version string; verified byte-identical to `site/index.html` on
|
|
43
|
+
2026-08-28.
|
|
7
44
|
- **RELEASED (2026-07-18) — `rapier-runtime` 0.4.0 on PyPI. Governance: runs
|
|
8
45
|
persist by default + THE RECORD provenance.** Git tag `v0.4.0`;
|
|
9
46
|
https://pypi.org/project/rapier-runtime/0.4.0/.
|
|
@@ -9,7 +9,7 @@ build-backend = "hatchling.build"
|
|
|
9
9
|
|
|
10
10
|
[project]
|
|
11
11
|
name = "rapier-runtime"
|
|
12
|
-
version = "0.5.
|
|
12
|
+
version = "0.5.2"
|
|
13
13
|
description = "A code-orchestrated engine that runs the SPARRING method: grounded, cross-vendor adversarial review for AI-in-the-loop decisions."
|
|
14
14
|
readme = "README.md"
|
|
15
15
|
requires-python = ">=3.10"
|
|
@@ -304,8 +304,34 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
304
304
|
ip.add_argument("--dir", default=".", help="directory to write .env.example into (default: cwd)")
|
|
305
305
|
sub.add_parser("mcp", help="run the MCP server (stdio) exposing frame/proposer/spar/sparring as tools")
|
|
306
306
|
|
|
307
|
+
vr = sub.add_parser(
|
|
308
|
+
"verify-rider",
|
|
309
|
+
help="check a run's trust rider against its own transcript (model-free)",
|
|
310
|
+
)
|
|
311
|
+
vr.add_argument("run", help="path to a persisted run directory, or a run id under the runs root")
|
|
312
|
+
vr.add_argument("--json", action="store_true", help="emit the report as JSON")
|
|
313
|
+
|
|
307
314
|
args = parser.parse_args(argv)
|
|
308
315
|
|
|
316
|
+
if args.cmd == "verify-rider":
|
|
317
|
+
import json as _json
|
|
318
|
+
import os as _os
|
|
319
|
+
|
|
320
|
+
from .ledger import default_runs_root
|
|
321
|
+
from .verify.rider_record import check_run, render
|
|
322
|
+
|
|
323
|
+
run_dir = args.run
|
|
324
|
+
if not _os.path.isdir(run_dir):
|
|
325
|
+
run_dir = _os.path.join(default_runs_root(), args.run)
|
|
326
|
+
if not _os.path.isdir(run_dir):
|
|
327
|
+
print(f"no such run: {args.run}", file=sys.stderr)
|
|
328
|
+
return 2
|
|
329
|
+
report = check_run(run_dir)
|
|
330
|
+
print(_json.dumps(report, indent=2) if args.json else render(report))
|
|
331
|
+
# failed -> 1 so CI can gate on it; unchecked -> 3, distinct from both
|
|
332
|
+
# a pass and a failure, because an unverifiable claim is neither.
|
|
333
|
+
return {"failed": 1, "unchecked": 3}.get(report["status"], 0)
|
|
334
|
+
|
|
309
335
|
if args.cmd == "doctor":
|
|
310
336
|
from .onboarding import doctor_report
|
|
311
337
|
|
|
@@ -178,14 +178,21 @@ class OpenAICompatibleModelClient(ModelClient):
|
|
|
178
178
|
# vendor -> (base_url, key_env, default_model). Defaults are overridable in the
|
|
179
179
|
# manifest; only xai's default is live-validated (2026-07-06 — Grok's API is
|
|
180
180
|
# OpenAI-compatible). Others are best-effort until validated with a real key.
|
|
181
|
+
# Checked against each vendor's deprecation page 2026-10-15 (see CHANGELOG.md).
|
|
181
182
|
_OPENAI_COMPATIBLE: dict[str, tuple[str, str, str]] = {
|
|
182
|
-
|
|
183
|
+
# gemini-2.5-flash is closed to new API users; Google points new projects here, same price.
|
|
184
|
+
"gemini": ("https://generativelanguage.googleapis.com/v1beta/openai", "GEMINI_API_KEY", "gemini-3.5-flash-lite"), # also has a native API
|
|
183
185
|
"xai": ("https://api.x.ai/v1", "XAI_API_KEY", "grok-4.3"),
|
|
184
|
-
|
|
185
|
-
"
|
|
186
|
-
"
|
|
187
|
-
|
|
188
|
-
"
|
|
186
|
+
# deepseek-chat was retired 2026-07-24; deepseek-flash is DeepSeek's current name.
|
|
187
|
+
"deepseek": ("https://api.deepseek.com/v1", "DEEPSEEK_API_KEY", "deepseek-flash"),
|
|
188
|
+
"mistral": ("https://api.mistral.ai/v1", "MISTRAL_API_KEY", "mistral-large-latest"), # alias, follows Mistral's current Large
|
|
189
|
+
# llama-3.3-70b-versatile was shut down 2026-08-16; this is Groq's named replacement.
|
|
190
|
+
"groq": ("https://api.groq.com/openai/v1", "GROQ_API_KEY", "openai/gpt-oss-120b"),
|
|
191
|
+
# Llama-3.3-70B-Instruct-Turbo leaves serverless 2026-10-22; the cheapest current open model there.
|
|
192
|
+
"together": ("https://api.together.xyz/v1", "TOGETHER_API_KEY", "openai/gpt-oss-120b"),
|
|
193
|
+
# gpt-4o stops working 2027-01-14. gpt-6-sol is OpenAI's current flagship, the same class
|
|
194
|
+
# gpt-4o was and slightly cheaper, so no caller gets a weaker critic or a bigger bill.
|
|
195
|
+
"openrouter": ("https://openrouter.ai/api/v1", "OPENROUTER_API_KEY", "openai/gpt-6-sol"),
|
|
189
196
|
"ollama": ("http://localhost:11434/v1", "OLLAMA_API_KEY", "llama3.1"), # local, zero-egress
|
|
190
197
|
}
|
|
191
198
|
|
|
@@ -79,7 +79,8 @@ def _reconcile_definitiveness_with_grounding(env: Envelope, verdicts: list) -> N
|
|
|
79
79
|
env.verdict = verdict
|
|
80
80
|
rider = dict(env.trust_rider or {})
|
|
81
81
|
rider["assumptions_to_verify"] = defin["rider_lines"]
|
|
82
|
-
rider["
|
|
82
|
+
rider["specifics_traceability"] = verdict
|
|
83
|
+
rider["overall_confidence"] = verdict # deprecated alias; remove after 0.6.x
|
|
83
84
|
rider["verified_externally"] = grounded_specs
|
|
84
85
|
env.trust_rider = rider
|
|
85
86
|
env.add_trace(
|
|
@@ -5,6 +5,14 @@ Assembles the final report (recommendation + trust rider), where the rider's
|
|
|
5
5
|
Proposer's forwarded standing objections (so the deliberation's dissent is
|
|
6
6
|
visible to the user). Writes the /spar-parity named files and the derived
|
|
7
7
|
ceremony-ledger row.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
Trust-rider field note: ``specifics_traceability`` carries the definitiveness
|
|
11
|
+
gate's verdict -- whether every hard specific in the answer traces to a given
|
|
12
|
+
fact or is flagged as an estimate. It is deliberately NOT named
|
|
13
|
+
"overall confidence": it is one narrow evidence check, and says nothing about
|
|
14
|
+
coverage, soundness, or the decision. ``overall_confidence`` remains as a
|
|
15
|
+
deprecated alias with the same value.
|
|
8
16
|
"""
|
|
9
17
|
from __future__ import annotations
|
|
10
18
|
|
|
@@ -40,6 +48,13 @@ def _verdict_sentence(verdict, gate) -> str:
|
|
|
40
48
|
"to check, or the checker was unavailable), so treat the stated specifics as unverified.")
|
|
41
49
|
|
|
42
50
|
|
|
51
|
+
def _challenge_phrase(review) -> str:
|
|
52
|
+
"""Who raised the challenge, worded so a same-vendor review is never called independent."""
|
|
53
|
+
if review.get("cross_vendor"):
|
|
54
|
+
return "an independent, different-vendor reviewer"
|
|
55
|
+
return "a reviewer on the same vendor as the author (not an independent check)"
|
|
56
|
+
|
|
57
|
+
|
|
43
58
|
def _reviewer_sentence(review) -> str:
|
|
44
59
|
"""Whether the challenge was genuinely independent — in plain words."""
|
|
45
60
|
if review.get("cross_vendor"):
|
|
@@ -124,11 +139,38 @@ def _bullets(items) -> str:
|
|
|
124
139
|
"""One finding per bullet, hanging-indented so the marker column stays a clean
|
|
125
140
|
vertical scan-line."""
|
|
126
141
|
return "\n".join(
|
|
127
|
-
textwrap.fill(str(it).strip(), width=_W, initial_indent="• ",
|
|
142
|
+
textwrap.fill(_plain_math(str(it)).strip(), width=_W, initial_indent="• ",
|
|
143
|
+
subsequent_indent=" ")
|
|
128
144
|
for it in items
|
|
129
145
|
)
|
|
130
146
|
|
|
131
147
|
|
|
148
|
+
# Models often write arithmetic as LaTeX (\( H \times 0.60 \)), which a terminal
|
|
149
|
+
# prints literally. Only text inside \( \) or \[ \] is rewritten, so a stray
|
|
150
|
+
# backslash elsewhere in the prose is left alone.
|
|
151
|
+
_MATH_SPAN_RE = re.compile(r"\\\((.+?)\\\)|\\\[(.+?)\\\]", re.S)
|
|
152
|
+
_MATH_SYMBOLS = {
|
|
153
|
+
"times": "×", "div": "÷", "cdot": "·", "approx": "≈", "pm": "±",
|
|
154
|
+
"leq": "≤", "le": "≤", "geq": "≥", "ge": "≥", "neq": "≠", "ne": "≠",
|
|
155
|
+
"to": "→", "rightarrow": "→", "infty": "∞", "%": "%", "$": "$",
|
|
156
|
+
",": " ", ";": " ", "!": "", " ": " ", "quad": " ", "qquad": " ",
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _plain_math(text: str) -> str:
|
|
161
|
+
def span(m: re.Match) -> str:
|
|
162
|
+
t = m.group(1) if m.group(1) is not None else m.group(2)
|
|
163
|
+
for _ in range(3): # nested \frac / \text unwrap from the inside out
|
|
164
|
+
t = re.sub(r"\\[dt]?frac\{([^{}]*)\}\{([^{}]*)\}", r"(\1)/(\2)", t)
|
|
165
|
+
t = re.sub(r"\\(?:text|mathrm|mathbf|operatorname)\{([^{}]*)\}", r"\1", t)
|
|
166
|
+
t = re.sub(r"\\left|\\right", "", t)
|
|
167
|
+
t = re.sub(r"\\([A-Za-z]+|[%$,;! ])",
|
|
168
|
+
lambda k: _MATH_SYMBOLS.get(k.group(1), k.group(0)), t)
|
|
169
|
+
t = t.replace("{", "").replace("}", "")
|
|
170
|
+
return re.sub(r"\s+", " ", t).strip()
|
|
171
|
+
return _MATH_SPAN_RE.sub(span, text or "")
|
|
172
|
+
|
|
173
|
+
|
|
132
174
|
def _demote_md(text: str) -> str:
|
|
133
175
|
"""Neutralize an embedded recommendation's own markdown so it cannot compete
|
|
134
176
|
with the report's structure: ATX headings (`## X`) become bare Title-case
|
|
@@ -142,7 +184,7 @@ def _demote_md(text: str) -> str:
|
|
|
142
184
|
s = re.sub(r"\*\*(.+?)\*\*", r"\1", s) # bold
|
|
143
185
|
s = re.sub(r"(?<![\w*])\*(?!\s)([^*\n]+?)(?<!\s)\*(?![\w*])", r"\1", s) # italic
|
|
144
186
|
s = re.sub(r"(?<!\w)`([^`]+)`(?!\w)", r"\1", s) # inline code
|
|
145
|
-
return s
|
|
187
|
+
return _plain_math(s)
|
|
146
188
|
|
|
147
189
|
|
|
148
190
|
# The stored verdicts carry the citation gate's shape (``status``:
|
|
@@ -175,7 +217,7 @@ def _grounding_body(env: Envelope) -> str:
|
|
|
175
217
|
return ("The recommendation cited nothing externally checkable — no CVE, CWE, "
|
|
176
218
|
"RFC, DOI, or URL — so there was nothing to confirm against a public "
|
|
177
219
|
"registry. This is normal for judgment and strategy questions, where the "
|
|
178
|
-
"load-bearing work is the
|
|
220
|
+
"load-bearing work is the challenge below, not citation.")
|
|
179
221
|
lines = []
|
|
180
222
|
nver = 0
|
|
181
223
|
for v in verdicts:
|
|
@@ -291,7 +333,7 @@ def _render_report(env: Envelope) -> str:
|
|
|
291
333
|
L += ["", "", _HEAVY, " TRUST RIDER", _HEAVY, "",
|
|
292
334
|
"How far to trust the recommendation above: what is still yours to",
|
|
293
335
|
"confirm, what was verified for you, and how the answer held up under",
|
|
294
|
-
"
|
|
336
|
+
"challenge."]
|
|
295
337
|
|
|
296
338
|
assumptions = rider.get("assumptions_to_verify") or []
|
|
297
339
|
L += _sec("STILL YOURS TO CHECK",
|
|
@@ -314,13 +356,15 @@ def _render_report(env: Envelope) -> str:
|
|
|
314
356
|
|
|
315
357
|
contested = _texts(review.get("objections"))
|
|
316
358
|
L += _sec("ALREADY FIXED UNDER CHALLENGE",
|
|
317
|
-
"Not action items — already folded into the recommendation above.
|
|
318
|
-
|
|
319
|
-
"answer was revised to address it.
|
|
320
|
-
"challenge caught and how the
|
|
359
|
+
"Not action items — already folded into the recommendation above. "
|
|
360
|
+
+ _challenge_phrase(review)[0].upper() + _challenge_phrase(review)[1:]
|
|
361
|
+
+ " raised each of these, and the answer was revised to address it. "
|
|
362
|
+
"Shown so you can see what the challenge caught and how the "
|
|
363
|
+
"recommendation got stronger.",
|
|
321
364
|
_bullets(contested) if contested
|
|
322
|
-
else "The
|
|
323
|
-
"
|
|
365
|
+
else "The review found nothing material to change — the recommendation "
|
|
366
|
+
"held up as written. See HOW MUCH TO TRUST THIS for how independent "
|
|
367
|
+
"that review was.")
|
|
324
368
|
|
|
325
369
|
dissent = rider.get("proposer_dissent_forwarded")
|
|
326
370
|
if dissent:
|
|
@@ -497,7 +541,8 @@ class ComposeStage(TransformStage):
|
|
|
497
541
|
rider.setdefault("contested_and_resolved", _texts(review.get("objections")))
|
|
498
542
|
if standing:
|
|
499
543
|
rider["proposer_dissent_forwarded"] = _texts(standing)
|
|
500
|
-
rider["
|
|
544
|
+
rider["specifics_traceability"] = env.verdict
|
|
545
|
+
rider["overall_confidence"] = env.verdict # deprecated alias; remove after 0.6.x
|
|
501
546
|
env.trust_rider = rider
|
|
502
547
|
|
|
503
548
|
env.meta["report"] = {
|
|
@@ -7,6 +7,10 @@ the anchored-correction stage to act on.
|
|
|
7
7
|
"""
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
+
import contextlib
|
|
11
|
+
import io
|
|
12
|
+
import sys
|
|
13
|
+
|
|
10
14
|
from ...envelope import Envelope
|
|
11
15
|
from ...stage import StageContext, TransformStage, register_stage
|
|
12
16
|
|
|
@@ -24,10 +28,23 @@ class CrossReviewStage(TransformStage):
|
|
|
24
28
|
primary_v, secondary_v = bind_pair(
|
|
25
29
|
env, secondary_pref=ctx.config.get("reviewer_vendor"), policy=ctx.policy
|
|
26
30
|
)
|
|
31
|
+
# The vendored reviewer names its two slots after Anthropic and OpenAI, so
|
|
32
|
+
# its fallback warning ("OPENAI_API_KEY absent; falling back to a Claude
|
|
33
|
+
# reviewer") is wrong once the slots are bound to other vendors. Capture
|
|
34
|
+
# it and say what actually happened instead; other stderr passes through.
|
|
35
|
+
captured = io.StringIO()
|
|
27
36
|
try:
|
|
28
|
-
|
|
37
|
+
with contextlib.redirect_stderr(captured):
|
|
38
|
+
result = B.review(env.request, env.recommendation, None, None)
|
|
29
39
|
finally:
|
|
30
40
|
B.reset_slots()
|
|
41
|
+
for line in captured.getvalue().splitlines():
|
|
42
|
+
if line.startswith("WARN spar-cross-review:") and not result.get("cross_vendor"):
|
|
43
|
+
if result.get("reviewer_vendor") is None:
|
|
44
|
+
continue # no keys at all; the pipeline's own preflight reports that
|
|
45
|
+
line = (f"WARN cross_review: no second vendor available; the reviewer runs on "
|
|
46
|
+
f"{primary_v}, the author's vendor (NOT cross-vendor).")
|
|
47
|
+
sys.stderr.write(line + "\n")
|
|
31
48
|
|
|
32
49
|
env.meta["review"] = result
|
|
33
50
|
n = len(result.get("objections") or [])
|
{rapier_runtime-0.5.0 → rapier_runtime-0.5.2}/src/rapier/stages/resolver/definitiveness_gate.py
RENAMED
|
@@ -34,7 +34,8 @@ class DefinitivenessGateStage(TransformStage):
|
|
|
34
34
|
|
|
35
35
|
rider = dict(env.trust_rider or {})
|
|
36
36
|
rider["assumptions_to_verify"] = result.get("rider_lines") or []
|
|
37
|
-
rider["
|
|
37
|
+
rider["specifics_traceability"] = env.verdict
|
|
38
|
+
rider["overall_confidence"] = env.verdict # deprecated alias; remove after 0.6.x
|
|
38
39
|
review = env.meta.get("review") or {}
|
|
39
40
|
if review.get("objections"):
|
|
40
41
|
rider["contested_and_resolved"] = [
|