rotguard 0.1.0a1__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.
- rotguard-0.1.0a1/.github/CODEOWNERS +3 -0
- rotguard-0.1.0a1/.github/FUNDING.yml +3 -0
- rotguard-0.1.0a1/.github/ISSUE_TEMPLATE/bug_report.yml +23 -0
- rotguard-0.1.0a1/.github/ISSUE_TEMPLATE/config.yml +5 -0
- rotguard-0.1.0a1/.github/ISSUE_TEMPLATE/feature_request.yml +14 -0
- rotguard-0.1.0a1/.github/PULL_REQUEST_TEMPLATE.md +19 -0
- rotguard-0.1.0a1/.github/SECURITY.md +24 -0
- rotguard-0.1.0a1/.github/dependabot.yml +15 -0
- rotguard-0.1.0a1/.github/workflows/ci.yml +33 -0
- rotguard-0.1.0a1/.github/workflows/nightly-eval.yml +41 -0
- rotguard-0.1.0a1/.github/workflows/release.yml +47 -0
- rotguard-0.1.0a1/.gitignore +234 -0
- rotguard-0.1.0a1/.pre-commit-config.yaml +13 -0
- rotguard-0.1.0a1/CHANGELOG.md +184 -0
- rotguard-0.1.0a1/CODE_OF_CONDUCT.md +69 -0
- rotguard-0.1.0a1/CONTRIBUTING.md +54 -0
- rotguard-0.1.0a1/LICENSE +661 -0
- rotguard-0.1.0a1/PKG-INFO +371 -0
- rotguard-0.1.0a1/README.md +333 -0
- rotguard-0.1.0a1/docs/advanced.md +47 -0
- rotguard-0.1.0a1/docs/audit.md +76 -0
- rotguard-0.1.0a1/docs/cli.md +65 -0
- rotguard-0.1.0a1/docs/evaluation.md +110 -0
- rotguard-0.1.0a1/docs/index.md +40 -0
- rotguard-0.1.0a1/docs/policies.md +104 -0
- rotguard-0.1.0a1/docs/quickstart.md +120 -0
- rotguard-0.1.0a1/docs/recall.md +126 -0
- rotguard-0.1.0a1/evals/__init__.py +0 -0
- rotguard-0.1.0a1/evals/baselines.py +121 -0
- rotguard-0.1.0a1/evals/core.py +162 -0
- rotguard-0.1.0a1/evals/e2e_langchain.py +78 -0
- rotguard-0.1.0a1/evals/gate1.py +122 -0
- rotguard-0.1.0a1/evals/gate2.py +202 -0
- rotguard-0.1.0a1/evals/gate3.py +310 -0
- rotguard-0.1.0a1/evals/lc_ollama.py +43 -0
- rotguard-0.1.0a1/evals/models.py +220 -0
- rotguard-0.1.0a1/evals/nightly.py +97 -0
- rotguard-0.1.0a1/evals/rescore.py +28 -0
- rotguard-0.1.0a1/evals/results/.gitkeep +0 -0
- rotguard-0.1.0a1/evals/run.py +76 -0
- rotguard-0.1.0a1/evals/suites/__init__.py +11 -0
- rotguard-0.1.0a1/evals/suites/_util.py +14 -0
- rotguard-0.1.0a1/evals/suites/goal_drift.py +43 -0
- rotguard-0.1.0a1/evals/suites/holdout.py +118 -0
- rotguard-0.1.0a1/evals/suites/holdout2.py +72 -0
- rotguard-0.1.0a1/evals/suites/planted_facts.py +51 -0
- rotguard-0.1.0a1/evals/suites/superseded.py +57 -0
- rotguard-0.1.0a1/mkdocs.yml +22 -0
- rotguard-0.1.0a1/pyproject.toml +83 -0
- rotguard-0.1.0a1/refix.md +577 -0
- rotguard-0.1.0a1/schemas/README.md +9 -0
- rotguard-0.1.0a1/schemas/audit_event.schema.json +96 -0
- rotguard-0.1.0a1/schemas/ledger_entry.schema.json +89 -0
- rotguard-0.1.0a1/schemas/message.schema.json +209 -0
- rotguard-0.1.0a1/schemas/recall_event.schema.json +89 -0
- rotguard-0.1.0a1/schemas/report.schema.json +348 -0
- rotguard-0.1.0a1/src/rotguard/__init__.py +1 -0
- rotguard-0.1.0a1/src/rotguard/adapters/__init__.py +0 -0
- rotguard-0.1.0a1/src/rotguard/adapters/anthropic.py +194 -0
- rotguard-0.1.0a1/src/rotguard/adapters/langchain.py +532 -0
- rotguard-0.1.0a1/src/rotguard/adapters/langgraph.py +118 -0
- rotguard-0.1.0a1/src/rotguard/adapters/mcp.py +95 -0
- rotguard-0.1.0a1/src/rotguard/adapters/openai_agents.py +129 -0
- rotguard-0.1.0a1/src/rotguard/cli/__init__.py +0 -0
- rotguard-0.1.0a1/src/rotguard/cli/__main__.py +3 -0
- rotguard-0.1.0a1/src/rotguard/cli/callers.py +71 -0
- rotguard-0.1.0a1/src/rotguard/cli/main.py +120 -0
- rotguard-0.1.0a1/src/rotguard/core/__init__.py +21 -0
- rotguard-0.1.0a1/src/rotguard/core/messages.py +74 -0
- rotguard-0.1.0a1/src/rotguard/core/observer.py +68 -0
- rotguard-0.1.0a1/src/rotguard/handoff.py +89 -0
- rotguard-0.1.0a1/src/rotguard/ledger/__init__.py +3 -0
- rotguard-0.1.0a1/src/rotguard/ledger/ledger.py +80 -0
- rotguard-0.1.0a1/src/rotguard/log.py +38 -0
- rotguard-0.1.0a1/src/rotguard/policies/__init__.py +5 -0
- rotguard-0.1.0a1/src/rotguard/policies/dedup.py +44 -0
- rotguard-0.1.0a1/src/rotguard/policies/offload.py +87 -0
- rotguard-0.1.0a1/src/rotguard/policies/staged.py +113 -0
- rotguard-0.1.0a1/src/rotguard/probes/__init__.py +27 -0
- rotguard-0.1.0a1/src/rotguard/probes/audit.py +173 -0
- rotguard-0.1.0a1/src/rotguard/probes/caller.py +17 -0
- rotguard-0.1.0a1/src/rotguard/probes/extract.py +118 -0
- rotguard-0.1.0a1/src/rotguard/probes/pool.py +39 -0
- rotguard-0.1.0a1/src/rotguard/probes/quiz.py +413 -0
- rotguard-0.1.0a1/src/rotguard/probes/recall.py +179 -0
- rotguard-0.1.0a1/src/rotguard/probes/signals.py +73 -0
- rotguard-0.1.0a1/src/rotguard/replay.py +72 -0
- rotguard-0.1.0a1/src/rotguard/report/__init__.py +3 -0
- rotguard-0.1.0a1/src/rotguard/report/html.py +177 -0
- rotguard-0.1.0a1/src/rotguard/report/models.py +96 -0
- rotguard-0.1.0a1/src/rotguard/report/query.py +62 -0
- rotguard-0.1.0a1/src/rotguard/schemas.py +43 -0
- rotguard-0.1.0a1/src/rotguard/session.py +343 -0
- rotguard-0.1.0a1/src/rotguard/store/__init__.py +10 -0
- rotguard-0.1.0a1/src/rotguard/store/protocol.py +32 -0
- rotguard-0.1.0a1/src/rotguard/store/sqlite.py +123 -0
- rotguard-0.1.0a1/src/rotguard/telemetry.py +92 -0
- rotguard-0.1.0a1/src/rotguard/testing.py +61 -0
- rotguard-0.1.0a1/src/rotguard/tokens.py +67 -0
- rotguard-0.1.0a1/tests/test_anthropic.py +214 -0
- rotguard-0.1.0a1/tests/test_audit.py +395 -0
- rotguard-0.1.0a1/tests/test_baselines.py +111 -0
- rotguard-0.1.0a1/tests/test_cli.py +123 -0
- rotguard-0.1.0a1/tests/test_extract.py +119 -0
- rotguard-0.1.0a1/tests/test_gate1.py +49 -0
- rotguard-0.1.0a1/tests/test_gate2.py +15 -0
- rotguard-0.1.0a1/tests/test_gate3.py +114 -0
- rotguard-0.1.0a1/tests/test_handoff.py +101 -0
- rotguard-0.1.0a1/tests/test_harness.py +85 -0
- rotguard-0.1.0a1/tests/test_langchain.py +467 -0
- rotguard-0.1.0a1/tests/test_langchain_policies.py +215 -0
- rotguard-0.1.0a1/tests/test_langgraph.py +109 -0
- rotguard-0.1.0a1/tests/test_ledger.py +34 -0
- rotguard-0.1.0a1/tests/test_mcp.py +58 -0
- rotguard-0.1.0a1/tests/test_nightly.py +47 -0
- rotguard-0.1.0a1/tests/test_observer_report.py +81 -0
- rotguard-0.1.0a1/tests/test_openai_agents.py +115 -0
- rotguard-0.1.0a1/tests/test_policies.py +201 -0
- rotguard-0.1.0a1/tests/test_recall.py +307 -0
- rotguard-0.1.0a1/tests/test_schemas.py +30 -0
- rotguard-0.1.0a1/tests/test_store_protocol.py +59 -0
- rotguard-0.1.0a1/tests/test_telemetry.py +174 -0
- rotguard-0.1.0a1/uv.lock +3586 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Something in rotguard behaves wrongly
|
|
3
|
+
labels: [bug]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: what
|
|
7
|
+
attributes:
|
|
8
|
+
label: What happened, and what did you expect?
|
|
9
|
+
validations:
|
|
10
|
+
required: true
|
|
11
|
+
- type: textarea
|
|
12
|
+
id: repro
|
|
13
|
+
attributes:
|
|
14
|
+
label: Minimal reproduction
|
|
15
|
+
description: Ideally a short script. For middleware issues, include your `middleware=[...]` list in order.
|
|
16
|
+
render: python
|
|
17
|
+
- type: input
|
|
18
|
+
id: versions
|
|
19
|
+
attributes:
|
|
20
|
+
label: Versions
|
|
21
|
+
description: rotguard, langchain, Python
|
|
22
|
+
validations:
|
|
23
|
+
required: true
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
name: Feature request
|
|
2
|
+
description: Suggest an improvement
|
|
3
|
+
labels: [enhancement]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: problem
|
|
7
|
+
attributes:
|
|
8
|
+
label: What problem are you trying to solve?
|
|
9
|
+
validations:
|
|
10
|
+
required: true
|
|
11
|
+
- type: textarea
|
|
12
|
+
id: idea
|
|
13
|
+
attributes:
|
|
14
|
+
label: What would you like rotguard to do?
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
## Summary
|
|
2
|
+
|
|
3
|
+
<!-- What does this change, and why? -->
|
|
4
|
+
|
|
5
|
+
## Checklist
|
|
6
|
+
|
|
7
|
+
- [ ] `ruff check .` passes
|
|
8
|
+
- [ ] `mypy` passes (`--strict`)
|
|
9
|
+
- [ ] `pytest` passes, and new surface area has test coverage
|
|
10
|
+
- [ ] Public API additions are documented in `README.md` with a runnable example
|
|
11
|
+
- [ ] `CHANGELOG.md` has an entry under `Unreleased`
|
|
12
|
+
- [ ] If this changes the message model or ledger entry shape, `schemas/` is regenerated
|
|
13
|
+
(`uv run python -m rotguard.schemas`)
|
|
14
|
+
- [ ] Any claim about quality or cost is backed by a number from `evals/`
|
|
15
|
+
|
|
16
|
+
## Scope
|
|
17
|
+
|
|
18
|
+
<!-- One concern per PR where possible. Call out here if this
|
|
19
|
+
intentionally spans more than one. -->
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported Versions
|
|
4
|
+
|
|
5
|
+
`rotguard` is pre-release. Security fixes target the latest released version only. Please upgrade
|
|
6
|
+
before reporting an issue to confirm it is still present.
|
|
7
|
+
|
|
8
|
+
## Reporting a Vulnerability
|
|
9
|
+
|
|
10
|
+
**Please do not open a public GitHub issue for security vulnerabilities.**
|
|
11
|
+
|
|
12
|
+
Use GitHub's private vulnerability reporting instead:
|
|
13
|
+
[Report a vulnerability](https://github.com/nikhilvdev/rotguard/security/advisories/new)
|
|
14
|
+
(the "Security" tab of this repository, then "Report a vulnerability"). This opens a private
|
|
15
|
+
advisory visible only to maintainers until a fix is ready.
|
|
16
|
+
|
|
17
|
+
Please include:
|
|
18
|
+
|
|
19
|
+
- A description of the vulnerability and its potential impact
|
|
20
|
+
- Steps to reproduce, or a minimal proof-of-concept
|
|
21
|
+
- The affected version(s)
|
|
22
|
+
|
|
23
|
+
Note that rotguard stores message text in a local SQLite file (ledger entries, provenance, and the
|
|
24
|
+
text of context items). Treat that file as sensitive as the conversations it came from.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: "pip"
|
|
4
|
+
directory: "/"
|
|
5
|
+
schedule:
|
|
6
|
+
interval: "weekly"
|
|
7
|
+
commit-message:
|
|
8
|
+
prefix: "chore"
|
|
9
|
+
|
|
10
|
+
- package-ecosystem: "github-actions"
|
|
11
|
+
directory: "/"
|
|
12
|
+
schedule:
|
|
13
|
+
interval: "weekly"
|
|
14
|
+
commit-message:
|
|
15
|
+
prefix: "chore"
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches: [main]
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
fail-fast: false
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v7
|
|
18
|
+
|
|
19
|
+
- uses: astral-sh/setup-uv@v7
|
|
20
|
+
|
|
21
|
+
- run: uv python install ${{ matrix.python-version }}
|
|
22
|
+
|
|
23
|
+
- run: >-
|
|
24
|
+
uv sync --python ${{ matrix.python-version }}
|
|
25
|
+
--extra langchain --extra anthropic --extra openai --extra mcp
|
|
26
|
+
|
|
27
|
+
- run: uv run ruff check .
|
|
28
|
+
|
|
29
|
+
- run: uv run ruff format --check .
|
|
30
|
+
|
|
31
|
+
- run: uv run mypy
|
|
32
|
+
|
|
33
|
+
- run: uv run pytest
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
name: Nightly eval
|
|
2
|
+
|
|
3
|
+
# A small fixed eval (one scenario per suite, one seed) on a cheap hosted model. It catches a
|
|
4
|
+
# break in goal retention or audit validity, not a small drift. It costs money on every run.
|
|
5
|
+
# Setup: add a repository secret ANTHROPIC_API_KEY. To record the first baseline, run this
|
|
6
|
+
# workflow by hand with "write_baseline" set, then commit evals/nightly_baseline.json.
|
|
7
|
+
|
|
8
|
+
on:
|
|
9
|
+
schedule:
|
|
10
|
+
- cron: "0 3 * * *"
|
|
11
|
+
workflow_dispatch:
|
|
12
|
+
inputs:
|
|
13
|
+
write_baseline:
|
|
14
|
+
description: "Record today's numbers as the baseline instead of comparing"
|
|
15
|
+
type: boolean
|
|
16
|
+
default: false
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
eval:
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v7
|
|
23
|
+
|
|
24
|
+
- uses: astral-sh/setup-uv@v7
|
|
25
|
+
|
|
26
|
+
- run: uv python install 3.12
|
|
27
|
+
|
|
28
|
+
- run: uv sync --python 3.12 --extra langchain --extra anthropic --extra eval
|
|
29
|
+
|
|
30
|
+
- name: Run the nightly eval
|
|
31
|
+
env:
|
|
32
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
33
|
+
run: >-
|
|
34
|
+
uv run python -m evals.nightly --model anthropic:claude-haiku-5-5 --out nightly-results
|
|
35
|
+
${{ inputs.write_baseline && '--write-baseline' || '' }}
|
|
36
|
+
|
|
37
|
+
- uses: actions/upload-artifact@v4
|
|
38
|
+
if: always()
|
|
39
|
+
with:
|
|
40
|
+
name: nightly-results
|
|
41
|
+
path: nightly-results/
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Publishes to PyPI when a version tag such as v0.1.0a1 is pushed.
|
|
4
|
+
# Uses PyPI trusted publishing (OIDC): no API token is stored in this repository.
|
|
5
|
+
# One-time setup on pypi.org: add a (pending) trusted publisher for project "rotguard" with
|
|
6
|
+
# owner "nikhilvdev", repository "rotguard", workflow "release.yml" and environment "pypi".
|
|
7
|
+
|
|
8
|
+
on:
|
|
9
|
+
push:
|
|
10
|
+
tags: ["v*"]
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
build:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v7
|
|
17
|
+
|
|
18
|
+
- uses: astral-sh/setup-uv@v7
|
|
19
|
+
|
|
20
|
+
- name: The tag must match the version in pyproject.toml
|
|
21
|
+
run: |
|
|
22
|
+
version=$(grep -m1 '^version = ' pyproject.toml | cut -d'"' -f2)
|
|
23
|
+
test "v$version" = "$GITHUB_REF_NAME" || {
|
|
24
|
+
echo "tag $GITHUB_REF_NAME does not match version $version"; exit 1; }
|
|
25
|
+
|
|
26
|
+
- run: uv build
|
|
27
|
+
|
|
28
|
+
- run: uvx twine check dist/*
|
|
29
|
+
|
|
30
|
+
- uses: actions/upload-artifact@v4
|
|
31
|
+
with:
|
|
32
|
+
name: dist
|
|
33
|
+
path: dist/
|
|
34
|
+
|
|
35
|
+
publish:
|
|
36
|
+
needs: build
|
|
37
|
+
runs-on: ubuntu-latest
|
|
38
|
+
environment: pypi
|
|
39
|
+
permissions:
|
|
40
|
+
id-token: write # required for trusted publishing
|
|
41
|
+
steps:
|
|
42
|
+
- uses: actions/download-artifact@v4
|
|
43
|
+
with:
|
|
44
|
+
name: dist
|
|
45
|
+
path: dist/
|
|
46
|
+
|
|
47
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
*.lcov
|
|
51
|
+
.hypothesis/
|
|
52
|
+
.pytest_cache/
|
|
53
|
+
cover/
|
|
54
|
+
|
|
55
|
+
# Translations
|
|
56
|
+
*.mo
|
|
57
|
+
*.pot
|
|
58
|
+
|
|
59
|
+
# Django stuff:
|
|
60
|
+
*.log
|
|
61
|
+
local_settings.py
|
|
62
|
+
db.sqlite3
|
|
63
|
+
db.sqlite3-journal
|
|
64
|
+
|
|
65
|
+
# Flask stuff:
|
|
66
|
+
instance/
|
|
67
|
+
.webassets-cache
|
|
68
|
+
|
|
69
|
+
# Scrapy stuff:
|
|
70
|
+
.scrapy
|
|
71
|
+
|
|
72
|
+
# Sphinx documentation
|
|
73
|
+
docs/_build/
|
|
74
|
+
|
|
75
|
+
# PyBuilder
|
|
76
|
+
.pybuilder/
|
|
77
|
+
target/
|
|
78
|
+
|
|
79
|
+
# Jupyter Notebook
|
|
80
|
+
.ipynb_checkpoints
|
|
81
|
+
|
|
82
|
+
# IPython
|
|
83
|
+
profile_default/
|
|
84
|
+
ipython_config.py
|
|
85
|
+
|
|
86
|
+
# pyenv
|
|
87
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
88
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
89
|
+
# .python-version
|
|
90
|
+
|
|
91
|
+
# pipenv
|
|
92
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
93
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
94
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
95
|
+
# install all needed dependencies.
|
|
96
|
+
# Pipfile.lock
|
|
97
|
+
|
|
98
|
+
# uv
|
|
99
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
100
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
101
|
+
# commonly ignored for libraries.
|
|
102
|
+
# uv.lock
|
|
103
|
+
|
|
104
|
+
# poetry
|
|
105
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
106
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
107
|
+
# commonly ignored for libraries.
|
|
108
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
109
|
+
# poetry.lock
|
|
110
|
+
# poetry.toml
|
|
111
|
+
|
|
112
|
+
# pdm
|
|
113
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
114
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
115
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
116
|
+
# pdm.lock
|
|
117
|
+
# pdm.toml
|
|
118
|
+
.pdm-python
|
|
119
|
+
.pdm-build/
|
|
120
|
+
|
|
121
|
+
# pixi
|
|
122
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
123
|
+
# pixi.lock
|
|
124
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
125
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
126
|
+
.pixi/*
|
|
127
|
+
!.pixi/config.toml
|
|
128
|
+
|
|
129
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
130
|
+
__pypackages__/
|
|
131
|
+
|
|
132
|
+
# Celery stuff
|
|
133
|
+
celerybeat-schedule*
|
|
134
|
+
celerybeat.pid
|
|
135
|
+
|
|
136
|
+
# Redis
|
|
137
|
+
*.rdb
|
|
138
|
+
*.aof
|
|
139
|
+
*.pid
|
|
140
|
+
|
|
141
|
+
# RabbitMQ
|
|
142
|
+
mnesia/
|
|
143
|
+
rabbitmq/
|
|
144
|
+
rabbitmq-data/
|
|
145
|
+
|
|
146
|
+
# ActiveMQ
|
|
147
|
+
activemq-data/
|
|
148
|
+
|
|
149
|
+
# SageMath parsed files
|
|
150
|
+
*.sage.py
|
|
151
|
+
|
|
152
|
+
# Environments
|
|
153
|
+
.env
|
|
154
|
+
.envrc
|
|
155
|
+
.venv
|
|
156
|
+
env/
|
|
157
|
+
venv/
|
|
158
|
+
ENV/
|
|
159
|
+
env.bak/
|
|
160
|
+
venv.bak/
|
|
161
|
+
|
|
162
|
+
# Spyder project settings
|
|
163
|
+
.spyderproject
|
|
164
|
+
.spyproject
|
|
165
|
+
|
|
166
|
+
# Rope project settings
|
|
167
|
+
.ropeproject
|
|
168
|
+
|
|
169
|
+
# mkdocs/Zensical documentation
|
|
170
|
+
/site
|
|
171
|
+
|
|
172
|
+
# mypy
|
|
173
|
+
.mypy_cache/
|
|
174
|
+
.dmypy.json
|
|
175
|
+
dmypy.json
|
|
176
|
+
|
|
177
|
+
# Pyre type checker
|
|
178
|
+
.pyre/
|
|
179
|
+
|
|
180
|
+
# pytype static type analyzer
|
|
181
|
+
.pytype/
|
|
182
|
+
|
|
183
|
+
# Cython debug symbols
|
|
184
|
+
cython_debug/
|
|
185
|
+
|
|
186
|
+
# PyCharm
|
|
187
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
188
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
189
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
190
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
191
|
+
# .idea/
|
|
192
|
+
|
|
193
|
+
# Abstra
|
|
194
|
+
# Abstra is an AI-powered process automation framework.
|
|
195
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
196
|
+
# Learn more at https://abstra.io/docs
|
|
197
|
+
.abstra/
|
|
198
|
+
|
|
199
|
+
# Visual Studio Code
|
|
200
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore that
|
|
201
|
+
# can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
202
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer, you
|
|
203
|
+
# could uncomment the following to ignore the entire vscode folder
|
|
204
|
+
# .vscode/
|
|
205
|
+
# Temporary file for partial code execution
|
|
206
|
+
tempCodeRunnerFile.py
|
|
207
|
+
|
|
208
|
+
# Ruff stuff:
|
|
209
|
+
.ruff_cache/
|
|
210
|
+
|
|
211
|
+
# PyPI configuration file
|
|
212
|
+
.pypirc
|
|
213
|
+
|
|
214
|
+
# Marimo
|
|
215
|
+
marimo/_static/
|
|
216
|
+
marimo/_lsp/
|
|
217
|
+
__marimo__/
|
|
218
|
+
|
|
219
|
+
# Streamlit
|
|
220
|
+
.streamlit/secrets.toml
|
|
221
|
+
|
|
222
|
+
# eval outputs
|
|
223
|
+
evals/results/*
|
|
224
|
+
!evals/results/.gitkeep
|
|
225
|
+
rotguard.md
|
|
226
|
+
# Claude Code project notes: kept local, never committed
|
|
227
|
+
CLAUDE.md
|
|
228
|
+
.claude
|
|
229
|
+
graphify-out/
|
|
230
|
+
.agents
|
|
231
|
+
# editor and agent-tool files
|
|
232
|
+
.vscode/
|
|
233
|
+
skills-lock.json
|
|
234
|
+
.agents/
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
3
|
+
rev: v0.6.9
|
|
4
|
+
hooks:
|
|
5
|
+
- id: ruff
|
|
6
|
+
args: [--fix]
|
|
7
|
+
- id: ruff-format
|
|
8
|
+
|
|
9
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
10
|
+
rev: v4.6.0
|
|
11
|
+
hooks:
|
|
12
|
+
- id: trailing-whitespace
|
|
13
|
+
- id: end-of-file-fixer
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
## Unreleased
|
|
6
|
+
|
|
7
|
+
- `refix.md` work, M1 to M4 (code and tests; nothing here has run on a hosted model):
|
|
8
|
+
- **Recall probe redesign (R1 to R4).** The default probe (`style="cued"`) asks one
|
|
9
|
+
fill-in-the-blank question per entry instead of one "list everything" question (`style="list"`
|
|
10
|
+
keeps the old one). `target="auto"` asks about facts from the conversation that the ledger does
|
|
11
|
+
not cover when the ledger is shown to the model on every call, and about the ledger otherwise;
|
|
12
|
+
the probed context now includes the injected ledger note and marker, as the model sees them.
|
|
13
|
+
A session-long `FactPool` keeps conversation facts after their source is compacted away.
|
|
14
|
+
Answers that give a replaced value are `stale` and count as a miss. The Gate 3 harness now
|
|
15
|
+
compounds compaction, runs a no-compaction control arm, reports calibrated `pass_at`, per-fact
|
|
16
|
+
lead and overhead, and compares styles and targets on identical simulations.
|
|
17
|
+
- **Result (qwen2.5 3B, `holdout`, 30 turns, 1 seed, 9 scenarios).** Recall score before any
|
|
18
|
+
compaction went from 0.03 (old probe) to 0.82 (cued), meeting the 0.7 bar, and false alarms in
|
|
19
|
+
control runs whose task passed went from 6 of 6 to 0 of 6. Early warning did **not** follow: the
|
|
20
|
+
alert came at or after the task failure in 7 of 8 failing runs (median lead -1 turn), with one
|
|
21
|
+
10-turn lead. The `history` target scored 0 of 510 turns because the oracle ledger covers every
|
|
22
|
+
planted fact. Gate 3 is still not met. Details in `docs/evaluation.md`.
|
|
23
|
+
- **Policies (F5, F4a).** `OffloadPolicy` stores large tool outputs and leaves a stub with the same
|
|
24
|
+
`tool_call_id`, read back with `recall_output`; `DedupPolicy` collapses repeated outputs in the
|
|
25
|
+
request only; `Session.pin` / `RotguardMiddleware.pin` / `ledger_record(pin=True)` keep a message
|
|
26
|
+
through cleanup and re-attach it if another tool compacted it away; `StagedCleanup` clears old
|
|
27
|
+
tool outputs first and summarizes only if still over budget (stage 2 turns itself off when another
|
|
28
|
+
summarizer is active); `RecallPolicy(action="compact")` runs it once on an alert. A tail that
|
|
29
|
+
must start on a user message is no longer required when a summary leads (a pure tool loop could
|
|
30
|
+
never be summarized before). `TokenCounter` uses a provider's counting API when given one and
|
|
31
|
+
otherwise estimates characters / 4 and says so.
|
|
32
|
+
- **Ledger extractor (F1x).** `LedgerExtractor` proposes entries from each reply; an entry must
|
|
33
|
+
share a distinctive token with the conversation, `confirm` can veto it, and entries carry
|
|
34
|
+
`origin` (`tool`, `manual`, `extractor`).
|
|
35
|
+
- **Architecture (A1 to A3).** Every stored event is also emitted as an OpenTelemetry span event
|
|
36
|
+
when `rotguard[otel]` is installed (text excluded unless `telemetry.configure(include_text=True)`);
|
|
37
|
+
a `Store` protocol with `open_store`; JSON schemas for the report and the stored `audit` and
|
|
38
|
+
`recall` events, validated against the real producers.
|
|
39
|
+
- **Evaluation tooling (E1, E2, E7, E8).** Eval clients count calls and tokens by purpose
|
|
40
|
+
(provider-reported where available) and every entry point has `--estimate`; baselines
|
|
41
|
+
`lc-summarize`, `anthropic-compaction` and `rg-staged`; stale-decision rate per result;
|
|
42
|
+
`assert_history_integrity`; a nightly workflow and `evals/nightly.py`. `anthropic-compaction`
|
|
43
|
+
was written from the SDK's type definitions and tested against a fake client, **not the live
|
|
44
|
+
API**. ContextForge is not wired in.
|
|
45
|
+
- Tests: 158 pass (99 before), ruff and strict mypy clean.
|
|
46
|
+
|
|
47
|
+
- License changed again, to a dual license: GNU AGPL-3.0 (`AGPL-3.0-only`, the full text in
|
|
48
|
+
`LICENSE`) for everyone, plus a commercial license from the author for those who can't or won't
|
|
49
|
+
comply with the AGPL. This replaces the PolyForm Noncommercial license below, which kept company
|
|
50
|
+
teams from even trying the audit in CI. Copies obtained under MIT or PolyForm keep those terms.
|
|
51
|
+
`CONTRIBUTING.md` now says contributions may be offered under both.
|
|
52
|
+
- Housekeeping: `ruff format --check` added to CI; `.vscode/` and `skills-lock.json` are no longer
|
|
53
|
+
tracked; references to the private plan file replaced; package metadata (URLs, keywords,
|
|
54
|
+
classifiers) and a tag-triggered release workflow added in preparation for a first PyPI alpha.
|
|
55
|
+
|
|
56
|
+
- (Superseded by the entry above.) License changed from MIT to the PolyForm Noncommercial License 1.0.0 (`LICENSE`, `pyproject.toml`
|
|
57
|
+
SPDX metadata, README, CONTRIBUTING). Noncommercial use is free; commercial use needs a separate
|
|
58
|
+
license. This is a source-available license, not an OSI open source one. Copies already obtained
|
|
59
|
+
under MIT keep MIT.
|
|
60
|
+
|
|
61
|
+
- Phase 4 (pre-release, in progress): CLI and MCP server.
|
|
62
|
+
- `rotguard` command (`sessions`, `report`, `blame`, `replay`, `mcp`) and `python -m rotguard.cli`.
|
|
63
|
+
- `report`: one self-contained HTML page per session (recall curve with compactions marked,
|
|
64
|
+
compaction and audit tables, ledger timeline); all stored text is escaped.
|
|
65
|
+
- `replay`: rebuilds each turn's context from a saved session and re-runs only rotguard's side
|
|
66
|
+
calls (audit, recall probes) under another model or policy (`--model anthropic|openai|ollama:`,
|
|
67
|
+
`--audit`, `--recall-every`, `--pass-at`, `--inject-ledger`); the agent's own calls are not
|
|
68
|
+
re-run. The source trace is untouched; results go to a new session.
|
|
69
|
+
- `rotguard.adapters.mcp` (`rotguard[mcp]`, needs `mcp>=2`): tools `ledger_record`, `recall`,
|
|
70
|
+
`blame`, `report` over stdio. The agent loads the ledger at start, so entries added here reach
|
|
71
|
+
it on its next run.
|
|
72
|
+
- `SQLiteStore.sessions()`.
|
|
73
|
+
- Fresh-context handoff (F7): `rotguard.handoff.handoff` builds system messages, a ledger note
|
|
74
|
+
and the last `keep` messages, widened so the tail starts on a user message and every tool
|
|
75
|
+
result keeps its call. `RecallPolicy(action="handoff", handoff_keep=)`: when an alert fires,
|
|
76
|
+
`RotguardMiddleware` and `RotguardGraph` replace the history on the next model call (the only
|
|
77
|
+
place rotguard rewrites stored history, and opt-in). It waits while it would not shrink the
|
|
78
|
+
context, and a passing probe cancels it. The Anthropic and OpenAI wrappers do not own the
|
|
79
|
+
history; call `session.take_handoff(context)` and replace it yourself.
|
|
80
|
+
- Tests: 99 pass (78 before Phase 4), ruff and strict mypy clean. The stdio server was checked
|
|
81
|
+
with a raw `initialize` and `tools/list` exchange. The provider callers (`anthropic`, `openai`,
|
|
82
|
+
`ollama`) are not exercised by tests; only their message shaping is.
|
|
83
|
+
- Docs: a MkDocs Material site in `docs/` (home, quickstart, audit, recall and handoff, CLI and
|
|
84
|
+
MCP, evaluation), built with `uv sync --group docs && uv run mkdocs build --strict`. The
|
|
85
|
+
Evaluation page restates the measured results and says Gate 3 is unmet. Install instructions say
|
|
86
|
+
"from source" because the package is not on PyPI.
|
|
87
|
+
- Not done: benchmark report and launch posts. Gate 3 is
|
|
88
|
+
not met (see Phase 3), so the early-warning claim must not be in launch material.
|
|
89
|
+
|
|
90
|
+
- Phase 3 (pre-release): recall checks.
|
|
91
|
+
- `rotguard.probes.recall`: tier 3 recall probes. Every `every` turns a side call over the
|
|
92
|
+
context the agent would use asks the model to restate sampled active ledger entries, graded
|
|
93
|
+
per entry; `RecallMonitor` keeps a rolling score and alerts after `alert_after` failed probes
|
|
94
|
+
in a row, never on one miss. `calibrate()` picks a per-model `pass_at` from clean-context
|
|
95
|
+
probes. Off by default; probes never enter history.
|
|
96
|
+
- `rotguard.probes.signals`: tier 1 passive signals (goal-word overlap, repeated replies, tool
|
|
97
|
+
error rate), no model call. A flag runs a probe now instead of on schedule. Overlap is lexical,
|
|
98
|
+
not embedding based.
|
|
99
|
+
- Tier 2 marker: `RecallPolicy(marker=...)` adds a standing instruction to the injected ledger
|
|
100
|
+
note; `Session.observe_reply` forces a probe when a reply lacks it. LangChain and LangGraph
|
|
101
|
+
only (`RotguardGraph.observe_reply`); the Anthropic and OpenAI wrappers inject nothing.
|
|
102
|
+
- `Session(recall=, on_alert=)`; `recall=` and `on_alert=` on `RotguardMiddleware`,
|
|
103
|
+
`RotguardGraph` and `RotguardAnthropic`. Probes run on the audit's background worker.
|
|
104
|
+
- `rotguard.adapters.openai_agents.RotguardSession`: wraps any Agents SDK session (trimming or
|
|
105
|
+
summarizing), sees compactions at `get_items`, audits and probes through `responses.create`.
|
|
106
|
+
Duck-typed; tested against a fake session, not the real SDK.
|
|
107
|
+
- `evals/gate3.py`: replays a scenario turn by turn and reports the lead of the recall alert
|
|
108
|
+
over the first task failure, misses, false alarms and probe calls per turn.
|
|
109
|
+
- Test status (2026-10-19, branch `feat/phase-3-recall-checks`): 78 tests pass (was 60 before
|
|
110
|
+
Phase 3; the new ones cover recall, signals, the marker, all four adapters and the Gate 3
|
|
111
|
+
summary); ruff, ruff format and strict mypy are clean. All tests use fake models. The Agents
|
|
112
|
+
SDK wrapper is tested against a fake session, not the real SDK. Gate 3 on a real model: see
|
|
113
|
+
the Gate 3 entry below and `docs/evaluation.md`.
|
|
114
|
+
- Gate 3, first run (qwen2.5 3B, `holdout`, 30 turns, 1 seed, 9 scenarios): not met. The recall
|
|
115
|
+
score before any compaction averaged 0.11, so the alert fired at turn 1 in 8 of 9 runs; 3 of 9
|
|
116
|
+
task probes failed before any compaction. The probe is not usable on this model as it stands,
|
|
117
|
+
and the "lead over task failure" is not evidence of early warning. No hosted-model run yet.
|
|
118
|
+
|
|
119
|
+
- Audit hardening after Phase 2 (uncommitted work on top of PR #2):
|
|
120
|
+
- Long extracted answers are graded on their distinctive tokens (identifiers and numbers), not
|
|
121
|
+
as an exact sentence. A cloze fallback (`AuditPolicy.cloze`, default 2) builds facts straight
|
|
122
|
+
from user and agent text when extraction finds fewer than `max_facts`: a sentence with an
|
|
123
|
+
identifier or number is blanked and asked back. It needs no model call and works on models too
|
|
124
|
+
weak to return the extraction JSON.
|
|
125
|
+
- Fix: a quiz question that contained another fact's answer let the model copy it from the
|
|
126
|
+
prompt, so a lost fact read as kept. Such facts are now dropped (`drop_leaky`). Matching is
|
|
127
|
+
whole-token (`45` no longer matches inside `ref4558`). Both have regression tests.
|
|
128
|
+
- `loss` now scores facts that did not come from tool output; `loss_all` keeps the old definition.
|
|
129
|
+
- Evidence (qwen2.5 3B, 30 turns; one run per cell, so treat as indicative): on the tuning suites
|
|
130
|
+
AUC rose from 0.61 to 0.79. A first held-out set then exposed the leakage bug above (AUC 0.55,
|
|
131
|
+
one "kept" verdict followed by a failure). After the fix, a second held-out set of 9 new
|
|
132
|
+
templates, never run before the configuration was fixed, gave AUC 0.90 (18 scored scenarios),
|
|
133
|
+
"kept" 0 failures of 8, "lost" 8 failures of 10. The 1.5B model now runs the audit (24 of 30
|
|
134
|
+
scenarios scored, from 1) but fails 24 of 27 probes, so its AUC (0.63) says little.
|
|
135
|
+
- Not fixed: coverage. The audit never checked the probed fact in 5 of the 3B's 13 failures
|
|
136
|
+
on the second held-out set, and in 11 of the 1.5B's 24. Nine templates, three seeds each: the
|
|
137
|
+
effective sample is nine, so the p-values are optimistic.
|
|
138
|
+
- Coverage: `run_audit` keeps at most one tool-origin fact, so small models no longer spend the
|
|
139
|
+
fact slots on tool listings and the cloze fallback can fill them. qwen2.5 3B, 30 turns, 3 seeds,
|
|
140
|
+
`holdout` + `holdout2` (54 scenarios, one run per cell): probed fact checked in 39 of 54 (was
|
|
141
|
+
33), AUC 0.96 over 35 scored (was 0.90 over 31). These suites informed this change, so they are
|
|
142
|
+
no longer clean held-out sets.
|
|
143
|
+
- `evals`: two held-out suites (`holdout`, `holdout2`, selected by name only), `--cloze`,
|
|
144
|
+
`ROTGUARD_OPENAI_API_KEY` for hosted runs (a placeholder `OPENAI_API_KEY` is rejected).
|
|
145
|
+
|
|
146
|
+
- Phase 2 (pre-release): compaction audit.
|
|
147
|
+
- `rotguard.probes`: the audit. Facts come from the removed span (extracted by the model, kept
|
|
148
|
+
only if they appear in it literally) and from the ledger; the same quiz is asked of the old and
|
|
149
|
+
the new context and graded as text. Report: loss, facts lost, and suggestions. Policy modes
|
|
150
|
+
`off` / `sampled` / `every` / `ci`, with a per-session budget. Opt-in.
|
|
151
|
+
- `rotguard.session.Session`: observation, store writes and audit scheduling shared by all
|
|
152
|
+
adapters (audits run on a background thread, or synchronously in `ci` mode).
|
|
153
|
+
- `RotguardMiddleware(audit=...)`; in-place context-editing clearing is audited once.
|
|
154
|
+
- `rotguard.adapters.anthropic.RotguardAnthropic`: recognises threshold and on-demand
|
|
155
|
+
compaction blocks, audits them, and sends side calls with the request's tools and betas.
|
|
156
|
+
- `rotguard.adapters.langgraph.RotguardGraph`: a node, a ledger tool, and a ledger kept in graph
|
|
157
|
+
state so checkpointers persist it.
|
|
158
|
+
- `rotguard.testing.assert_audit_loss` for CI.
|
|
159
|
+
- `evals/gate2.py`: does the loss score predict later probe failures? Result on local models
|
|
160
|
+
(qwen2.5 3B, qwen2.5-coder 7B, 30 turns, 50 scenarios, naive summarizer): the aggregate `loss`
|
|
161
|
+
does not predict probe failure (AUC 0.61 and 0.44). Two exploratory follow-ups: excluding
|
|
162
|
+
tool-origin facts (about 70% of what gets checked, mostly random ids) gives AUC 0.71 and 0.75
|
|
163
|
+
(p about 0.09 and 0.10); and the audit's verdict on the probed fact itself was "kept" for 13
|
|
164
|
+
scenarios, none of which failed, against 10 failures in 23 "lost" (pooled Fisher p = 0.0045,
|
|
165
|
+
unadjusted). Treat the per-fact verdict and the list of lost facts as the useful output, not
|
|
166
|
+
the single number. The 1.5B model cannot run the audit at all.
|
|
167
|
+
- Fix: the fact-extraction prompt could be hijacked by an instruction inside the transcript
|
|
168
|
+
(a transcript ending in "Summarize in one line" made a 3B model summarize instead of
|
|
169
|
+
extracting). The transcript is now delimited and the task restated after it.
|
|
170
|
+
|
|
171
|
+
- Phase 1 core (pre-release, not published):
|
|
172
|
+
- Neutral message model with provenance (`rotguard.core`), and the observer that diffs
|
|
173
|
+
consecutive contexts and recognises compactions: removed items, rewritten items, summaries.
|
|
174
|
+
- Ledger of goals, decisions, constraints and open questions with supersession
|
|
175
|
+
(`rotguard.ledger`). Entries are never deleted.
|
|
176
|
+
- SQLite store, plus `diff` and `blame` over recorded provenance (`rotguard.report`).
|
|
177
|
+
- `RotguardMiddleware` for LangChain v1 (`rotguard.adapters.langchain`): a `ledger_record`
|
|
178
|
+
tool, per-call ledger injection that never touches stored history, and fail-open hooks.
|
|
179
|
+
- Verified middleware ordering against langchain 1.4.4: list rotguard after
|
|
180
|
+
`SummarizationMiddleware` and `ContextEditingMiddleware` (plan section 7.2).
|
|
181
|
+
- JSON schemas for the message and ledger entry (`schemas/`).
|
|
182
|
+
- Phase 0 evaluation harness (`evals/`): planted-facts, superseded-decision and goal-drift suites,
|
|
183
|
+
`none` / `truncate` / `summarize` strategies, a `+ledger` variant, and `evals.rescore`.
|
|
184
|
+
- Logging through [logquill](https://pypi.org/project/logquill/).
|