From b0f168ab4d6a959d4e28611e345a250c14ca2ea9 Mon Sep 17 00:00:00 2001 From: Muhammad Labeeb Aryan <93521146+Labeeb2339@users.noreply.github.com> Date: Sun, 19 Jul 2026 20:32:45 +0800 Subject: [PATCH 1/4] Implement SFTGuard release candidate --- .github/ISSUE_TEMPLATE/bug_report.yml | 67 + .github/ISSUE_TEMPLATE/config.yml | 8 + .github/ISSUE_TEMPLATE/feature_request.yml | 46 + .github/dependabot.yml | 37 + .github/pull_request_template.md | 24 + .github/release.yml | 24 + .github/workflows/ci.yml | 79 + .github/workflows/codeql.yml | 34 + .github/workflows/dependency-audit.yml | 58 + .github/workflows/pages.yml | 55 + .gitignore | 2 + CHANGELOG.md | 18 + CITATION.cff | 24 + CODE_OF_CONDUCT.md | 50 + CONTRIBUTING.md | 66 + README.md | 210 ++- SECURITY.md | 55 + docs/ARCHITECTURE.md | 105 ++ docs/EVIDENCE.md | 65 + docs/RELATED_WORK.md | 81 + docs/ROADMAP.md | 33 + docs/SHARING.md | 71 + examples/adapter-eval.jsonl | 6 + examples/base-eval.jsonl | 6 + examples/clean-eval.jsonl | 1 + examples/clean-train.jsonl | 2 + examples/regression-contract.json | 17 + pyproject.toml | 58 + schemas/audit-report.schema.json | 48 + schemas/confirmatory-fault-matrix.schema.json | 32 + schemas/confirmatory-manifest.schema.json | 33 + schemas/regression-card.schema.json | 47 + schemas/regression-contract.schema.json | 32 + .../regression-evaluation-record.schema.json | 14 + schemas/sft-record.schema.json | 42 + scripts/confirmatory_evidence.py | 296 ++++ src/sftguard/__init__.py | 8 + src/sftguard/__main__.py | 3 + src/sftguard/artifacts.py | 88 + src/sftguard/audit.py | 396 +++++ src/sftguard/benchmark.py | 124 ++ src/sftguard/cli.py | 146 ++ src/sftguard/confirmatory.py | 231 +++ src/sftguard/demo.py | 77 + src/sftguard/io.py | 153 ++ src/sftguard/models.py | 59 + src/sftguard/privacy.py | 63 + src/sftguard/regression.py | 387 +++++ src/sftguard/synthetic.py | 156 ++ src/sftguard/tokenizer.py | 94 ++ tests/python/test_artifacts_io.py | 70 + tests/python/test_audit.py | 121 ++ tests/python/test_benchmark.py | 53 + tests/python/test_cli.py | 119 ++ tests/python/test_confirmatory_contract.py | 57 + tests/python/test_regression.py | 153 ++ web/README.md | 41 + web/index.html | 17 + web/package-lock.json | 1373 ++++++++++++++++ web/package.json | 26 + web/src/App.test.tsx | 31 + web/src/App.tsx | 715 +++++++++ web/src/data/sample-audit.json | 54 + web/src/data/sample-fault.json | 65 + web/src/data/sample-regression.json | 64 + web/src/main.tsx | 13 + web/src/reportSchema.test.ts | 330 ++++ web/src/reportSchema.ts | 1325 ++++++++++++++++ web/src/sampleReports.ts | 28 + web/src/styles.css | 1411 +++++++++++++++++ web/src/vite-env.d.ts | 1 + web/tsconfig.json | 21 + web/vite.config.ts | 13 + 73 files changed, 9912 insertions(+), 20 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/dependabot.yml create mode 100644 .github/pull_request_template.md create mode 100644 .github/release.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/codeql.yml create mode 100644 .github/workflows/dependency-audit.yml create mode 100644 .github/workflows/pages.yml create mode 100644 CHANGELOG.md create mode 100644 CITATION.cff create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 SECURITY.md create mode 100644 docs/ARCHITECTURE.md create mode 100644 docs/EVIDENCE.md create mode 100644 docs/RELATED_WORK.md create mode 100644 docs/ROADMAP.md create mode 100644 docs/SHARING.md create mode 100644 examples/adapter-eval.jsonl create mode 100644 examples/base-eval.jsonl create mode 100644 examples/clean-eval.jsonl create mode 100644 examples/clean-train.jsonl create mode 100644 examples/regression-contract.json create mode 100644 pyproject.toml create mode 100644 schemas/audit-report.schema.json create mode 100644 schemas/confirmatory-fault-matrix.schema.json create mode 100644 schemas/confirmatory-manifest.schema.json create mode 100644 schemas/regression-card.schema.json create mode 100644 schemas/regression-contract.schema.json create mode 100644 schemas/regression-evaluation-record.schema.json create mode 100644 schemas/sft-record.schema.json create mode 100644 scripts/confirmatory_evidence.py create mode 100644 src/sftguard/__init__.py create mode 100644 src/sftguard/__main__.py create mode 100644 src/sftguard/artifacts.py create mode 100644 src/sftguard/audit.py create mode 100644 src/sftguard/benchmark.py create mode 100644 src/sftguard/cli.py create mode 100644 src/sftguard/confirmatory.py create mode 100644 src/sftguard/demo.py create mode 100644 src/sftguard/io.py create mode 100644 src/sftguard/models.py create mode 100644 src/sftguard/privacy.py create mode 100644 src/sftguard/regression.py create mode 100644 src/sftguard/synthetic.py create mode 100644 src/sftguard/tokenizer.py create mode 100644 tests/python/test_artifacts_io.py create mode 100644 tests/python/test_audit.py create mode 100644 tests/python/test_benchmark.py create mode 100644 tests/python/test_cli.py create mode 100644 tests/python/test_confirmatory_contract.py create mode 100644 tests/python/test_regression.py create mode 100644 web/README.md create mode 100644 web/index.html create mode 100644 web/package-lock.json create mode 100644 web/package.json create mode 100644 web/src/App.test.tsx create mode 100644 web/src/App.tsx create mode 100644 web/src/data/sample-audit.json create mode 100644 web/src/data/sample-fault.json create mode 100644 web/src/data/sample-regression.json create mode 100644 web/src/main.tsx create mode 100644 web/src/reportSchema.test.ts create mode 100644 web/src/reportSchema.ts create mode 100644 web/src/sampleReports.ts create mode 100644 web/src/styles.css create mode 100644 web/src/vite-env.d.ts create mode 100644 web/tsconfig.json create mode 100644 web/vite.config.ts diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..6156725 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,67 @@ +name: Bug report +description: Report a reproducible problem using synthetic or sanitized data. +title: "[Bug]: " +labels: [bug] +body: + - type: markdown + attributes: + value: | + Do not attach private datasets, prompts, responses, credentials, model weights, or unsanitized Regression Cards. Use [private vulnerability reporting](https://github.com/Labeeb2339/sftguard/security/advisories/new) for a security or privacy failure. + - type: input + id: version + attributes: + label: SFTGuard version or commit + placeholder: 0.1.0 or a full Git commit + validations: + required: true + - type: dropdown + id: component + attributes: + label: Component + options: + - Python CLI or library + - Dataset audit + - Tokenizer or loss mask + - Regression Card or gate + - Static report viewer + - CI or packaging + - Documentation + validations: + required: true + - type: input + id: environment + attributes: + label: Sanitized environment + description: Operating system, Python or Node version, and relevant package versions. Remove usernames and private paths. + placeholder: Windows 11, Python 3.12.4 + validations: + required: true + - type: textarea + id: steps + attributes: + label: Reproduction steps + description: Use the smallest synthetic fixture that demonstrates the problem. + validations: + required: true + - type: textarea + id: expected + attributes: + label: Expected behavior + validations: + required: true + - type: textarea + id: actual + attributes: + label: Actual behavior + description: Quote only sanitized output; do not paste a real record or secret candidate. + validations: + required: true + - type: checkboxes + id: privacy + attributes: + label: Privacy check + options: + - label: I used synthetic or sanitized data and removed credentials, raw records, local usernames, and private paths. + required: true + - label: This report is not a vulnerability or private-data exposure that should be submitted privately. + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..4ec9197 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Private security report + url: https://github.com/Labeeb2339/sftguard/security/advisories/new + about: Report vulnerabilities, secret exposure, or private-data leakage without opening a public issue. + - name: Safe sharing guidance + url: https://github.com/Labeeb2339/sftguard/blob/main/docs/SHARING.md + about: Check what can be included safely in a public issue or reproduction. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..142f76e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,46 @@ +name: Feature request +description: Propose a bounded addition to SFTGuard's correctness or regression contract. +title: "[Feature]: " +labels: [enhancement] +body: + - type: markdown + attributes: + value: | + SFTGuard does not train models, predict final fine-tuning performance, certify safety, or automatically publish reports. Explain how the proposal fits the local-first, fail-closed boundary. + - type: textarea + id: problem + attributes: + label: Problem and user evidence + description: Describe the observable pipeline failure or missing release evidence. + validations: + required: true + - type: textarea + id: proposal + attributes: + label: Proposed behavior + description: Include the expected PASS, FAIL, and ABSTAIN behavior. + validations: + required: true + - type: textarea + id: validation + attributes: + label: Validation plan + description: State the synthetic fixture, negative control, and measurable gate. + validations: + required: true + - type: textarea + id: privacy + attributes: + label: Privacy and sharing impact + description: Identify any new input, artifact field, network access, or disclosure risk. + validations: + required: true + - type: checkboxes + id: boundaries + attributes: + label: Boundary check + options: + - label: The proposal does not require posting private data or reports to an external service. + required: true + - label: Missing or untrustworthy evidence will not be converted into PASS. + required: true diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..8cb70df --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,37 @@ +version: 2 +updates: + - package-ecosystem: pip + directory: / + schedule: + interval: weekly + day: monday + time: "03:00" + timezone: Asia/Kuala_Lumpur + open-pull-requests-limit: 5 + labels: [dependencies, python] + + - package-ecosystem: npm + directory: /web + schedule: + interval: weekly + day: monday + time: "03:15" + timezone: Asia/Kuala_Lumpur + open-pull-requests-limit: 5 + labels: [dependencies, web] + groups: + npm-minor-and-patch: + update-types: [minor, patch] + + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + day: monday + time: "03:30" + timezone: Asia/Kuala_Lumpur + open-pull-requests-limit: 5 + labels: [dependencies, github-actions] + groups: + actions-minor-and-patch: + update-types: [minor, patch] diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..a4dc474 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,24 @@ +## Change + +Describe the user-visible behavior and why it belongs inside SFTGuard's bounded +correctness or regression contract. + +## Evidence + +List the deterministic tests, synthetic fixtures, commands, and observed +results. Do not attach private datasets or an unsanitized report. + +## Privacy and security + +Describe new inputs, outputs, paths, dependencies, or network behavior. State +whether any artifact schema or safe-sharing guidance changed. + +## Checklist + +- [ ] Python tests pass on the supported versions, when applicable. +- [ ] The Node 22 viewer checks and production build pass, when applicable. +- [ ] New behavior has a deterministic synthetic test and negative control. +- [ ] `PASS`, `FAIL`, and `ABSTAIN` remain distinct and fail closed. +- [ ] Artifacts and fixtures contain no raw private records, credentials, or model weights. +- [ ] No telemetry, automatic upload, or automatic posting was introduced. +- [ ] Documentation and the frozen evidence boundary remain accurate. diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 0000000..7f88699 --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,24 @@ +changelog: + exclude: + labels: + - skip-changelog + categories: + - title: Security and privacy + labels: + - security + - privacy + - title: Detectors and regression gates + labels: + - detector + - regression + - title: Viewer and reports + labels: + - web + - reporting + - title: Documentation and maintenance + labels: + - documentation + - dependencies + - title: Other changes + labels: + - "*" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..a14f0ea --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,79 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + python: + name: Python ${{ matrix.python-version }} / ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + python-version: ["3.11", "3.12"] + steps: + - uses: actions/checkout@v7 + with: + fetch-depth: 0 + - uses: actions/setup-python@v6 + with: + python-version: ${{ matrix.python-version }} + cache: pip + cache-dependency-path: pyproject.toml + - name: Install package and test tools + run: >- + python -m pip install --upgrade pip + && python -m pip install -e ".[dev]" build + - name: Check installed dependencies + run: python -m pip check + - name: Compile Python sources + run: python -m compileall -q src tests scripts + - name: Lint and format-check Python + run: >- + python -m ruff check src tests/python scripts + && python -m ruff format --check src tests/python scripts + - name: Run Python tests + run: python -m pytest + - name: Verify checked-in evidence integrity + if: ${{ hashFiles('evidence/v0.1.0-confirmatory.json') != '' }} + run: python scripts/confirmatory_evidence.py verify --integrity-only + - name: Replay evidence from the implementation commit + if: ${{ matrix.python-version == '3.11' && hashFiles('evidence/v0.1.0-confirmatory.json') != '' }} + run: python scripts/confirmatory_evidence.py verify + - name: Smoke-test the CLI + run: sftguard --help + - name: Build source and wheel distributions + run: python -m build + + web: + name: Node 22 / ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + defaults: + run: + working-directory: web + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-node@v7 + with: + node-version: "22" + cache: npm + cache-dependency-path: web/package-lock.json + - name: Install locked dependencies + run: npm ci + - name: Type-check, test, and build the viewer + run: npm run check diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..59d2f37 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,34 @@ +name: CodeQL + +on: + push: + branches: [main] + pull_request: + branches: [main] + schedule: + - cron: "37 4 * * 4" + workflow_dispatch: + +permissions: + contents: read + security-events: write + +jobs: + analyze: + name: Analyze ${{ matrix.language }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + language: [python, javascript-typescript] + steps: + - uses: actions/checkout@v7 + - name: Initialize CodeQL + uses: github/codeql-action/init@v4 + with: + languages: ${{ matrix.language }} + build-mode: none + - name: Analyze + uses: github/codeql-action/analyze@v4 + with: + category: /language:${{ matrix.language }} diff --git a/.github/workflows/dependency-audit.yml b/.github/workflows/dependency-audit.yml new file mode 100644 index 0000000..5d23b43 --- /dev/null +++ b/.github/workflows/dependency-audit.yml @@ -0,0 +1,58 @@ +name: Dependency audit + +on: + push: + branches: [main] + paths: + - pyproject.toml + - web/package.json + - web/package-lock.json + - .github/workflows/dependency-audit.yml + pull_request: + paths: + - pyproject.toml + - web/package.json + - web/package-lock.json + - .github/workflows/dependency-audit.yml + schedule: + - cron: "19 3 * * 1" + workflow_dispatch: + +permissions: + contents: read + +jobs: + python: + name: Python dependency audit + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-python@v6 + with: + python-version: "3.12" + cache: pip + cache-dependency-path: pyproject.toml + - name: Install the project auditor + run: >- + python -m pip install --upgrade pip + && python -m pip install pip-audit + - name: Audit declared project dependencies + run: python -m pip_audit --strict . + + web: + name: npm dependency audit + runs-on: ubuntu-latest + defaults: + run: + working-directory: web + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-node@v7 + with: + node-version: "22" + cache: npm + cache-dependency-path: web/package-lock.json + - name: Install locked dependencies + run: npm ci + - name: Audit production and development dependencies + run: npm audit --audit-level=high diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 0000000..12e72ec --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,55 @@ +name: Deploy static report viewer + +on: + push: + branches: [main] + paths: + - web/** + - .github/workflows/pages.yml + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: false + +jobs: + build: + name: Build Pages artifact + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - name: Read Pages configuration + id: pages + uses: actions/configure-pages@v6 + - uses: actions/setup-node@v7 + with: + node-version: "22" + cache: npm + cache-dependency-path: web/package-lock.json + - name: Install locked dependencies + working-directory: web + run: npm ci + - name: Build for the repository Pages base path + working-directory: web + run: npm run build -- --base "${{ steps.pages.outputs.base_path }}/" + - name: Upload static site only + uses: actions/upload-pages-artifact@v5 + with: + path: web/dist + + deploy: + name: Deploy to GitHub Pages + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy Pages artifact + id: deployment + uses: actions/deploy-pages@v5 diff --git a/.gitignore b/.gitignore index 975f2d6..e2949c9 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ __pycache__/ *.py[cod] *.egg-info/ .pytest_cache/ +.pytest-tmp-*/ .ruff_cache/ .coverage htmlcov/ @@ -12,6 +13,7 @@ build/ web/node_modules/ web/dist/ web/.vite/ +*.tsbuildinfo .env .env.* diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..8e8fa2e --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,18 @@ +# Changelog + +All notable changes to SFTGuard are documented here. The project follows +[Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added + +- Preregistered v0.1 product and fault-injection protocols. +- Dependency-light `audit`, `gate`, `benchmark`, and `demo` CLI workflows. +- Bounded JSON/JSONL parsing, redacted data findings, and exported mask checks. +- Paired target/retention Regression Cards with deterministic seed-cluster + bootstrap intervals and a fixed work budget. +- One-shot, fresh-process confirmatory evidence generation and replay tooling. +- Static browser-local report viewer for native SFTGuard artifacts. +- Cross-platform CI, CodeQL, dependency audits, Pages deployment, community + templates, citation metadata, and security documentation. diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 0000000..4006410 --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,24 @@ +cff-version: 1.2.0 +message: >- + If SFTGuard supports your work, cite the software and the exact released + version or commit used. +title: SFTGuard +type: software +authors: + - given-names: Muhammad Labeeb Aryan + name-particle: bin + family-names: Mohd Lokman +repository-code: https://github.com/Labeeb2339/sftguard +url: https://github.com/Labeeb2339/sftguard +license: Apache-2.0 +version: 0.1.0 +abstract: >- + SFTGuard is a local-first correctness and regression gate for supervised + fine-tuning pipelines. It audits bounded pipeline evidence and emits + reproducible PASS, FAIL, or ABSTAIN artifacts without claiming to train, + predict, or certify a model. +keywords: + - supervised fine-tuning + - regression testing + - dataset auditing + - reproducibility diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..23ef9ea --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,50 @@ +# Code of conduct + +## Our pledge + +We pledge to make participation in SFTGuard a harassment-free experience for +everyone, regardless of age, body size, visible or invisible disability, +ethnicity, sex characteristics, gender identity and expression, level of +experience, education, socioeconomic status, nationality, personal appearance, +race, caste, color, religion, or sexual identity and orientation. + +We will act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Expected behavior + +Examples of behavior that supports the community include: + +- demonstrating empathy and kindness; +- respecting differing opinions and experiences; +- giving and accepting constructive feedback; +- taking responsibility, apologizing, and learning from mistakes; +- focusing on what is best for the project and community. + +Unacceptable behavior includes harassment, sexualized language or attention, +trolling or insulting comments, threats, sustained disruption, publishing +someone else's private information, and other conduct that would reasonably be +considered inappropriate in a professional setting. + +## Enforcement + +Maintainers may remove, edit, or reject contributions and communication that do +not follow this code, and may temporarily or permanently restrict participation +for harmful behavior. Enforcement decisions should be proportionate, private +where practical, and focused on community safety. + +Do not place personal details in a public issue. Use private contact information +published by the repository owner when available. If no private project contact +is available, use [GitHub Support's abuse-reporting channel](https://support.github.com/contact/report-abuse) +rather than publishing sensitive allegations. + +## Scope + +This code applies in project spaces and when an individual is officially +representing the project in public spaces. + +## Attribution + +This policy is adapted from the +[Contributor Covenant, version 2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct.html), +available under the [CC BY 4.0 license](https://creativecommons.org/licenses/by/4.0/). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..f6416b2 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,66 @@ +# Contributing to SFTGuard + +SFTGuard welcomes small, reviewable contributions that preserve its +fail-closed and local-first boundaries. Before beginning a large change, open a +feature request describing the intended evidence and failure behavior. + +## Privacy first + +Use synthetic fixtures in issues, pull requests, tests, and screenshots. Never +submit private training data, prompts, responses, access tokens, model weights, +or reports containing them. Redact local usernames and absolute paths. Security +and privacy vulnerabilities belong in a private report under +[SECURITY.md](SECURITY.md), not a public issue. + +SFTGuard does not automatically publish reports or post results to external +services. Contributions must not introduce telemetry, remote report processing, +or automatic uploads without an explicit design review and an opt-in boundary. + +## Development setup + +Python 3.11 or 3.12 and Node.js 22 are supported for development. + +```bash +python -m venv .venv +python -m pip install --upgrade pip +python -m pip install -e ".[dev]" +python -m pytest +``` + +Build and test the static viewer separately: + +```bash +cd web +npm ci +npm run check +``` + +The repository CI is the authority for the complete supported-platform matrix. +If a command changes, update the workflow and this file in the same pull +request. + +## Change requirements + +- Keep input parsing bounded and avoid reading entire untrusted files when a + streaming implementation is possible. +- Never construct shell commands from dataset values. +- Preserve `PASS`, `FAIL`, and `ABSTAIN` as distinct outcomes. Missing evidence + must not become a pass. +- Keep public artifacts free of raw prompt, response, and injected-secret text. +- Add deterministic tests for every detector, gate, parser, and schema change. +- Document new report fields and state whether they are safe to share. +- Do not describe synthetic validation as a real-world safety guarantee. + +The confirmatory seed range in +[docs/FAULT_INJECTION_PROTOCOL.md](docs/FAULT_INJECTION_PROTOCOL.md) is frozen. +Do not tune rules or thresholds after inspecting confirmatory results. A failed +gate should remain visible with `carryForward: false`. + +## Pull requests + +Create a focused branch, run the relevant Python and web checks, and complete +the pull-request checklist. Explain the user-visible behavior, evidence, and +privacy impact. Generated build directories, local artifacts, datasets, and +model files must remain untracked. + +By participating, you agree to follow [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md). diff --git a/README.md b/README.md index 83e4b99..8bfecf2 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,200 @@ # SFTGuard -SFTGuard is a local-first correctness and regression gate for supervised -fine-tuning pipelines. +[![CI](https://github.com/Labeeb2339/sftguard/actions/workflows/ci.yml/badge.svg)](https://github.com/Labeeb2339/sftguard/actions/workflows/ci.yml) +[![CodeQL](https://github.com/Labeeb2339/sftguard/actions/workflows/codeql.yml/badge.svg)](https://github.com/Labeeb2339/sftguard/actions/workflows/codeql.yml) +[![License](https://img.shields.io/github/license/Labeeb2339/sftguard)](LICENSE) -The project is currently under preregistered development. The v0.1 protocol is -public before the confirmatory fault matrix is executed. Implementation, -checked-in evidence, and a browser report will follow on a review branch. +**Fail-closed preflight and regression CI for supervised fine-tuning.** -## Intended v0.1 boundary +SFTGuard catches a narrow set of expensive-to-discover pipeline mistakes before +an adapter is released: malformed chat records, conflicting or leaked examples, +credential-shaped text, broken EOS handling, prompt tokens included in loss, and +answer-destroying truncation. It then gates paired base-versus-adapter scores +against explicit target and retention budgets. -SFTGuard will: +It runs locally, emits content-addressed JSON, and returns `PASS`, `FAIL`, or +`ABSTAIN`. Missing evidence never becomes a silent pass. -- inspect chat records, template output, and supervised-token masks; -- detect deterministic data and pipeline faults such as leakage, duplicated or - conflicting examples, missing EOS handling, and answer-destroying truncation; -- compare base and adapter evaluation records against explicit target and - retention budgets; -- emit reproducible `PASS`, `FAIL`, or `ABSTAIN` artifacts without embedding - dataset text. +## Why this exists -SFTGuard will **not** train a model, prove that a dataset is high quality, -predict final fine-tuning performance, or guarantee that an adapter is safe. +A training job can complete while optimizing the wrong tokens, leaking an +evaluation prompt into training, or degrading a capability that was not the +fine-tuning target. Training loss alone does not answer those questions. +SFTGuard turns the checks that *can* be established deterministically into a +small release contract: -Read the frozen [product specification](docs/PRODUCT_SPEC.md) and -[fault-injection protocol](docs/FAULT_INJECTION_PROTOCOL.md). +```text +local JSONL + exported token/mask evidence ──> audit ──> PASS / FAIL / ABSTAIN +paired base + adapter evaluation records ───> gate ──> Regression Card + └──> CI exit code + viewer +``` -## License +## Try it in two minutes -Apache-2.0. +SFTGuard requires Python 3.11 or newer and has no runtime dependencies. + +```bash +git clone https://github.com/Labeeb2339/sftguard.git +cd sftguard +python -m venv .venv + +# Windows PowerShell +.venv\Scripts\Activate.ps1 + +# macOS / Linux +# source .venv/bin/activate + +python -m pip install -e . +sftguard demo --output artifacts/local/demo.json +``` + +The demo is deterministic and uses synthetic records only. Exit codes are +`0 = PASS`, `1 = FAIL`, and `2 = ABSTAIN`. + +Audit the checked-in safe fixtures: + +```bash +sftguard audit \ + --train examples/clean-train.jsonl \ + --eval examples/clean-eval.jsonl \ + --output artifacts/local/audit.json +``` + +Gate paired base and adapter evaluation evidence: + +```bash +sftguard gate \ + --base examples/base-eval.jsonl \ + --adapter examples/adapter-eval.jsonl \ + --contract examples/regression-contract.json \ + --output artifacts/local/regression-card.json +``` + +## Exact mask evidence, not a heuristic + +The data checks accept ordinary `messages` records. Template, EOS, truncation, +and loss-mask claims require an `_sftguard` annotation exported from the exact +tokenization/collation path used for training: + +```json +{ + "messages": [ + {"role": "user", "content": "Classify this."}, + {"role": "assistant", "content": "class_a"} + ], + "_sftguard": { + "token_ids": [10, 20, 30, 2], + "token_roles": ["user", "user", "assistant", "assistant"], + "loss_mask": [false, false, true, true], + "eos_token_id": 2, + "original_assistant_tokens": 2, + "truncated": false + } +} +``` + +v0.1 intentionally does **not** guess an assistant mask or claim direct +integration with Transformers, TRL, Axolotl, or LLaMA-Factory. If trustworthy +mask evidence is missing or malformed, the full audit returns `ABSTAIN`. The +`MaskInspector` protocol in `src/sftguard/tokenizer.py` is the extension point +for trainer-specific exporters. + +For compatible Hugging Face templates, a future adapter can use +`apply_chat_template(..., tokenize=True, return_dict=True, +return_assistant_tokens_mask=True)` only when the template marks assistant spans +with `{% generation %}`. Until that adapter is implemented and tested, it is not +part of SFTGuard's claims. + +## Regression Cards + +The gate pairs base and adapter measurements by seed, sample ID, group, and +metric. A contract declares: + +- a minimum confidence-bound improvement for each `target` metric; +- a maximum confidence-bound regression for each `retention` metric; and +- the bootstrap draw count, confidence level, and deterministic seed. + +SFTGuard normalizes lower-is-better metrics, resamples seed clusters with a +frozen xorshift32 stream, and applies the threshold to the lower confidence +bound. Unpaired, duplicated, undeclared, non-finite, or incomplete evidence +returns `ABSTAIN`. + +## Static report viewer + +The viewer is a static React application with no analytics, report endpoint, +external font, or server-side storage. An imported report stays in the current +browser tab. + +```bash +cd web +npm ci +npm run dev +``` + +It accepts the native audit, Regression Card, development-benchmark, and frozen +confirmatory schemas. The viewer treats a supplied digest as **declared but +unverified** because it cannot promise byte-for-byte parity with Python's +canonical JSON serializer. Use the Python verifier for an integrity attestation. + +## Preregistered evidence + +The v0.1 fault protocol was committed before confirmatory seeds were generated +or inspected. It freezes nine single-fault templates, seeds `4100–4129`, six +carry-forward gates, and the allowed claim. The implementation and one-shot +evidence runner are committed and pushed before the confirmatory run. + +- [Frozen protocol](docs/FAULT_INJECTION_PROTOCOL.md) +- [Product specification](docs/PRODUCT_SPEC.md) +- [Evidence runner](scripts/confirmatory_evidence.py) +- [Evidence reproduction guide](docs/EVIDENCE.md) +- [Related work and boundaries](docs/RELATED_WORK.md) + +The matrix is an **internal detector regression**: each seed changes synthetic +markers, while each fault class keeps the same structural injection template. +It is not 30 independent real-world datasets, an external benchmark, a safety +certification, or proof that SFTGuard catches unknown failures. + +Checked-in confirmatory evidence is added only after the runner verifies a clean +and pushed implementation commit and reproduces the artifact in two fresh +processes. The result is preserved even if a frozen gate fails. + +## Privacy and security boundary + +- Files are bounded and parsed as untrusted UTF-8 JSON/JSONL. +- Findings contain signal categories and bounded locations, not dataset text. +- Reports contain aggregate values and hashes; hashes are identifiers, **not + anonymization**. +- The repository contains no model weights, adapters, real datasets, prompts, + responses, or credentials. +- Do not publish a real report until you have followed the + [safe-sharing checklist](docs/SHARING.md). + +See [SECURITY.md](SECURITY.md) for private vulnerability reports. + +## What SFTGuard does not prove + +SFTGuard does not train a model, score dataset quality, predict fine-tuning +success, detect every secret, replace a real evaluation harness, or establish +that an adapter is generally safe. A Regression Card is valid only for the +records and thresholds supplied to it. + +## Develop + +```bash +python -m pip install -e ".[dev]" +python -m pytest +python -m ruff check src tests/python scripts +python -m ruff format --check src tests/python scripts + +cd web +npm ci +npm run check +``` + +Contributions that add a detector should include a minimal fault fixture, a +clean control, redaction tests, and a precise claim boundary. Start with +[CONTRIBUTING.md](CONTRIBUTING.md) and the [roadmap](docs/ROADMAP.md). + +## License and citation + +Apache-2.0. Citation metadata is available in [CITATION.cff](CITATION.cff). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..2b6b663 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,55 @@ +# Security policy + +## Supported versions + +Security fixes are applied to the latest released version and the `main` branch. +Pre-release branches may change without a compatibility guarantee. + +## Report a vulnerability privately + +Do not open a public issue for a vulnerability, exposed credential, private +dataset, or personally identifying record. Use GitHub's +[private vulnerability reporting](https://github.com/Labeeb2339/sftguard/security/advisories/new) +for this repository. Include only the minimum synthetic information needed to +reproduce the problem. + +If a credential has already been exposed, revoke or rotate it with its provider +before changing local files. Do not paste the credential into a report, commit, +chat, screenshot, or Regression Card. Rewriting Git history is a separate, +potentially disruptive action and should be planned only after rotation and +current-file redaction. + +Please allow the maintainer a reasonable period to reproduce and address a +report before public disclosure. There is no guaranteed response or remediation +time. Good-faith reports that avoid privacy violations and service disruption +are welcome. + +## Local-data privacy boundary + +SFTGuard is designed for local analysis. The command-line tool must not upload +datasets, prompts, responses, model weights, or generated reports. Its public +static viewer has no report-upload endpoint, analytics, or server-side report +processing; a report selected in the viewer remains in that browser context. + +Generated artifacts are intended to contain aggregates, configuration, +content-addressing information, and bounded findings rather than raw dataset +text. This is a design boundary, not a guarantee that every user-supplied file +is safe to publish. Before sharing any artifact, follow +[docs/SHARING.md](docs/SHARING.md). Treat hashes as stable identifiers that can +still reveal equality or permit guessing when the underlying data is simple. + +When reporting a suspected privacy failure: + +- name the affected command, version, and output field; +- describe the data category, not the sensitive value; +- use synthetic reproduction data whenever possible; +- remove local usernames, absolute paths, access tokens, and model credentials; +- do not attach the original dataset or an unsanitized report. + +## Security scope + +Reports about unsafe parsing, path traversal, command construction, secret or +raw-text leakage, unbounded resource use, cross-site scripting in the viewer, +and dependency compromise are in scope. A `PASS` Regression Card is not a +safety certification and does not show that a model, adapter, or dataset is +secure. diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 0000000..710e72d --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,105 @@ +# Architecture + +This document describes the intended v0.1 boundaries. The frozen behavior is +defined by [PRODUCT_SPEC.md](PRODUCT_SPEC.md); implementation details may evolve +only within that contract. + +## System boundary + +SFTGuard is an evidence gate around a supervised fine-tuning workflow. It does +not train an adapter. The Python package audits local records and paired +evaluation evidence, writes a content-addressed Regression Card, and exits with +a machine-readable status. The static web application renders an explicitly +selected card without sending it to a report-processing service. + +```mermaid +flowchart LR + A["Local JSONL records"] --> B["Bounded parser and schema audit"] + T["Optional compatible tokenizer"] --> C["Template and loss-mask audit"] + B --> C + C --> D["Deterministic findings"] + E["Paired base and adapter evaluations"] --> F["Target and retention gates"] + D --> G["Status reducer"] + F --> G + G --> H["Content-addressed Regression Card"] + H --> I["Static browser viewer"] + H --> J["CI exit code and artifact validation"] +``` + +## Components + +### Bounded input layer + +The CLI accepts local JSONL and evaluation files subject to explicit file, +line, record, and field-size limits. Parsing is line-oriented. Dataset values +are treated as untrusted data and are never interpolated into shell commands. +Malformed, oversized, or incomplete inputs produce a finding or `ABSTAIN` +rather than an implicit pass. + +### Dataset, template, and mask audits + +Deterministic checks cover the preregistered schema, ordering, duplication, +conflict, leakage, EOS, truncation, supervision-mask, and credential-shaped +fault classes. When a tokenizer cannot expose a trustworthy assistant-token +mask, the template-dependent checks abstain. A heuristic mask must not be +reported as verified evidence. + +### Regression gate + +The regression layer consumes supplied, paired base-versus-adapter evaluation +records. It evaluates target improvement and retention budgets defined by the +user's evaluation contract and reports deterministic bootstrap intervals where +configured. It does not infer model quality from training loss and does not +predict the outcome of a future run. + +### Status reducer + +The reducer preserves three terminal states: + +- `PASS`: every required check ran and met its frozen gate; +- `FAIL`: at least one completed required check violated a gate; +- `ABSTAIN`: required evidence was unavailable or untrustworthy. + +`ABSTAIN` has release-blocking semantics. Mixed results retain the individual +finding states so that missing evidence is not hidden behind an aggregate. + +### Artifact layer + +Canonical JSON serialization supports byte-stable hashing and independent +verification. Public artifacts contain configuration, provenance, aggregates, +bounded findings, and content identifiers—not raw prompts, responses, injected +secrets, model weights, or access credentials. Exact protocol and +implementation commits are part of confirmatory evidence. + +### Static report viewer + +The viewer is a build-time static React application. It has no dataset or report +upload endpoint, telemetry, external fonts, or server-side rendering of an +imported Regression Card. File selection is an explicit user action; imported +content remains in the browser. The viewer treats all report strings as data +and must not render supplied HTML. + +## Trust boundaries + +| Boundary | Trusted assumption | Required response when it fails | +| --- | --- | --- | +| Dataset and evaluation files | Files are untrusted and may be malformed or large | Bound work; emit a finding or abstain | +| Exported mask evidence | The producer exported the exact training token and loss mask | Validate structure; abstain when evidence is absent or malformed | +| Regression contract | Metrics and pair identifiers are supplied honestly | Validate shape, pairing, and work budget; do not invent missing pairs | +| Browser artifact reader | A report may be modified or hostile | Allow-list aggregate fields, escape values, and label the declared digest unverified | +| Python evidence verifier | Checked-in evidence and manifest may be modified | Recompute canonical and raw hashes; fail on any mismatch | +| GitHub Pages | Application assets are public | Never bundle private reports or datasets | + +## Determinism and evidence + +The synthetic fault matrix validates SFTGuard's own bounded detectors. Its +confirmatory result is written only after the protocol and implementation are +committed, and a fresh process must reproduce the canonical payload. This +evidence does not establish coverage of unknown real-world faults. + +## Deployment + +Python and web checks run on Linux and Windows. Dependency audits and static +analysis run separately from behavior tests. GitHub Pages deploys only the +contents of `web/dist`; local Regression Cards are not part of that directory +and are not deployed automatically. diff --git a/docs/EVIDENCE.md b/docs/EVIDENCE.md new file mode 100644 index 0000000..7a704e3 --- /dev/null +++ b/docs/EVIDENCE.md @@ -0,0 +1,65 @@ +# Reproducing the v0.1 evidence + +The confirmatory bundle is generated once from the frozen implementation and +then retained whether its gates pass or fail. The runner does not overwrite an +existing result. + +## Phase separation + +1. Commit and publish the [fault protocol](FAULT_INJECTION_PROTOCOL.md). +2. Implement and test only with development seeds `0–9`. +3. Commit and push the complete detector, runner, tests, and frozen thresholds. +4. From that clean pushed commit, execute the confirmatory seeds `4100–4129`. +5. Commit the resulting artifact and manifest without changing a detector or + threshold in response to the result. + +The runner enforces steps 3 and 4 by refusing a dirty tree, an implementation +commit not represented by a local `origin/*` ref, or existing evidence paths. + +## Generate the bundle once + +```bash +python scripts/confirmatory_evidence.py generate +``` + +Two fresh Python processes independently produce the complete JSON bytes. The +runner writes files only if those bytes match and both artifacts pass their +canonical integrity check. + +The bundle contains: + +- `evidence/v0.1.0-confirmatory.json` — aggregate gates, per-fault recall, + per-case identifiers/statuses, provenance, and no synthetic record text; +- `evidence/v0.1.0-confirmatory.manifest.json` — raw file SHA-256, canonical + payload SHA-256, protocol commit, and implementation commit. + +The raw file digest lives in the manifest because a file cannot contain its own +SHA-256 without changing that digest. + +## Verify integrity + +This check needs only the two evidence files: + +```bash +python scripts/confirmatory_evidence.py verify --integrity-only +``` + +## Replay the exact implementation + +With full Git history available, this creates a temporary detached worktree at +the recorded implementation commit and regenerates the bytes: + +```bash +python scripts/confirmatory_evidence.py verify +``` + +Replay requires the same Python major/minor version recorded by the artifact. +CI performs the exact replay on Python 3.11 and integrity checks across the +supported matrix. + +## Interpretation + +A passing artifact supports only the claim frozen in the protocol. The nine +injections are structural templates repeated across deterministic synthetic +markers. The result is internal detector regression evidence, not an external +dataset benchmark or a claim about unknown fine-tuning failures. diff --git a/docs/RELATED_WORK.md b/docs/RELATED_WORK.md new file mode 100644 index 0000000..1dcf302 --- /dev/null +++ b/docs/RELATED_WORK.md @@ -0,0 +1,81 @@ +# Related work and project boundary + +This comparison defines SFTGuard's scope; it is not an exhaustive novelty +search. SFTGuard is a local correctness and regression gate around a fine-tuning +pipeline. It does not replace trainers, parameter-efficient tuning methods, +data-selection research, or general model benchmarks. + +## Training frameworks + +[TRL](https://github.com/huggingface/trl) provides post-training algorithms and +trainers, including supervised fine-tuning. Its official +[SFTTrainer documentation](https://huggingface.co/docs/trl/main/en/sft_trainer) +defines dataset formats, chat-template application, token-level loss, and +assistant/completion masking. SFTGuard inspects evidence about those semantics; +it does not implement or accelerate the training loop. + +[LLaMA-Factory](https://github.com/hiyouga/LLaMA-Factory) and +[Axolotl](https://github.com/axolotl-ai-cloud/axolotl) are configurable +fine-tuning systems spanning model loading, dataset preparation, distributed +training, parameter-efficient methods, and evaluation integrations. SFTGuard is +designed to sit beside such a system: it can audit exported local records and +gate supplied base-versus-adapter results, but it does not launch their jobs or +claim configuration parity with them. + +## Parameter-efficient fine-tuning + +[PEFT](https://github.com/huggingface/peft) implements LoRA and other +parameter-efficient methods. Its +[LoRA API](https://huggingface.co/docs/peft/main/en/package_reference/lora) +supports target modules, per-module rank and alpha patterns, multiple +initialization methods, quantization-aware workflows, and automatic target +selection. SFTGuard does not propose a new adapter, choose an optimal rank, or +allocate modules. It checks pipeline correctness and evaluates an explicit +release contract for an adapter that another tool produced. + +## Prediction and experiment screening + +[TuneAhead](https://arxiv.org/abs/2606.17660) predicts full fine-tuning +performance using static dataset descriptors and features from a short probe +run. SFTGuard deliberately makes a different claim: it does not estimate a +future score. It verifies bounded, observable properties and returns `ABSTAIN` +when the evidence required for a gate is missing. + +## Data selection and processing + +[LESS](https://github.com/princeton-nlp/LESS) selects influential examples for +targeted instruction tuning using optimizer-aware low-rank gradient features. +[DataInf](https://github.com/ykwon0407/DataInf) estimates data influence for +LoRA-tuned language and diffusion models. These methods rank training examples +by estimated utility. SFTGuard does not rank examples or assert that a dataset +is high quality; its data checks target deterministic conditions such as schema +errors, exact duplication, conflicting answers, split leakage, and bounded +privacy signals. + +[Data-Juicer](https://github.com/datajuicer/data-juicer) is a broad data +processing system with filtering, analysis, transformation, and distributed +execution operators for foundation-model data. SFTGuard is narrower: it uses a +small frozen audit contract and emits a release decision artifact. It is not a +general cleaning or data-transformation pipeline. + +## Evaluation and forgetting + +[lm-evaluation-harness](https://github.com/EleutherAI/lm-evaluation-harness) +runs a large catalog of language-model evaluations. +[TRACE](https://github.com/BeyonderXX/TRACE), introduced in the +[TRACE paper](https://arxiv.org/abs/2310.06762), benchmarks continual learning +across multiple tasks and measures degradation in general and +instruction-following abilities. SFTGuard does not reproduce those benchmark +suites. It consumes paired evaluation records chosen by the user and applies +explicit target and retention budgets; the resulting Regression Card is valid +only for that supplied contract. + +## Practical distinction + +SFTGuard's intended contribution is the integration of four conservative +software checks: exact training-data semantics where they can be verified, +deterministic fault findings, paired regression budgets, and shareable +content-addressed provenance. Existing projects above can provide the trainer, +adapter, data pipeline, selector, or benchmark. SFTGuard records whether a +specific proposed release met its declared evidence gates, without converting +missing evidence into success or claiming that the release is safe in general. diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md new file mode 100644 index 0000000..b98490e --- /dev/null +++ b/docs/ROADMAP.md @@ -0,0 +1,33 @@ +# Roadmap + +SFTGuard grows through evidence, not feature count. + +## v0.1 — correctness and regression gate + +- Local JSONL audit with bounded parsing and redacted findings. +- Exact inspection of trusted, exported token-role and loss-mask evidence. +- Base-versus-adapter Regression Cards. +- Deterministic synthetic fault matrix. +- Static report viewer and GitHub Action integration. + +## v0.2 — real training integration + +- TRL callback for checkpoint-level target and retention evaluation. +- Tested Transformers chat-template mask exporter for compatible templates. +- LLaMA-Factory and Axolotl result importers. +- One small, redistributable real-model benchmark with pinned revisions. + +## Research track + +- Fault-injection benchmark across multiple trainers and model families. +- False-alarm and fault-detection calibration on independently authored cases. +- Retention-contract design for structured output, multilingual behavior, and + safety probes. +- Post-merge and post-quantization Regression Cards. + +## Deliberately out of scope + +- Hosted dataset upload or model training. +- A new LoRA rank allocator or optimizer. +- Predicted final performance without full evaluation. +- Automatic claims that an adapter is safe or production-ready. diff --git a/docs/SHARING.md b/docs/SHARING.md new file mode 100644 index 0000000..9c9aaa3 --- /dev/null +++ b/docs/SHARING.md @@ -0,0 +1,71 @@ +# Sharing SFTGuard results safely + +SFTGuard is local-first. It does not automatically post, upload, or publish a +dataset, Regression Card, screenshot, or result. Sharing is a separate decision +made by the person who controls the underlying data. + +## Usually appropriate to share + +After inspection, these fields are generally intended for reproducibility: + +- SFTGuard version and implementation commit; +- protocol version, configuration, and frozen thresholds; +- aggregate finding counts and `PASS`, `FAIL`, or `ABSTAIN` status; +- synthetic benchmark seeds and synthetic evidence hashes; +- Python, Node.js, operating-system, and dependency versions; +- evaluation metric names, pair counts, and aggregate intervals when the + evaluation contract permits disclosure. + +Hashes are identifiers, not anonymization. A dataset or row hash can reveal +that two parties used the same content and may be guessable for short or common +records. Do not publish a hash when even dataset identity or equality is +sensitive. + +## Do not share + +- raw prompts, responses, system messages, labels, or private evaluation data; +- credentials, tokens, cookies, private keys, connection strings, or `.env` + files; +- names, contact details, health, school, financial, or other identifying data; +- absolute local paths that reveal usernames or private project names; +- proprietary model weights, adapter files, or licensed data without + permission; +- an unsanitized report, terminal log, screenshot, issue attachment, or browser + export. + +Finding locations should use bounded record indexes and field categories, not +the sensitive value itself. A secret-pattern finding should say where and what +category was detected without copying the candidate secret. + +## Pre-publication checklist + +1. Work from a copy of the intended artifact; keep the original private. +2. Validate the card schema and canonical hash. +3. Search the copy for raw prompt/response fields, credentials, local paths, + emails, phone numbers, and other identifiers. +4. Confirm that no data sample was embedded in metadata, error text, or a + screenshot. +5. Check the dataset and model licenses and any competition or institutional + disclosure rules. +6. State the exact evidence boundary. A synthetic pass is not a safety + certification or a claim about unknown faults. +7. When uncertain, share only the aggregate conclusion and reproduction code, + or do not publish the artifact. + +## Browser viewer and GitHub Pages + +The public Pages site contains only static application assets from `web/dist`. +Selecting a local report reads it into the current browser; the project has no +report upload endpoint or analytics. Browser extensions, modified builds, and +the surrounding device are outside SFTGuard's control, so sensitive reports +should still be opened only in a trusted local environment. + +Never commit a real Regression Card merely to view it on Pages. Use synthetic +fixtures for documentation, tests, issues, and demonstrations. + +## Security reports and support requests + +Use synthetic reproduction data in public issues. For suspected leakage or +another vulnerability, follow [SECURITY.md](../SECURITY.md) and report it +privately. Rotate an exposed credential before discussing code remediation, and +never include the credential value in the report. diff --git a/examples/adapter-eval.jsonl b/examples/adapter-eval.jsonl new file mode 100644 index 0000000..52c7752 --- /dev/null +++ b/examples/adapter-eval.jsonl @@ -0,0 +1,6 @@ +{"seed":0,"sample_id":"target-0","group":"target","metric":"task_score","value":0.62} +{"seed":0,"sample_id":"retention-0","group":"retention","metric":"general_score","value":0.795} +{"seed":1,"sample_id":"target-1","group":"target","metric":"task_score","value":0.63} +{"seed":1,"sample_id":"retention-1","group":"retention","metric":"general_score","value":0.785} +{"seed":2,"sample_id":"target-2","group":"target","metric":"task_score","value":0.63} +{"seed":2,"sample_id":"retention-2","group":"retention","metric":"general_score","value":0.805} diff --git a/examples/base-eval.jsonl b/examples/base-eval.jsonl new file mode 100644 index 0000000..2aebafb --- /dev/null +++ b/examples/base-eval.jsonl @@ -0,0 +1,6 @@ +{"seed":0,"sample_id":"target-0","group":"target","metric":"task_score","value":0.50} +{"seed":0,"sample_id":"retention-0","group":"retention","metric":"general_score","value":0.80} +{"seed":1,"sample_id":"target-1","group":"target","metric":"task_score","value":0.52} +{"seed":1,"sample_id":"retention-1","group":"retention","metric":"general_score","value":0.79} +{"seed":2,"sample_id":"target-2","group":"target","metric":"task_score","value":0.51} +{"seed":2,"sample_id":"retention-2","group":"retention","metric":"general_score","value":0.81} diff --git a/examples/clean-eval.jsonl b/examples/clean-eval.jsonl new file mode 100644 index 0000000..8f281c6 --- /dev/null +++ b/examples/clean-eval.jsonl @@ -0,0 +1 @@ +{"messages":[{"role":"system","content":"Return one class label."},{"role":"user","content":"Classify the held-out development fixture."},{"role":"assistant","content":"class_a"}],"_sftguard":{"token_ids":[12,22,32,2],"token_roles":["system","user","assistant","assistant"],"loss_mask":[false,false,true,true],"eos_token_id":2,"original_assistant_tokens":2,"truncated":false}} diff --git a/examples/clean-train.jsonl b/examples/clean-train.jsonl new file mode 100644 index 0000000..fa7fdd1 --- /dev/null +++ b/examples/clean-train.jsonl @@ -0,0 +1,2 @@ +{"messages":[{"role":"system","content":"Return one class label."},{"role":"user","content":"Classify the first development fixture."},{"role":"assistant","content":"class_a"}],"_sftguard":{"token_ids":[10,20,30,2],"token_roles":["system","user","assistant","assistant"],"loss_mask":[false,false,true,true],"eos_token_id":2,"original_assistant_tokens":2,"truncated":false}} +{"messages":[{"role":"system","content":"Return one class label."},{"role":"user","content":"Classify the second development fixture."},{"role":"assistant","content":"class_b"}],"_sftguard":{"token_ids":[11,21,31,2],"token_roles":["system","user","assistant","assistant"],"loss_mask":[false,false,true,true],"eos_token_id":2,"original_assistant_tokens":2,"truncated":false}} diff --git a/examples/regression-contract.json b/examples/regression-contract.json new file mode 100644 index 0000000..7c85d0c --- /dev/null +++ b/examples/regression-contract.json @@ -0,0 +1,17 @@ +{ + "bootstrap": {"draws": 10000, "confidence": 0.95, "seed": 2339}, + "metrics": [ + { + "group": "target", + "metric": "task_score", + "higher_is_better": true, + "min_improvement": 0.05 + }, + { + "group": "retention", + "metric": "general_score", + "higher_is_better": true, + "max_regression": 0.02 + } + ] +} diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..8462fd3 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,58 @@ +[build-system] +requires = ["setuptools>=77"] +build-backend = "setuptools.build_meta" + +[project] +name = "sftguard" +version = "0.1.0" +description = "Local-first correctness and regression gates for supervised fine-tuning pipelines" +readme = "README.md" +requires-python = ">=3.11" +license = "Apache-2.0" +license-files = ["LICENSE"] +authors = [{ name = "Muhammad Labeeb Aryan" }] +keywords = ["fine-tuning", "llm", "regression-testing", "sft"] +classifiers = [ + "Development Status :: 3 - Alpha", + "Environment :: Console", + "License :: OSI Approved :: Apache Software License", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Scientific/Engineering :: Artificial Intelligence", +] +dependencies = [] + +[project.urls] +Homepage = "https://github.com/Labeeb2339/sftguard" +Documentation = "https://github.com/Labeeb2339/sftguard#readme" +Issues = "https://github.com/Labeeb2339/sftguard/issues" +Source = "https://github.com/Labeeb2339/sftguard" + +[project.optional-dependencies] +dev = [ + "build>=1.2,<2", + "pytest>=8,<9", + "ruff>=0.9,<1", +] + +[project.scripts] +sftguard = "sftguard.cli:main" + +[tool.setuptools] +package-dir = { "" = "src" } + +[tool.setuptools.packages.find] +where = ["src"] + +[tool.pytest.ini_options] +testpaths = ["tests/python"] +addopts = "-q --basetemp=.pytest_cache/basetemp" + +[tool.ruff] +target-version = "py311" +line-length = 100 + +[tool.ruff.lint] +select = ["E", "F", "I", "UP", "B"] diff --git a/schemas/audit-report.schema.json b/schemas/audit-report.schema.json new file mode 100644 index 0000000..f25a22c --- /dev/null +++ b/schemas/audit-report.schema.json @@ -0,0 +1,48 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "urn:sftguard:schema:audit-report:v1", + "title": "SFTGuard audit report", + "type": "object", + "required": [ + "schema_version", + "tool", + "status", + "inputs", + "checks", + "summary", + "findings", + "integrity" + ], + "properties": { + "schema_version": {"const": "sftguard.audit.v1"}, + "status": {"enum": ["PASS", "FAIL", "ABSTAIN"]}, + "inputs": {"type": "array"}, + "checks": {"type": "object"}, + "summary": {"type": "object"}, + "findings": { + "type": "array", + "items": { + "type": "object", + "required": ["signal", "severity", "location"], + "properties": { + "signal": {"type": "string"}, + "severity": {"enum": ["info", "warning", "error", "critical"]}, + "location": { + "type": "object", + "required": ["split", "line"], + "properties": { + "split": {"enum": ["train", "evaluation"]}, + "line": {"type": "integer", "minimum": 1}, + "field": {"type": "string"} + } + }, + "redacted": {"type": "boolean"} + } + } + }, + "integrity": { + "type": "object", + "required": ["algorithm", "canonical_payload_sha256"] + } + } +} diff --git a/schemas/confirmatory-fault-matrix.schema.json b/schemas/confirmatory-fault-matrix.schema.json new file mode 100644 index 0000000..9facef1 --- /dev/null +++ b/schemas/confirmatory-fault-matrix.schema.json @@ -0,0 +1,32 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "urn:sftguard:schema:confirmatory-fault-matrix:v1", + "title": "SFTGuard confirmatory fault-matrix evidence", + "type": "object", + "required": [ + "schema_version", + "tool", + "provenance", + "protocol", + "summary", + "per_fault", + "cases", + "gates", + "carry_forward", + "status", + "integrity" + ], + "properties": { + "schema_version": {"const": "sftguard.confirmatory-fault-matrix.v1"}, + "status": {"enum": ["PASS", "FAIL"]}, + "carry_forward": {"type": "boolean"}, + "tool": {"type": "object"}, + "provenance": {"type": "object"}, + "protocol": {"type": "object"}, + "summary": {"type": "object"}, + "per_fault": {"type": "array", "minItems": 9, "maxItems": 9}, + "cases": {"type": "array", "minItems": 300, "maxItems": 300}, + "gates": {"type": "object"}, + "integrity": {"type": "object"} + } +} diff --git a/schemas/confirmatory-manifest.schema.json b/schemas/confirmatory-manifest.schema.json new file mode 100644 index 0000000..6f04eaf --- /dev/null +++ b/schemas/confirmatory-manifest.schema.json @@ -0,0 +1,33 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "urn:sftguard:schema:confirmatory-manifest:v1", + "title": "SFTGuard confirmatory evidence manifest", + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "artifact_file_sha256", + "artifact_integrity_sha256", + "artifact_path", + "implementation_git_commit", + "protocol_git_commit", + "integrity" + ], + "properties": { + "schema_version": {"const": "sftguard.confirmatory-manifest.v1"}, + "artifact_file_sha256": {"type": "string", "pattern": "^[0-9a-f]{64}$"}, + "artifact_integrity_sha256": {"type": "string", "pattern": "^[0-9a-f]{64}$"}, + "artifact_path": {"const": "evidence/v0.1.0-confirmatory.json"}, + "implementation_git_commit": {"type": "string", "pattern": "^[0-9a-f]{40}$"}, + "protocol_git_commit": {"type": "string", "pattern": "^[0-9a-f]{40}$"}, + "integrity": { + "type": "object", + "additionalProperties": false, + "required": ["algorithm", "canonical_payload_sha256"], + "properties": { + "algorithm": {"const": "sha256"}, + "canonical_payload_sha256": {"type": "string", "pattern": "^[0-9a-f]{64}$"} + } + } + } +} diff --git a/schemas/regression-card.schema.json b/schemas/regression-card.schema.json new file mode 100644 index 0000000..87239d9 --- /dev/null +++ b/schemas/regression-card.schema.json @@ -0,0 +1,47 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "urn:sftguard:schema:regression-card:v1", + "title": "SFTGuard Regression Card", + "type": "object", + "required": [ + "schema_version", + "tool", + "status", + "inputs", + "metrics", + "summary", + "integrity" + ], + "properties": { + "schema_version": {"const": "sftguard.regression-card.v1"}, + "status": {"enum": ["PASS", "FAIL", "ABSTAIN"]}, + "inputs": {"type": "array"}, + "bootstrap": {"type": "object"}, + "metrics": { + "type": "array", + "items": { + "type": "object", + "required": [ + "group", + "metric", + "status", + "pair_count", + "mean_normalized_improvement", + "confidence_interval", + "threshold", + "decision_rule" + ], + "properties": { + "group": {"enum": ["target", "retention"]}, + "metric": {"type": "string"}, + "status": {"enum": ["PASS", "FAIL"]} + } + } + }, + "summary": {"type": "object"}, + "integrity": { + "type": "object", + "required": ["algorithm", "canonical_payload_sha256"] + } + } +} diff --git a/schemas/regression-contract.schema.json b/schemas/regression-contract.schema.json new file mode 100644 index 0000000..1fb19c3 --- /dev/null +++ b/schemas/regression-contract.schema.json @@ -0,0 +1,32 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "urn:sftguard:schema:regression-contract:v1", + "title": "SFTGuard Regression Card contract", + "type": "object", + "required": ["metrics"], + "properties": { + "bootstrap": { + "type": "object", + "properties": { + "draws": {"type": "integer", "minimum": 1, "maximum": 100000}, + "confidence": {"type": "number", "exclusiveMinimum": 0, "exclusiveMaximum": 1}, + "seed": {"type": "integer"} + } + }, + "metrics": { + "type": "array", + "minItems": 2, + "items": { + "type": "object", + "required": ["group", "metric", "higher_is_better"], + "properties": { + "group": {"enum": ["target", "retention"]}, + "metric": {"type": "string", "pattern": "^[A-Za-z0-9_.-]{1,64}$"}, + "higher_is_better": {"type": "boolean"}, + "min_improvement": {"type": "number"}, + "max_regression": {"type": "number", "minimum": 0} + } + } + } + } +} diff --git a/schemas/regression-evaluation-record.schema.json b/schemas/regression-evaluation-record.schema.json new file mode 100644 index 0000000..f466a9c --- /dev/null +++ b/schemas/regression-evaluation-record.schema.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "urn:sftguard:schema:regression-evaluation-record:v1", + "title": "SFTGuard paired evaluation record", + "type": "object", + "required": ["seed", "sample_id", "group", "metric", "value"], + "properties": { + "seed": {"type": "integer"}, + "sample_id": {"type": "string", "minLength": 1, "maxLength": 512}, + "group": {"type": "string", "pattern": "^[A-Za-z0-9_.-]{1,64}$"}, + "metric": {"type": "string", "pattern": "^[A-Za-z0-9_.-]{1,64}$"}, + "value": {"type": "number"} + } +} diff --git a/schemas/sft-record.schema.json b/schemas/sft-record.schema.json new file mode 100644 index 0000000..a0eb932 --- /dev/null +++ b/schemas/sft-record.schema.json @@ -0,0 +1,42 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "urn:sftguard:schema:sft-record:v1", + "title": "SFTGuard chat record", + "type": "object", + "required": ["messages"], + "properties": { + "messages": { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "required": ["role", "content"], + "properties": { + "role": {"enum": ["system", "user", "assistant", "tool"]}, + "content": {"type": "string"} + } + } + }, + "_sftguard": { + "type": "object", + "required": [ + "token_ids", + "token_roles", + "loss_mask", + "eos_token_id", + "original_assistant_tokens" + ], + "properties": { + "token_ids": {"type": "array", "items": {"type": "integer"}}, + "token_roles": { + "type": "array", + "items": {"enum": ["system", "user", "assistant", "tool"]} + }, + "loss_mask": {"type": "array", "items": {"type": "boolean"}}, + "eos_token_id": {"type": "integer"}, + "original_assistant_tokens": {"type": "integer", "minimum": 0}, + "truncated": {"type": "boolean", "default": false} + } + } + } +} diff --git a/scripts/confirmatory_evidence.py b/scripts/confirmatory_evidence.py new file mode 100644 index 0000000..daf5242 --- /dev/null +++ b/scripts/confirmatory_evidence.py @@ -0,0 +1,296 @@ +"""Generate once or independently verify the frozen SFTGuard v0.1 evidence.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import os +import subprocess +import sys +import tempfile +from pathlib import Path +from typing import Any + +PROTOCOL_COMMIT = "d4c74cefca6729124142635dc8893b5e3ddec2d3" +REPOSITORY_ROOT = Path(__file__).resolve().parents[1] +DEFAULT_ARTIFACT = Path("evidence/v0.1.0-confirmatory.json") +DEFAULT_MANIFEST = Path("evidence/v0.1.0-confirmatory.manifest.json") +MAX_EVIDENCE_BYTES = 16 * 1024 * 1024 + + +class EvidenceError(RuntimeError): + """Raised when an evidence safety or verification condition fails.""" + + +def _git(*arguments: str, cwd: Path = REPOSITORY_ROOT) -> str: + completed = subprocess.run( + ["git", *arguments], + cwd=cwd, + check=False, + capture_output=True, + text=True, + encoding="utf-8", + ) + if completed.returncode != 0: + detail = completed.stderr.strip() or completed.stdout.strip() or "git command failed" + raise EvidenceError(detail) + return completed.stdout.strip() + + +def _sha256(value: bytes) -> str: + return hashlib.sha256(value).hexdigest() + + +def _canonical_json_bytes(value: Any) -> bytes: + return json.dumps( + value, + ensure_ascii=False, + allow_nan=False, + separators=(",", ":"), + sort_keys=True, + ).encode("utf-8") + + +def _seal(payload: dict[str, Any]) -> dict[str, Any]: + sealed = dict(payload) + sealed["integrity"] = { + "algorithm": "sha256", + "canonical_payload_sha256": _sha256(_canonical_json_bytes(sealed)), + } + return sealed + + +def _pretty_json_bytes(value: Any) -> bytes: + return ( + json.dumps( + value, + ensure_ascii=False, + allow_nan=False, + indent=2, + sort_keys=True, + ) + + "\n" + ).encode("utf-8") + + +def _verify_integrity(value: dict[str, Any]) -> bool: + integrity = value.get("integrity") + if not isinstance(integrity, dict): + return False + expected = integrity.get("canonical_payload_sha256") + payload = dict(value) + payload.pop("integrity", None) + return isinstance(expected, str) and expected == _sha256(_canonical_json_bytes(payload)) + + +def _reject_duplicate_keys(pairs: list[tuple[str, Any]]) -> dict[str, Any]: + result: dict[str, Any] = {} + for key, value in pairs: + if key in result: + raise EvidenceError("JSON object contains a duplicate key") + result[key] = value + return result + + +def _parse_object(raw: bytes, label: str) -> dict[str, Any]: + if len(raw) > MAX_EVIDENCE_BYTES: + raise EvidenceError(f"{label} exceeds the evidence byte limit") + try: + value = json.loads(raw.decode("utf-8"), object_pairs_hook=_reject_duplicate_keys) + except (UnicodeDecodeError, json.JSONDecodeError) as exc: + raise EvidenceError(f"{label} is not valid UTF-8 JSON") from exc + if not isinstance(value, dict): + raise EvidenceError(f"{label} must contain a JSON object") + return value + + +def _worker_bytes(source_root: Path, implementation_commit: str) -> bytes: + environment = os.environ.copy() + existing_path = environment.get("PYTHONPATH") + source_path = str(source_root / "src") + environment["PYTHONPATH"] = ( + source_path if not existing_path else os.pathsep.join((source_path, existing_path)) + ) + environment["PYTHONHASHSEED"] = "0" + completed = subprocess.run( + [ + sys.executable, + "-m", + "sftguard.confirmatory", + "--protocol-commit", + PROTOCOL_COMMIT, + "--implementation-commit", + implementation_commit, + ], + cwd=source_root, + env=environment, + check=False, + capture_output=True, + ) + if completed.returncode not in {0, 1}: + detail = completed.stderr.decode("utf-8", errors="replace").strip() + raise EvidenceError(detail or "confirmatory worker failed") + artifact = _parse_object(completed.stdout, "worker output") + if not _verify_integrity(artifact): + raise EvidenceError("worker produced an invalid artifact integrity hash") + return completed.stdout + + +def _resolve_output(path: Path) -> Path: + resolved = (REPOSITORY_ROOT / path).resolve() if not path.is_absolute() else path.resolve() + try: + resolved.relative_to(REPOSITORY_ROOT) + except ValueError as exc: + raise EvidenceError("evidence outputs must remain inside the repository") from exc + return resolved + + +def _write_exclusive(path: Path, raw: bytes) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + try: + with path.open("xb") as stream: + stream.write(raw) + stream.flush() + os.fsync(stream.fileno()) + except FileExistsError as exc: + raise EvidenceError(f"refusing to overwrite existing evidence: {path}") from exc + + +def generate(artifact_path: Path, manifest_path: Path) -> None: + artifact = _resolve_output(artifact_path) + manifest = _resolve_output(manifest_path) + if artifact.exists() or manifest.exists(): + raise EvidenceError("refusing to overwrite an existing evidence bundle") + if _git("status", "--porcelain", "--untracked-files=all"): + raise EvidenceError("generation requires a completely clean Git worktree") + + implementation_commit = _git("rev-parse", "HEAD") + _git("merge-base", "--is-ancestor", PROTOCOL_COMMIT, implementation_commit) + remote_branches = _git("branch", "-r", "--contains", implementation_commit) + if not any(line.strip().startswith("origin/") for line in remote_branches.splitlines()): + raise EvidenceError("implementation commit must be pushed to origin before generation") + + first = _worker_bytes(REPOSITORY_ROOT, implementation_commit) + second = _worker_bytes(REPOSITORY_ROOT, implementation_commit) + if first != second: + raise EvidenceError("fresh worker processes did not reproduce byte-identical evidence") + + evidence = _parse_object(first, "confirmatory artifact") + manifest_value = _seal( + { + "schema_version": "sftguard.confirmatory-manifest.v1", + "artifact_file_sha256": _sha256(first), + "artifact_integrity_sha256": evidence["integrity"]["canonical_payload_sha256"], + "artifact_path": artifact.relative_to(REPOSITORY_ROOT).as_posix(), + "implementation_git_commit": implementation_commit, + "protocol_git_commit": PROTOCOL_COMMIT, + } + ) + _write_exclusive(artifact, first) + try: + _write_exclusive(manifest, _pretty_json_bytes(manifest_value)) + except BaseException: + artifact.unlink(missing_ok=True) + raise + print(f"wrote {artifact.relative_to(REPOSITORY_ROOT)}") + print(f"artifact sha256: {_sha256(first)}") + print(f"carry forward: {str(evidence.get('carry_forward')).lower()}") + + +def _verify_bundle(artifact_path: Path, manifest_path: Path) -> tuple[bytes, dict[str, Any]]: + artifact = _resolve_output(artifact_path) + manifest = _resolve_output(manifest_path) + try: + if artifact.stat().st_size > MAX_EVIDENCE_BYTES: + raise EvidenceError("artifact exceeds the evidence byte limit") + if manifest.stat().st_size > MAX_EVIDENCE_BYTES: + raise EvidenceError("manifest exceeds the evidence byte limit") + artifact_raw = artifact.read_bytes() + manifest_raw = manifest.read_bytes() + except OSError as exc: + raise EvidenceError("evidence bundle cannot be read") from exc + artifact_value = _parse_object(artifact_raw, "artifact") + manifest_value = _parse_object(manifest_raw, "manifest") + if not _verify_integrity(artifact_value) or not _verify_integrity(manifest_value): + raise EvidenceError("artifact or manifest integrity verification failed") + if manifest_value.get("artifact_file_sha256") != _sha256(artifact_raw): + raise EvidenceError("raw artifact SHA-256 does not match the manifest") + if ( + manifest_value.get("artifact_integrity_sha256") + != artifact_value["integrity"]["canonical_payload_sha256"] + ): + raise EvidenceError("canonical artifact SHA-256 does not match the manifest") + if manifest_value.get("protocol_git_commit") != PROTOCOL_COMMIT: + raise EvidenceError("manifest protocol commit does not match the frozen runner") + if manifest_value.get("artifact_path") != artifact.relative_to(REPOSITORY_ROOT).as_posix(): + raise EvidenceError("manifest artifact path does not match the selected artifact") + if artifact_value.get("schema_version") != "sftguard.confirmatory-fault-matrix.v1": + raise EvidenceError("artifact schema version is not the frozen v0.1 schema") + provenance = artifact_value.get("provenance") + if not isinstance(provenance, dict): + raise EvidenceError("artifact provenance is missing") + if provenance.get("protocol_git_commit") != PROTOCOL_COMMIT: + raise EvidenceError("artifact protocol commit does not match the frozen runner") + implementation_commit = provenance.get("implementation_git_commit") + if ( + not isinstance(implementation_commit, str) + or len(implementation_commit) != 40 + or any(character not in "0123456789abcdef" for character in implementation_commit) + ): + raise EvidenceError("artifact implementation commit is invalid") + if implementation_commit != manifest_value.get("implementation_git_commit"): + raise EvidenceError("implementation commit differs between artifact and manifest") + return artifact_raw, manifest_value + + +def verify(artifact_path: Path, manifest_path: Path, *, replay: bool) -> None: + artifact_raw, manifest = _verify_bundle(artifact_path, manifest_path) + implementation_commit = manifest["implementation_git_commit"] + if replay: + _git("cat-file", "-e", f"{implementation_commit}^{{commit}}") + with tempfile.TemporaryDirectory(prefix="sftguard-evidence-") as temporary: + worktree = Path(temporary) / "source" + _git("worktree", "add", "--detach", str(worktree), implementation_commit) + try: + replayed = _worker_bytes(worktree, implementation_commit) + finally: + _git("worktree", "remove", "--force", str(worktree)) + if replayed != artifact_raw: + raise EvidenceError("exact implementation replay did not match checked-in evidence") + print("evidence integrity verified") + if replay: + print("exact implementation replay matched") + + +def _parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description=__doc__) + commands = parser.add_subparsers(dest="command", required=True) + generate_command = commands.add_parser("generate") + verify_command = commands.add_parser("verify") + for command in (generate_command, verify_command): + command.add_argument("--artifact", type=Path, default=DEFAULT_ARTIFACT) + command.add_argument("--manifest", type=Path, default=DEFAULT_MANIFEST) + verify_command.add_argument( + "--integrity-only", + action="store_true", + help="verify hashes without recreating the implementation worktree", + ) + return parser + + +def main() -> int: + args = _parser().parse_args() + try: + if args.command == "generate": + generate(args.artifact, args.manifest) + else: + verify(args.artifact, args.manifest, replay=not args.integrity_only) + except EvidenceError as exc: + print(f"error: {exc}", file=sys.stderr) + return 2 + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/src/sftguard/__init__.py b/src/sftguard/__init__.py new file mode 100644 index 0000000..a547413 --- /dev/null +++ b/src/sftguard/__init__.py @@ -0,0 +1,8 @@ +"""SFTGuard public package surface.""" + +__version__ = "0.1.0" + +from .audit import AuditConfig, audit_files, audit_records +from .regression import build_regression_card + +__all__ = ["AuditConfig", "audit_files", "audit_records", "build_regression_card"] diff --git a/src/sftguard/__main__.py b/src/sftguard/__main__.py new file mode 100644 index 0000000..eb53e2f --- /dev/null +++ b/src/sftguard/__main__.py @@ -0,0 +1,3 @@ +from .cli import main + +raise SystemExit(main()) diff --git a/src/sftguard/artifacts.py b/src/sftguard/artifacts.py new file mode 100644 index 0000000..caa2b72 --- /dev/null +++ b/src/sftguard/artifacts.py @@ -0,0 +1,88 @@ +"""Deterministic, content-addressed JSON artifact helpers.""" + +from __future__ import annotations + +import copy +import hashlib +import json +import os +import tempfile +from collections.abc import Mapping +from pathlib import Path +from typing import Any + + +def canonical_json_bytes(value: Any) -> bytes: + """Return the repository's canonical UTF-8 JSON representation.""" + + return json.dumps( + value, + ensure_ascii=False, + allow_nan=False, + separators=(",", ":"), + sort_keys=True, + ).encode("utf-8") + + +def sha256_bytes(value: bytes) -> str: + return hashlib.sha256(value).hexdigest() + + +def seal_artifact(payload: Mapping[str, Any]) -> dict[str, Any]: + """Attach a hash of the payload excluding the integrity field itself.""" + + sealed = copy.deepcopy(dict(payload)) + sealed.pop("integrity", None) + sealed["integrity"] = { + "algorithm": "sha256", + "canonical_payload_sha256": sha256_bytes(canonical_json_bytes(sealed)), + } + return sealed + + +def verify_artifact(artifact: Mapping[str, Any]) -> bool: + integrity = artifact.get("integrity") + if not isinstance(integrity, Mapping): + return False + expected = integrity.get("canonical_payload_sha256") + if not isinstance(expected, str): + return False + payload = copy.deepcopy(dict(artifact)) + payload.pop("integrity", None) + return expected == sha256_bytes(canonical_json_bytes(payload)) + + +def write_artifact(path: str | Path | None, artifact: Mapping[str, Any]) -> None: + """Write an artifact atomically, or print it when path is ``None``.""" + + rendered = ( + json.dumps( + artifact, + ensure_ascii=False, + allow_nan=False, + indent=2, + sort_keys=True, + ) + + "\n" + ) + if path is None: + print(rendered, end="") + return + + destination = Path(path) + destination.parent.mkdir(parents=True, exist_ok=True) + handle, temporary_name = tempfile.mkstemp( + prefix=f".{destination.name}.", suffix=".tmp", dir=destination.parent + ) + try: + with os.fdopen(handle, "w", encoding="utf-8", newline="\n") as stream: + stream.write(rendered) + stream.flush() + os.fsync(stream.fileno()) + os.replace(temporary_name, destination) + except BaseException: + try: + os.unlink(temporary_name) + except FileNotFoundError: + pass + raise diff --git a/src/sftguard/audit.py b/src/sftguard/audit.py new file mode 100644 index 0000000..cfee05f --- /dev/null +++ b/src/sftguard/audit.py @@ -0,0 +1,396 @@ +"""Deterministic SFT dataset and rendered-mask audits.""" + +from __future__ import annotations + +from collections import Counter +from collections.abc import Iterable +from dataclasses import dataclass +from typing import Any + +from . import __version__ +from .artifacts import canonical_json_bytes, seal_artifact, sha256_bytes +from .io import ( + DEFAULT_MAX_FILE_BYTES, + DEFAULT_MAX_LINE_BYTES, + DEFAULT_MAX_RECORDS, + InputError, + InputSummary, + read_jsonl_bounded, + summarize_records, +) +from .models import SEVERITY_ORDER, Finding, Status, combine_status +from .privacy import scan_record +from .tokenizer import FixtureMaskInspector, MaskInspector + +ALLOWED_MESSAGE_ROLES = {"system", "user", "assistant", "tool"} + + +@dataclass(frozen=True) +class AuditConfig: + max_file_bytes: int = DEFAULT_MAX_FILE_BYTES + max_line_bytes: int = DEFAULT_MAX_LINE_BYTES + max_records: int = DEFAULT_MAX_RECORDS + require_assistant_mask: bool = True + scan_privacy: bool = True + + def as_dict(self) -> dict[str, Any]: + return { + "max_file_bytes": self.max_file_bytes, + "max_line_bytes": self.max_line_bytes, + "max_records": self.max_records, + "require_assistant_mask": self.require_assistant_mask, + "scan_privacy": self.scan_privacy, + } + + +@dataclass(frozen=True) +class _RecordIdentity: + split: str + line: int + record_sha256: str + prompt_sha256: str | None + answer_sha256: str | None + + +def _record_hash(record: dict[str, Any], *, split: str, line: int) -> tuple[str, bool]: + try: + return sha256_bytes(canonical_json_bytes(record)), True + except (TypeError, ValueError, RecursionError): + marker = canonical_json_bytes({"invalid_record": True, "split": split, "line": line}) + return sha256_bytes(marker), False + + +def _message_identity( + record: dict[str, Any], + *, + split: str, + line: int, + record_sha256: str, +) -> tuple[str | None, str | None, list[Finding]]: + findings: list[Finding] = [] + messages = record.get("messages") + if not isinstance(messages, list) or not messages: + findings.append( + Finding("invalid_messages", "error", split, line, record_sha256=record_sha256) + ) + return None, None, findings + + valid_messages: list[dict[str, str]] = [] + awaiting_assistant = False + for message in messages: + if not isinstance(message, dict): + findings.append( + Finding("invalid_messages", "error", split, line, record_sha256=record_sha256) + ) + continue + role = message.get("role") + content = message.get("content") + if not isinstance(role, str) or role not in ALLOWED_MESSAGE_ROLES: + findings.append( + Finding( + "invalid_role", + "error", + split, + line, + field="role", + record_sha256=record_sha256, + ) + ) + continue + if not isinstance(content, str): + findings.append( + Finding( + "invalid_messages", + "error", + split, + line, + field="content", + record_sha256=record_sha256, + ) + ) + continue + valid_messages.append({"role": role, "content": content}) + if role == "user": + awaiting_assistant = True + elif role == "assistant": + if not awaiting_assistant: + findings.append( + Finding("role_order", "error", split, line, record_sha256=record_sha256) + ) + awaiting_assistant = False + + if len(valid_messages) != len(messages): + return None, None, findings + + assistant_indexes = [ + index for index, message in enumerate(valid_messages) if message["role"] == "assistant" + ] + if not assistant_indexes: + findings.append( + Finding("missing_assistant", "error", split, line, record_sha256=record_sha256) + ) + return None, None, findings + final_index = assistant_indexes[-1] + prompt = valid_messages[:final_index] + answer = valid_messages[final_index]["content"] + prompt_hash = sha256_bytes(canonical_json_bytes(prompt)) + answer_hash = sha256_bytes(canonical_json_bytes(answer)) + return prompt_hash, answer_hash, findings + + +def _check_status(findings: Iterable[Finding], signals: set[str]) -> Status: + relevant = [finding for finding in findings if finding.signal in signals] + if any(SEVERITY_ORDER[finding.severity] >= SEVERITY_ORDER["error"] for finding in relevant): + return Status.FAIL + return Status.PASS + + +def _abstain_report(config: AuditConfig, reason: str) -> dict[str, Any]: + return seal_artifact( + { + "schema_version": "sftguard.audit.v1", + "tool": {"name": "sftguard", "version": __version__}, + "status": Status.ABSTAIN, + "config": config.as_dict(), + "inputs": [], + "checks": {"input": {"status": Status.ABSTAIN, "reason": reason}}, + "summary": {"finding_count": 0, "signal_counts": {}}, + "findings": [], + } + ) + + +def audit_files( + train_path: str, + evaluation_path: str, + *, + config: AuditConfig | None = None, + mask_inspector: MaskInspector | None = None, +) -> dict[str, Any]: + selected = config or AuditConfig() + try: + train = read_jsonl_bounded( + train_path, + max_file_bytes=selected.max_file_bytes, + max_line_bytes=selected.max_line_bytes, + max_records=selected.max_records, + ) + evaluation = read_jsonl_bounded( + evaluation_path, + max_file_bytes=selected.max_file_bytes, + max_line_bytes=selected.max_line_bytes, + max_records=selected.max_records, + ) + except InputError: + return _abstain_report(selected, "invalid_unreadable_or_oversized_input") + return audit_records( + train.records, + evaluation.records, + train_summary=train.summary, + evaluation_summary=evaluation.summary, + config=selected, + mask_inspector=mask_inspector, + ) + + +def audit_records( + train_records: Iterable[tuple[int, dict[str, Any]]], + evaluation_records: Iterable[tuple[int, dict[str, Any]]], + *, + train_summary: InputSummary | None = None, + evaluation_summary: InputSummary | None = None, + config: AuditConfig | None = None, + mask_inspector: MaskInspector | None = None, +) -> dict[str, Any]: + selected = config or AuditConfig() + inspector = mask_inspector or FixtureMaskInspector() + train = tuple(train_records) + evaluation = tuple(evaluation_records) + train_summary = train_summary or summarize_records(train) + evaluation_summary = evaluation_summary or summarize_records(evaluation) + + if not train or not evaluation: + return _abstain_report(selected, "train_and_evaluation_records_required") + + findings: list[Finding] = [] + identities: list[_RecordIdentity] = [] + mask_abstentions = 0 + seen_records: dict[tuple[str, str], int] = {} + + for split, records in (("train", train), ("evaluation", evaluation)): + for line, record in records: + record_sha, record_serializable = _record_hash(record, split=split, line=line) + if not record_serializable: + findings.append( + Finding( + "invalid_record", + "error", + split, + line, + record_sha256=record_sha, + ) + ) + prompt_sha, answer_sha, schema_findings = _message_identity( + record, + split=split, + line=line, + record_sha256=record_sha, + ) + findings.extend(schema_findings) + identities.append(_RecordIdentity(split, line, record_sha, prompt_sha, answer_sha)) + + duplicate_key = (split, record_sha) + if duplicate_key in seen_records: + findings.append( + Finding( + "exact_duplicate", + "warning", + split, + line, + related_line=seen_records[duplicate_key], + record_sha256=record_sha, + ) + ) + else: + seen_records[duplicate_key] = line + + if selected.scan_privacy: + for signal, category, field in scan_record(record): + findings.append( + Finding( + signal, + "critical" if signal == "secret_pattern" else "warning", + split, + line, + field=field, + category=category, + record_sha256=record_sha, + redacted=True, + ) + ) + + inspection = inspector.inspect(record) + if not inspection.available: + if selected.require_assistant_mask: + mask_abstentions += 1 + findings.append( + Finding( + inspection.reason or "assistant_mask_unavailable", + "info", + split, + line, + record_sha256=record_sha, + redacted=True, + ) + ) + else: + for signal in inspection.signals: + findings.append( + Finding( + signal.signal, + signal.severity, + split, + line, + record_sha256=record_sha, + ) + ) + + prompt_answers: dict[str, dict[str, int]] = {} + for identity in identities: + if ( + identity.split != "train" + or identity.prompt_sha256 is None + or identity.answer_sha256 is None + ): + continue + prior = prompt_answers.setdefault(identity.prompt_sha256, {}) + if prior and identity.answer_sha256 not in prior: + findings.append( + Finding( + "prompt_conflict", + "error", + identity.split, + identity.line, + related_line=min(prior.values()), + record_sha256=identity.record_sha256, + ) + ) + prior.setdefault(identity.answer_sha256, identity.line) + + prompts_by_split: dict[str, dict[str, int]] = {"train": {}, "evaluation": {}} + for identity in identities: + if identity.prompt_sha256 is not None: + prompts_by_split[identity.split].setdefault(identity.prompt_sha256, identity.line) + for prompt_sha in sorted(set(prompts_by_split["train"]) & set(prompts_by_split["evaluation"])): + findings.append( + Finding( + "train_eval_leakage", + "critical", + "train", + prompts_by_split["train"][prompt_sha], + related_line=prompts_by_split["evaluation"][prompt_sha], + record_sha256=prompt_sha, + ) + ) + + findings.sort( + key=lambda finding: ( + finding.split, + finding.line, + finding.signal, + finding.related_line or 0, + finding.category or "", + ) + ) + signal_counts = Counter(finding.signal for finding in findings) + + schema_signals = { + "invalid_record", + "invalid_messages", + "invalid_role", + "role_order", + "missing_assistant", + } + exact_signals = {"exact_duplicate", "prompt_conflict", "train_eval_leakage"} + privacy_signals = {"secret_pattern", "pii_like"} + mask_signals = { + "prompt_token_supervised", + "assistant_fully_truncated", + "missing_eos", + } + checks = { + "schema": {"status": _check_status(findings, schema_signals)}, + "duplicates_conflicts_and_leakage": {"status": _check_status(findings, exact_signals)}, + "privacy_patterns": {"status": _check_status(findings, privacy_signals)}, + "assistant_mask": { + "status": ( + Status.ABSTAIN if mask_abstentions else _check_status(findings, mask_signals) + ), + "abstained_record_count": mask_abstentions, + }, + } + overall = combine_status(*(entry["status"] for entry in checks.values())) + + return seal_artifact( + { + "schema_version": "sftguard.audit.v1", + "tool": {"name": "sftguard", "version": __version__}, + "status": overall, + "config": selected.as_dict(), + "inputs": [ + train_summary.as_dict("train"), + evaluation_summary.as_dict("evaluation"), + ], + "checks": checks, + "summary": { + "finding_count": len(findings), + "signal_counts": dict(sorted(signal_counts.items())), + "error_or_critical_count": sum( + SEVERITY_ORDER[finding.severity] >= SEVERITY_ORDER["error"] + for finding in findings + ), + "redacted_finding_count": sum(finding.redacted for finding in findings), + }, + "findings": [finding.as_dict() for finding in findings], + } + ) diff --git a/src/sftguard/benchmark.py b/src/sftguard/benchmark.py new file mode 100644 index 0000000..a506879 --- /dev/null +++ b/src/sftguard/benchmark.py @@ -0,0 +1,124 @@ +"""Development-only execution of the preregistered synthetic fault matrix.""" + +from __future__ import annotations + +import statistics +from collections import defaultdict +from collections.abc import Iterable +from typing import Any + +from . import __version__ +from .artifacts import canonical_json_bytes, seal_artifact +from .audit import audit_records +from .models import Status +from .synthetic import DEVELOPMENT_SEEDS, FAULT_SPECS, generate_case + + +def validate_development_seeds(seeds: Iterable[int]) -> tuple[int, ...]: + selected = tuple(sorted(set(seeds))) + if not selected: + raise ValueError("at least one development seed is required") + if any(seed not in DEVELOPMENT_SEEDS for seed in selected): + raise ValueError("benchmark execution is restricted to development seeds 0 through 9") + return selected + + +def _run_once(seeds: tuple[int, ...]) -> tuple[dict[str, Any], tuple[str, ...]]: + cases: list[dict[str, Any]] = [] + raw_markers: list[str] = [] + recall_by_fault: dict[str, list[bool]] = defaultdict(list) + clean_false_positives = 0 + clean_count = 0 + + for seed in seeds: + for fault_id in (None, *(spec.fault_id for spec in FAULT_SPECS)): + case = generate_case(seed, fault_id) + raw_markers.extend(case.private_markers) + report = audit_records(case.train, case.evaluation) + observed_signals = sorted(report["summary"]["signal_counts"]) + expected = case.fault.expected_signal if case.fault else None + observed_primary = expected in observed_signals if expected else not observed_signals + if case.fault is None: + clean_count += 1 + if observed_signals: + clean_false_positives += 1 + else: + recall_by_fault[case.fault.fault_id].append(observed_primary) + cases.append( + { + "seed": seed, + "kind": "fault" if case.fault else "clean_control", + "fault_id": case.fault.fault_id if case.fault else None, + "expected_primary_signal": expected, + "observed_primary_signal": observed_primary, + "observed_signals": observed_signals, + "audit_status": report["status"], + "case_sha256": case.case_sha256, + } + ) + + per_fault: list[dict[str, Any]] = [] + fault_recalls: list[float] = [] + for spec in FAULT_SPECS: + observations = recall_by_fault[spec.fault_id] + detected = sum(observations) + recall = detected / len(observations) + fault_recalls.append(recall) + per_fault.append( + { + "fault_id": spec.fault_id, + "expected_signal": spec.expected_signal, + "severity": spec.severity, + "case_count": len(observations), + "detected_count": detected, + "recall": round(recall, 12), + } + ) + + critical = [item for item in per_fault if item["severity"] == "critical"] + macro_recall = statistics.fmean(fault_recalls) + clean_false_positive_rate = clean_false_positives / clean_count + base_gates = { + "critical_fault_recall_100_percent": all(item["recall"] == 1.0 for item in critical), + "macro_recall_at_least_90_percent": macro_recall >= 0.9, + "clean_false_positive_rate_at_most_10_percent": clean_false_positive_rate <= 0.1, + "expected_and_observed_primary_recorded": all( + case["expected_primary_signal"] is None + or isinstance(case["observed_primary_signal"], bool) + for case in cases + ), + } + payload = { + "schema_version": "sftguard.development-benchmark.v1", + "tool": {"name": "sftguard", "version": __version__}, + "development_only": True, + "seed_range": {"first": seeds[0], "last": seeds[-1], "count": len(seeds)}, + "fault_count": len(FAULT_SPECS), + "summary": { + "case_count": len(cases), + "clean_control_count": clean_count, + "clean_false_positive_count": clean_false_positives, + "clean_false_positive_rate": round(clean_false_positive_rate, 12), + "macro_recall": round(macro_recall, 12), + }, + "per_fault": per_fault, + "cases": cases, + "gates": base_gates, + } + return payload, tuple(raw_markers) + + +def run_development_benchmark( + seeds: Iterable[int] = DEVELOPMENT_SEEDS, +) -> dict[str, Any]: + selected = validate_development_seeds(seeds) + first, markers = _run_once(selected) + second, _ = _run_once(selected) + reproducible = canonical_json_bytes(first) == canonical_json_bytes(second) + rendered = canonical_json_bytes(first).decode("utf-8") + redacted = all(marker not in rendered for marker in markers) + first["gates"]["in_process_byte_reproducible"] = reproducible + first["gates"]["raw_synthetic_text_absent"] = redacted + first["all_development_gates_passed"] = all(first["gates"].values()) + first["status"] = Status.PASS if first["all_development_gates_passed"] else Status.FAIL + return seal_artifact(first) diff --git a/src/sftguard/cli.py b/src/sftguard/cli.py new file mode 100644 index 0000000..c488e3a --- /dev/null +++ b/src/sftguard/cli.py @@ -0,0 +1,146 @@ +"""Command-line interface for audit, gate, benchmark, and demo workflows.""" + +from __future__ import annotations + +import argparse +import re +from collections.abc import Sequence +from typing import Any + +from . import __version__ +from .artifacts import seal_artifact, write_artifact +from .audit import AuditConfig, audit_files +from .benchmark import run_development_benchmark, validate_development_seeds +from .demo import run_demo +from .io import InputError, read_json_bounded, read_jsonl_bounded +from .models import Status +from .regression import build_regression_card + +EXIT_CODES = {Status.PASS: 0, Status.FAIL: 1, Status.ABSTAIN: 2} + + +def _bounded_positive_int(value: str) -> int: + parsed = int(value) + if parsed <= 0: + raise argparse.ArgumentTypeError("value must be positive") + return parsed + + +def _parse_seeds(value: str) -> tuple[int, ...]: + try: + selected: list[int] = [] + for part in value.split(","): + part = part.strip() + range_match = re.fullmatch(r"([0-9])-([0-9])", part) + if range_match: + start, end = (int(component) for component in range_match.groups()) + if end < start: + raise ValueError + selected.extend(range(start, end + 1)) + elif re.fullmatch(r"[0-9]", part): + selected.append(int(part)) + else: + raise ValueError + return validate_development_seeds(selected) + except ValueError as exc: + raise argparse.ArgumentTypeError( + "seeds must be a subset of development seeds 0 through 9" + ) from exc + + +def _input_abstention(schema_version: str) -> dict[str, Any]: + return seal_artifact( + { + "schema_version": schema_version, + "tool": {"name": "sftguard", "version": __version__}, + "status": Status.ABSTAIN, + "reason_codes": ["invalid_unreadable_or_oversized_input"], + } + ) + + +def _emit(artifact: dict[str, Any], output: str | None) -> int: + write_artifact(output, artifact) + status = Status(artifact["status"]) + return EXIT_CODES[status] + + +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser( + prog="sftguard", + description="Local-first correctness and regression gates for SFT pipelines.", + ) + parser.add_argument("--version", action="version", version=__version__) + commands = parser.add_subparsers(dest="command", required=True) + + audit = commands.add_parser("audit", help="audit training and evaluation JSONL files") + audit.add_argument("--train", required=True) + audit.add_argument("--eval", required=True, dest="evaluation") + audit.add_argument("--output") + audit.add_argument("--max-file-bytes", type=_bounded_positive_int, default=16 * 1024 * 1024) + audit.add_argument("--max-line-bytes", type=_bounded_positive_int, default=1024 * 1024) + audit.add_argument("--max-records", type=_bounded_positive_int, default=100_000) + + gate = commands.add_parser("gate", help="build a paired target/retention Regression Card") + gate.add_argument("--base", required=True) + gate.add_argument("--adapter", required=True) + gate.add_argument("--contract", required=True) + gate.add_argument("--output") + gate.add_argument("--max-file-bytes", type=_bounded_positive_int, default=16 * 1024 * 1024) + gate.add_argument("--max-line-bytes", type=_bounded_positive_int, default=1024 * 1024) + gate.add_argument("--max-records", type=_bounded_positive_int, default=100_000) + + benchmark = commands.add_parser( + "benchmark", help="run the deterministic development-only synthetic matrix" + ) + benchmark.add_argument("--seeds", type=_parse_seeds, default=tuple(range(10))) + benchmark.add_argument("--output") + + demo = commands.add_parser("demo", help="run a redacted deterministic end-to-end demo") + demo.add_argument("--output") + return parser + + +def main(argv: Sequence[str] | None = None) -> int: + args = build_parser().parse_args(argv) + if args.command == "audit": + config = AuditConfig( + max_file_bytes=args.max_file_bytes, + max_line_bytes=args.max_line_bytes, + max_records=args.max_records, + ) + return _emit(audit_files(args.train, args.evaluation, config=config), args.output) + + if args.command == "gate": + try: + base = read_jsonl_bounded( + args.base, + max_file_bytes=args.max_file_bytes, + max_line_bytes=args.max_line_bytes, + max_records=args.max_records, + ) + adapter = read_jsonl_bounded( + args.adapter, + max_file_bytes=args.max_file_bytes, + max_line_bytes=args.max_line_bytes, + max_records=args.max_records, + ) + contract, _, _ = read_json_bounded(args.contract, max_file_bytes=args.max_file_bytes) + artifact = build_regression_card( + base.records, + adapter.records, + contract, + base_summary=base.summary, + adapter_summary=adapter.summary, + ) + except InputError: + artifact = _input_abstention("sftguard.regression-card.v1") + return _emit(artifact, args.output) + + if args.command == "benchmark": + return _emit(run_development_benchmark(args.seeds), args.output) + + if args.command == "demo": + return _emit(run_demo(), args.output) + + raise AssertionError("argparse accepted an unknown command") diff --git a/src/sftguard/confirmatory.py b/src/sftguard/confirmatory.py new file mode 100644 index 0000000..8bda5fc --- /dev/null +++ b/src/sftguard/confirmatory.py @@ -0,0 +1,231 @@ +"""Frozen v0.1 confirmatory fault-matrix evidence construction. + +The public orchestration script invokes this module only from a clean, pushed +implementation commit. Keeping construction separate from orchestration makes +the payload testable on development seeds without touching the confirmatory +range before the implementation is frozen. +""" + +from __future__ import annotations + +import argparse +import json +import platform +import re +import statistics +import sys +from collections import defaultdict +from collections.abc import Callable, Iterable, Sequence +from typing import Any + +from . import __version__ +from .artifacts import canonical_json_bytes, seal_artifact +from .audit import AuditConfig, audit_records +from .models import Status +from .synthetic import ( + CONFIRMATORY_SEEDS, + FAULT_SPECS, + SyntheticCase, + generate_confirmatory_case, +) + +_FULL_COMMIT = re.compile(r"^[0-9a-f]{40}$") + + +def pretty_json_bytes(value: Any) -> bytes: + """Render stable human-readable JSON for the checked-in evidence file.""" + + return ( + json.dumps( + value, + ensure_ascii=False, + allow_nan=False, + indent=2, + sort_keys=True, + ) + + "\n" + ).encode("utf-8") + + +def _validate_commit(value: str, label: str) -> None: + if _FULL_COMMIT.fullmatch(value) is None: + raise ValueError(f"{label} must be a full lowercase Git commit") + + +def _build_artifact( + seeds: Iterable[int], + case_factory: Callable[[int, str | None], SyntheticCase], + *, + protocol_commit: str, + implementation_commit: str, + fresh_process_byte_reproducible: bool, +) -> dict[str, Any]: + """Build a sealed matrix; tests exercise this with development seeds.""" + + _validate_commit(protocol_commit, "protocol_commit") + _validate_commit(implementation_commit, "implementation_commit") + selected = tuple(seeds) + if not selected or tuple(sorted(set(selected))) != selected: + raise ValueError("seeds must be a non-empty sorted unique sequence") + + cases: list[dict[str, Any]] = [] + recall_by_fault: dict[str, list[bool]] = defaultdict(list) + private_text: set[str] = set() + clean_false_positives = 0 + + for seed in selected: + for fault_id in (None, *(spec.fault_id for spec in FAULT_SPECS)): + case = case_factory(seed, fault_id) + private_text.update(case.private_markers) + for _, record in (*case.train, *case.evaluation): + messages = record.get("messages") + if isinstance(messages, list): + private_text.update( + message["content"] + for message in messages + if isinstance(message, dict) and isinstance(message.get("content"), str) + ) + + report = audit_records(case.train, case.evaluation) + observed_signals = tuple(sorted(report["summary"]["signal_counts"])) + expected = case.fault.expected_signal if case.fault else None + expectation_met = expected in observed_signals if expected else not observed_signals + observed_primary = expected if expected in observed_signals else None + + if case.fault is None: + if observed_signals: + clean_false_positives += 1 + else: + recall_by_fault[case.fault.fault_id].append(expectation_met) + + cases.append( + { + "audit_status": report["status"], + "case_sha256": case.case_sha256, + "expected_primary_signal": expected, + "fault_id": case.fault.fault_id if case.fault else None, + "kind": "fault" if case.fault else "clean_control", + "observed_primary_signal": observed_primary, + "observed_signal_ids": list(observed_signals), + "primary_expectation_met": expectation_met, + "seed": seed, + } + ) + + per_fault: list[dict[str, Any]] = [] + recalls: list[float] = [] + for spec in FAULT_SPECS: + observations = recall_by_fault[spec.fault_id] + detected_count = sum(observations) + recall = detected_count / len(observations) + recalls.append(recall) + per_fault.append( + { + "case_count": len(observations), + "detected_count": detected_count, + "expected_signal": spec.expected_signal, + "fault_id": spec.fault_id, + "recall": round(recall, 12), + "severity": spec.severity, + } + ) + + clean_count = len(selected) + clean_false_positive_rate = clean_false_positives / clean_count + macro_recall = statistics.fmean(recalls) + critical_faults = [item for item in per_fault if item["severity"] == "critical"] + expected_observed_recorded = all( + "expected_primary_signal" in case and "observed_primary_signal" in case for case in cases + ) + + payload_without_redaction_gate: dict[str, Any] = { + "schema_version": "sftguard.confirmatory-fault-matrix.v1", + "tool": {"name": "sftguard", "version": __version__}, + "provenance": { + "implementation_git_commit": implementation_commit, + "protocol_git_commit": protocol_commit, + "python_implementation": platform.python_implementation(), + "python_version": f"{sys.version_info.major}.{sys.version_info.minor}", + }, + "protocol": { + "audit_config": AuditConfig().as_dict(), + "faults": [ + { + "expected_signal": spec.expected_signal, + "fault_id": spec.fault_id, + "severity": spec.severity, + } + for spec in FAULT_SPECS + ], + "seed_range": { + "count": len(selected), + "first": selected[0], + "last": selected[-1], + }, + }, + "summary": { + "case_count": len(cases), + "clean_control_count": clean_count, + "clean_false_positive_count": clean_false_positives, + "clean_false_positive_rate": round(clean_false_positive_rate, 12), + "fault_case_count": len(cases) - clean_count, + "macro_recall": round(macro_recall, 12), + }, + "per_fault": per_fault, + "cases": cases, + "gates": { + "clean_false_positive_rate_at_most_10_percent": (clean_false_positive_rate <= 0.10), + "critical_fault_recall_100_percent": all( + item["recall"] == 1.0 for item in critical_faults + ), + "expected_and_observed_primary_recorded": expected_observed_recorded, + "fresh_process_byte_reproducible": fresh_process_byte_reproducible, + "macro_recall_at_least_90_percent": macro_recall >= 0.90, + }, + } + rendered = canonical_json_bytes(payload_without_redaction_gate).decode("utf-8") + payload_without_redaction_gate["gates"]["raw_synthetic_text_absent"] = all( + value not in rendered for value in private_text + ) + carry_forward = all(payload_without_redaction_gate["gates"].values()) + payload_without_redaction_gate["carry_forward"] = carry_forward + payload_without_redaction_gate["status"] = Status.PASS if carry_forward else Status.FAIL + return seal_artifact(payload_without_redaction_gate) + + +def build_confirmatory_artifact( + *, + protocol_commit: str, + implementation_commit: str, + fresh_process_byte_reproducible: bool = True, +) -> dict[str, Any]: + """Execute the exact preregistered seed range and fault matrix.""" + + return _build_artifact( + CONFIRMATORY_SEEDS, + generate_confirmatory_case, + protocol_commit=protocol_commit, + implementation_commit=implementation_commit, + fresh_process_byte_reproducible=fresh_process_byte_reproducible, + ) + + +def _parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description="Build frozen SFTGuard evidence bytes.") + parser.add_argument("--protocol-commit", required=True) + parser.add_argument("--implementation-commit", required=True) + return parser + + +def main(argv: Sequence[str] | None = None) -> int: + args = _parser().parse_args(argv) + artifact = build_confirmatory_artifact( + protocol_commit=args.protocol_commit, + implementation_commit=args.implementation_commit, + ) + sys.stdout.buffer.write(pretty_json_bytes(artifact)) + return 0 if artifact["status"] == Status.PASS else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/src/sftguard/demo.py b/src/sftguard/demo.py new file mode 100644 index 0000000..fdf6e6e --- /dev/null +++ b/src/sftguard/demo.py @@ -0,0 +1,77 @@ +"""Small deterministic, redacted end-to-end demonstration.""" + +from __future__ import annotations + +from typing import Any + +from . import __version__ +from .artifacts import seal_artifact +from .audit import audit_records +from .models import Status +from .regression import build_regression_card +from .synthetic import generate_case + + +def _evaluation_records() -> tuple[ + tuple[tuple[int, dict[str, Any]], ...], tuple[tuple[int, dict[str, Any]], ...] +]: + base: list[tuple[int, dict[str, Any]]] = [] + adapter: list[tuple[int, dict[str, Any]]] = [] + line = 1 + for seed in range(4): + for group, metric, base_value, adapter_value in ( + ("target", "task_score", 0.50 + seed * 0.01, 0.65 + seed * 0.01), + ("retention", "general_score", 0.80 - seed * 0.01, 0.795 - seed * 0.01), + ): + common = { + "seed": seed, + "sample_id": f"development-{group}-{seed}", + "group": group, + "metric": metric, + } + base.append((line, {**common, "value": base_value})) + adapter.append((line, {**common, "value": adapter_value})) + line += 1 + return tuple(base), tuple(adapter) + + +def run_demo() -> dict[str, Any]: + case = generate_case(0) + audit = audit_records(case.train, case.evaluation) + base, adapter = _evaluation_records() + card = build_regression_card( + base, + adapter, + { + "bootstrap": {"draws": 10_000, "confidence": 0.95, "seed": 2339}, + "metrics": [ + { + "group": "target", + "metric": "task_score", + "higher_is_better": True, + "min_improvement": 0.10, + }, + { + "group": "retention", + "metric": "general_score", + "higher_is_better": True, + "max_regression": 0.02, + }, + ], + }, + ) + status = ( + Status.PASS + if audit["status"] == Status.PASS and card["status"] == Status.PASS + else Status.FAIL + ) + return seal_artifact( + { + "schema_version": "sftguard.demo.v1", + "tool": {"name": "sftguard", "version": __version__}, + "status": status, + "note": "Deterministic synthetic development demonstration; not external evidence.", + "audit": audit, + "regression_card": card, + } + ) diff --git a/src/sftguard/io.py b/src/sftguard/io.py new file mode 100644 index 0000000..cb1a21f --- /dev/null +++ b/src/sftguard/io.py @@ -0,0 +1,153 @@ +"""Bounded JSON and JSONL readers used by every untrusted input path.""" + +from __future__ import annotations + +import hashlib +import json +from collections.abc import Iterable +from dataclasses import dataclass +from pathlib import Path +from typing import Any + +DEFAULT_MAX_FILE_BYTES = 16 * 1024 * 1024 +DEFAULT_MAX_LINE_BYTES = 1024 * 1024 +DEFAULT_MAX_RECORDS = 100_000 + + +class InputError(ValueError): + """Raised when an input cannot be safely or unambiguously parsed.""" + + +@dataclass(frozen=True) +class InputSummary: + sha256: str + byte_count: int + record_count: int + + def as_dict(self, split: str) -> dict[str, Any]: + return { + "split": split, + "sha256": self.sha256, + "byte_count": self.byte_count, + "record_count": self.record_count, + } + + +@dataclass(frozen=True) +class ParsedJSONL: + records: tuple[tuple[int, dict[str, Any]], ...] + summary: InputSummary + + +def _reject_duplicate_keys(pairs: list[tuple[str, Any]]) -> dict[str, Any]: + result: dict[str, Any] = {} + for key, value in pairs: + if key in result: + raise InputError("JSON object contains a duplicate key") + result[key] = value + return result + + +def _decode_json(raw: bytes, *, context: str) -> Any: + try: + text = raw.decode("utf-8") + except UnicodeDecodeError as exc: + raise InputError(f"{context} is not valid UTF-8") from exc + try: + return json.loads( + text, + object_pairs_hook=_reject_duplicate_keys, + parse_constant=lambda _value: (_ for _ in ()).throw( + InputError(f"{context} contains a non-finite number") + ), + ) + except InputError: + raise + except (json.JSONDecodeError, RecursionError) as exc: + raise InputError(f"{context} is not valid JSON") from exc + + +def read_jsonl_bounded( + path: str | Path, + *, + max_file_bytes: int = DEFAULT_MAX_FILE_BYTES, + max_line_bytes: int = DEFAULT_MAX_LINE_BYTES, + max_records: int = DEFAULT_MAX_RECORDS, +) -> ParsedJSONL: + source = Path(path) + try: + size = source.stat().st_size + except OSError as exc: + raise InputError("input file cannot be read") from exc + if size > max_file_bytes: + raise InputError("input file exceeds the configured byte limit") + + digest = hashlib.sha256() + records: list[tuple[int, dict[str, Any]]] = [] + bytes_seen = 0 + try: + with source.open("rb") as stream: + for line_number, raw_line in enumerate(stream, start=1): + bytes_seen += len(raw_line) + digest.update(raw_line) + if bytes_seen > max_file_bytes: + raise InputError("input file grew beyond the configured byte limit") + if len(raw_line) > max_line_bytes: + raise InputError(f"JSONL line {line_number} exceeds the byte limit") + stripped = raw_line.strip() + if not stripped: + continue + if len(records) >= max_records: + raise InputError("input file exceeds the configured record limit") + decoded = _decode_json(stripped, context=f"JSONL line {line_number}") + if not isinstance(decoded, dict): + raise InputError(f"JSONL line {line_number} must be an object") + records.append((line_number, decoded)) + except InputError: + raise + except OSError as exc: + raise InputError("input file cannot be read") from exc + + return ParsedJSONL( + records=tuple(records), + summary=InputSummary( + sha256=digest.hexdigest(), + byte_count=bytes_seen, + record_count=len(records), + ), + ) + + +def read_json_bounded( + path: str | Path, *, max_file_bytes: int = DEFAULT_MAX_FILE_BYTES +) -> tuple[Any, str, int]: + source = Path(path) + try: + size = source.stat().st_size + if size > max_file_bytes: + raise InputError("JSON file exceeds the configured byte limit") + raw = source.read_bytes() + except InputError: + raise + except OSError as exc: + raise InputError("JSON file cannot be read") from exc + if len(raw) > max_file_bytes: + raise InputError("JSON file grew beyond the configured byte limit") + return _decode_json(raw, context="JSON file"), hashlib.sha256(raw).hexdigest(), len(raw) + + +def summarize_records(records: Iterable[tuple[int, dict[str, Any]]]) -> InputSummary: + """Create a deterministic input summary for generated in-memory records.""" + + from .artifacts import canonical_json_bytes + + materialized = [record for _, record in records] + try: + raw = canonical_json_bytes(materialized) + except (TypeError, ValueError, RecursionError): + raw = canonical_json_bytes({"invalid_input": True, "record_count": len(materialized)}) + return InputSummary( + sha256=hashlib.sha256(raw).hexdigest(), + byte_count=len(raw), + record_count=len(materialized), + ) diff --git a/src/sftguard/models.py b/src/sftguard/models.py new file mode 100644 index 0000000..f31699b --- /dev/null +++ b/src/sftguard/models.py @@ -0,0 +1,59 @@ +"""Shared status and finding types.""" + +from __future__ import annotations + +from dataclasses import dataclass +from dataclasses import field as dataclass_field +from enum import StrEnum +from typing import Any + + +class Status(StrEnum): + PASS = "PASS" + FAIL = "FAIL" + ABSTAIN = "ABSTAIN" + + +SEVERITY_ORDER = {"info": 0, "warning": 1, "error": 2, "critical": 3} + + +@dataclass(frozen=True) +class Finding: + signal: str + severity: str + split: str + line: int + field: str | None = None + category: str | None = None + related_line: int | None = None + record_sha256: str | None = None + redacted: bool = False + details: dict[str, Any] = dataclass_field(default_factory=dict) + + def as_dict(self) -> dict[str, Any]: + result: dict[str, Any] = { + "signal": self.signal, + "severity": self.severity, + "location": {"split": self.split, "line": self.line}, + } + if self.field is not None: + result["location"]["field"] = self.field + if self.category is not None: + result["category"] = self.category + if self.related_line is not None: + result["related_line"] = self.related_line + if self.record_sha256 is not None: + result["record_sha256"] = self.record_sha256 + if self.redacted: + result["redacted"] = True + if self.details: + result["details"] = dict(sorted(self.details.items())) + return result + + +def combine_status(*statuses: Status) -> Status: + if Status.FAIL in statuses: + return Status.FAIL + if Status.ABSTAIN in statuses: + return Status.ABSTAIN + return Status.PASS diff --git a/src/sftguard/privacy.py b/src/sftguard/privacy.py new file mode 100644 index 0000000..a8bb9c7 --- /dev/null +++ b/src/sftguard/privacy.py @@ -0,0 +1,63 @@ +"""Conservative secret and PII-like pattern location without value retention.""" + +from __future__ import annotations + +import re +from collections.abc import Iterator +from typing import Any + +_SECRET_PATTERNS = ( + ("openai_style_key", re.compile(r"\bsk-(?:test-)?[A-Za-z0-9_-]{12,}\b")), + ("aws_access_key", re.compile(r"\bAKIA[0-9A-Z]{16}\b")), + ( + "assigned_credential", + re.compile( + r"(?i)\b(?:api[_-]?key|access[_-]?token|auth[_-]?token|password|secret)" + r"\s*[:=]\s*[\"']?[A-Za-z0-9_./+=-]{12,}" + ), + ), + ("bearer_token", re.compile(r"(?i)\bbearer\s+[A-Za-z0-9._~+/=-]{12,}")), +) + +_PII_PATTERNS = ( + ( + "email_address", + re.compile(r"(?i)(? str: + for component in reversed(path): + if component in {"content", "arguments", "name", "role"}: + return str(component) + return "other" + + +def iter_strings(value: Any, path: tuple[str | int, ...] = ()) -> Iterator[tuple[str, str]]: + if isinstance(value, str): + yield _field_label(path), value + elif isinstance(value, list): + for index, item in enumerate(value): + yield from iter_strings(item, path + (index,)) + elif isinstance(value, dict): + for key in sorted(value): + yield from iter_strings(value[key], path + (key,)) + + +def scan_record(value: dict[str, Any]) -> tuple[tuple[str, str, str], ...]: + """Return unique ``(signal, category, safe-field)`` tuples, never matches.""" + + findings: set[tuple[str, str, str]] = set() + for field, text in iter_strings(value): + for category, pattern in _SECRET_PATTERNS: + if pattern.search(text): + findings.add(("secret_pattern", category, field)) + for category, pattern in _PII_PATTERNS: + if pattern.search(text): + findings.add(("pii_like", category, field)) + return tuple(sorted(findings)) diff --git a/src/sftguard/regression.py b/src/sftguard/regression.py new file mode 100644 index 0000000..032f419 --- /dev/null +++ b/src/sftguard/regression.py @@ -0,0 +1,387 @@ +"""Paired target/retention Regression Cards with deterministic bootstrap CIs.""" + +from __future__ import annotations + +import math +import re +import statistics +from collections.abc import Iterable, Mapping +from dataclasses import dataclass +from typing import Any + +from . import __version__ +from .artifacts import canonical_json_bytes, seal_artifact, sha256_bytes +from .io import InputSummary, summarize_records +from .models import Status + +_IDENTIFIER = re.compile(r"^[A-Za-z0-9_.-]{1,64}$") +MAX_BOOTSTRAP_WORK = 10_000_000 + + +@dataclass(frozen=True) +class BootstrapConfig: + draws: int = 10_000 + confidence: float = 0.95 + seed: int = 2339 + + +class _XorShift32: + def __init__(self, seed: int): + self.state = seed & 0xFFFFFFFF or 0x6D2B79F5 + + def next_u32(self) -> int: + value = self.state + value ^= (value << 13) & 0xFFFFFFFF + value ^= value >> 17 + value ^= (value << 5) & 0xFFFFFFFF + self.state = value & 0xFFFFFFFF + return self.state + + def randbelow(self, stop: int) -> int: + if stop <= 0: + raise ValueError("stop must be positive") + return self.next_u32() % stop + + +def _round(value: float) -> float: + return round(value, 12) + + +def _percentile(sorted_values: list[float], probability: float) -> float: + if not sorted_values: + raise ValueError("cannot calculate a percentile of no values") + position = (len(sorted_values) - 1) * probability + lower = math.floor(position) + upper = math.ceil(position) + if lower == upper: + return sorted_values[lower] + fraction = position - lower + return sorted_values[lower] * (1.0 - fraction) + sorted_values[upper] * fraction + + +def seed_cluster_bootstrap( + values_by_seed: Mapping[int, tuple[float, ...]], config: BootstrapConfig +) -> tuple[float, float]: + """Cluster-resample seeds, retaining all pairs inside each selected seed.""" + + seeds = sorted(values_by_seed) + if not seeds: + raise ValueError("at least one seed cluster is required") + if config.draws <= 0 or config.draws > 100_000: + raise ValueError("bootstrap draws must be between 1 and 100000") + if not 0.0 < config.confidence < 1.0: + raise ValueError("bootstrap confidence must be between 0 and 1") + + generator = _XorShift32(config.seed) + estimates: list[float] = [] + for _ in range(config.draws): + sampled_values: list[float] = [] + for _ in seeds: + selected_seed = seeds[generator.randbelow(len(seeds))] + sampled_values.extend(values_by_seed[selected_seed]) + estimates.append(statistics.fmean(sampled_values)) + estimates.sort() + tail = (1.0 - config.confidence) / 2.0 + return _round(_percentile(estimates, tail)), _round(_percentile(estimates, 1.0 - tail)) + + +@dataclass(frozen=True) +class _Evaluation: + seed: int + sample_id: str + group: str + metric: str + value: float + + @property + def key(self) -> tuple[int, str, str, str]: + return self.seed, self.sample_id, self.group, self.metric + + +def _parse_evaluations( + records: Iterable[tuple[int, dict[str, Any]]], +) -> tuple[dict[tuple[int, str, str, str], _Evaluation], set[str]]: + parsed: dict[tuple[int, str, str, str], _Evaluation] = {} + errors: set[str] = set() + for _, record in records: + seed = record.get("seed") + sample_id = record.get("sample_id") + group = record.get("group") + metric = record.get("metric") + value = record.get("value") + valid = ( + isinstance(seed, int) + and not isinstance(seed, bool) + and isinstance(sample_id, str) + and 0 < len(sample_id) <= 512 + and isinstance(group, str) + and _IDENTIFIER.fullmatch(group) is not None + and isinstance(metric, str) + and _IDENTIFIER.fullmatch(metric) is not None + and isinstance(value, (int, float)) + and not isinstance(value, bool) + and math.isfinite(float(value)) + ) + if not valid: + errors.add("invalid_evaluation_record") + continue + evaluation = _Evaluation(seed, sample_id, group, metric, float(value)) + if evaluation.key in parsed: + errors.add("duplicate_pair_key") + else: + parsed[evaluation.key] = evaluation + return parsed, errors + + +def _parse_contract(contract: Any) -> tuple[list[dict[str, Any]], BootstrapConfig, set[str]]: + errors: set[str] = set() + if not isinstance(contract, dict): + return [], BootstrapConfig(), {"invalid_contract"} + raw_metrics = contract.get("metrics") + raw_bootstrap = contract.get("bootstrap", {}) + if not isinstance(raw_metrics, list) or not raw_metrics: + errors.add("invalid_contract_metrics") + raw_metrics = [] + if not isinstance(raw_bootstrap, dict): + errors.add("invalid_bootstrap_config") + raw_bootstrap = {} + + draws = raw_bootstrap.get("draws", 10_000) + confidence = raw_bootstrap.get("confidence", 0.95) + seed = raw_bootstrap.get("seed", 2339) + if ( + not isinstance(draws, int) + or isinstance(draws, bool) + or not 1 <= draws <= 100_000 + or not isinstance(confidence, (int, float)) + or isinstance(confidence, bool) + or not 0.0 < float(confidence) < 1.0 + or not isinstance(seed, int) + or isinstance(seed, bool) + ): + errors.add("invalid_bootstrap_config") + bootstrap = BootstrapConfig() + else: + bootstrap = BootstrapConfig(draws, float(confidence), seed) + + parsed: list[dict[str, Any]] = [] + seen: set[tuple[str, str]] = set() + groups: set[str] = set() + for raw in raw_metrics: + if not isinstance(raw, dict): + errors.add("invalid_metric_contract") + continue + group = raw.get("group") + metric = raw.get("metric") + higher = raw.get("higher_is_better") + if ( + group not in {"target", "retention"} + or not isinstance(metric, str) + or _IDENTIFIER.fullmatch(metric) is None + or not isinstance(higher, bool) + ): + errors.add("invalid_metric_contract") + continue + key = (group, metric) + if key in seen: + errors.add("duplicate_metric_contract") + continue + seen.add(key) + groups.add(group) + + if group == "target": + threshold = raw.get("min_improvement", 0.0) + threshold_name = "min_improvement" + if not isinstance(threshold, (int, float)) or isinstance(threshold, bool): + errors.add("invalid_metric_threshold") + continue + else: + threshold = raw.get("max_regression") + threshold_name = "max_regression" + if ( + not isinstance(threshold, (int, float)) + or isinstance(threshold, bool) + or float(threshold) < 0.0 + ): + errors.add("invalid_metric_threshold") + continue + if not math.isfinite(float(threshold)): + errors.add("invalid_metric_threshold") + continue + parsed.append( + { + "group": group, + "metric": metric, + "higher_is_better": higher, + threshold_name: float(threshold), + } + ) + + if groups != {"target", "retention"}: + errors.add("target_and_retention_contracts_required") + return parsed, bootstrap, errors + + +def _abstain_card( + reasons: set[str], + *, + base_summary: InputSummary, + adapter_summary: InputSummary, + contract_hash: str, +) -> dict[str, Any]: + return seal_artifact( + { + "schema_version": "sftguard.regression-card.v1", + "tool": {"name": "sftguard", "version": __version__}, + "status": Status.ABSTAIN, + "inputs": [ + base_summary.as_dict("base"), + adapter_summary.as_dict("adapter"), + {"split": "contract", "sha256": contract_hash}, + ], + "reason_codes": sorted(reasons), + "metrics": [], + "summary": {"metric_count": 0, "pair_count": 0, "seed_cluster_count": 0}, + } + ) + + +def _safe_summary(records: tuple[tuple[int, dict[str, Any]], ...]) -> InputSummary: + try: + return summarize_records(records) + except (TypeError, ValueError, RecursionError): + marker = canonical_json_bytes({"invalid_input": True, "record_count": len(records)}) + return InputSummary( + sha256=sha256_bytes(marker), + byte_count=len(marker), + record_count=len(records), + ) + + +def _safe_contract_hash(contract: Any) -> str: + try: + material = canonical_json_bytes(contract) + except (TypeError, ValueError, RecursionError): + material = canonical_json_bytes({"invalid_contract": True}) + return sha256_bytes(material) + + +def build_regression_card( + base_records: Iterable[tuple[int, dict[str, Any]]], + adapter_records: Iterable[tuple[int, dict[str, Any]]], + contract: Any, + *, + base_summary: InputSummary | None = None, + adapter_summary: InputSummary | None = None, +) -> dict[str, Any]: + base_materialized = tuple(base_records) + adapter_materialized = tuple(adapter_records) + base_summary = base_summary or _safe_summary(base_materialized) + adapter_summary = adapter_summary or _safe_summary(adapter_materialized) + contract_hash = _safe_contract_hash(contract) + + base, base_errors = _parse_evaluations(base_materialized) + adapter, adapter_errors = _parse_evaluations(adapter_materialized) + metric_contracts, bootstrap, contract_errors = _parse_contract(contract) + errors = base_errors | adapter_errors | contract_errors + if set(base) != set(adapter): + errors.add("unpaired_evaluation_records") + if not base: + errors.add("paired_evaluation_records_required") + + declared = {(item["group"], item["metric"]) for item in metric_contracts} + observed = {(item.group, item.metric) for item in base.values()} + if observed - declared: + errors.add("undeclared_metric_records") + if declared - observed: + errors.add("contract_metric_without_pairs") + if bootstrap.draws * len(base) > MAX_BOOTSTRAP_WORK: + errors.add("bootstrap_work_limit_exceeded") + if errors: + return _abstain_card( + errors, + base_summary=base_summary, + adapter_summary=adapter_summary, + contract_hash=contract_hash, + ) + + metric_results: list[dict[str, Any]] = [] + all_seeds: set[int] = set() + for metric_contract in sorted( + metric_contracts, key=lambda item: (item["group"], item["metric"]) + ): + group = metric_contract["group"] + metric = metric_contract["metric"] + sign = 1.0 if metric_contract["higher_is_better"] else -1.0 + values_by_seed: dict[int, list[float]] = {} + for key, base_value in base.items(): + if base_value.group != group or base_value.metric != metric: + continue + delta = sign * (adapter[key].value - base_value.value) + values_by_seed.setdefault(base_value.seed, []).append(delta) + all_seeds.add(base_value.seed) + frozen_clusters = {seed: tuple(values) for seed, values in sorted(values_by_seed.items())} + all_values = [value for values in frozen_clusters.values() for value in values] + estimate = _round(statistics.fmean(all_values)) + ci_low, ci_high = seed_cluster_bootstrap(frozen_clusters, bootstrap) + + if group == "target": + threshold_name = "min_improvement" + threshold = metric_contract[threshold_name] + passed = ci_low >= threshold + decision = "lower_confidence_bound_at_or_above_minimum" + else: + threshold_name = "max_regression" + threshold = metric_contract[threshold_name] + passed = ci_low >= -threshold + decision = "lower_confidence_bound_within_regression_budget" + metric_results.append( + { + "group": group, + "metric": metric, + "higher_is_better": metric_contract["higher_is_better"], + "status": Status.PASS if passed else Status.FAIL, + "pair_count": len(all_values), + "seed_cluster_count": len(frozen_clusters), + "mean_normalized_improvement": estimate, + "confidence_interval": {"low": ci_low, "high": ci_high}, + "threshold": {threshold_name: _round(threshold)}, + "decision_rule": decision, + } + ) + + overall = ( + Status.PASS + if all(result["status"] == Status.PASS for result in metric_results) + else Status.FAIL + ) + return seal_artifact( + { + "schema_version": "sftguard.regression-card.v1", + "tool": {"name": "sftguard", "version": __version__}, + "status": overall, + "inputs": [ + base_summary.as_dict("base"), + adapter_summary.as_dict("adapter"), + {"split": "contract", "sha256": contract_hash}, + ], + "bootstrap": { + "method": "paired_seed_cluster_xorshift32", + "draws": bootstrap.draws, + "confidence": bootstrap.confidence, + "seed": bootstrap.seed, + }, + "metrics": metric_results, + "summary": { + "metric_count": len(metric_results), + "pair_count": len(base), + "seed_cluster_count": len(all_seeds), + "passed_metric_count": sum( + result["status"] == Status.PASS for result in metric_results + ), + "failed_metric_count": sum( + result["status"] == Status.FAIL for result in metric_results + ), + }, + } + ) diff --git a/src/sftguard/synthetic.py b/src/sftguard/synthetic.py new file mode 100644 index 0000000..8f31207 --- /dev/null +++ b/src/sftguard/synthetic.py @@ -0,0 +1,156 @@ +"""Deterministic synthetic fault generation with phase-separated seeds.""" + +from __future__ import annotations + +import copy +import hashlib +from dataclasses import dataclass +from typing import Any + +from .artifacts import canonical_json_bytes, sha256_bytes + +DEVELOPMENT_SEEDS = tuple(range(10)) +CONFIRMATORY_SEEDS = tuple(range(4100, 4130)) + + +@dataclass(frozen=True) +class FaultSpec: + fault_id: str + expected_signal: str + severity: str + + +FAULT_SPECS = ( + FaultSpec("schema.invalid_role", "invalid_role", "error"), + FaultSpec("schema.role_order", "role_order", "error"), + FaultSpec("template.missing_eos", "missing_eos", "error"), + FaultSpec("mask.prompt_supervised", "prompt_token_supervised", "critical"), + FaultSpec("truncation.answer_removed", "assistant_fully_truncated", "critical"), + FaultSpec("data.exact_duplicate", "exact_duplicate", "warning"), + FaultSpec("data.prompt_conflict", "prompt_conflict", "error"), + FaultSpec("data.train_eval_leakage", "train_eval_leakage", "critical"), + FaultSpec("privacy.secret_pattern", "secret_pattern", "critical"), +) + + +@dataclass(frozen=True) +class SyntheticCase: + seed: int + fault: FaultSpec | None + train: tuple[tuple[int, dict[str, Any]], ...] + evaluation: tuple[tuple[int, dict[str, Any]], ...] + case_sha256: str + private_markers: tuple[str, ...] + + +def _marker(seed: int, split: str, index: int) -> str: + hexadecimal = hashlib.sha256(f"sftguard:{seed}:{split}:{index}".encode()).hexdigest()[:16] + return hexadecimal.translate(str.maketrans("0123456789abcdef", "abcdefghijklmnop")) + + +def _record(marker: str, answer: str, offset: int) -> dict[str, Any]: + return { + "messages": [ + {"role": "system", "content": "Return the requested class label."}, + {"role": "user", "content": f"Classify marker_{marker}."}, + {"role": "assistant", "content": answer}, + ], + "_sftguard": { + "token_ids": [10 + offset, 20 + offset, 30 + offset, 2], + "token_roles": ["system", "user", "assistant", "assistant"], + "loss_mask": [False, False, True, True], + "eos_token_id": 2, + "original_assistant_tokens": 2, + "truncated": False, + }, + } + + +def _clean(seed: int) -> tuple[list[dict[str, Any]], list[dict[str, Any]], list[str]]: + train_markers = [_marker(seed, "train", index) for index in range(3)] + evaluation_markers = [_marker(seed, "evaluation", index) for index in range(2)] + train = [ + _record(marker, "class_a" if index % 2 == 0 else "class_b", index) + for index, marker in enumerate(train_markers) + ] + evaluation = [ + _record(marker, "class_b" if index % 2 == 0 else "class_a", index + 10) + for index, marker in enumerate(evaluation_markers) + ] + return train, evaluation, train_markers + evaluation_markers + + +def _generate_case(seed: int, fault_id: str | None = None) -> SyntheticCase: + spec = next((item for item in FAULT_SPECS if item.fault_id == fault_id), None) + if fault_id is not None and spec is None: + raise ValueError("unknown synthetic fault identifier") + + train, evaluation, markers = _clean(seed) + private_markers = list(markers) + if spec is not None: + if spec.fault_id == "schema.invalid_role": + train[0]["messages"][1]["role"] = "invalid_development_role" + elif spec.fault_id == "schema.role_order": + train[0]["messages"].insert(0, {"role": "assistant", "content": "class_a"}) + elif spec.fault_id == "template.missing_eos": + train[0]["_sftguard"]["token_ids"][-1] = 99 + elif spec.fault_id == "mask.prompt_supervised": + train[0]["_sftguard"]["loss_mask"][1] = True + elif spec.fault_id == "truncation.answer_removed": + train[0]["_sftguard"] = { + "token_ids": [10, 20], + "token_roles": ["system", "user"], + "loss_mask": [False, False], + "eos_token_id": 2, + "original_assistant_tokens": 2, + "truncated": True, + } + elif spec.fault_id == "data.exact_duplicate": + train.append(copy.deepcopy(train[0])) + elif spec.fault_id == "data.prompt_conflict": + conflicting = copy.deepcopy(train[0]) + conflicting["messages"][-1]["content"] = "class_conflict" + train.append(conflicting) + elif spec.fault_id == "data.train_eval_leakage": + evaluation.append(copy.deepcopy(train[0])) + elif spec.fault_id == "privacy.secret_pattern": + secret = f"sk-test-{hashlib.sha256(f'secret:{seed}'.encode()).hexdigest()[:20]}" + train[0]["messages"][1]["content"] += f" Credential {secret}." + private_markers.append(secret) + + numbered_train = tuple((index + 1, record) for index, record in enumerate(train)) + numbered_evaluation = tuple((index + 1, record) for index, record in enumerate(evaluation)) + case_hash = sha256_bytes( + canonical_json_bytes( + { + "seed": seed, + "fault_id": fault_id, + "train": train, + "evaluation": evaluation, + } + ) + ) + return SyntheticCase( + seed=seed, + fault=spec, + train=numbered_train, + evaluation=numbered_evaluation, + case_sha256=case_hash, + private_markers=tuple(private_markers), + ) + + +def generate_case(seed: int, fault_id: str | None = None) -> SyntheticCase: + """Generate one development case without exposing confirmatory seeds.""" + + if seed not in DEVELOPMENT_SEEDS: + raise ValueError("synthetic generation is restricted to development seeds 0 through 9") + return _generate_case(seed, fault_id) + + +def generate_confirmatory_case(seed: int, fault_id: str | None = None) -> SyntheticCase: + """Generate a preregistered confirmatory case for the evidence runner only.""" + + if seed not in CONFIRMATORY_SEEDS: + raise ValueError("seed is outside the frozen confirmatory range") + return _generate_case(seed, fault_id) diff --git a/src/sftguard/tokenizer.py b/src/sftguard/tokenizer.py new file mode 100644 index 0000000..c1a2399 --- /dev/null +++ b/src/sftguard/tokenizer.py @@ -0,0 +1,94 @@ +"""Tokenizer-independent assistant-mask inspection contracts and fixtures.""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, Protocol + +FIXTURE_FIELD = "_sftguard" + + +@dataclass(frozen=True) +class InspectionSignal: + signal: str + severity: str + + +@dataclass(frozen=True) +class MaskInspection: + available: bool + signals: tuple[InspectionSignal, ...] = () + reason: str | None = None + + +class MaskInspector(Protocol): + def inspect(self, record: dict[str, Any]) -> MaskInspection: + """Inspect rendered tokens without returning their text.""" + + +class FixtureMaskInspector: + """Inspect a deterministic token/mask fixture embedded in a JSONL record. + + Real tokenizer integrations can implement :class:`MaskInspector` and supply + the same semantic output. The fixture deliberately contains token IDs and + role labels only; audit artifacts never copy either. + """ + + _ROLES = {"system", "user", "assistant", "tool"} + + def inspect(self, record: dict[str, Any]) -> MaskInspection: + raw = record.get(FIXTURE_FIELD) + if raw is None: + return MaskInspection(available=False, reason="assistant_mask_unavailable") + if not isinstance(raw, dict): + return MaskInspection(available=False, reason="mask_fixture_invalid") + + token_ids = raw.get("token_ids") + token_roles = raw.get("token_roles") + loss_mask = raw.get("loss_mask") + eos_token_id = raw.get("eos_token_id") + original_assistant_tokens = raw.get("original_assistant_tokens") + truncated = raw.get("truncated", False) + + valid = ( + isinstance(token_ids, list) + and isinstance(token_roles, list) + and isinstance(loss_mask, list) + and len(token_ids) == len(token_roles) == len(loss_mask) + and all(isinstance(token, int) and not isinstance(token, bool) for token in token_ids) + and all(isinstance(role, str) and role in self._ROLES for role in token_roles) + and all(isinstance(masked, bool) for masked in loss_mask) + and isinstance(eos_token_id, int) + and not isinstance(eos_token_id, bool) + and isinstance(original_assistant_tokens, int) + and not isinstance(original_assistant_tokens, bool) + and original_assistant_tokens >= 0 + and isinstance(truncated, bool) + ) + if not valid: + return MaskInspection(available=False, reason="mask_fixture_invalid") + + signals: set[InspectionSignal] = set() + if any( + masked and role != "assistant" + for masked, role in zip(loss_mask, token_roles, strict=True) + ): + signals.add(InspectionSignal("prompt_token_supervised", "critical")) + + supervised_assistant = [ + index + for index, (masked, role) in enumerate(zip(loss_mask, token_roles, strict=True)) + if masked and role == "assistant" + ] + assistant_positions = [ + index for index, role in enumerate(token_roles) if role == "assistant" + ] + if truncated and original_assistant_tokens > 0 and not supervised_assistant: + signals.add(InspectionSignal("assistant_fully_truncated", "critical")) + elif assistant_positions and token_ids[assistant_positions[-1]] != eos_token_id: + signals.add(InspectionSignal("missing_eos", "error")) + + return MaskInspection( + available=True, + signals=tuple(sorted(signals, key=lambda item: (item.signal, item.severity))), + ) diff --git a/tests/python/test_artifacts_io.py b/tests/python/test_artifacts_io.py new file mode 100644 index 0000000..b8dbb03 --- /dev/null +++ b/tests/python/test_artifacts_io.py @@ -0,0 +1,70 @@ +from __future__ import annotations + +import json + +import pytest + +from sftguard.artifacts import ( + canonical_json_bytes, + seal_artifact, + verify_artifact, + write_artifact, +) +from sftguard.io import InputError, read_json_bounded, read_jsonl_bounded + + +def test_canonical_json_is_sorted_compact_and_utf8() -> None: + assert canonical_json_bytes({"z": 1, "a": "Sarawak"}) == b'{"a":"Sarawak","z":1}' + + +def test_sealed_artifact_verifies_and_tampering_fails() -> None: + artifact = seal_artifact({"status": "PASS", "count": 2}) + assert verify_artifact(artifact) + artifact["count"] = 3 + assert not verify_artifact(artifact) + + +def test_write_artifact_round_trips(tmp_path) -> None: + artifact = seal_artifact({"status": "PASS"}) + destination = tmp_path / "nested" / "report.json" + write_artifact(destination, artifact) + assert json.loads(destination.read_text(encoding="utf-8")) == artifact + + +def test_jsonl_reader_streams_objects_and_hashes_bytes(tmp_path) -> None: + source = tmp_path / "records.jsonl" + source.write_bytes(b'{"a":1}\n\n{"b":2}\n') + parsed = read_jsonl_bounded(source) + assert parsed.records == ((1, {"a": 1}), (3, {"b": 2})) + assert parsed.summary.byte_count == source.stat().st_size + assert parsed.summary.record_count == 2 + assert len(parsed.summary.sha256) == 64 + + +@pytest.mark.parametrize( + ("contents", "kwargs"), + [ + (b'{"a":1}\n', {"max_file_bytes": 2}), + (b'{"long":"value"}\n', {"max_line_bytes": 4}), + (b'{"a":1}\n{"b":2}\n', {"max_records": 1}), + (b'{"a":1,"a":2}\n', {}), + (b"[1,2,3]\n", {}), + (b'{"score":NaN}\n', {}), + (b"\xff\xfe\n", {}), + ], +) +def test_jsonl_reader_rejects_malformed_or_bounded_input(tmp_path, contents, kwargs) -> None: + source = tmp_path / "bad.jsonl" + source.write_bytes(contents) + with pytest.raises(InputError): + read_jsonl_bounded(source, **kwargs) + + +def test_bounded_json_rejects_duplicate_keys_and_large_file(tmp_path) -> None: + source = tmp_path / "contract.json" + source.write_text('{"x":1,"x":2}', encoding="utf-8") + with pytest.raises(InputError): + read_json_bounded(source) + source.write_text('{"x":1}', encoding="utf-8") + with pytest.raises(InputError): + read_json_bounded(source, max_file_bytes=2) diff --git a/tests/python/test_audit.py b/tests/python/test_audit.py new file mode 100644 index 0000000..3742798 --- /dev/null +++ b/tests/python/test_audit.py @@ -0,0 +1,121 @@ +from __future__ import annotations + +import copy +import json + +import pytest + +from sftguard.audit import audit_files, audit_records +from sftguard.models import Status +from sftguard.synthetic import FAULT_SPECS, generate_case +from sftguard.tokenizer import FixtureMaskInspector + + +def test_clean_control_passes_without_findings() -> None: + case = generate_case(0) + report = audit_records(case.train, case.evaluation) + assert report["status"] == Status.PASS + assert report["findings"] == [] + assert report["summary"]["signal_counts"] == {} + + +@pytest.mark.parametrize("fault", FAULT_SPECS, ids=lambda fault: fault.fault_id) +def test_each_registered_fault_emits_its_primary_signal(fault) -> None: + case = generate_case(1, fault.fault_id) + report = audit_records(case.train, case.evaluation) + assert fault.expected_signal in report["summary"]["signal_counts"] + + +def test_warning_duplicate_is_detected_without_forcing_failure() -> None: + case = generate_case(2, "data.exact_duplicate") + report = audit_records(case.train, case.evaluation) + assert report["status"] == Status.PASS + assert report["summary"]["signal_counts"] == {"exact_duplicate": 1} + + +def test_missing_mask_abstains_and_is_not_silently_passed() -> None: + case = generate_case(2) + train = copy.deepcopy(case.train) + train[0][1].pop("_sftguard") + report = audit_records(train, case.evaluation) + assert report["status"] == Status.ABSTAIN + assert report["checks"]["assistant_mask"]["status"] == Status.ABSTAIN + assert report["summary"]["signal_counts"]["assistant_mask_unavailable"] == 1 + + +def test_known_failure_dominates_mask_abstention() -> None: + case = generate_case(3, "schema.invalid_role") + train = copy.deepcopy(case.train) + train[0][1].pop("_sftguard") + report = audit_records(train, case.evaluation) + assert report["status"] == Status.FAIL + + +def test_invalid_mask_fixture_abstains() -> None: + case = generate_case(4) + train = copy.deepcopy(case.train) + train[0][1]["_sftguard"]["loss_mask"] = [True] + report = audit_records(train, case.evaluation) + assert report["status"] == Status.ABSTAIN + assert "mask_fixture_invalid" in report["summary"]["signal_counts"] + + +def test_direct_nonfinite_record_fails_closed() -> None: + case = generate_case(4) + train = copy.deepcopy(case.train) + train[0][1]["score"] = float("nan") + report = audit_records(train, case.evaluation) + assert report["status"] == Status.FAIL + assert report["summary"]["signal_counts"]["invalid_record"] == 1 + + +def test_secret_and_pii_findings_are_redacted_and_values_absent() -> None: + case = generate_case(5) + secret = "sk-test-abcdefghijklmnopqrst" + email = "person@example.invalid" + train = copy.deepcopy(case.train) + train[0][1]["messages"][1]["content"] += f" {secret} {email}" + report = audit_records(train, case.evaluation) + rendered = json.dumps(report, sort_keys=True) + assert secret not in rendered + assert email not in rendered + findings = [ + finding + for finding in report["findings"] + if finding["signal"] in {"secret_pattern", "pii_like"} + ] + assert {finding["signal"] for finding in findings} == {"secret_pattern", "pii_like"} + assert all(finding["redacted"] is True for finding in findings) + assert all("category" in finding and "location" in finding for finding in findings) + + +def test_artifact_never_contains_prompt_or_response_text() -> None: + case = generate_case(6) + report = audit_records(case.train, case.evaluation) + rendered = json.dumps(report, sort_keys=True) + for _, record in (*case.train, *case.evaluation): + for message in record["messages"]: + assert message["content"] not in rendered + + +def test_mask_inspector_flags_each_semantic_fault() -> None: + inspector = FixtureMaskInspector() + for fault_id, signal in ( + ("template.missing_eos", "missing_eos"), + ("mask.prompt_supervised", "prompt_token_supervised"), + ("truncation.answer_removed", "assistant_fully_truncated"), + ): + case = generate_case(7, fault_id) + inspection = inspector.inspect(case.train[0][1]) + assert inspection.available + assert signal in {item.signal for item in inspection.signals} + + +def test_audit_files_returns_abstention_for_bad_input(tmp_path) -> None: + train = tmp_path / "train.jsonl" + evaluation = tmp_path / "eval.jsonl" + train.write_text("not-json\n", encoding="utf-8") + evaluation.write_text("{}\n", encoding="utf-8") + report = audit_files(str(train), str(evaluation)) + assert report["status"] == Status.ABSTAIN + assert report["inputs"] == [] diff --git a/tests/python/test_benchmark.py b/tests/python/test_benchmark.py new file mode 100644 index 0000000..755522a --- /dev/null +++ b/tests/python/test_benchmark.py @@ -0,0 +1,53 @@ +from __future__ import annotations + +import json + +import pytest + +from sftguard.artifacts import canonical_json_bytes, verify_artifact +from sftguard.benchmark import run_development_benchmark, validate_development_seeds +from sftguard.models import Status +from sftguard.synthetic import DEVELOPMENT_SEEDS, FAULT_SPECS, generate_case + + +def test_generator_accepts_every_development_seed() -> None: + for seed in DEVELOPMENT_SEEDS: + assert generate_case(seed).seed == seed + + +@pytest.mark.parametrize("seed", [-1, 10, 99]) +def test_generator_rejects_non_development_seeds(seed) -> None: + with pytest.raises(ValueError, match="development seeds"): + generate_case(seed) + + +def test_seed_validator_deduplicates_and_sorts() -> None: + assert validate_development_seeds([3, 1, 3, 2]) == (1, 2, 3) + + +def test_benchmark_passes_all_registered_faults_on_development_matrix() -> None: + result = run_development_benchmark() + assert result["status"] == Status.PASS + assert result["development_only"] is True + assert result["summary"]["case_count"] == len(DEVELOPMENT_SEEDS) * (len(FAULT_SPECS) + 1) + assert result["summary"]["macro_recall"] == 1.0 + assert result["summary"]["clean_false_positive_rate"] == 0.0 + assert all(result["gates"].values()) + assert verify_artifact(result) + + +def test_benchmark_is_byte_deterministic() -> None: + first = run_development_benchmark([0, 1, 2]) + second = run_development_benchmark([0, 1, 2]) + assert canonical_json_bytes(first) == canonical_json_bytes(second) + + +def test_benchmark_artifact_excludes_synthetic_record_text_and_secret() -> None: + case = generate_case(0, "privacy.secret_pattern") + result = run_development_benchmark([0]) + rendered = json.dumps(result, sort_keys=True) + for marker in case.private_markers: + assert marker not in rendered + for _, record in (*case.train, *case.evaluation): + for message in record["messages"]: + assert message["content"] not in rendered diff --git a/tests/python/test_cli.py b/tests/python/test_cli.py new file mode 100644 index 0000000..f55fb74 --- /dev/null +++ b/tests/python/test_cli.py @@ -0,0 +1,119 @@ +from __future__ import annotations + +import json +from pathlib import Path + +import pytest + +from sftguard.cli import build_parser, main + +REPOSITORY = Path(__file__).resolve().parents[2] + + +def _read(path: Path): + return json.loads(path.read_text(encoding="utf-8")) + + +def test_audit_cli_pass_exit_and_artifact(tmp_path) -> None: + output = tmp_path / "audit.json" + code = main( + [ + "audit", + "--train", + str(REPOSITORY / "examples" / "clean-train.jsonl"), + "--eval", + str(REPOSITORY / "examples" / "clean-eval.jsonl"), + "--output", + str(output), + ] + ) + assert code == 0 + assert _read(output)["status"] == "PASS" + + +def test_gate_cli_pass_exit_and_artifact(tmp_path) -> None: + output = tmp_path / "card.json" + code = main( + [ + "gate", + "--base", + str(REPOSITORY / "examples" / "base-eval.jsonl"), + "--adapter", + str(REPOSITORY / "examples" / "adapter-eval.jsonl"), + "--contract", + str(REPOSITORY / "examples" / "regression-contract.json"), + "--output", + str(output), + ] + ) + assert code == 0 + assert _read(output)["status"] == "PASS" + + +def test_demo_and_benchmark_cli_pass(tmp_path) -> None: + demo = tmp_path / "demo.json" + benchmark = tmp_path / "benchmark.json" + assert main(["demo", "--output", str(demo)]) == 0 + assert main(["benchmark", "--seeds", "0-2", "--output", str(benchmark)]) == 0 + assert _read(demo)["status"] == "PASS" + assert _read(benchmark)["development_only"] is True + + +def test_audit_cli_abstains_on_unreadable_input(tmp_path) -> None: + output = tmp_path / "audit.json" + code = main( + [ + "audit", + "--train", + str(tmp_path / "missing.jsonl"), + "--eval", + str(tmp_path / "also-missing.jsonl"), + "--output", + str(output), + ] + ) + assert code == 2 + assert _read(output)["status"] == "ABSTAIN" + + +def test_gate_cli_fail_exit(tmp_path) -> None: + base = REPOSITORY / "examples" / "base-eval.jsonl" + adapter = tmp_path / "adapter.jsonl" + rows = [json.loads(line) for line in base.read_text(encoding="utf-8").splitlines()] + for row in rows: + if row["group"] == "target": + row["value"] -= 0.1 + adapter.write_text( + "".join(json.dumps(row, separators=(",", ":")) + "\n" for row in rows), + encoding="utf-8", + ) + output = tmp_path / "card.json" + code = main( + [ + "gate", + "--base", + str(base), + "--adapter", + str(adapter), + "--contract", + str(REPOSITORY / "examples" / "regression-contract.json"), + "--output", + str(output), + ] + ) + assert code == 1 + assert _read(output)["status"] == "FAIL" + + +def test_cli_rejects_out_of_scope_benchmark_seed() -> None: + parser = build_parser() + with pytest.raises(SystemExit) as exc: + parser.parse_args(["benchmark", "--seeds", "10"]) + assert exc.value.code == 2 + + +def test_cli_rejects_huge_benchmark_range_without_expanding_it() -> None: + parser = build_parser() + with pytest.raises(SystemExit) as exc: + parser.parse_args(["benchmark", "--seeds", "0-1000000000"]) + assert exc.value.code == 2 diff --git a/tests/python/test_confirmatory_contract.py b/tests/python/test_confirmatory_contract.py new file mode 100644 index 0000000..14d80b6 --- /dev/null +++ b/tests/python/test_confirmatory_contract.py @@ -0,0 +1,57 @@ +from __future__ import annotations + +import json + +import pytest + +from sftguard.artifacts import verify_artifact +from sftguard.confirmatory import _build_artifact, pretty_json_bytes +from sftguard.synthetic import ( + CONFIRMATORY_SEEDS, + FAULT_SPECS, + generate_case, + generate_confirmatory_case, +) + +FAKE_COMMIT = "a" * 40 + + +def test_frozen_confirmatory_range_is_exact_without_generating_it() -> None: + assert CONFIRMATORY_SEEDS == tuple(range(4100, 4130)) + assert len(CONFIRMATORY_SEEDS) == 30 + + +def test_phase_separation_rejects_the_other_phase_before_generation() -> None: + with pytest.raises(ValueError, match="development seeds"): + generate_case(CONFIRMATORY_SEEDS[0]) + with pytest.raises(ValueError, match="confirmatory range"): + generate_confirmatory_case(0) + + +def test_confirmatory_payload_logic_is_covered_only_with_development_seed() -> None: + artifact = _build_artifact( + (0,), + generate_case, + protocol_commit=FAKE_COMMIT, + implementation_commit=FAKE_COMMIT, + fresh_process_byte_reproducible=True, + ) + assert artifact["status"] == "PASS" + assert artifact["carry_forward"] is True + assert artifact["summary"]["case_count"] == len(FAULT_SPECS) + 1 + assert artifact["gates"]["raw_synthetic_text_absent"] is True + assert all(artifact["gates"].values()) + assert verify_artifact(artifact) + assert json.loads(pretty_json_bytes(artifact)) == artifact + + +@pytest.mark.parametrize("commit", ["short", "G" * 40, "a" * 39]) +def test_evidence_payload_rejects_noncanonical_commits(commit: str) -> None: + with pytest.raises(ValueError, match="full lowercase Git commit"): + _build_artifact( + (0,), + generate_case, + protocol_commit=commit, + implementation_commit=FAKE_COMMIT, + fresh_process_byte_reproducible=True, + ) diff --git a/tests/python/test_regression.py b/tests/python/test_regression.py new file mode 100644 index 0000000..887acb3 --- /dev/null +++ b/tests/python/test_regression.py @@ -0,0 +1,153 @@ +from __future__ import annotations + +import json + +import pytest + +from sftguard.artifacts import canonical_json_bytes, verify_artifact +from sftguard.models import Status +from sftguard.regression import ( + MAX_BOOTSTRAP_WORK, + BootstrapConfig, + build_regression_card, + seed_cluster_bootstrap, +) + + +def _records( + target_delta: float = 0.12, + retention_delta: float = -0.005, + seed_count: int = 4, +): + base = [] + adapter = [] + line = 1 + for seed in range(seed_count): + for group, metric, baseline, delta in ( + ("target", "task_score", 0.5, target_delta), + ("retention", "general_score", 0.8, retention_delta), + ): + common = { + "seed": seed, + "sample_id": f"private-sample-{group}-{seed}", + "group": group, + "metric": metric, + } + base.append((line, {**common, "value": baseline})) + adapter.append((line, {**common, "value": baseline + delta})) + line += 1 + return tuple(base), tuple(adapter) + + +def _contract(): + return { + "bootstrap": {"draws": 1000, "confidence": 0.95, "seed": 2339}, + "metrics": [ + { + "group": "target", + "metric": "task_score", + "higher_is_better": True, + "min_improvement": 0.05, + }, + { + "group": "retention", + "metric": "general_score", + "higher_is_better": True, + "max_regression": 0.02, + }, + ], + } + + +def test_regression_card_passes_target_and_retention_contract() -> None: + base, adapter = _records() + card = build_regression_card(base, adapter, _contract()) + assert card["status"] == Status.PASS + assert all(metric["status"] == Status.PASS for metric in card["metrics"]) + assert card["summary"]["pair_count"] == 8 + assert verify_artifact(card) + + +@pytest.mark.parametrize( + ("target_delta", "retention_delta", "failed_group"), + [(0.01, -0.005, "target"), (0.12, -0.05, "retention")], +) +def test_regression_card_fails_violated_gate(target_delta, retention_delta, failed_group) -> None: + base, adapter = _records(target_delta, retention_delta) + card = build_regression_card(base, adapter, _contract()) + assert card["status"] == Status.FAIL + assert ( + next(item for item in card["metrics"] if item["group"] == failed_group)["status"] + == Status.FAIL + ) + + +def test_unpaired_records_abstain() -> None: + base, adapter = _records() + card = build_regression_card(base, adapter[:-1], _contract()) + assert card["status"] == Status.ABSTAIN + assert "unpaired_evaluation_records" in card["reason_codes"] + + +def test_duplicate_pair_key_abstains() -> None: + base, adapter = _records() + card = build_regression_card((*base, base[0]), adapter, _contract()) + assert card["status"] == Status.ABSTAIN + assert "duplicate_pair_key" in card["reason_codes"] + + +def test_contract_requires_both_target_and_retention() -> None: + base, adapter = _records() + contract = _contract() + contract["metrics"] = contract["metrics"][:1] + card = build_regression_card(base, adapter, contract) + assert card["status"] == Status.ABSTAIN + assert "target_and_retention_contracts_required" in card["reason_codes"] + + +def test_nonfinite_value_abstains_without_serializing_it() -> None: + base, adapter = _records() + broken = list(adapter) + broken[0] = (broken[0][0], {**broken[0][1], "value": float("nan")}) + card = build_regression_card(base, broken, _contract()) + assert card["status"] == Status.ABSTAIN + assert b"NaN" not in canonical_json_bytes(card) + + +def test_nonfinite_contract_abstains_without_serializing_it() -> None: + base, adapter = _records() + contract = _contract() + contract["metrics"][0]["min_improvement"] = float("nan") + card = build_regression_card(base, adapter, contract) + assert card["status"] == Status.ABSTAIN + assert "invalid_metric_threshold" in card["reason_codes"] + assert b"NaN" not in canonical_json_bytes(card) + + +def test_seed_cluster_bootstrap_is_deterministic() -> None: + clusters = {0: (0.1, 0.2), 1: (0.3,), 2: (0.4, 0.5)} + config = BootstrapConfig(draws=2000, confidence=0.9, seed=2339) + assert seed_cluster_bootstrap(clusters, config) == seed_cluster_bootstrap(clusters, config) + + +def test_constant_cluster_bootstrap_has_exact_interval() -> None: + interval = seed_cluster_bootstrap( + {0: (0.25,), 1: (0.25,), 2: (0.25,)}, BootstrapConfig(draws=100) + ) + assert interval == (0.25, 0.25) + + +def test_regression_card_contains_no_sample_ids() -> None: + base, adapter = _records() + rendered = json.dumps(build_regression_card(base, adapter, _contract()), sort_keys=True) + assert "private-sample" not in rendered + + +def test_bootstrap_joint_work_limit_abstains_before_resampling() -> None: + base, adapter = _records(seed_count=51) + contract = _contract() + contract["bootstrap"]["draws"] = 100_000 + assert contract["bootstrap"]["draws"] * len(base) > MAX_BOOTSTRAP_WORK + card = build_regression_card(base, adapter, contract) + assert card["status"] == Status.ABSTAIN + assert "bootstrap_work_limit_exceeded" in card["reason_codes"] diff --git a/web/README.md b/web/README.md new file mode 100644 index 0000000..c6aa2a7 --- /dev/null +++ b/web/README.md @@ -0,0 +1,41 @@ +# SFTGuard report viewer + +Static, local-first browser UI for SFTGuard aggregate report artifacts. + +```bash +npm ci +npm run dev +``` + +`npm run check` performs the TypeScript, unit-test, and production-build checks. + +## Supported artifacts + +The viewer validates and adapts these checked-in core artifact formats: + +- `sftguard.audit.v1`; +- `sftguard.regression-card.v1`; +- `sftguard.development-benchmark.v1`; and +- `sftguard.confirmatory-fault-matrix.v1`. + +The illustrative UI fixtures use the additional `sftguard.report.v1` render +envelope documented in `src/reportSchema.ts`. The allow-listed render model +contains: + +- a `PASS`, `FAIL`, or `ABSTAIN` decision; +- one audit, regression, or fault-matrix summary; +- aggregate issue categories and counts; +- content-addressed provenance; and +- claim and privacy boundaries. + +Unknown fields are not copied into the render model. Audit record locations, +record hashes and details, evaluation rows, benchmark `cases`, and confirmatory +protocol internals are intentionally discarded by the adapters. Missing +timestamps, commits, runtime details, or replay evidence are shown as +unavailable rather than inferred. Artifact digests are displayed as declared +and explicitly marked unverified because this static viewer does not reproduce +the Python canonical-JSON hash calculation. + +Imports are read into the current browser tab only and are neither uploaded nor +stored. The three sample artifacts are intentionally illustrative placeholders, +not confirmatory project evidence. diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..ed33e74 --- /dev/null +++ b/web/index.html @@ -0,0 +1,17 @@ + + + + + + + + SFTGuard — Local report viewer + + +
+ + + diff --git a/web/package-lock.json b/web/package-lock.json new file mode 100644 index 0000000..8bf6d35 --- /dev/null +++ b/web/package-lock.json @@ -0,0 +1,1373 @@ +{ + "name": "sftguard-report-viewer", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "sftguard-report-viewer", + "version": "0.1.0", + "dependencies": { + "react": "19.2.7", + "react-dom": "19.2.7" + }, + "devDependencies": { + "@types/react": "19.2.17", + "@types/react-dom": "19.2.3", + "@vitejs/plugin-react": "6.0.3", + "typescript": "5.9.3", + "vite": "8.1.5", + "vitest": "4.1.10" + } + }, + "node_modules/@emnapi/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.139.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.139.0.tgz", + "integrity": "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz", + "integrity": "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.5.tgz", + "integrity": "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.5.tgz", + "integrity": "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.5.tgz", + "integrity": "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.5.tgz", + "integrity": "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.5.tgz", + "integrity": "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.5.tgz", + "integrity": "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.5.tgz", + "integrity": "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.5.tgz", + "integrity": "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.5.tgz", + "integrity": "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.5.tgz", + "integrity": "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.5.tgz", + "integrity": "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.5.tgz", + "integrity": "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.6" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.5.tgz", + "integrity": "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.5.tgz", + "integrity": "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.2.17", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", + "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.3.tgz", + "integrity": "sha512-vmFvco5/QuC2f9Oj+wTk0+9XeDFkHxSamwZKYc7MxYwKICfvUvlMhqKI0VuICPltGqh1neqBKDvO4kes1ya8vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", + "babel-plugin-react-compiler": "^1.0.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + } + } + }, + "node_modules/@vitest/expect": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.10.tgz", + "integrity": "sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.10.tgz", + "integrity": "sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.10", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.10.tgz", + "integrity": "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.10.tgz", + "integrity": "sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.10", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.10.tgz", + "integrity": "sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.10", + "@vitest/utils": "4.1.10", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.10.tgz", + "integrity": "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.10.tgz", + "integrity": "sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.10", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/es-module-lexer": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", + "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/expect-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/nanoid": { + "version": "3.3.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", + "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/obug": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.4.tgz", + "integrity": "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.20", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.20.tgz", + "integrity": "sha512-lW616l85ucIQL+FocMmL7pQFPqBmwejrCMg+iPxyImlrANNJG9NHq/RkyCZopDhd8C3LA03PHRJDjkbGu8vvug==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.16", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", + "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz", + "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.7" + } + }, + "node_modules/rolldown": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz", + "integrity": "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.139.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.1.5", + "@rolldown/binding-darwin-arm64": "1.1.5", + "@rolldown/binding-darwin-x64": "1.1.5", + "@rolldown/binding-freebsd-x64": "1.1.5", + "@rolldown/binding-linux-arm-gnueabihf": "1.1.5", + "@rolldown/binding-linux-arm64-gnu": "1.1.5", + "@rolldown/binding-linux-arm64-musl": "1.1.5", + "@rolldown/binding-linux-ppc64-gnu": "1.1.5", + "@rolldown/binding-linux-s390x-gnu": "1.1.5", + "@rolldown/binding-linux-x64-gnu": "1.1.5", + "@rolldown/binding-linux-x64-musl": "1.1.5", + "@rolldown/binding-openharmony-arm64": "1.1.5", + "@rolldown/binding-wasm32-wasi": "1.1.5", + "@rolldown/binding-win32-arm64-msvc": "1.1.5", + "@rolldown/binding-win32-x64-msvc": "1.1.5" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", + "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", + "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyrainbow": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/vite": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.5.tgz", + "integrity": "sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.17", + "rolldown": "~1.1.5", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.3.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitest": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.10.tgz", + "integrity": "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.10", + "@vitest/mocker": "4.1.10", + "@vitest/pretty-format": "4.1.10", + "@vitest/runner": "4.1.10", + "@vitest/snapshot": "4.1.10", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.10", + "@vitest/browser-preview": "4.1.10", + "@vitest/browser-webdriverio": "4.1.10", + "@vitest/coverage-istanbul": "4.1.10", + "@vitest/coverage-v8": "4.1.10", + "@vitest/ui": "4.1.10", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + } + } +} diff --git a/web/package.json b/web/package.json new file mode 100644 index 0000000..d987643 --- /dev/null +++ b/web/package.json @@ -0,0 +1,26 @@ +{ + "name": "sftguard-report-viewer", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc --noEmit && vite build", + "preview": "vite preview", + "typecheck": "tsc --noEmit --pretty false", + "test": "vitest run", + "check": "npm run typecheck && npm run test && npm run build" + }, + "dependencies": { + "react": "19.2.7", + "react-dom": "19.2.7" + }, + "devDependencies": { + "@types/react": "19.2.17", + "@types/react-dom": "19.2.3", + "@vitejs/plugin-react": "6.0.3", + "typescript": "5.9.3", + "vite": "8.1.5", + "vitest": "4.1.10" + } +} diff --git a/web/src/App.test.tsx b/web/src/App.test.tsx new file mode 100644 index 0000000..b27bfb6 --- /dev/null +++ b/web/src/App.test.tsx @@ -0,0 +1,31 @@ +import { renderToStaticMarkup } from "react-dom/server"; +import { describe, expect, it } from "vitest"; +import App from "./App"; + +describe("SFTGuard report viewer", () => { + it("renders all decision states and the local-only import boundary", () => { + const html = renderToStaticMarkup(); + + expect(html).toContain("PASS"); + expect(html).toContain("FAIL"); + expect(html).toContain("ABSTAIN"); + expect(html).toContain("Choose local file"); + expect(html).toContain("Nothing is uploaded"); + expect(html).toContain("Claim boundary"); + expect(html).toContain("Privacy boundary"); + expect(html).toContain("Unverified — digest not recomputed"); + expect(html).not.toContain("dangerouslySetInnerHTML"); + expect(html).not.toMatch(/Ã|Â|â€|�/u); + }); + + it("uses accessible landmarks and explicit progress semantics", () => { + const html = renderToStaticMarkup(); + + expect(html).toContain('href="#report-main"'); + expect(html).toContain('
= { + PASS: { + eyebrow: "Release gate cleared", + action: "Proceed within this evidence contract", + explanation: "Every required check represented by this report passed.", + }, + FAIL: { + eyebrow: "Release gate blocked", + action: "Resolve failures before release", + explanation: "At least one required check violated its declared gate.", + }, + ABSTAIN: { + eyebrow: "Decision withheld", + action: "Supply the missing evidence", + explanation: "The harness could not make a trustworthy pass/fail decision.", + }, +}; + +function reportSourceKey(source: ReportSource): string { + return `${source.origin}:${source.report.reportId}`; +} + +function signedPoints(value: number): string { + const sign = value > 0 ? "+" : value < 0 ? "\u2212" : ""; + return `${sign}${Math.abs(value).toFixed(1)} pp`; +} + +function signedMetric( + value: number, + unit: ScoreContract["unit"], +): string { + if (unit === "percentage-points") return signedPoints(value); + const sign = value > 0 ? "+" : value < 0 ? "\u2212" : ""; + return `${sign}${Math.abs(value).toFixed(3)} normalized`; +} + +function compactNumber(value: number | null): string { + if (value === null) return "Not verified"; + return new Intl.NumberFormat("en", { notation: "compact" }).format(value); +} + +function displayDate(value: string): string { + return new Intl.DateTimeFormat("en", { + year: "numeric", + month: "short", + day: "2-digit", + hour: "2-digit", + minute: "2-digit", + hour12: false, + timeZone: "UTC", + timeZoneName: "short", + }).format(new Date(value)); +} + +function shortHash(hash: string): string { + return `${hash.slice(0, 10)}\u2026${hash.slice(-8)}`; +} + +function StatusBadge({ status, compact = false }: { status: ReportStatus; compact?: boolean }) { + return ( + + + ); +} + +function SectionHeading({ + number, + title, + detail, +}: { + number: string; + title: string; + detail: string; +}) { + return ( +
+ +
+

{title}

+

{detail}

+
+
+ ); +} + +function MetricCard({ + label, + value, + context, + tone = "neutral", + children, +}: { + label: string; + value: string; + context: string; + tone?: "positive" | "negative" | "caution" | "neutral"; + children?: ReactNode; +}) { + return ( +
+

{label}

+

{value}

+

{context}

+ {children} +
+ ); +} + +function ScoreEvidence({ score, label }: { score: ScoreContract; label: string }) { + if (score.baseScore !== null && score.adapterScore !== null) { + return ( +
+ Base {score.baseScore.toFixed(1)} +
+ ); + } + if (score.confidenceInterval) { + return ( +
+ Pairs {score.pairCount ?? "unavailable"} +
+ ); + } + return null; +} + +function RegressionContract({ report }: { report: SftGuardReport }) { + const regression = report.regression; + if (!regression) { + return ( +
+ + +
+ ); + } + + const targetPassed = regression.target.status === "PASS"; + const retentionPassed = regression.retention.status === "PASS"; + + return ( +
+ + + + + + +
+ ); +} + +function AuditContract({ report }: { report: SftGuardReport }) { + if (!report.audit) return null; + const audit = report.audit; + return ( +
+ + + +
+ ); +} + +function FaultCoveragePanel({ report }: { report: SftGuardReport }) { + const coverage = report.faultCoverage; + if (!coverage) { + return ( +
+

Not part of this artifact

+

No fault matrix attached

+

+ Open the checked-in fault-matrix sample or import a compatible report + to inspect detection coverage and clean-control behavior. +

+
+ ); + } + + const coveragePercent = + coverage.expectedFaultClasses === 0 + ? 0 + : (coverage.detectedFaultClasses / coverage.expectedFaultClasses) * 100; + return ( +
+
+
+ {coverage.detectedFaultClasses} + of {coverage.expectedFaultClasses} +
+
+
+

+ {coverage.carryForward ? "Carry-forward allowed" : "Carry-forward blocked"} +

+

+ This reports deterministic synthetic coverage of SFTGuard’s own + checks—not coverage of unknown real-world faults. +

+
+ + + +
+
+
+ ); +} + +function CoverageBar({ + label, + value, + target, + inverse = false, +}: { + label: string; + value: number; + target: string; + inverse?: boolean; +}) { + return ( +
+
+ {label} + {target} +
+
{value.toFixed(1)}%
+
+ +
+
+ ); +} + +function IssueSummary({ issues }: { issues: ReportIssue[] }) { + const categoryCounts = useMemo(() => { + const counts = new Map(); + for (const issue of issues) { + counts.set(issue.category, (counts.get(issue.category) ?? 0) + issue.count); + } + return [...counts.entries()].sort((left, right) => right[1] - left[1]); + }, [issues]); + + if (issues.length === 0) { + return ( +
+

No reported issues

+

The artifact contains no issue records.

+
+ ); + } + + return ( + <> +
+ {categoryCounts.map(([category, count]) => ( + + {category} {count} + + ))} +
+
+ + + + + + + + + + + + {issues.map((issue) => ( + + + + + + + ))} + +
Reported SFTGuard issues
SignalSeverityOutcomeCount
+
{issue.title}
+ {issue.id} +

{issue.detail}

+
+ + {issue.severity} + + + + {issue.count}
+
+ + ); +} + +function ProvenancePanel({ report }: { report: SftGuardReport }) { + const provenance = report.provenance; + const values: Array<{ + label: string; + value: string | null; + isHash?: boolean; + }> = [ + { label: "Tool version", value: provenance.toolVersion }, + { label: "Protocol commit", value: provenance.protocolCommit, isHash: true }, + { + label: "Implementation", + value: provenance.implementationCommit, + isHash: true, + }, + { label: "Input SHA-256", value: provenance.inputSha256, isHash: true }, + { + label: "Declared artifact SHA-256", + value: provenance.artifactSha256, + isHash: true, + }, + { + label: "Digest verification", + value: + provenance.integrityStatus === "verified" + ? "Verified" + : provenance.integrityStatus === "mismatch" + ? "Mismatch" + : "Unverified — digest not recomputed", + }, + { label: "Runtime", value: provenance.runtime }, + ]; + const replayTone = + provenance.deterministicReplay === null + ? "replay-unknown" + : provenance.deterministicReplay + ? "replay-pass" + : "replay-fail"; + const replayTitle = + provenance.deterministicReplay === null + ? "Unavailable" + : provenance.deterministicReplay + ? "Matched" + : "Not matched"; + const replayDetail = + provenance.deterministicReplay === null + ? "The artifact does not declare a replay result" + : provenance.deterministicReplay + ? "Canonical payload reproduced" + : "Reproduction claim is blocked"; + return ( +
+
+

Deterministic replay

+ {replayTitle} + {replayDetail} +
+
+ {values.map(({ label, value, isHash = false }) => { + return ( +
+
{label}
+
+ {value === null + ? "Unavailable in artifact" + : isHash + ? shortHash(value) + : value} +
+
+ ); + })} +
+
+ ); +} + +function BoundaryPanel({ report }: { report: SftGuardReport }) { + return ( +
+
+

Claim boundary

+

Evidence, not certification

+
    + {report.boundaries.claim.map((item) => ( +
  • {item}
  • + ))} +
+
+
+

Privacy boundary

+

Local viewing, narrow display

+
    + {report.boundaries.privacy.map((item) => ( +
  • {item}
  • + ))} +
+
+
+ ); +} + +export default function App() { + const [selectedKey, setSelectedKey] = useState( + reportSourceKey(SAMPLE_REPORTS[0]), + ); + const [importedReport, setImportedReport] = useState(null); + const [importMessage, setImportMessage] = useState( + "Choose a compatible JSON report. The file never leaves this browser tab.", + ); + const [importError, setImportError] = useState(false); + const fileInputRef = useRef(null); + + const reports = importedReport + ? [importedReport, ...SAMPLE_REPORTS] + : SAMPLE_REPORTS; + const activeSource = + reports.find((source) => reportSourceKey(source) === selectedKey) ?? reports[0]; + const report = activeSource.report; + const statusCopy = STATUS_COPY[report.status]; + + async function handleImport(event: ChangeEvent) { + const file = event.target.files?.[0]; + if (!file) return; + try { + if (file.size > MAX_REPORT_BYTES) { + throw new RangeError("Report exceeds the 2 MB local import limit."); + } + const parsed = parseReportText(await file.text()); + const source: ReportSource = { + report: parsed, + origin: "local import", + sourceLabel: file.name, + }; + setImportedReport(source); + setSelectedKey(reportSourceKey(source)); + setImportError(false); + setImportMessage(`${file.name} loaded locally. Nothing was uploaded or stored.`); + } catch (error) { + setImportError(true); + setImportMessage( + error instanceof Error ? error.message : "The report could not be read.", + ); + } finally { + event.target.value = ""; + } + } + + function removeImportedReport() { + setImportedReport(null); + setSelectedKey(reportSourceKey(SAMPLE_REPORTS[0])); + setImportError(false); + setImportMessage("Local import cleared from memory. Checked-in samples remain available."); + fileInputRef.current?.focus(); + } + + return ( + <> + + Skip to report + +
+
+ + + + SFTGuard + Local report viewer + + +
+
+
+ +
+ + +
+
+
+
+ + {reportTypeLabel(report.reportType)} + {activeSource.origin} + report-declared decision +
+

{statusCopy.eyebrow}

+

{report.title}

+

{report.summary}

+

{statusCopy.action}

+
+
+ {report.status.slice(0, 1)} +
+ {report.status} + {statusCopy.explanation} +
+
+
+ Report {report.reportId} + + {report.generatedAt + ? `Generated ${displayDate(report.generatedAt)}` + : "Generated time unavailable in artifact"} + +
+
+ +
+ +
Evaluation contract results
+ + +
+ +
+ +
Signals and issue categories
+ +
+ +
+ +
Fault coverage results
+ +
+ +
+ +
Artifact provenance
+ +
+ +
+ +
Claim and privacy boundaries
+ +
+ +
+ SFTGuard v0.1 viewer +

+ This viewer displays supplied evidence. It does not train a + model, certify safety, prove dataset quality, or detect unknown + failures. It validates the display schema but does not + recompute or independently attest declared hashes. Imported JSON remains in + this tab and is discarded on refresh. +

+
+
+
+
+ + ); +} diff --git a/web/src/data/sample-audit.json b/web/src/data/sample-audit.json new file mode 100644 index 0000000..9b39d28 --- /dev/null +++ b/web/src/data/sample-audit.json @@ -0,0 +1,54 @@ +{ + "schemaVersion": "sftguard.report.v1", + "reportType": "audit", + "reportId": "sample-audit-mask-abstention", + "title": "Training data and mask audit", + "generatedAt": "2026-07-19T09:30:00.000Z", + "status": "ABSTAIN", + "summary": "Structural checks completed, but the tokenizer could not expose a trustworthy assistant-token mask. Release approval is withheld.", + "audit": { + "recordsScanned": 1280, + "checksPassed": 7, + "checksTotal": 8, + "supervisedTokens": null + }, + "issues": [ + { + "id": "template.mask_unavailable", + "category": "Template & mask", + "severity": "error", + "outcome": "ABSTAIN", + "count": 1, + "title": "Assistant-token mask unavailable", + "detail": "The selected tokenizer template did not expose a verifiable assistant-token mask. SFTGuard did not infer one." + }, + { + "id": "data.exact_duplicate", + "category": "Data integrity", + "severity": "warning", + "outcome": "FAIL", + "count": 3, + "title": "Exact duplicate records", + "detail": "Three content hashes occurred more than once. Report data contains counts and hashes only." + } + ], + "provenance": { + "toolVersion": "0.1.0-sample", + "protocolCommit": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "implementationCommit": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "inputSha256": "1111111111111111111111111111111111111111111111111111111111111111", + "artifactSha256": "2222222222222222222222222222222222222222222222222222222222222222", + "runtime": "Python 3.12 · local illustrative fixture", + "deterministicReplay": true + }, + "boundaries": { + "claim": [ + "Illustrative UI fixture, not checked confirmatory evidence.", + "An abstention means the evidence contract was incomplete; it is not a pass." + ], + "privacy": [ + "No prompts, responses, model weights, or detected secret values are included.", + "Counts and content-addressed identifiers are illustrative placeholders." + ] + } +} diff --git a/web/src/data/sample-fault.json b/web/src/data/sample-fault.json new file mode 100644 index 0000000..96df531 --- /dev/null +++ b/web/src/data/sample-fault.json @@ -0,0 +1,65 @@ +{ + "schemaVersion": "sftguard.report.v1", + "reportType": "fault-matrix", + "reportId": "sample-fault-matrix-fail", + "title": "Synthetic fault-injection matrix", + "generatedAt": "2026-07-19T10:00:00.000Z", + "status": "FAIL", + "summary": "Eight of nine illustrative fault classes were detected. The macro-recall gate failed, so carry-forward remains blocked.", + "faultCoverage": { + "expectedFaultClasses": 9, + "detectedFaultClasses": 8, + "criticalRecallPercent": 100, + "macroRecallPercent": 88.9, + "cleanFalsePositiveRatePercent": 6.7, + "carryForward": false + }, + "issues": [ + { + "id": "data.exact_duplicate", + "category": "Data integrity", + "severity": "warning", + "outcome": "FAIL", + "count": 1, + "title": "Duplicate fault missed", + "detail": "The illustrative exact-duplicate case did not emit its required primary signal." + }, + { + "id": "gate.macro_recall", + "category": "Fault coverage", + "severity": "error", + "outcome": "FAIL", + "count": 1, + "title": "Macro recall below gate", + "detail": "Observed 88.9% against the frozen 90% minimum." + }, + { + "id": "gate.critical_recall", + "category": "Fault coverage", + "severity": "info", + "outcome": "PASS", + "count": 1, + "title": "Critical recall met", + "detail": "All illustrative critical fault classes emitted their expected primary signal." + } + ], + "provenance": { + "toolVersion": "0.1.0-sample", + "protocolCommit": "dddddddddddddddddddddddddddddddddddddddd", + "implementationCommit": "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + "inputSha256": "5555555555555555555555555555555555555555555555555555555555555555", + "artifactSha256": "6666666666666666666666666666666666666666666666666666666666666666", + "runtime": "Python 3.12 · local illustrative fixture", + "deterministicReplay": false + }, + "boundaries": { + "claim": [ + "Illustrative UI fixture; confirmatory seeds have not been represented here.", + "Synthetic fault detection does not establish coverage of unknown real-world failures." + ], + "privacy": [ + "Injected strings and raw records are intentionally omitted.", + "Only expected and observed aggregate signals would appear in a real artifact." + ] + } +} diff --git a/web/src/data/sample-regression.json b/web/src/data/sample-regression.json new file mode 100644 index 0000000..5af9c67 --- /dev/null +++ b/web/src/data/sample-regression.json @@ -0,0 +1,64 @@ +{ + "schemaVersion": "sftguard.report.v1", + "reportType": "regression", + "reportId": "sample-regression-card-pass", + "title": "Adapter release regression card", + "generatedAt": "2026-07-19T09:45:00.000Z", + "status": "PASS", + "summary": "The adapter cleared the declared target gain and stayed inside the retention regression budget for this supplied evaluation contract.", + "regression": { + "target": { + "label": "Domain task accuracy", + "baseScore": 61.2, + "adapterScore": 73.6, + "deltaPercentagePoints": 12.4, + "minimumGainPercentagePoints": 5 + }, + "retention": { + "label": "General capability score", + "baseScore": 78.8, + "adapterScore": 78.2, + "deltaPercentagePoints": -0.6, + "maximumRegressionPercentagePoints": 2 + } + }, + "issues": [ + { + "id": "regression.target_gain", + "category": "Target behavior", + "severity": "info", + "outcome": "PASS", + "count": 1, + "title": "Target gain cleared", + "detail": "Observed +12.4 percentage points against a preregistered +5.0 point minimum." + }, + { + "id": "regression.retention_budget", + "category": "Retention", + "severity": "info", + "outcome": "PASS", + "count": 1, + "title": "Retention stayed in budget", + "detail": "Observed −0.6 percentage points against a maximum permitted regression of −2.0 points." + } + ], + "provenance": { + "toolVersion": "0.1.0-sample", + "protocolCommit": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "implementationCommit": "cccccccccccccccccccccccccccccccccccccccc", + "inputSha256": "3333333333333333333333333333333333333333333333333333333333333333", + "artifactSha256": "4444444444444444444444444444444444444444444444444444444444444444", + "runtime": "Python 3.12 · local illustrative fixture", + "deterministicReplay": true + }, + "boundaries": { + "claim": [ + "Illustrative UI fixture, not a measured adapter result.", + "A pass applies only to the supplied target and retention evaluation records." + ], + "privacy": [ + "No evaluation examples or model outputs are embedded.", + "The report contains only aggregate scores and placeholder hashes." + ] + } +} diff --git a/web/src/main.tsx b/web/src/main.tsx new file mode 100644 index 0000000..d172db6 --- /dev/null +++ b/web/src/main.tsx @@ -0,0 +1,13 @@ +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import App from "./App"; +import "./styles.css"; + +const root = document.getElementById("root"); +if (!root) throw new Error("SFTGuard viewer root element is missing."); + +createRoot(root).render( + + + , +); diff --git a/web/src/reportSchema.test.ts b/web/src/reportSchema.test.ts new file mode 100644 index 0000000..fe14c31 --- /dev/null +++ b/web/src/reportSchema.test.ts @@ -0,0 +1,330 @@ +import { describe, expect, it } from "vitest"; +import auditJson from "./data/sample-audit.json"; +import faultJson from "./data/sample-fault.json"; +import regressionJson from "./data/sample-regression.json"; +import { + MAX_REPORT_BYTES, + parseReport, + parseReportText, +} from "./reportSchema"; + +const HASH_A = "a".repeat(64); +const HASH_B = "b".repeat(64); +const HASH_C = "c".repeat(64); + +const coreAuditArtifact = { + schema_version: "sftguard.audit.v1", + tool: { name: "sftguard", version: "0.1.0" }, + status: "ABSTAIN", + inputs: [ + { + split: "train", + sha256: HASH_A, + byte_count: 120, + record_count: 2, + }, + { + split: "evaluation", + sha256: HASH_B, + byte_count: 80, + record_count: 1, + }, + ], + checks: { + schema: { status: "PASS" }, + assistant_mask: { + status: "ABSTAIN", + reason: "private-record-marker", + }, + }, + summary: { + finding_count: 1, + signal_counts: { pii_like: 1 }, + error_or_critical_count: 0, + redacted_finding_count: 1, + }, + findings: [ + { + signal: "pii_like", + severity: "warning", + category: "Privacy pattern", + location: { split: "train", line: 2, field: "messages" }, + record_sha256: HASH_C, + details: { raw_match: "private-record-marker" }, + }, + ], + integrity: { algorithm: "sha256", canonical_payload_sha256: HASH_C }, +}; + +const coreRegressionArtifact = { + schema_version: "sftguard.regression-card.v1", + tool: { name: "sftguard", version: "0.1.0" }, + status: "FAIL", + inputs: [ + { + split: "base", + sha256: HASH_A, + byte_count: 400, + record_count: 8, + }, + { + split: "adapter", + sha256: HASH_B, + byte_count: 400, + record_count: 8, + }, + { split: "contract", sha256: HASH_C }, + ], + bootstrap: { + method: "paired_seed_cluster_xorshift32", + draws: 1000, + confidence: 0.95, + seed: 7, + }, + metrics: [ + { + group: "target", + metric: "answer_accuracy", + higher_is_better: true, + status: "PASS", + pair_count: 8, + seed_cluster_count: 2, + mean_normalized_improvement: 0.12, + confidence_interval: { low: 0.08, high: 0.16 }, + threshold: { min_improvement: 0.05 }, + decision_rule: "lower bound meets minimum improvement", + }, + { + group: "retention", + metric: "general_accuracy", + higher_is_better: true, + status: "FAIL", + pair_count: 8, + seed_cluster_count: 2, + mean_normalized_improvement: -0.04, + confidence_interval: { low: -0.06, high: -0.02 }, + threshold: { max_regression: 0.03 }, + decision_rule: "lower bound stays above retention floor", + }, + ], + summary: { metric_count: 2, pair_count: 8, seed_cluster_count: 2 }, + integrity: { algorithm: "sha256", canonical_payload_sha256: HASH_B }, +}; + +const developmentBenchmarkArtifact = { + schema_version: "sftguard.development-benchmark.v1", + tool: { name: "sftguard", version: "0.1.0" }, + development_only: true, + seed_range: { first: 1, last: 2, count: 2 }, + fault_count: 2, + summary: { + case_count: 6, + clean_control_count: 2, + clean_false_positive_count: 0, + clean_false_positive_rate: 0, + macro_recall: 0.75, + }, + per_fault: [ + { + fault_id: "train_eval_leakage", + expected_signal: "train_eval_leakage", + severity: "critical", + case_count: 2, + detected_count: 2, + recall: 1, + }, + { + fault_id: "missing_eos", + expected_signal: "missing_eos", + severity: "error", + case_count: 2, + detected_count: 1, + recall: 0.5, + }, + ], + cases: [{ raw_synthetic_text: "raw-case-marker" }], + gates: { + critical_fault_recall_100_percent: true, + macro_recall_at_least_90_percent: false, + clean_false_positive_rate_at_most_10_percent: true, + expected_and_observed_primary_recorded: true, + in_process_byte_reproducible: true, + raw_synthetic_text_absent: true, + }, + all_development_gates_passed: false, + status: "FAIL", + integrity: { algorithm: "sha256", canonical_payload_sha256: HASH_A }, +}; + +const confirmatoryFaultMatrixArtifact = { + schema_version: "sftguard.confirmatory-fault-matrix.v1", + tool: { name: "sftguard", version: "0.1.0" }, + provenance: { + implementation_git_commit: "1".repeat(40), + protocol_git_commit: "2".repeat(40), + python_implementation: "CPython", + python_version: "3.13", + }, + protocol: { private_fixture_note: "protocol-private-marker" }, + summary: { + case_count: 6, + clean_control_count: 2, + clean_false_positive_count: 0, + clean_false_positive_rate: 0, + fault_case_count: 4, + macro_recall: 0.75, + }, + per_fault: [ + { + fault_id: "train_eval_leakage", + expected_signal: "train_eval_leakage", + severity: "critical", + case_count: 2, + detected_count: 2, + recall: 1, + }, + { + fault_id: "missing_eos", + expected_signal: "missing_eos", + severity: "error", + case_count: 2, + detected_count: 1, + recall: 0.5, + }, + ], + cases: [{ raw_private_text: "confirmatory-case-marker" }], + gates: { + clean_false_positive_rate_at_most_10_percent: true, + critical_fault_recall_100_percent: true, + expected_and_observed_primary_recorded: true, + fresh_process_byte_reproducible: true, + macro_recall_at_least_90_percent: false, + raw_synthetic_text_absent: true, + }, + carry_forward: false, + status: "FAIL", + integrity: { algorithm: "sha256", canonical_payload_sha256: HASH_C }, +}; + +describe("SFTGuard report schema", () => { + it("loads the three checked-in status and report variants", () => { + const audit = parseReport(auditJson); + const regression = parseReport(regressionJson); + const fault = parseReport(faultJson); + + expect(audit.status).toBe("ABSTAIN"); + expect(audit.audit?.supervisedTokens).toBeNull(); + expect(regression.status).toBe("PASS"); + expect(regression.regression?.target.delta).toBe(12.4); + expect(fault.status).toBe("FAIL"); + expect(fault.faultCoverage?.detectedFaultClasses).toBe(8); + }); + + it("allow-lists aggregate fields and ignores unknown raw content", () => { + const report = parseReport({ + ...regressionJson, + rawPrompt: "this must never be rendered", + messages: [{ role: "user", content: "private" }], + }); + expect(report).not.toHaveProperty("rawPrompt"); + expect(report).not.toHaveProperty("messages"); + }); + + it("adapts the core audit without retaining record-level context", () => { + const report = parseReport(coreAuditArtifact); + const serialized = JSON.stringify(report); + + expect(report.reportType).toBe("audit"); + expect(report.status).toBe("ABSTAIN"); + expect(report.audit?.recordsScanned).toBe(3); + expect(report.audit?.checksPassed).toBe(1); + expect(report.generatedAt).toBeNull(); + expect(report.provenance.protocolCommit).toBeNull(); + expect(report.provenance.integrityStatus).toBe("unverified"); + expect(serialized).not.toContain("private-record-marker"); + expect(serialized).not.toContain("record_sha256"); + expect(report.issues[0]).not.toHaveProperty("location"); + }); + + it("adapts normalized regression metrics and preserves metric decisions", () => { + const report = parseReport(coreRegressionArtifact); + + expect(report.reportType).toBe("regression"); + expect(report.status).toBe("FAIL"); + expect(report.regression?.target.unit).toBe("normalized-delta"); + expect(report.regression?.target.delta).toBe(0.12); + expect(report.regression?.target.baseScore).toBeNull(); + expect(report.regression?.target.status).toBe("PASS"); + expect(report.regression?.retention.status).toBe("FAIL"); + expect(report.provenance.runtime).toBeNull(); + }); + + it("adapts development fault aggregates and drops the raw cases array", () => { + const report = parseReport(developmentBenchmarkArtifact); + const serialized = JSON.stringify(report); + + expect(report.reportType).toBe("fault-matrix"); + expect(report.faultCoverage?.detectedFaultClasses).toBe(1); + expect(report.faultCoverage?.criticalRecallPercent).toBe(100); + expect(report.faultCoverage?.macroRecallPercent).toBe(75); + expect(report.provenance.deterministicReplay).toBe(true); + expect(serialized).not.toContain("raw-case-marker"); + expect(report).not.toHaveProperty("cases"); + }); + + it("adapts confirmatory aggregates, commits, gates, and fresh-process replay", () => { + const report = parseReport(confirmatoryFaultMatrixArtifact); + const serialized = JSON.stringify(report); + + expect(report.title).toBe("Confirmatory fault-injection matrix"); + expect(report.status).toBe("FAIL"); + expect(report.faultCoverage?.carryForward).toBe(false); + expect(report.faultCoverage?.macroRecallPercent).toBe(75); + expect(report.provenance.protocolCommit).toBe("2".repeat(40)); + expect(report.provenance.implementationCommit).toBe("1".repeat(40)); + expect(report.provenance.runtime).toBe("CPython 3.13"); + expect(report.provenance.deterministicReplay).toBe(true); + expect(report.provenance.integrityStatus).toBe("unverified"); + expect(serialized).not.toContain("confirmatory-case-marker"); + expect(serialized).not.toContain("protocol-private-marker"); + expect(report).not.toHaveProperty("cases"); + expect(report).not.toHaveProperty("protocol"); + }); + + it("fails closed on unsupported status and malformed provenance", () => { + expect(() => + parseReport({ ...regressionJson, status: "UNKNOWN" }), + ).toThrow(/status must be one of/i); + expect(() => + parseReport({ + ...regressionJson, + provenance: { ...regressionJson.provenance, artifactSha256: "short" }, + }), + ).toThrow(/sha-256/i); + expect(() => + parseReport({ + ...regressionJson, + regression: { + ...regressionJson.regression, + target: { + ...regressionJson.regression.target, + deltaPercentagePoints: 99, + }, + }, + }), + ).toThrow(/adapterScore minus baseScore/i); + }); + + it("rejects invalid JSON and files beyond the local limit", () => { + expect(() => parseReportText("not json")).toThrow(/not valid json/i); + expect(() => parseReportText(" ".repeat(MAX_REPORT_BYTES + 1))).toThrow( + /2 MB/i, + ); + }); + + it("rejects unsupported artifact schemas", () => { + expect(() => + parseReport({ schema_version: "sftguard.unknown.v9" }), + ).toThrow(/unsupported sftguard report schema/i); + }); +}); diff --git a/web/src/reportSchema.ts b/web/src/reportSchema.ts new file mode 100644 index 0000000..d3b8862 --- /dev/null +++ b/web/src/reportSchema.ts @@ -0,0 +1,1325 @@ +/** + * SFTGuard browser render model and artifact adapters. + * + * The parser is deliberately allow-list based: it copies only documented + * aggregate fields into the render model. Unknown fields are ignored and are + * never rendered. Core artifact adapters intentionally discard record-level + * locations, hashes, details, and synthetic cases before React sees them. + */ +export const REPORT_SCHEMA_VERSION = "sftguard.report.v1" as const; +export const AUDIT_SCHEMA_VERSION = "sftguard.audit.v1" as const; +export const REGRESSION_SCHEMA_VERSION = + "sftguard.regression-card.v1" as const; +export const DEVELOPMENT_BENCHMARK_SCHEMA_VERSION = + "sftguard.development-benchmark.v1" as const; +export const CONFIRMATORY_FAULT_MATRIX_SCHEMA_VERSION = + "sftguard.confirmatory-fault-matrix.v1" as const; +export const MAX_REPORT_BYTES = 2 * 1024 * 1024; + +export type ReportStatus = "PASS" | "FAIL" | "ABSTAIN"; +export type ReportType = "audit" | "regression" | "fault-matrix"; +export type IssueSeverity = "critical" | "error" | "warning" | "info"; + +export interface ReportIssue { + id: string; + category: string; + severity: IssueSeverity; + outcome: ReportStatus; + count: number; + title: string; + detail: string; +} + +export interface AuditSummary { + recordsScanned: number; + checksPassed: number; + checksTotal: number; + supervisedTokens: number | null; +} + +export interface ScoreContract { + label: string; + status: "PASS" | "FAIL"; + baseScore: number | null; + adapterScore: number | null; + delta: number; + unit: "percentage-points" | "normalized-delta"; + pairCount?: number; + confidenceInterval?: { low: number; high: number }; +} + +export interface TargetScoreContract extends ScoreContract { + minimumGain: number; +} + +export interface RetentionScoreContract extends ScoreContract { + maximumRegression: number; +} + +export interface RegressionSummary { + target: TargetScoreContract; + retention: RetentionScoreContract; +} + +export interface FaultCoverage { + expectedFaultClasses: number; + detectedFaultClasses: number; + criticalRecallPercent: number; + macroRecallPercent: number; + cleanFalsePositiveRatePercent: number; + carryForward: boolean; +} + +export interface ReportProvenance { + toolVersion: string; + protocolCommit: string | null; + implementationCommit: string | null; + inputSha256: string | null; + artifactSha256: string; + integrityStatus: "verified" | "mismatch" | "unverified"; + runtime: string | null; + deterministicReplay: boolean | null; +} + +export interface ReportBoundaries { + claim: string[]; + privacy: string[]; +} + +export interface SftGuardReport { + schemaVersion: typeof REPORT_SCHEMA_VERSION; + reportType: ReportType; + reportId: string; + title: string; + generatedAt: string | null; + status: ReportStatus; + summary: string; + audit?: AuditSummary; + regression?: RegressionSummary; + faultCoverage?: FaultCoverage; + issues: ReportIssue[]; + provenance: ReportProvenance; + boundaries: ReportBoundaries; +} + +function asRecord(value: unknown, label: string): Record { + if (typeof value !== "object" || value === null || Array.isArray(value)) { + throw new TypeError(`${label} must be an object.`); + } + return value as Record; +} + +function asString( + value: unknown, + label: string, + maximumLength = 500, +): string { + if (typeof value !== "string" || value.trim().length === 0) { + throw new TypeError(`${label} must be a non-empty string.`); + } + if (value.length > maximumLength) { + throw new RangeError(`${label} is longer than ${maximumLength} characters.`); + } + return value; +} + +function asFiniteNumber(value: unknown, label: string): number { + if (typeof value !== "number" || !Number.isFinite(value)) { + throw new TypeError(`${label} must be a finite number.`); + } + return value; +} + +function asNonNegativeInteger(value: unknown, label: string): number { + const number = asFiniteNumber(value, label); + if (!Number.isSafeInteger(number) || number < 0) { + throw new RangeError(`${label} must be a non-negative safe integer.`); + } + return number; +} + +function asBoolean(value: unknown, label: string): boolean { + if (typeof value !== "boolean") { + throw new TypeError(`${label} must be true or false.`); + } + return value; +} + +function asEnum( + value: unknown, + label: string, + choices: readonly T[], +): T { + if (typeof value !== "string" || !choices.includes(value as T)) { + throw new RangeError(`${label} must be one of ${choices.join(", ")}.`); + } + return value as T; +} + +function asSha256(value: unknown, label: string): string { + const hash = asString(value, label, 64); + if (!/^[0-9a-f]{64}$/.test(hash)) { + throw new RangeError(`${label} must be a lowercase SHA-256 digest.`); + } + return hash; +} + +function asCommit(value: unknown, label: string): string { + const commit = asString(value, label, 40); + if (!/^[0-9a-f]{40}$/.test(commit)) { + throw new RangeError(`${label} must be a full lowercase Git commit.`); + } + return commit; +} + +function asStringArray(value: unknown, label: string): string[] { + if (!Array.isArray(value) || value.length === 0 || value.length > 20) { + throw new RangeError(`${label} must contain between 1 and 20 entries.`); + } + return value.map((entry, index) => + asString(entry, `${label}[${index}]`, 300), + ); +} + +function parseIssue(value: unknown, index: number): ReportIssue { + const issue = asRecord(value, `issues[${index}]`); + return { + id: asString(issue.id, `issues[${index}].id`, 120), + category: asString(issue.category, `issues[${index}].category`, 80), + severity: asEnum(issue.severity, `issues[${index}].severity`, [ + "critical", + "error", + "warning", + "info", + ] as const), + outcome: asEnum(issue.outcome, `issues[${index}].outcome`, [ + "PASS", + "FAIL", + "ABSTAIN", + ] as const), + count: asNonNegativeInteger(issue.count, `issues[${index}].count`), + title: asString(issue.title, `issues[${index}].title`, 140), + detail: asString(issue.detail, `issues[${index}].detail`, 500), + }; +} + +function parseAudit(value: unknown): AuditSummary { + const audit = asRecord(value, "audit"); + const supervisedTokens = + audit.supervisedTokens === null + ? null + : asNonNegativeInteger(audit.supervisedTokens, "audit.supervisedTokens"); + const parsed = { + recordsScanned: asNonNegativeInteger( + audit.recordsScanned, + "audit.recordsScanned", + ), + checksPassed: asNonNegativeInteger( + audit.checksPassed, + "audit.checksPassed", + ), + checksTotal: asNonNegativeInteger(audit.checksTotal, "audit.checksTotal"), + supervisedTokens, + }; + if (parsed.checksPassed > parsed.checksTotal) { + throw new RangeError("audit.checksPassed cannot exceed checksTotal."); + } + return parsed; +} + +function parseScore( + value: unknown, + label: string, +): Omit { + const score = asRecord(value, label); + const parsed = { + label: asString(score.label, `${label}.label`, 100), + baseScore: asFiniteNumber(score.baseScore, `${label}.baseScore`), + adapterScore: asFiniteNumber(score.adapterScore, `${label}.adapterScore`), + delta: asFiniteNumber( + score.deltaPercentagePoints, + `${label}.deltaPercentagePoints`, + ), + unit: "percentage-points" as const, + }; + const calculatedDelta = parsed.adapterScore - parsed.baseScore; + if (Math.abs(calculatedDelta - parsed.delta) > 1e-9) { + throw new RangeError( + `${label}.deltaPercentagePoints must equal adapterScore minus baseScore.`, + ); + } + return parsed; +} + +function parseRegression(value: unknown): RegressionSummary { + const regression = asRecord(value, "regression"); + const target = asRecord(regression.target, "regression.target"); + const retention = asRecord(regression.retention, "regression.retention"); + const targetScore = parseScore(target, "regression.target"); + const retentionScore = parseScore(retention, "regression.retention"); + const minimumGain = asFiniteNumber( + target.minimumGainPercentagePoints, + "regression.target.minimumGainPercentagePoints", + ); + const maximumRegression = asFiniteNumber( + retention.maximumRegressionPercentagePoints, + "regression.retention.maximumRegressionPercentagePoints", + ); + return { + target: { + ...targetScore, + status: targetScore.delta >= minimumGain ? "PASS" : "FAIL", + minimumGain, + }, + retention: { + ...retentionScore, + status: + retentionScore.delta >= -maximumRegression ? "PASS" : "FAIL", + maximumRegression, + }, + }; +} + +function asPercent(value: unknown, label: string): number { + const percent = asFiniteNumber(value, label); + if (percent < 0 || percent > 100) { + throw new RangeError(`${label} must be between 0 and 100.`); + } + return percent; +} + +function parseFaultCoverage(value: unknown): FaultCoverage { + const coverage = asRecord(value, "faultCoverage"); + const expected = asNonNegativeInteger( + coverage.expectedFaultClasses, + "faultCoverage.expectedFaultClasses", + ); + const detected = asNonNegativeInteger( + coverage.detectedFaultClasses, + "faultCoverage.detectedFaultClasses", + ); + if (detected > expected) { + throw new RangeError( + "faultCoverage.detectedFaultClasses cannot exceed expectedFaultClasses.", + ); + } + return { + expectedFaultClasses: expected, + detectedFaultClasses: detected, + criticalRecallPercent: asPercent( + coverage.criticalRecallPercent, + "faultCoverage.criticalRecallPercent", + ), + macroRecallPercent: asPercent( + coverage.macroRecallPercent, + "faultCoverage.macroRecallPercent", + ), + cleanFalsePositiveRatePercent: asPercent( + coverage.cleanFalsePositiveRatePercent, + "faultCoverage.cleanFalsePositiveRatePercent", + ), + carryForward: asBoolean( + coverage.carryForward, + "faultCoverage.carryForward", + ), + }; +} + +function parseProvenance(value: unknown): ReportProvenance { + const provenance = asRecord(value, "provenance"); + return { + toolVersion: asString(provenance.toolVersion, "provenance.toolVersion", 80), + protocolCommit: asCommit( + provenance.protocolCommit, + "provenance.protocolCommit", + ), + implementationCommit: asCommit( + provenance.implementationCommit, + "provenance.implementationCommit", + ), + inputSha256: asSha256(provenance.inputSha256, "provenance.inputSha256"), + artifactSha256: asSha256( + provenance.artifactSha256, + "provenance.artifactSha256", + ), + integrityStatus: "unverified", + runtime: asString(provenance.runtime, "provenance.runtime", 160), + deterministicReplay: asBoolean( + provenance.deterministicReplay, + "provenance.deterministicReplay", + ), + }; +} + +function parseEnvelopeReport(value: unknown): SftGuardReport { + const report = asRecord(value, "report"); + const schemaVersion = asEnum(report.schemaVersion, "schemaVersion", [ + REPORT_SCHEMA_VERSION, + ] as const); + const reportType = asEnum(report.reportType, "reportType", [ + "audit", + "regression", + "fault-matrix", + ] as const); + const generatedAt = asString(report.generatedAt, "generatedAt", 80); + if (!Number.isFinite(Date.parse(generatedAt))) { + throw new RangeError("generatedAt must be an ISO-compatible date string."); + } + if (!Array.isArray(report.issues) || report.issues.length > 100) { + throw new RangeError("issues must be an array with at most 100 entries."); + } + const boundaries = asRecord(report.boundaries, "boundaries"); + + const parsed: SftGuardReport = { + schemaVersion, + reportType, + reportId: asString(report.reportId, "reportId", 120), + title: asString(report.title, "title", 160), + generatedAt, + status: asEnum(report.status, "status", [ + "PASS", + "FAIL", + "ABSTAIN", + ] as const), + summary: asString(report.summary, "summary", 800), + issues: report.issues.map(parseIssue), + provenance: parseProvenance(report.provenance), + boundaries: { + claim: asStringArray(boundaries.claim, "boundaries.claim"), + privacy: asStringArray(boundaries.privacy, "boundaries.privacy"), + }, + }; + + if (reportType === "audit") parsed.audit = parseAudit(report.audit); + if (reportType === "regression") { + parsed.regression = parseRegression(report.regression); + } + if (reportType === "fault-matrix") { + parsed.faultCoverage = parseFaultCoverage(report.faultCoverage); + } + return parsed; +} + +const REPORT_STATUSES = ["PASS", "FAIL", "ABSTAIN"] as const; +const ISSUE_SEVERITIES = ["critical", "error", "warning", "info"] as const; + +function asBoundedArray( + value: unknown, + label: string, + maximumLength: number, +): unknown[] { + if (!Array.isArray(value) || value.length > maximumLength) { + throw new RangeError( + `${label} must be an array with at most ${maximumLength} entries.`, + ); + } + return value; +} + +function asFraction(value: unknown, label: string): number { + const fraction = asFiniteNumber(value, label); + if (fraction < 0 || fraction > 1) { + throw new RangeError(`${label} must be between 0 and 1.`); + } + return fraction; +} + +function asNonNegativeNumber(value: unknown, label: string): number { + const number = asFiniteNumber(value, label); + if (number < 0) { + throw new RangeError(`${label} must be non-negative.`); + } + return number; +} + +function humanizeIdentifier(value: string): string { + const words = value.replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim(); + return `${words.slice(0, 1).toUpperCase()}${words.slice(1)}`; +} + +function formatDecimal(value: number): string { + const normalized = Object.is(value, -0) ? 0 : value; + return normalized.toFixed(4).replace(/\.0+$|(?<=\.[0-9]*?)0+$/g, ""); +} + +function parseCoreMetadata( + report: Record, + label: string, + deterministicReplay: boolean | null = null, +): ReportProvenance { + const tool = asRecord(report.tool, `${label}.tool`); + if (asString(tool.name, `${label}.tool.name`, 80) !== "sftguard") { + throw new RangeError(`${label}.tool.name must be sftguard.`); + } + const integrity = asRecord(report.integrity, `${label}.integrity`); + if ( + asString(integrity.algorithm, `${label}.integrity.algorithm`, 20) !== + "sha256" + ) { + throw new RangeError(`${label}.integrity.algorithm must be sha256.`); + } + return { + toolVersion: asString(tool.version, `${label}.tool.version`, 80), + protocolCommit: null, + implementationCommit: null, + inputSha256: null, + artifactSha256: asSha256( + integrity.canonical_payload_sha256, + `${label}.integrity.canonical_payload_sha256`, + ), + integrityStatus: "unverified", + runtime: null, + deterministicReplay, + }; +} + +function validateCoreInputs( + value: unknown, + label: string, + requireRecordCount = false, +): number { + const inputs = asBoundedArray(value, label, 100); + let records = 0; + inputs.forEach((entry, index) => { + const input = asRecord(entry, `${label}[${index}]`); + asString(input.split, `${label}[${index}].split`, 80); + asSha256(input.sha256, `${label}[${index}].sha256`); + if (input.byte_count !== undefined) { + asNonNegativeInteger(input.byte_count, `${label}[${index}].byte_count`); + } + if (input.record_count === undefined) { + if (requireRecordCount) { + throw new TypeError(`${label}[${index}].record_count is required.`); + } + } else { + records += asNonNegativeInteger( + input.record_count, + `${label}[${index}].record_count`, + ); + } + }); + return records; +} + +function parseAuditArtifact(report: Record): SftGuardReport { + const label = "audit artifact"; + const status = asEnum(report.status, `${label}.status`, REPORT_STATUSES); + const provenance = parseCoreMetadata(report, label); + const recordsScanned = validateCoreInputs(report.inputs, `${label}.inputs`, true); + + const checks = asRecord(report.checks, `${label}.checks`); + const checkEntries = Object.entries(checks); + if (checkEntries.length === 0 || checkEntries.length > 100) { + throw new RangeError(`${label}.checks must contain between 1 and 100 entries.`); + } + const parsedChecks = checkEntries.map(([name, value]) => { + asString(name, `${label}.checks key`, 100); + const check = asRecord(value, `${label}.checks.${name}`); + return { + name, + status: asEnum( + check.status, + `${label}.checks.${name}.status`, + REPORT_STATUSES, + ), + }; + }); + + const findings = asBoundedArray(report.findings, `${label}.findings`, 100_000); + type AuditAggregate = { + signal: string; + category: string; + severity: IssueSeverity; + count: number; + }; + const aggregates = new Map(); + const derivedSignalCounts = new Map(); + findings.forEach((entry, index) => { + const finding = asRecord(entry, `${label}.findings[${index}]`); + const signal = asString( + finding.signal, + `${label}.findings[${index}].signal`, + 120, + ); + const severity = asEnum( + finding.severity, + `${label}.findings[${index}].severity`, + ISSUE_SEVERITIES, + ); + const location = asRecord( + finding.location, + `${label}.findings[${index}].location`, + ); + asEnum( + location.split, + `${label}.findings[${index}].location.split`, + ["train", "evaluation"] as const, + ); + const line = asNonNegativeInteger( + location.line, + `${label}.findings[${index}].location.line`, + ); + if (line < 1) { + throw new RangeError( + `${label}.findings[${index}].location.line must be at least 1.`, + ); + } + if (location.field !== undefined) { + asString( + location.field, + `${label}.findings[${index}].location.field`, + 160, + ); + } + const category = + finding.category === undefined + ? "Audit finding" + : asString( + finding.category, + `${label}.findings[${index}].category`, + 80, + ); + const key = `${category}\u0000${signal}\u0000${severity}`; + const aggregate = aggregates.get(key); + if (aggregate) { + aggregate.count += 1; + } else { + if (aggregates.size >= 100) { + throw new RangeError( + `${label}.findings contain more than 100 aggregate categories.`, + ); + } + aggregates.set(key, { signal, category, severity, count: 1 }); + } + derivedSignalCounts.set( + signal, + (derivedSignalCounts.get(signal) ?? 0) + 1, + ); + }); + + const summary = asRecord(report.summary, `${label}.summary`); + const findingCount = asNonNegativeInteger( + summary.finding_count, + `${label}.summary.finding_count`, + ); + if (findingCount !== findings.length) { + throw new RangeError( + `${label}.summary.finding_count must match findings length.`, + ); + } + const signalCounts = asRecord( + summary.signal_counts, + `${label}.summary.signal_counts`, + ); + if (Object.keys(signalCounts).length > 100) { + throw new RangeError(`${label}.summary.signal_counts has too many entries.`); + } + for (const [signal, count] of Object.entries(signalCounts)) { + const parsedCount = asNonNegativeInteger( + count, + `${label}.summary.signal_counts.${signal}`, + ); + if (derivedSignalCounts.get(signal) !== parsedCount) { + throw new RangeError( + `${label}.summary.signal_counts must match aggregate findings.`, + ); + } + } + if (Object.keys(signalCounts).length !== derivedSignalCounts.size) { + throw new RangeError( + `${label}.summary.signal_counts must cover every finding signal.`, + ); + } + + const issues: ReportIssue[] = [...aggregates.values()].map( + (aggregate, index) => ({ + id: `audit-signal-${index + 1}`, + category: aggregate.category, + severity: aggregate.severity, + outcome: + status === "ABSTAIN" + ? "ABSTAIN" + : aggregate.severity === "critical" || aggregate.severity === "error" + ? "FAIL" + : "PASS", + count: aggregate.count, + title: humanizeIdentifier(aggregate.signal), + detail: `${aggregate.count} aggregate finding${aggregate.count === 1 ? "" : "s"} reported. Record locations and record-level details are not copied into this viewer.`, + }), + ); + for (const check of parsedChecks.filter((entry) => entry.status === "ABSTAIN")) { + issues.push({ + id: `audit-check-${check.name}`, + category: "Audit check", + severity: "warning", + outcome: "ABSTAIN", + count: 1, + title: humanizeIdentifier(check.name), + detail: + "This check abstained. Its raw reason and record context remain outside the aggregate render model.", + }); + } + + return { + schemaVersion: REPORT_SCHEMA_VERSION, + reportType: "audit", + reportId: `core-audit-${provenance.artifactSha256.slice(0, 12)}`, + title: "Dataset and mask audit", + generatedAt: null, + status, + summary: `The audit declared ${status} across ${recordsScanned} record${recordsScanned === 1 ? "" : "s"}, ${findingCount} finding${findingCount === 1 ? "" : "s"}, and ${parsedChecks.length} check${parsedChecks.length === 1 ? "" : "s"}.`, + audit: { + recordsScanned, + checksPassed: parsedChecks.filter((entry) => entry.status === "PASS").length, + checksTotal: parsedChecks.length, + supervisedTokens: null, + }, + issues, + provenance, + boundaries: { + claim: [ + "The decision reflects the supplied local audit artifact only.", + "A passing audit does not prove dataset quality, model quality, or model safety.", + ], + privacy: [ + "Only aggregate signals, severities, and counts are rendered.", + "Record locations, record hashes, finding details, and source records are discarded by the adapter.", + ], + }, + }; +} + +interface ParsedCoreMetric { + group: "target" | "retention"; + metric: string; + status: "PASS" | "FAIL"; + pairCount: number; + mean: number; + interval: { low: number; high: number }; + threshold: number; +} + +function parseCoreMetric(value: unknown, index: number): ParsedCoreMetric { + const label = `regression artifact.metrics[${index}]`; + const metric = asRecord(value, label); + const group = asEnum(metric.group, `${label}.group`, [ + "target", + "retention", + ] as const); + if (metric.higher_is_better !== undefined) { + asBoolean(metric.higher_is_better, `${label}.higher_is_better`); + } + if (metric.seed_cluster_count !== undefined) { + asNonNegativeInteger( + metric.seed_cluster_count, + `${label}.seed_cluster_count`, + ); + } + const interval = asRecord( + metric.confidence_interval, + `${label}.confidence_interval`, + ); + const low = asFiniteNumber(interval.low, `${label}.confidence_interval.low`); + const high = asFiniteNumber( + interval.high, + `${label}.confidence_interval.high`, + ); + if (low > high) { + throw new RangeError(`${label}.confidence_interval.low cannot exceed high.`); + } + const threshold = asRecord(metric.threshold, `${label}.threshold`); + const thresholdKey = + group === "target" ? "min_improvement" : "max_regression"; + asString(metric.decision_rule, `${label}.decision_rule`, 300); + return { + group, + metric: asString(metric.metric, `${label}.metric`, 120), + status: asEnum(metric.status, `${label}.status`, ["PASS", "FAIL"] as const), + pairCount: asNonNegativeInteger(metric.pair_count, `${label}.pair_count`), + mean: asFiniteNumber( + metric.mean_normalized_improvement, + `${label}.mean_normalized_improvement`, + ), + interval: { low, high }, + threshold: asNonNegativeNumber( + threshold[thresholdKey], + `${label}.threshold.${thresholdKey}`, + ), + }; +} + +function parseRegressionArtifact( + report: Record, +): SftGuardReport { + const label = "regression artifact"; + const status = asEnum(report.status, `${label}.status`, REPORT_STATUSES); + const provenance = parseCoreMetadata(report, label); + validateCoreInputs(report.inputs, `${label}.inputs`); + const rawMetrics = asBoundedArray(report.metrics, `${label}.metrics`, 100); + const metrics = rawMetrics + .map(parseCoreMetric) + .sort((left, right) => + `${left.group}:${left.metric}`.localeCompare(`${right.group}:${right.metric}`), + ); + const summary = asRecord(report.summary, `${label}.summary`); + const metricCount = asNonNegativeInteger( + summary.metric_count, + `${label}.summary.metric_count`, + ); + if (metricCount !== metrics.length) { + throw new RangeError(`${label}.summary.metric_count must match metrics length.`); + } + asNonNegativeInteger(summary.pair_count, `${label}.summary.pair_count`); + asNonNegativeInteger( + summary.seed_cluster_count, + `${label}.summary.seed_cluster_count`, + ); + + const reasonCodes = + report.reason_codes === undefined + ? [] + : asBoundedArray(report.reason_codes, `${label}.reason_codes`, 100).map( + (reason, index) => + asString(reason, `${label}.reason_codes[${index}]`, 160), + ); + const issues: ReportIssue[] = metrics.map((metric, index) => ({ + id: `regression-metric-${index + 1}`, + category: + metric.group === "target" ? "Target behavior" : "Retention behavior", + severity: metric.status === "FAIL" ? "error" : "info", + outcome: metric.status, + count: 1, + title: metric.metric, + detail: `Mean normalized improvement ${formatDecimal(metric.mean)} with reported interval [${formatDecimal(metric.interval.low)}, ${formatDecimal(metric.interval.high)}] across ${metric.pairCount} pairs; ${metric.group === "target" ? "minimum improvement" : "maximum regression"} ${formatDecimal(metric.threshold)}.`, + })); + reasonCodes.forEach((reason, index) => { + issues.push({ + id: `regression-reason-${index + 1}`, + category: "Decision evidence", + severity: "warning", + outcome: "ABSTAIN", + count: 1, + title: humanizeIdentifier(reason), + detail: + "The regression card reported this aggregate reason code without exposing evaluation examples.", + }); + }); + if (status === "ABSTAIN" && reasonCodes.length === 0) { + issues.push({ + id: "regression-reason-unavailable", + category: "Decision evidence", + severity: "warning", + outcome: "ABSTAIN", + count: 1, + title: "Reason unavailable", + detail: "The artifact abstained without a reason code in its aggregate fields.", + }); + } + + const target = metrics.find((metric) => metric.group === "target"); + const retention = metrics.find((metric) => metric.group === "retention"); + const adapted: SftGuardReport = { + schemaVersion: REPORT_SCHEMA_VERSION, + reportType: "regression", + reportId: `core-regression-${provenance.artifactSha256.slice(0, 12)}`, + title: "Target and retention regression card", + generatedAt: null, + status, + summary: `The regression card declared ${status} across ${metricCount} aggregate metric${metricCount === 1 ? "" : "s"}. Estimates are normalized paired improvements, not raw percentage-point scores.`, + issues, + provenance, + boundaries: { + claim: [ + "The decision applies only to the supplied metric contract and paired evaluation artifact.", + "Passing declared gates does not establish broad model quality or safety.", + ], + privacy: [ + "Only aggregate estimates, intervals, thresholds, pair counts, and reason codes are rendered.", + "Evaluation rows, prompts, responses, and per-example results are not copied into the viewer.", + ], + }, + }; + if (target && retention) { + adapted.regression = { + target: { + label: target.metric, + status: target.status, + baseScore: null, + adapterScore: null, + delta: target.mean, + unit: "normalized-delta", + pairCount: target.pairCount, + confidenceInterval: target.interval, + minimumGain: target.threshold, + }, + retention: { + label: retention.metric, + status: retention.status, + baseScore: null, + adapterScore: null, + delta: retention.mean, + unit: "normalized-delta", + pairCount: retention.pairCount, + confidenceInterval: retention.interval, + maximumRegression: retention.threshold, + }, + }; + } + return adapted; +} + +interface ParsedFaultClass { + faultId: string; + expectedSignal: string; + severity: IssueSeverity; + caseCount: number; + detectedCount: number; + recall: number; +} + +function parseFaultClasses(value: unknown, label: string): ParsedFaultClass[] { + return asBoundedArray(value, label, 90).map((entry, index) => { + const faultLabel = `${label}[${index}]`; + const fault = asRecord(entry, faultLabel); + const caseCount = asNonNegativeInteger( + fault.case_count, + `${faultLabel}.case_count`, + ); + if (caseCount < 1) { + throw new RangeError(`${faultLabel}.case_count must be at least 1.`); + } + const detectedCount = asNonNegativeInteger( + fault.detected_count, + `${faultLabel}.detected_count`, + ); + if (detectedCount > caseCount) { + throw new RangeError( + `${faultLabel}.detected_count cannot exceed case_count.`, + ); + } + const recall = asFraction(fault.recall, `${faultLabel}.recall`); + if (Math.abs(recall - detectedCount / caseCount) > 1e-9) { + throw new RangeError(`${faultLabel}.recall must match aggregate counts.`); + } + return { + faultId: asString(fault.fault_id, `${faultLabel}.fault_id`, 120), + expectedSignal: asString( + fault.expected_signal, + `${faultLabel}.expected_signal`, + 120, + ), + severity: asEnum( + fault.severity, + `${faultLabel}.severity`, + ISSUE_SEVERITIES, + ), + caseCount, + detectedCount, + recall, + }; + }); +} + +function parseDevelopmentBenchmarkArtifact( + report: Record, +): SftGuardReport { + const label = "development benchmark artifact"; + if (asBoolean(report.development_only, `${label}.development_only`) !== true) { + throw new RangeError(`${label}.development_only must be true.`); + } + const status = asEnum(report.status, `${label}.status`, ["PASS", "FAIL"] as const); + const gates = asRecord(report.gates, `${label}.gates`); + const gateKeys = [ + "critical_fault_recall_100_percent", + "macro_recall_at_least_90_percent", + "clean_false_positive_rate_at_most_10_percent", + "expected_and_observed_primary_recorded", + "in_process_byte_reproducible", + "raw_synthetic_text_absent", + ] as const; + const parsedGates = new Map( + gateKeys.map((key) => [ + key, + asBoolean(gates[key], `${label}.gates.${key}`), + ]), + ); + const allGatesPassed = asBoolean( + report.all_development_gates_passed, + `${label}.all_development_gates_passed`, + ); + if (allGatesPassed !== [...parsedGates.values()].every(Boolean)) { + throw new RangeError( + `${label}.all_development_gates_passed must match the declared gates.`, + ); + } + if ((status === "PASS") !== allGatesPassed) { + throw new RangeError(`${label}.status must match the development gates.`); + } + const provenance = parseCoreMetadata( + report, + label, + parsedGates.get("in_process_byte_reproducible") ?? null, + ); + + const seedRange = asRecord(report.seed_range, `${label}.seed_range`); + asNonNegativeInteger(seedRange.first, `${label}.seed_range.first`); + asNonNegativeInteger(seedRange.last, `${label}.seed_range.last`); + const seedCount = asNonNegativeInteger( + seedRange.count, + `${label}.seed_range.count`, + ); + if (seedCount < 1) { + throw new RangeError(`${label}.seed_range.count must be at least 1.`); + } + + const faults = parseFaultClasses(report.per_fault, `${label}.per_fault`); + const faultCount = asNonNegativeInteger( + report.fault_count, + `${label}.fault_count`, + ); + if (faultCount !== faults.length) { + throw new RangeError(`${label}.fault_count must match per_fault length.`); + } + + const summary = asRecord(report.summary, `${label}.summary`); + const caseCount = asNonNegativeInteger( + summary.case_count, + `${label}.summary.case_count`, + ); + const cleanControlCount = asNonNegativeInteger( + summary.clean_control_count, + `${label}.summary.clean_control_count`, + ); + const cleanFalsePositiveCount = asNonNegativeInteger( + summary.clean_false_positive_count, + `${label}.summary.clean_false_positive_count`, + ); + if (cleanFalsePositiveCount > cleanControlCount) { + throw new RangeError( + `${label}.summary.clean_false_positive_count cannot exceed clean_control_count.`, + ); + } + if ( + caseCount !== + cleanControlCount + faults.reduce((total, fault) => total + fault.caseCount, 0) + ) { + throw new RangeError( + `${label}.summary.case_count must match clean and injected aggregate counts.`, + ); + } + const cleanFalsePositiveRate = asFraction( + summary.clean_false_positive_rate, + `${label}.summary.clean_false_positive_rate`, + ); + if ( + cleanControlCount > 0 && + Math.abs(cleanFalsePositiveRate - cleanFalsePositiveCount / cleanControlCount) > + 1e-9 + ) { + throw new RangeError( + `${label}.summary.clean_false_positive_rate must match aggregate counts.`, + ); + } + const macroRecall = asFraction( + summary.macro_recall, + `${label}.summary.macro_recall`, + ); + const calculatedMacro = + faults.length === 0 + ? 0 + : faults.reduce((total, fault) => total + fault.recall, 0) / faults.length; + if (Math.abs(macroRecall - calculatedMacro) > 1e-9) { + throw new RangeError(`${label}.summary.macro_recall must match per_fault recall.`); + } + + const criticalFaults = faults.filter((fault) => fault.severity === "critical"); + const criticalRecall = + criticalFaults.length === 0 + ? 0 + : Math.min(...criticalFaults.map((fault) => fault.recall)); + const issues: ReportIssue[] = faults.map((fault, index) => ({ + id: `fault-class-${index + 1}`, + category: "Fault detection", + severity: fault.severity, + outcome: fault.detectedCount === fault.caseCount ? "PASS" : "FAIL", + count: fault.caseCount, + title: humanizeIdentifier(fault.faultId), + detail: `Expected signal ${fault.expectedSignal}; detected ${fault.detectedCount} of ${fault.caseCount} aggregate synthetic cases (${(fault.recall * 100).toFixed(1)}% recall).`, + })); + for (const [key, passed] of parsedGates) { + if (!passed) { + issues.push({ + id: `development-gate-${key}`, + category: "Development gate", + severity: key === "critical_fault_recall_100_percent" ? "critical" : "error", + outcome: "FAIL", + count: 1, + title: humanizeIdentifier(key), + detail: "This aggregate development-only gate was reported as false.", + }); + } + } + + return { + schemaVersion: REPORT_SCHEMA_VERSION, + reportType: "fault-matrix", + reportId: `core-benchmark-${provenance.artifactSha256.slice(0, 12)}`, + title: "Development fault-injection matrix", + generatedAt: null, + status, + summary: `The development-only benchmark declared ${status} across ${caseCount} synthetic cases and ${faultCount} known fault classes.`, + faultCoverage: { + expectedFaultClasses: faultCount, + detectedFaultClasses: faults.filter( + (fault) => fault.detectedCount === fault.caseCount, + ).length, + criticalRecallPercent: criticalRecall * 100, + macroRecallPercent: macroRecall * 100, + cleanFalsePositiveRatePercent: cleanFalsePositiveRate * 100, + carryForward: allGatesPassed, + }, + issues, + provenance, + boundaries: { + claim: [ + "This is a development-only synthetic benchmark of known SFTGuard checks.", + "It is not a confirmatory benchmark and does not demonstrate coverage of unknown real-world faults.", + ], + privacy: [ + "Only per-fault aggregate counts, recall, and gate results are rendered.", + "The cases array and any raw synthetic case content are discarded by the adapter.", + ], + }, + }; +} + +function parseConfirmatoryFaultMatrixArtifact( + report: Record, +): SftGuardReport { + const label = "confirmatory fault-matrix artifact"; + const status = asEnum(report.status, `${label}.status`, ["PASS", "FAIL"] as const); + asRecord(report.protocol, `${label}.protocol`); + asBoundedArray(report.cases, `${label}.cases`, 100_000); + + const gates = asRecord(report.gates, `${label}.gates`); + const gateKeys = [ + "clean_false_positive_rate_at_most_10_percent", + "critical_fault_recall_100_percent", + "expected_and_observed_primary_recorded", + "fresh_process_byte_reproducible", + "macro_recall_at_least_90_percent", + "raw_synthetic_text_absent", + ] as const; + const parsedGates = new Map( + gateKeys.map((key) => [ + key, + asBoolean(gates[key], `${label}.gates.${key}`), + ]), + ); + const carryForward = asBoolean( + report.carry_forward, + `${label}.carry_forward`, + ); + if (carryForward !== [...parsedGates.values()].every(Boolean)) { + throw new RangeError(`${label}.carry_forward must match the declared gates.`); + } + if ((status === "PASS") !== carryForward) { + throw new RangeError(`${label}.status must match carry_forward.`); + } + + const artifactProvenance = asRecord( + report.provenance, + `${label}.provenance`, + ); + const pythonImplementation = asString( + artifactProvenance.python_implementation, + `${label}.provenance.python_implementation`, + 80, + ); + const pythonVersion = asString( + artifactProvenance.python_version, + `${label}.provenance.python_version`, + 40, + ); + const baseProvenance = parseCoreMetadata( + report, + label, + parsedGates.get("fresh_process_byte_reproducible") ?? null, + ); + const provenance: ReportProvenance = { + ...baseProvenance, + protocolCommit: asCommit( + artifactProvenance.protocol_git_commit, + `${label}.provenance.protocol_git_commit`, + ), + implementationCommit: asCommit( + artifactProvenance.implementation_git_commit, + `${label}.provenance.implementation_git_commit`, + ), + runtime: `${pythonImplementation} ${pythonVersion}`, + }; + + const faults = parseFaultClasses(report.per_fault, `${label}.per_fault`); + if (faults.length === 0) { + throw new RangeError(`${label}.per_fault must contain at least one entry.`); + } + const summary = asRecord(report.summary, `${label}.summary`); + const caseCount = asNonNegativeInteger( + summary.case_count, + `${label}.summary.case_count`, + ); + const cleanControlCount = asNonNegativeInteger( + summary.clean_control_count, + `${label}.summary.clean_control_count`, + ); + const faultCaseCount = asNonNegativeInteger( + summary.fault_case_count, + `${label}.summary.fault_case_count`, + ); + const cleanFalsePositiveCount = asNonNegativeInteger( + summary.clean_false_positive_count, + `${label}.summary.clean_false_positive_count`, + ); + if (cleanFalsePositiveCount > cleanControlCount) { + throw new RangeError( + `${label}.summary.clean_false_positive_count cannot exceed clean_control_count.`, + ); + } + if (caseCount !== cleanControlCount + faultCaseCount) { + throw new RangeError( + `${label}.summary.case_count must equal clean and fault case counts.`, + ); + } + if ( + faultCaseCount !== + faults.reduce((total, fault) => total + fault.caseCount, 0) + ) { + throw new RangeError( + `${label}.summary.fault_case_count must match per_fault aggregate counts.`, + ); + } + const cleanFalsePositiveRate = asFraction( + summary.clean_false_positive_rate, + `${label}.summary.clean_false_positive_rate`, + ); + if ( + cleanControlCount > 0 && + Math.abs(cleanFalsePositiveRate - cleanFalsePositiveCount / cleanControlCount) > + 1e-9 + ) { + throw new RangeError( + `${label}.summary.clean_false_positive_rate must match aggregate counts.`, + ); + } + const macroRecall = asFraction( + summary.macro_recall, + `${label}.summary.macro_recall`, + ); + const calculatedMacro = + faults.reduce((total, fault) => total + fault.recall, 0) / faults.length; + if (Math.abs(macroRecall - calculatedMacro) > 1e-9) { + throw new RangeError(`${label}.summary.macro_recall must match per_fault recall.`); + } + + const criticalFaults = faults.filter((fault) => fault.severity === "critical"); + const criticalRecall = + criticalFaults.length === 0 + ? 0 + : Math.min(...criticalFaults.map((fault) => fault.recall)); + const issues: ReportIssue[] = faults.map((fault, index) => ({ + id: `confirmatory-fault-class-${index + 1}`, + category: "Confirmatory fault detection", + severity: fault.severity, + outcome: fault.detectedCount === fault.caseCount ? "PASS" : "FAIL", + count: fault.caseCount, + title: humanizeIdentifier(fault.faultId), + detail: `Expected signal ${fault.expectedSignal}; detected ${fault.detectedCount} of ${fault.caseCount} aggregate confirmatory cases (${(fault.recall * 100).toFixed(1)}% recall).`, + })); + for (const [key, passed] of parsedGates) { + if (!passed) { + issues.push({ + id: `confirmatory-gate-${key}`, + category: "Confirmatory gate", + severity: key === "critical_fault_recall_100_percent" ? "critical" : "error", + outcome: "FAIL", + count: 1, + title: humanizeIdentifier(key), + detail: "This aggregate confirmatory gate was reported as false.", + }); + } + } + + return { + schemaVersion: REPORT_SCHEMA_VERSION, + reportType: "fault-matrix", + reportId: `confirmatory-matrix-${provenance.artifactSha256.slice(0, 12)}`, + title: "Confirmatory fault-injection matrix", + generatedAt: null, + status, + summary: `The confirmatory matrix declared ${status} across ${caseCount} synthetic cases and ${faults.length} known fault classes.`, + faultCoverage: { + expectedFaultClasses: faults.length, + detectedFaultClasses: faults.filter( + (fault) => fault.detectedCount === fault.caseCount, + ).length, + criticalRecallPercent: criticalRecall * 100, + macroRecallPercent: macroRecall * 100, + cleanFalsePositiveRatePercent: cleanFalsePositiveRate * 100, + carryForward, + }, + issues, + provenance, + boundaries: { + claim: [ + "The result is scoped to the declared frozen protocol, provenance, and known fault matrix.", + "It does not establish coverage of unknown faults, model quality, or model safety.", + ], + privacy: [ + "Only summary, per-fault aggregates, gates, and declared provenance are rendered.", + "All per-case fields and any private synthetic content are discarded by the adapter.", + ], + }, + }; +} + +export function parseReport(value: unknown): SftGuardReport { + const report = asRecord(value, "report"); + if ("schemaVersion" in report) { + return parseEnvelopeReport(value); + } + const schemaVersion = asString(report.schema_version, "schema_version", 100); + if (schemaVersion === AUDIT_SCHEMA_VERSION) { + return parseAuditArtifact(report); + } + if (schemaVersion === REGRESSION_SCHEMA_VERSION) { + return parseRegressionArtifact(report); + } + if (schemaVersion === DEVELOPMENT_BENCHMARK_SCHEMA_VERSION) { + return parseDevelopmentBenchmarkArtifact(report); + } + if (schemaVersion === CONFIRMATORY_FAULT_MATRIX_SCHEMA_VERSION) { + return parseConfirmatoryFaultMatrixArtifact(report); + } + throw new RangeError( + `Unsupported SFTGuard report schema: ${schemaVersion}.`, + ); +} + +export function parseReportText(text: string): SftGuardReport { + if (new TextEncoder().encode(text).byteLength > MAX_REPORT_BYTES) { + throw new RangeError("Report exceeds the 2 MB local import limit."); + } + let value: unknown; + try { + value = JSON.parse(text) as unknown; + } catch { + throw new SyntaxError("The selected file is not valid JSON."); + } + return parseReport(value); +} + +export function reportTypeLabel(reportType: ReportType): string { + if (reportType === "fault-matrix") return "Fault matrix"; + if (reportType === "regression") return "Regression card"; + return "Dataset audit"; +} diff --git a/web/src/sampleReports.ts b/web/src/sampleReports.ts new file mode 100644 index 0000000..10829f2 --- /dev/null +++ b/web/src/sampleReports.ts @@ -0,0 +1,28 @@ +import auditJson from "./data/sample-audit.json"; +import faultJson from "./data/sample-fault.json"; +import regressionJson from "./data/sample-regression.json"; +import { parseReport, type SftGuardReport } from "./reportSchema"; + +export interface ReportSource { + report: SftGuardReport; + origin: "checked-in sample" | "local import"; + sourceLabel: string; +} + +export const SAMPLE_REPORTS: ReportSource[] = [ + { + report: parseReport(regressionJson), + origin: "checked-in sample", + sourceLabel: "Regression sample", + }, + { + report: parseReport(faultJson), + origin: "checked-in sample", + sourceLabel: "Fault-matrix sample", + }, + { + report: parseReport(auditJson), + origin: "checked-in sample", + sourceLabel: "Audit sample", + }, +]; diff --git a/web/src/styles.css b/web/src/styles.css new file mode 100644 index 0000000..2c44f57 --- /dev/null +++ b/web/src/styles.css @@ -0,0 +1,1411 @@ +:root { + color: #13221f; + background: #ecece5; + font-family: + Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", + sans-serif; + font-synthesis: none; + text-rendering: optimizeLegibility; + --ink: #13221f; + --muted: #60706c; + --line: #d7dad1; + --canvas: #f6f6f0; + --paper: #fdfdf9; + --navy: #152c2a; + --teal: #126d62; + --teal-soft: #dcece6; + --pass: #16745f; + --pass-soft: #dcefe7; + --fail: #b84238; + --fail-soft: #f5dfda; + --abstain: #9a6317; + --abstain-soft: #f5e9cf; + --shadow: 0 24px 60px rgba(24, 41, 37, 0.09); +} + +* { + box-sizing: border-box; +} + +html { + scroll-behavior: smooth; +} + +body { + margin: 0; + min-width: 320px; + min-height: 100vh; + background: + radial-gradient(circle at 12% 8%, rgba(25, 112, 98, 0.08), transparent 30rem), + linear-gradient(135deg, #efefe8 0%, #e8e9e2 100%); +} + +button, +input { + font: inherit; +} + +button, +a, +label[for] { + -webkit-tap-highlight-color: transparent; +} + +button { + color: inherit; +} + +code { + font-family: "SFMono-Regular", Consolas, "Liberation Mono", monospace; +} + +.skip-link { + position: fixed; + top: 0.75rem; + left: 0.75rem; + z-index: 100; + transform: translateY(-160%); + border-radius: 0.45rem; + background: var(--ink); + color: #fff; + padding: 0.7rem 1rem; + text-decoration: none; + transition: transform 160ms ease; +} + +.skip-link:focus { + transform: translateY(0); +} + +:focus-visible { + outline: 3px solid #1c8072; + outline-offset: 3px; +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +.app-shell { + width: min(1540px, calc(100% - 2rem)); + margin: 1rem auto; + overflow: hidden; + border: 1px solid rgba(75, 93, 88, 0.2); + border-radius: 1.4rem; + background: var(--canvas); + box-shadow: var(--shadow); +} + +.topbar { + min-height: 76px; + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; + padding: 0.95rem 1.3rem; + border-bottom: 1px solid var(--line); + background: rgba(253, 253, 249, 0.92); +} + +.brand { + display: inline-flex; + align-items: center; + gap: 0.8rem; + color: inherit; + text-decoration: none; +} + +.brand-mark { + display: grid; + place-items: center; + width: 2.5rem; + height: 2.5rem; + border-radius: 0.75rem; + background: var(--navy); + color: #f6fffb; + font-family: "SFMono-Regular", Consolas, monospace; + font-weight: 750; + font-size: 0.78rem; + letter-spacing: 0.08em; + box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.12); +} + +.brand > span:last-child { + display: flex; + flex-direction: column; + gap: 0.08rem; +} + +.brand strong { + font-size: 1rem; + letter-spacing: -0.02em; +} + +.brand small { + color: var(--muted); + font-size: 0.7rem; + letter-spacing: 0.06em; + text-transform: uppercase; +} + +.local-indicator { + display: inline-flex; + align-items: center; + gap: 0.55rem; + color: #3e5a53; + font-size: 0.76rem; + font-weight: 650; +} + +.local-indicator > span { + width: 0.55rem; + height: 0.55rem; + border-radius: 50%; + background: #2a9b82; + box-shadow: 0 0 0 5px rgba(42, 155, 130, 0.1); +} + +.workspace { + display: grid; + grid-template-columns: 292px minmax(0, 1fr); + align-items: start; +} + +.report-rail { + position: sticky; + top: 0; + min-height: calc(100vh - 108px); + display: flex; + flex-direction: column; + gap: 1.15rem; + padding: 1.3rem 1.05rem; + border-right: 1px solid var(--line); + background: #f0f1ea; +} + +.rail-heading { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 0.3rem; +} + +.rail-heading p { + margin: 0; + color: #60706c; + font-size: 0.69rem; + font-weight: 750; + letter-spacing: 0.12em; + text-transform: uppercase; +} + +.rail-heading span { + min-width: 1.8rem; + border-radius: 999px; + background: #dfe3da; + padding: 0.24rem 0.45rem; + color: #4b5d58; + text-align: center; + font-family: "SFMono-Regular", Consolas, monospace; + font-size: 0.66rem; +} + +.report-list { + display: grid; + gap: 0.52rem; +} + +.report-list button { + appearance: none; + width: 100%; + display: grid; + gap: 0.5rem; + border: 1px solid transparent; + border-radius: 0.85rem; + background: transparent; + padding: 0.85rem; + text-align: left; + cursor: pointer; + transition: + border-color 150ms ease, + background 150ms ease, + transform 150ms ease; +} + +.report-list button:hover { + border-color: #d3d8ce; + background: rgba(255, 255, 255, 0.6); +} + +.report-list button.is-active { + border-color: #cad4cc; + background: var(--paper); + box-shadow: 0 8px 24px rgba(31, 52, 47, 0.06); +} + +.report-list button.is-active::before { + content: ""; + position: absolute; +} + +.report-list-topline { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.4rem; + color: #61716d; + font-size: 0.66rem; + font-weight: 700; + letter-spacing: 0.06em; + text-transform: uppercase; +} + +.report-list button > strong { + color: #1b2b28; + font-size: 0.86rem; + line-height: 1.3; +} + +.report-list button > small { + color: #7a8884; + font-size: 0.68rem; +} + +.status-badge { + width: fit-content; + display: inline-flex; + align-items: center; + gap: 0.5rem; + border: 1px solid currentColor; + border-radius: 999px; + padding: 0.4rem 0.68rem; + font-size: 0.72rem; + font-weight: 800; + letter-spacing: 0.08em; +} + +.status-badge.is-compact { + gap: 0.3rem; + border-color: transparent; + padding: 0.12rem 0; + background: transparent; + font-size: 0.59rem; +} + +.status-dot { + width: 0.45rem; + height: 0.45rem; + border-radius: 50%; + background: currentColor; +} + +.status-pass { + color: var(--pass); + background: var(--pass-soft); +} + +.status-fail { + color: var(--fail); + background: var(--fail-soft); +} + +.status-abstain { + color: var(--abstain); + background: var(--abstain-soft); +} + +.import-card { + margin-top: auto; + border: 1px solid #d5dad0; + border-radius: 1rem; + background: #e7e9e1; + padding: 1rem; +} + +.import-eyebrow { + margin: 0 0 0.45rem; + color: var(--teal); + font-size: 0.65rem; + font-weight: 800; + letter-spacing: 0.11em; + text-transform: uppercase; +} + +.import-card h2 { + margin: 0; + font-size: 1.02rem; + letter-spacing: -0.025em; +} + +.import-card > p:not(.import-eyebrow, .import-message) { + margin: 0.6rem 0 0.9rem; + color: #61706c; + font-size: 0.73rem; + line-height: 1.55; +} + +.import-button { + appearance: none; + min-height: 42px; + display: inline-flex; + align-items: center; + justify-content: center; + width: 100%; + border-radius: 0.65rem; + border: 0; + background: var(--navy); + color: #f7fffb; + padding: 0.65rem 0.8rem; + font-size: 0.76rem; + font-weight: 750; + cursor: pointer; + transition: + background 150ms ease, + transform 150ms ease; +} + +.import-button:hover { + background: #1c413c; + transform: translateY(-1px); +} + +.visually-hidden-input { + position: absolute; + width: 1px; + height: 1px; + opacity: 0; + pointer-events: none; +} + +.import-message { + min-height: 2.5em; + margin: 0.7rem 0 0; + color: #677570; + font-size: 0.65rem; + line-height: 1.4; +} + +.import-message.is-error { + color: var(--fail); +} + +.clear-import { + appearance: none; + border: 0; + background: none; + padding: 0.4rem 0 0; + color: #476760; + font-size: 0.68rem; + font-weight: 700; + text-decoration: underline; + text-underline-offset: 0.2rem; + cursor: pointer; +} + +.report-main { + min-width: 0; + padding: 1.35rem; +} + +.decision-hero { + position: relative; + overflow: hidden; + display: grid; + grid-template-columns: minmax(0, 1fr) 240px; + gap: 2rem; + border-radius: 1.25rem; + background: var(--navy); + color: #f5faf7; + padding: clamp(1.5rem, 4vw, 3.2rem); + isolation: isolate; +} + +.decision-hero::before, +.decision-hero::after { + content: ""; + position: absolute; + z-index: -1; + border-radius: 50%; + pointer-events: none; +} + +.decision-hero::before { + width: 28rem; + height: 28rem; + right: -11rem; + top: -15rem; + background: radial-gradient(circle, rgba(103, 211, 183, 0.18), transparent 66%); +} + +.decision-hero::after { + width: 15rem; + height: 15rem; + left: 34%; + bottom: -11rem; + border: 1px solid rgba(255, 255, 255, 0.1); + box-shadow: + 0 0 0 2.5rem rgba(255, 255, 255, 0.018), + 0 0 0 5rem rgba(255, 255, 255, 0.012); +} + +.hero-pass { + --hero-accent: #73d8b9; +} + +.hero-fail { + --hero-accent: #ff988b; +} + +.hero-abstain { + --hero-accent: #edc173; +} + +.hero-meta { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.7rem; + margin-bottom: 2.8rem; +} + +.hero-meta > span:not(.status-badge) { + color: #9fb3ad; + font-size: 0.7rem; + font-weight: 650; + letter-spacing: 0.05em; + text-transform: capitalize; +} + +.hero-meta > span:not(.status-badge) + span:not(.status-badge)::before { + content: "/"; + margin-right: 0.7rem; + color: #687d77; +} + +.hero-eyebrow { + margin: 0 0 0.7rem; + color: var(--hero-accent); + font-size: 0.74rem; + font-weight: 800; + letter-spacing: 0.11em; + text-transform: uppercase; +} + +.decision-copy h1 { + max-width: 760px; + margin: 0; + font-size: clamp(2rem, 5vw, 4.2rem); + line-height: 0.98; + letter-spacing: -0.055em; + text-wrap: balance; +} + +.hero-summary { + max-width: 720px; + margin: 1.35rem 0 0; + color: #c6d3cf; + font-size: clamp(0.92rem, 1.5vw, 1.07rem); + line-height: 1.68; +} + +.hero-action { + width: fit-content; + margin: 1.45rem 0 0; + border-left: 2px solid var(--hero-accent); + padding: 0.2rem 0 0.2rem 0.8rem; + color: #f0f7f4; + font-size: 0.8rem; + font-weight: 700; +} + +.decision-seal { + align-self: center; + display: grid; + justify-items: center; + gap: 1rem; + text-align: center; +} + +.decision-seal > span { + display: grid; + place-items: center; + width: 8rem; + height: 8rem; + border: 1px solid color-mix(in srgb, var(--hero-accent) 70%, transparent); + border-radius: 50%; + background: color-mix(in srgb, var(--hero-accent) 8%, transparent); + color: var(--hero-accent); + font-family: "SFMono-Regular", Consolas, monospace; + font-size: 3.7rem; + font-weight: 300; + box-shadow: + inset 0 0 0 0.75rem rgba(255, 255, 255, 0.025), + 0 0 0 0.65rem rgba(255, 255, 255, 0.025); +} + +.decision-seal div { + display: grid; + gap: 0.35rem; +} + +.decision-seal strong { + color: var(--hero-accent); + font-size: 0.78rem; + letter-spacing: 0.14em; +} + +.decision-seal small { + max-width: 200px; + color: #9fb1ac; + font-size: 0.66rem; + line-height: 1.45; +} + +.hero-footer { + grid-column: 1 / -1; + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; + border-top: 1px solid rgba(255, 255, 255, 0.1); + margin-top: 0.5rem; + padding-top: 1.1rem; + color: #91a69f; + font-size: 0.68rem; +} + +.hero-footer code { + color: #c9d7d3; +} + +.report-section { + scroll-margin-top: 1rem; + padding: clamp(2.3rem, 5vw, 4.5rem) clamp(0.1rem, 2vw, 1rem); + border-bottom: 1px solid var(--line); +} + +.section-heading { + display: grid; + grid-template-columns: 3rem minmax(0, 1fr); + gap: 1rem; + align-items: start; + margin-bottom: 1.5rem; +} + +.section-number { + padding-top: 0.34rem; + color: #8b9894; + font-family: "SFMono-Regular", Consolas, monospace; + font-size: 0.72rem; +} + +.section-heading h2 { + margin: 0; + color: #182824; + font-size: clamp(1.45rem, 2.6vw, 2.2rem); + letter-spacing: -0.04em; +} + +.section-heading p { + margin: 0.45rem 0 0; + color: #6a7975; + font-size: 0.83rem; + line-height: 1.55; +} + +.metric-grid { + display: grid; + gap: 0.8rem; +} + +.metric-grid.two-up { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.metric-grid.three-up { + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.supplemental-metrics { + margin-top: 0.8rem; +} + +.metric-card { + position: relative; + overflow: hidden; + min-height: 190px; + border: 1px solid var(--line); + border-radius: 1rem; + background: var(--paper); + padding: 1.25rem; +} + +.metric-card::after { + content: ""; + position: absolute; + right: 0; + top: 0; + width: 3rem; + height: 3rem; + border-radius: 0 0 0 3rem; + background: #e8ebe4; +} + +.metric-positive::after { + background: var(--pass-soft); +} + +.metric-negative::after { + background: var(--fail-soft); +} + +.metric-caution::after { + background: var(--abstain-soft); +} + +.metric-label { + margin: 0; + color: #687772; + font-size: 0.69rem; + font-weight: 800; + letter-spacing: 0.09em; + text-transform: uppercase; +} + +.metric-value { + margin: 1.05rem 0 0; + color: #172723; + font-size: clamp(2rem, 4vw, 3.25rem); + font-weight: 600; + letter-spacing: -0.055em; +} + +.metric-positive .metric-value { + color: var(--pass); +} + +.metric-negative .metric-value { + color: var(--fail); +} + +.metric-caution .metric-value { + color: var(--abstain); +} + +.metric-context { + max-width: 440px; + margin: 0.35rem 0 0; + color: #71807c; + font-size: 0.73rem; + line-height: 1.45; +} + +.score-pair { + display: grid; + grid-template-columns: auto minmax(2rem, 1fr) auto; + align-items: center; + gap: 0.65rem; + margin-top: 1.2rem; + color: #687772; + font-family: "SFMono-Regular", Consolas, monospace; + font-size: 0.64rem; +} + +.score-line { + height: 1px; + background: linear-gradient(90deg, #d8ded7, #7eb5a6, #d8ded7); +} + +.category-strip { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + margin-bottom: 0.8rem; +} + +.category-strip span { + display: inline-flex; + align-items: center; + gap: 0.55rem; + border: 1px solid #d8ddd4; + border-radius: 999px; + background: #f8f8f3; + padding: 0.45rem 0.7rem; + color: #586863; + font-size: 0.69rem; +} + +.category-strip strong { + min-width: 1.3rem; + border-radius: 999px; + background: #e5e9e1; + padding: 0.12rem 0.32rem; + text-align: center; + color: #2b423c; + font-size: 0.63rem; +} + +.table-shell { + overflow-x: auto; + border: 1px solid var(--line); + border-radius: 1rem; + background: var(--paper); +} + +table { + width: 100%; + border-collapse: collapse; + text-align: left; +} + +th { + background: #f0f2eb; + padding: 0.78rem 1rem; + color: #687772; + font-size: 0.64rem; + font-weight: 800; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +td { + padding: 1rem; + border-top: 1px solid #e1e4dd; + color: #3a4c47; + font-size: 0.76rem; + vertical-align: top; +} + +td:first-child { + width: 62%; +} + +.issue-title { + color: #1a2a26; + font-size: 0.85rem; + font-weight: 750; +} + +td code { + display: inline-block; + margin-top: 0.35rem; + color: #697a75; + font-size: 0.64rem; +} + +td p { + max-width: 680px; + margin: 0.55rem 0 0; + color: #778580; + font-size: 0.69rem; + line-height: 1.52; +} + +.number-cell { + text-align: right; + font-variant-numeric: tabular-nums; +} + +.severity { + display: inline-block; + border-radius: 999px; + padding: 0.25rem 0.48rem; + font-size: 0.61rem; + font-weight: 750; + text-transform: capitalize; +} + +.severity-critical, +.severity-error { + background: var(--fail-soft); + color: var(--fail); +} + +.severity-warning { + background: var(--abstain-soft); + color: var(--abstain); +} + +.severity-info { + background: var(--teal-soft); + color: var(--teal); +} + +.empty-panel { + border: 1px dashed #cbd1c8; + border-radius: 1rem; + background: rgba(255, 255, 255, 0.45); + padding: clamp(1.4rem, 4vw, 2.4rem); +} + +.empty-panel h3 { + margin: 0; + font-size: 1.2rem; + letter-spacing: -0.03em; +} + +.empty-panel p { + max-width: 650px; + margin: 0.55rem 0 0; + color: #71807c; + font-size: 0.76rem; + line-height: 1.6; +} + +.empty-panel .empty-kicker { + margin: 0 0 0.5rem; + color: var(--teal); + font-size: 0.63rem; + font-weight: 800; + letter-spacing: 0.09em; + text-transform: uppercase; +} + +.compact-empty { + padding: 1.4rem; +} + +.coverage-layout { + display: grid; + grid-template-columns: 230px minmax(0, 1fr); + align-items: center; + gap: clamp(1.5rem, 5vw, 4rem); + border: 1px solid var(--line); + border-radius: 1rem; + background: var(--paper); + padding: clamp(1.5rem, 4vw, 2.5rem); +} + +.coverage-ring { + --coverage: 0%; + width: min(100%, 210px); + aspect-ratio: 1; + display: grid; + place-items: center; + border-radius: 50%; + background: conic-gradient(var(--teal) var(--coverage), #e4e8e0 0); + position: relative; +} + +.coverage-ring::after { + content: ""; + position: absolute; + inset: 0.85rem; + border-radius: 50%; + background: var(--paper); +} + +.coverage-ring > div { + position: relative; + z-index: 1; + display: grid; + justify-items: center; +} + +.coverage-ring strong { + color: #1c342e; + font-size: 3.2rem; + font-weight: 550; + letter-spacing: -0.06em; +} + +.coverage-ring span { + color: #74827e; + font-size: 0.72rem; +} + +.coverage-result { + margin: 0; + color: #263d37; + font-size: 1.12rem; + font-weight: 750; + letter-spacing: -0.025em; +} + +.coverage-copy > p:nth-child(2) { + max-width: 670px; + margin: 0.55rem 0 1.4rem; + color: #71807c; + font-size: 0.76rem; + line-height: 1.55; +} + +.coverage-bars { + display: grid; + gap: 1rem; +} + +.coverage-row { + display: grid; + grid-template-columns: minmax(150px, 1fr) auto; + gap: 0.4rem 1rem; +} + +.coverage-row > div:first-child { + display: flex; + align-items: baseline; + gap: 0.5rem; + color: #43554f; + font-size: 0.72rem; + font-weight: 700; +} + +.coverage-target { + color: #8a9692; + font-size: 0.62rem; + font-weight: 500; +} + +.coverage-value { + color: #263b35; + font-family: "SFMono-Regular", Consolas, monospace; + font-size: 0.7rem; +} + +.bar-track { + grid-column: 1 / -1; + height: 0.38rem; + overflow: hidden; + border-radius: 999px; + background: #e2e6de; +} + +.bar-track span { + display: block; + height: 100%; + border-radius: inherit; + background: var(--teal); +} + +.bar-track.is-inverse span { + background: var(--abstain); +} + +.provenance-grid { + display: grid; + grid-template-columns: minmax(190px, 0.65fr) minmax(0, 1.35fr); + gap: 0.8rem; +} + +.replay-card, +.provenance-list { + border: 1px solid var(--line); + border-radius: 1rem; + background: var(--paper); +} + +.replay-card { + min-height: 220px; + display: flex; + flex-direction: column; + justify-content: flex-end; + padding: 1.3rem; +} + +.replay-card::before { + content: ""; + width: 2.5rem; + height: 2.5rem; + margin-bottom: auto; + border: 1px solid currentColor; + border-radius: 50%; + background: radial-gradient(circle, currentColor 0 19%, transparent 21%); +} + +.replay-pass { + color: var(--pass); +} + +.replay-fail { + color: var(--fail); +} + +.replay-unknown { + color: var(--muted); +} + +.replay-card p { + margin: 0; + color: #72817c; + font-size: 0.68rem; + font-weight: 750; + letter-spacing: 0.07em; + text-transform: uppercase; +} + +.replay-card strong { + margin-top: 0.45rem; + font-size: 1.55rem; + letter-spacing: -0.04em; +} + +.replay-card span { + margin-top: 0.25rem; + color: #7a8884; + font-size: 0.68rem; +} + +.provenance-list { + margin: 0; + overflow: hidden; +} + +.provenance-list > div { + display: grid; + grid-template-columns: minmax(130px, 0.8fr) minmax(0, 1.2fr); + gap: 1rem; + padding: 0.84rem 1rem; + border-bottom: 1px solid #e2e5de; +} + +.provenance-list > div:last-child { + border-bottom: 0; +} + +.provenance-list dt { + color: #74827e; + font-size: 0.68rem; +} + +.provenance-list dd { + min-width: 0; + margin: 0; + color: #30443e; + font-size: 0.7rem; + font-weight: 650; + overflow-wrap: anywhere; +} + +.provenance-list .hash-value { + font-family: "SFMono-Regular", Consolas, monospace; + font-size: 0.66rem; +} + +.boundaries-section { + border-bottom: 0; +} + +.boundary-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0.8rem; +} + +.boundary-card { + min-height: 260px; + border-radius: 1rem; + padding: clamp(1.3rem, 3vw, 2rem); +} + +.claim-boundary { + background: #1b302d; + color: #ecf5f1; +} + +.privacy-boundary { + border: 1px solid #cddbd4; + background: var(--teal-soft); + color: #1c3932; +} + +.boundary-label { + margin: 0; + color: #79c9b3; + font-size: 0.65rem; + font-weight: 800; + letter-spacing: 0.1em; + text-transform: uppercase; +} + +.privacy-boundary .boundary-label { + color: var(--teal); +} + +.boundary-card h3 { + margin: 0.75rem 0 1.2rem; + font-size: 1.35rem; + letter-spacing: -0.035em; +} + +.boundary-card ul { + display: grid; + gap: 0.8rem; + margin: 0; + padding: 0; + list-style: none; +} + +.boundary-card li { + position: relative; + padding-left: 1rem; + color: #b9cbc5; + font-size: 0.73rem; + line-height: 1.55; +} + +.privacy-boundary li { + color: #4e6b63; +} + +.boundary-card li::before { + content: ""; + position: absolute; + left: 0; + top: 0.56rem; + width: 0.35rem; + height: 0.35rem; + border-radius: 50%; + background: #73cbb3; +} + +.report-footer { + display: grid; + grid-template-columns: auto minmax(0, 720px); + justify-content: space-between; + gap: 2rem; + border-top: 1px solid var(--line); + padding: 1.6rem 1rem 0.7rem; + color: #75837f; + font-size: 0.67rem; +} + +.report-footer span { + color: #40554f; + font-weight: 750; +} + +.report-footer p { + margin: 0; + line-height: 1.6; + text-align: right; +} + +@media (max-width: 1080px) { + .workspace { + grid-template-columns: 240px minmax(0, 1fr); + } + + .report-rail { + padding-inline: 0.75rem; + } + + .decision-hero { + grid-template-columns: minmax(0, 1fr) 170px; + } + + .decision-seal > span { + width: 6.5rem; + height: 6.5rem; + font-size: 3rem; + } + + .coverage-layout { + grid-template-columns: 180px minmax(0, 1fr); + } +} + +@media (max-width: 820px) { + .app-shell { + width: 100%; + margin: 0; + border: 0; + border-radius: 0; + } + + .topbar { + position: sticky; + top: 0; + z-index: 20; + } + + .workspace { + display: block; + } + + .report-rail { + position: static; + min-height: 0; + border-right: 0; + border-bottom: 1px solid var(--line); + padding: 1rem; + } + + .report-list { + display: grid; + grid-auto-flow: column; + grid-auto-columns: minmax(230px, 72vw); + overflow-x: auto; + padding-bottom: 0.4rem; + scroll-snap-type: x proximity; + } + + .report-list button { + scroll-snap-align: start; + } + + .import-card { + margin-top: 0; + } + + .decision-hero { + grid-template-columns: 1fr; + } + + .decision-seal { + grid-template-columns: auto minmax(0, 1fr); + justify-items: start; + text-align: left; + } + + .decision-seal > span { + width: 4.5rem; + height: 4.5rem; + font-size: 2.1rem; + } + + .hero-footer { + flex-direction: column; + align-items: flex-start; + } + + .metric-grid.three-up { + grid-template-columns: 1fr; + } + + .coverage-layout { + grid-template-columns: 1fr; + } + + .coverage-ring { + width: 170px; + } + + .provenance-grid { + grid-template-columns: 1fr; + } +} + +@media (max-width: 600px) { + .topbar { + min-height: 68px; + padding-inline: 1rem; + } + + .local-indicator { + max-width: 9rem; + font-size: 0.65rem; + line-height: 1.3; + text-align: right; + } + + .report-main { + padding: 0.75rem; + } + + .decision-hero { + border-radius: 1rem; + padding: 1.25rem; + } + + .hero-meta { + margin-bottom: 2rem; + } + + .decision-copy h1 { + font-size: clamp(2rem, 11vw, 3rem); + } + + .metric-grid.two-up, + .boundary-grid { + grid-template-columns: 1fr; + } + + .section-heading { + grid-template-columns: 2rem minmax(0, 1fr); + gap: 0.5rem; + } + + .table-shell { + border: 0; + background: transparent; + overflow: visible; + } + + table, + thead, + tbody, + tr, + th, + td { + display: block; + } + + thead { + position: absolute; + width: 1px; + height: 1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + } + + tbody { + display: grid; + gap: 0.7rem; + } + + tr { + border: 1px solid var(--line); + border-radius: 0.85rem; + background: var(--paper); + padding: 0.9rem; + } + + td, + td:first-child { + width: auto; + border: 0; + padding: 0.35rem 0; + } + + .number-cell { + text-align: left; + } + + .number-cell::before { + content: "Count: "; + color: #7b8985; + } + + .provenance-list > div { + grid-template-columns: 1fr; + gap: 0.3rem; + } + + .report-footer { + grid-template-columns: 1fr; + gap: 0.8rem; + } + + .report-footer p { + text-align: left; + } +} + +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + scroll-behavior: auto !important; + transition-duration: 0.01ms !important; + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + } +} diff --git a/web/src/vite-env.d.ts b/web/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/web/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/web/tsconfig.json b/web/tsconfig.json new file mode 100644 index 0000000..220f11d --- /dev/null +++ b/web/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ES2022", + "useDefineForClassFields": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "allowJs": false, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "moduleResolution": "Bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "types": ["vite/client", "vitest/globals"] + }, + "include": ["src", "vite.config.ts"] +} diff --git a/web/vite.config.ts b/web/vite.config.ts new file mode 100644 index 0000000..2fb6b7b --- /dev/null +++ b/web/vite.config.ts @@ -0,0 +1,13 @@ +import react from "@vitejs/plugin-react"; +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + plugins: [react()], + build: { + target: "es2022", + sourcemap: true, + }, + test: { + environment: "node", + }, +}); From 97bb02364c027b787d7750931434734c07ca7644 Mon Sep 17 00:00:00 2001 From: Muhammad Labeeb Aryan <93521146+Labeeb2339@users.noreply.github.com> Date: Sun, 19 Jul 2026 20:41:23 +0800 Subject: [PATCH 2/4] Publish preregistered v0.1 evidence --- .github/workflows/pages.yml | 1 + CHANGELOG.md | 3 + README.md | 61 + docs/ANNOUNCEMENT.md | 71 + docs/ARCHITECTURE.md | 2 +- docs/RESULTS.md | 50 + .../sftguard-confirmatory-dashboard.png | Bin 0 -> 62580 bytes evidence/v0.1.0-confirmatory.json | 4046 +++++++++++++++++ evidence/v0.1.0-confirmatory.manifest.json | 12 + pyproject.toml | 1 - web/README.md | 5 +- web/src/App.tsx | 8 +- web/src/sampleReports.ts | 8 +- 13 files changed, 4262 insertions(+), 6 deletions(-) create mode 100644 docs/ANNOUNCEMENT.md create mode 100644 docs/RESULTS.md create mode 100644 docs/assets/sftguard-confirmatory-dashboard.png create mode 100644 evidence/v0.1.0-confirmatory.json create mode 100644 evidence/v0.1.0-confirmatory.manifest.json diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 12e72ec..5c91abc 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -5,6 +5,7 @@ on: branches: [main] paths: - web/** + - evidence/** - .github/workflows/pages.yml workflow_dispatch: diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e8fa2e..62f7619 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,9 @@ All notable changes to SFTGuard are documented here. The project follows - Paired target/retention Regression Cards with deterministic seed-cluster bootstrap intervals and a fixed work budget. - One-shot, fresh-process confirmatory evidence generation and replay tooling. +- Checked-in v0.1 confirmatory evidence: 270/270 known synthetic faults + detected, 0/30 clean controls flagged, and byte-identical replay from the + frozen implementation commit. - Static browser-local report viewer for native SFTGuard artifacts. - Cross-platform CI, CodeQL, dependency audits, Pages deployment, community templates, citation metadata, and security documentation. diff --git a/README.md b/README.md index 8bfecf2..6d344bd 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ **Fail-closed preflight and regression CI for supervised fine-tuning.** +![SFTGuard confirmatory dashboard](docs/assets/sftguard-confirmatory-dashboard.png) + SFTGuard catches a narrow set of expensive-to-discover pipeline mistakes before an adapter is released: malformed chat records, conflicting or leaked examples, credential-shaped text, broken EOS handling, prompt tokens included in loss, and @@ -119,6 +121,34 @@ frozen xorshift32 stream, and applies the threshold to the lower confidence bound. Unpaired, duplicated, undeclared, non-finite, or incomplete evidence returns `ABSTAIN`. +The fixed joint work budget also returns `ABSTAIN` before an extreme +draw-count × pair-count request can consume unbounded CPU time. + +### Put the gate in GitHub Actions + +```yaml +name: SFT preflight +on: [push, pull_request] + +jobs: + sftguard: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-python@v6 + with: + python-version: "3.11" + - run: python -m pip install git+https://github.com/Labeeb2339/sftguard.git + - run: >- + sftguard audit + --train data/train.jsonl + --eval data/eval.jsonl + --output artifacts/sftguard-audit.json +``` + +The command's exit code blocks `FAIL` and `ABSTAIN` by default. Keep private +datasets out of Actions artifacts unless their disclosure is explicitly safe. + ## Static report viewer The viewer is a static React application with no analytics, report endpoint, @@ -147,6 +177,7 @@ evidence runner are committed and pushed before the confirmatory run. - [Product specification](docs/PRODUCT_SPEC.md) - [Evidence runner](scripts/confirmatory_evidence.py) - [Evidence reproduction guide](docs/EVIDENCE.md) +- [Confirmatory result summary](docs/RESULTS.md) - [Related work and boundaries](docs/RELATED_WORK.md) The matrix is an **internal detector regression**: each seed changes synthetic @@ -158,6 +189,33 @@ Checked-in confirmatory evidence is added only after the runner verifies a clean and pushed implementation commit and reproduces the artifact in two fresh processes. The result is preserved even if a frozen gate fails. +### v0.1 confirmatory result + +All six frozen carry-forward gates passed without post-result changes: + +| Measure | Frozen gate | Observed | +| --- | ---: | ---: | +| Critical fault recall | 100% per class | **100%** | +| Macro recall, nine fault classes | ≥ 90% | **100%** | +| Clean-control false-positive rate | ≤ 10% | **0 / 30 (0%)** | +| Fault injections detected | — | **270 / 270** | +| Fresh-process reproduction | byte-identical | **matched** | +| Raw synthetic text in report | none | **none detected** | + +`carry_forward: true` across 300 synthetic cases. Protocol commit: +`d4c74cefca6729124142635dc8893b5e3ddec2d3`. Implementation commit: +`b0f168ab4d6a959d4e28611e345a250c14ca2ea9`. + +- [Confirmatory artifact](evidence/v0.1.0-confirmatory.json) +- [Evidence manifest](evidence/v0.1.0-confirmatory.manifest.json) +- Raw artifact SHA-256: + `961b2842d7791f6f3af7bec5bce6105e433b68d41bf6f7b82a9f3a98e1f695ab` +- Canonical payload SHA-256: + `eb55eaa16a492881ff1bcec8812b2ef37bb2ed6055b6b90a1da408386f632e3a` + +This supports the preregistered synthetic-detector statement only. It is not a +claim that 300 varied real-world fine-tuning pipelines were evaluated. + ## Privacy and security boundary - Files are bounded and parsed as untrusted UTF-8 JSON/JSONL. @@ -195,6 +253,9 @@ Contributions that add a detector should include a minimal fault fixture, a clean control, redaction tests, and a precise claim boundary. Start with [CONTRIBUTING.md](CONTRIBUTING.md) and the [roadmap](docs/ROADMAP.md). +Planning to share the project? Use the truthful, limitation-preserving +[Reddit and Discord drafts](docs/ANNOUNCEMENT.md). + ## License and citation Apache-2.0. Citation metadata is available in [CITATION.cff](CITATION.cff). diff --git a/docs/ANNOUNCEMENT.md b/docs/ANNOUNCEMENT.md new file mode 100644 index 0000000..fcf303a --- /dev/null +++ b/docs/ANNOUNCEMENT.md @@ -0,0 +1,71 @@ +# Share copy + +These drafts describe the checked-in v0.1 result without calling it a +breakthrough, benchmark win, or safety certification. Check each community's +current self-promotion rules before posting and do not mass-post identical copy. + +## Reddit + +**Title** + +> I built SFTGuard: a fail-closed preflight and regression gate for supervised fine-tuning + +**Body** + +> I kept seeing fine-tuning checks discussed separately, so I built a small +> local tool that turns the deterministic ones into a release gate. +> +> SFTGuard audits chat JSONL plus exported token/loss-mask evidence for schema, +> role order, exact duplicates, conflicting answers, train/eval leakage, +> credential-shaped text, EOS handling, prompt supervision, and destructive +> truncation. It also compares paired base-vs-adapter metrics against explicit +> target and retention budgets. Missing evidence returns ABSTAIN instead of +> silently passing. +> +> I preregistered the synthetic fault protocol before running its confirmatory +> seeds. On the frozen internal matrix it detected 270/270 injected faults, had +> 0/30 clean-control false positives, and reproduced the artifact byte-for-byte +> from the recorded implementation commit. +> +> Important limitation: this is internal regression evidence using one +> structural template per known fault class—not an external benchmark, model +> quality result, or proof that it catches unknown failures. +> +> I would especially value technical feedback on the exported mask-evidence +> contract and the paired retention gate. +> +> Repo: https://github.com/Labeeb2339/sftguard +> +> Static viewer: https://labeeb2339.github.io/sftguard/ + +## Discord + +> I built **SFTGuard**, a local fail-closed preflight + regression gate for SFT +> pipelines. It checks chat data and exported loss-mask evidence, then gates +> paired base-vs-adapter target/retention metrics with PASS / FAIL / ABSTAIN. +> +> Frozen synthetic result: 270/270 known fault injections detected, 0/30 clean +> controls flagged, exact implementation replay matched. This is internal +> detector regression evidence, not an external benchmark or safety claim. +> +> Feedback on the mask-evidence format and regression contract would help: +> https://github.com/Labeeb2339/sftguard + +## Optional author line + +Use this only where age/context is relevant; the technical work should stand on +its own: + +> I’m a Form 3 student in Sarawak, Malaysia, and I’m using this project to learn +> how to make AI tooling claims reproducible instead of relying on demos alone. + +## Before posting + +- Open the public repo and Pages link in a signed-out window. +- Use only [`docs/assets/sftguard-confirmatory-dashboard.png`](assets/sftguard-confirmatory-dashboard.png); + do not capture private tabs, + local file paths, Discord messages, tokens, or notifications. +- Keep the limitation paragraph intact. +- Ask for specific technical feedback; do not trade stars, spam, or imply that + stars validate the research claim. +- If a community bans project promotion, do not post there. diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 710e72d..1925009 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -15,7 +15,7 @@ selected card without sending it to a report-processing service. ```mermaid flowchart LR A["Local JSONL records"] --> B["Bounded parser and schema audit"] - T["Optional compatible tokenizer"] --> C["Template and loss-mask audit"] + T["Trusted exported token/mask evidence"] --> C["Template and loss-mask audit"] B --> C C --> D["Deterministic findings"] E["Paired base and adapter evaluations"] --> F["Target and retention gates"] diff --git a/docs/RESULTS.md b/docs/RESULTS.md new file mode 100644 index 0000000..6e0bc2d --- /dev/null +++ b/docs/RESULTS.md @@ -0,0 +1,50 @@ +# v0.1 confirmatory result + +Status: `PASS` + +Carry forward: `true` + +The result was generated after the protocol and implementation were committed +and pushed. No detector or threshold was changed after confirmatory execution. +Post-result changes are limited to checked-in evidence, documentation, +packaging metadata, Pages routing, and rendering that evidence in the static +viewer; the recorded implementation remains the replay authority. + +## Frozen gates + +| Gate | Result | +| --- | ---: | +| 100% recall for every critical fault class | pass — 30/30 for each class | +| Macro recall across all nine fault classes ≥ 90% | pass — 100% | +| Clean-control false-positive rate ≤ 10% | pass — 0/30 (0%) | +| Expected and observed primary signal recorded | pass | +| Fresh process reproduces result bytes | pass | +| Raw prompts, responses, and injected secrets absent | pass | + +Across 30 seeds, all 270 injected cases emitted their required primary signal. +All 30 clean controls emitted no signal. The exact implementation replay +matched the checked-in artifact byte-for-byte. + +## Provenance + +| Item | Value | +| --- | --- | +| Protocol commit | `d4c74cefca6729124142635dc8893b5e3ddec2d3` | +| Implementation commit | `b0f168ab4d6a959d4e28611e345a250c14ca2ea9` | +| Python | CPython 3.11 | +| Raw artifact SHA-256 | `961b2842d7791f6f3af7bec5bce6105e433b68d41bf6f7b82a9f3a98e1f695ab` | +| Canonical payload SHA-256 | `eb55eaa16a492881ff1bcec8812b2ef37bb2ed6055b6b90a1da408386f632e3a` | + +See the [artifact](../evidence/v0.1.0-confirmatory.json), +[manifest](../evidence/v0.1.0-confirmatory.manifest.json), and +[reproduction guide](EVIDENCE.md). + +## Allowed claim + +> SFTGuard detected the preregistered synthetic fault matrix with the reported +> false-positive rate and reproduced its content-addressed evidence. + +This is internal detector regression evidence. Each class uses one structural +fault template across deterministic marker variations. The result does not show +coverage of unknown faults, model quality, or safety, and it is not an external +fine-tuning benchmark. diff --git a/docs/assets/sftguard-confirmatory-dashboard.png b/docs/assets/sftguard-confirmatory-dashboard.png new file mode 100644 index 0000000000000000000000000000000000000000..acbe4eecb829915fe0aa61e63863fd7f9190947e GIT binary patch literal 62580 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

hHR!?50WRfGJCgmXKKr@ij%`RCp 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 NKg|(A8u$8t69CrV0Vx0g literal 0 HcmV?d00001 diff --git a/evidence/v0.1.0-confirmatory.json b/evidence/v0.1.0-confirmatory.json new file mode 100644 index 0000000..f05a1d6 --- /dev/null +++ b/evidence/v0.1.0-confirmatory.json @@ -0,0 +1,4046 @@ +{ + "carry_forward": true, + "cases": [ + { + "audit_status": "PASS", + "case_sha256": "5f57f7b095e2563bdfe60e10e6a808433a09811b55752bd32aaf7cf79b001eb8", + "expected_primary_signal": null, + "fault_id": null, + "kind": "clean_control", + "observed_primary_signal": null, + "observed_signal_ids": [], + "primary_expectation_met": true, + "seed": 4100 + }, + { + "audit_status": "FAIL", + "case_sha256": "8c8e6297ef27bb9f5125037ba7f3eea04794bdc14ef2168404b008b53b4dcbff", + "expected_primary_signal": "invalid_role", + "fault_id": "schema.invalid_role", + "kind": "fault", + "observed_primary_signal": "invalid_role", + "observed_signal_ids": [ + "invalid_role", + "role_order" + ], + "primary_expectation_met": true, + "seed": 4100 + }, + { + "audit_status": "FAIL", + "case_sha256": "a7de518da70ae0f64ee7d96fcc3552a6a6945bd011024e0c25335fae67b4bbb3", + "expected_primary_signal": "role_order", + "fault_id": "schema.role_order", + "kind": "fault", + "observed_primary_signal": "role_order", + "observed_signal_ids": [ + "role_order" + ], + "primary_expectation_met": true, + "seed": 4100 + }, + { + "audit_status": "FAIL", + "case_sha256": "0597243d651815b44146970c3dc9744c5a43fd11c98aeb3d39a26fce9f5b3531", + "expected_primary_signal": "missing_eos", + "fault_id": "template.missing_eos", + "kind": "fault", + "observed_primary_signal": "missing_eos", + "observed_signal_ids": [ + "missing_eos" + ], + "primary_expectation_met": true, + "seed": 4100 + }, + { + "audit_status": "FAIL", + "case_sha256": "4ad0fcdb6576067ffdfe1d4b0216a2a58dff462bcc251edb496e07ddffb8ba95", + "expected_primary_signal": "prompt_token_supervised", + "fault_id": "mask.prompt_supervised", + "kind": "fault", + "observed_primary_signal": "prompt_token_supervised", + "observed_signal_ids": [ + "prompt_token_supervised" + ], + "primary_expectation_met": true, + "seed": 4100 + }, + { + "audit_status": "FAIL", + "case_sha256": "f986bc41a0b596c99c23127dd74dba3576fafad82fc021bf0e47ffe012724188", + "expected_primary_signal": "assistant_fully_truncated", + "fault_id": "truncation.answer_removed", + "kind": "fault", + "observed_primary_signal": "assistant_fully_truncated", + "observed_signal_ids": [ + "assistant_fully_truncated" + ], + "primary_expectation_met": true, + "seed": 4100 + }, + { + "audit_status": "PASS", + "case_sha256": "16a6b1085342447e14607a74f722fdc4a69d06a01843fb01938ecdef3a3fd8a1", + "expected_primary_signal": "exact_duplicate", + "fault_id": "data.exact_duplicate", + "kind": "fault", + "observed_primary_signal": "exact_duplicate", + "observed_signal_ids": [ + "exact_duplicate" + ], + "primary_expectation_met": true, + "seed": 4100 + }, + { + "audit_status": "FAIL", + "case_sha256": "042de45aa942895b11a6999a1df92ae7bf9a1e6c447cc0eccd32c67ee08dd8f0", + "expected_primary_signal": "prompt_conflict", + "fault_id": "data.prompt_conflict", + "kind": "fault", + "observed_primary_signal": "prompt_conflict", + "observed_signal_ids": [ + "prompt_conflict" + ], + "primary_expectation_met": true, + "seed": 4100 + }, + { + "audit_status": "FAIL", + "case_sha256": "359feff53fd877e5fd246484a82239cc64908dffd028f8294eed53122f7b20d4", + "expected_primary_signal": "train_eval_leakage", + "fault_id": "data.train_eval_leakage", + "kind": "fault", + "observed_primary_signal": "train_eval_leakage", + "observed_signal_ids": [ + "train_eval_leakage" + ], + "primary_expectation_met": true, + "seed": 4100 + }, + { + "audit_status": "FAIL", + "case_sha256": "f56ba366d44170e2e7019bc90c2de2501b803756b356f5f14c2668c2d4ffc0ac", + "expected_primary_signal": "secret_pattern", + "fault_id": "privacy.secret_pattern", + "kind": "fault", + "observed_primary_signal": "secret_pattern", + "observed_signal_ids": [ + "pii_like", + "secret_pattern" + ], + "primary_expectation_met": true, + "seed": 4100 + }, + { + "audit_status": "PASS", + "case_sha256": "120af0f7bef4f9f196721ec14a55a10cc8878a89c73d4e89386eec45d96b8103", + "expected_primary_signal": null, + "fault_id": null, + "kind": "clean_control", + "observed_primary_signal": null, + "observed_signal_ids": [], + "primary_expectation_met": true, + "seed": 4101 + }, + { + "audit_status": "FAIL", + "case_sha256": "acb5d289c02e4f9a2ae791b163ee53f4b8821ded0cbb5658626e13bf98061585", + "expected_primary_signal": "invalid_role", + "fault_id": "schema.invalid_role", + "kind": "fault", + "observed_primary_signal": "invalid_role", + "observed_signal_ids": [ + "invalid_role", + "role_order" + ], + "primary_expectation_met": true, + "seed": 4101 + }, + { + "audit_status": "FAIL", + "case_sha256": "3fd48a916b7961f34b8e799f28ba46f3f549c670dbaba442030db61391b12395", + "expected_primary_signal": "role_order", + "fault_id": "schema.role_order", + "kind": "fault", + "observed_primary_signal": "role_order", + "observed_signal_ids": [ + "role_order" + ], + "primary_expectation_met": true, + "seed": 4101 + }, + { + "audit_status": "FAIL", + "case_sha256": "36bb69f44f76c9f41ade010d997058efde81854cb1a942f6d2901e25c3ffc28b", + "expected_primary_signal": "missing_eos", + "fault_id": "template.missing_eos", + "kind": "fault", + "observed_primary_signal": "missing_eos", + "observed_signal_ids": [ + "missing_eos" + ], + "primary_expectation_met": true, + "seed": 4101 + }, + { + "audit_status": "FAIL", + "case_sha256": "b61e7a38c7c46599e4ce106fb6dfd06dc26b057299561a59873b892679315747", + "expected_primary_signal": "prompt_token_supervised", + "fault_id": "mask.prompt_supervised", + "kind": "fault", + "observed_primary_signal": "prompt_token_supervised", + "observed_signal_ids": [ + "prompt_token_supervised" + ], + "primary_expectation_met": true, + "seed": 4101 + }, + { + "audit_status": "FAIL", + "case_sha256": "775dd68b9272d1179f575b239c41b1881d58011ac0cbaefb0e135a499bae55c4", + "expected_primary_signal": "assistant_fully_truncated", + "fault_id": "truncation.answer_removed", + "kind": "fault", + "observed_primary_signal": "assistant_fully_truncated", + "observed_signal_ids": [ + "assistant_fully_truncated" + ], + "primary_expectation_met": true, + "seed": 4101 + }, + { + "audit_status": "PASS", + "case_sha256": "c23421a5aba39766532f9f64bea5cd28b85ac42b2b2dcee22904cca34c9d8c19", + "expected_primary_signal": "exact_duplicate", + "fault_id": "data.exact_duplicate", + "kind": "fault", + "observed_primary_signal": "exact_duplicate", + "observed_signal_ids": [ + "exact_duplicate" + ], + "primary_expectation_met": true, + "seed": 4101 + }, + { + "audit_status": "FAIL", + "case_sha256": "8f78488a8cbd048f3ad41be28707a60717270620ba896cdfd1073ea937983066", + "expected_primary_signal": "prompt_conflict", + "fault_id": "data.prompt_conflict", + "kind": "fault", + "observed_primary_signal": "prompt_conflict", + "observed_signal_ids": [ + "prompt_conflict" + ], + "primary_expectation_met": true, + "seed": 4101 + }, + { + "audit_status": "FAIL", + "case_sha256": "b66cfe8ff43543ddc543ae2ca5074fd634b0ac889debcff1270a13f30d4e1eeb", + "expected_primary_signal": "train_eval_leakage", + "fault_id": "data.train_eval_leakage", + "kind": "fault", + "observed_primary_signal": "train_eval_leakage", + "observed_signal_ids": [ + "train_eval_leakage" + ], + "primary_expectation_met": true, + "seed": 4101 + }, + { + "audit_status": "FAIL", + "case_sha256": "0730548b36ad1704412297fdb158b51c39ded538bfae2a69c74343c68e5469d2", + "expected_primary_signal": "secret_pattern", + "fault_id": "privacy.secret_pattern", + "kind": "fault", + "observed_primary_signal": "secret_pattern", + "observed_signal_ids": [ + "secret_pattern" + ], + "primary_expectation_met": true, + "seed": 4101 + }, + { + "audit_status": "PASS", + "case_sha256": "f31c71167af0fd046d6c777ede394ab3db0d1adbe54d53d3ca4a450fffb421c7", + "expected_primary_signal": null, + "fault_id": null, + "kind": "clean_control", + "observed_primary_signal": null, + "observed_signal_ids": [], + "primary_expectation_met": true, + "seed": 4102 + }, + { + "audit_status": "FAIL", + "case_sha256": "b1d213d79e5af97317cc8837a2739aaf308511dfc0269c209806850212fbe141", + "expected_primary_signal": "invalid_role", + "fault_id": "schema.invalid_role", + "kind": "fault", + "observed_primary_signal": "invalid_role", + "observed_signal_ids": [ + "invalid_role", + "role_order" + ], + "primary_expectation_met": true, + "seed": 4102 + }, + { + "audit_status": "FAIL", + "case_sha256": "367a5991a0512eb801b5a6d6571f7e2f2e9e01aed81172d89f00c7cbc44fbedd", + "expected_primary_signal": "role_order", + "fault_id": "schema.role_order", + "kind": "fault", + "observed_primary_signal": "role_order", + "observed_signal_ids": [ + "role_order" + ], + "primary_expectation_met": true, + "seed": 4102 + }, + { + "audit_status": "FAIL", + "case_sha256": "453e63e7af0b039c0a8b3ff01f1e8cd5041ca7fac7d215d8638ef4c9d3671f48", + "expected_primary_signal": "missing_eos", + "fault_id": "template.missing_eos", + "kind": "fault", + "observed_primary_signal": "missing_eos", + "observed_signal_ids": [ + "missing_eos" + ], + "primary_expectation_met": true, + "seed": 4102 + }, + { + "audit_status": "FAIL", + "case_sha256": "fafa1ea2b069ad8548bfe8ee128c231fe6ed0f171ad63de2a94f459e30db9a19", + "expected_primary_signal": "prompt_token_supervised", + "fault_id": "mask.prompt_supervised", + "kind": "fault", + "observed_primary_signal": "prompt_token_supervised", + "observed_signal_ids": [ + "prompt_token_supervised" + ], + "primary_expectation_met": true, + "seed": 4102 + }, + { + "audit_status": "FAIL", + "case_sha256": "254ec14b57f9114db01514ff91b51136ddb449c1e93ec23d2cb4e3b3a5803c10", + "expected_primary_signal": "assistant_fully_truncated", + "fault_id": "truncation.answer_removed", + "kind": "fault", + "observed_primary_signal": "assistant_fully_truncated", + "observed_signal_ids": [ + "assistant_fully_truncated" + ], + "primary_expectation_met": true, + "seed": 4102 + }, + { + "audit_status": "PASS", + "case_sha256": "a35f60a6af78c040dbcd16d045b75461eb991ae38c87d667d4adec9ac0c5e1e3", + "expected_primary_signal": "exact_duplicate", + "fault_id": "data.exact_duplicate", + "kind": "fault", + "observed_primary_signal": "exact_duplicate", + "observed_signal_ids": [ + "exact_duplicate" + ], + "primary_expectation_met": true, + "seed": 4102 + }, + { + "audit_status": "FAIL", + "case_sha256": "89b6a0312678a385d9d3e060f72dc30e7bd1ecf46544e0c0e2106edd08139e14", + "expected_primary_signal": "prompt_conflict", + "fault_id": "data.prompt_conflict", + "kind": "fault", + "observed_primary_signal": "prompt_conflict", + "observed_signal_ids": [ + "prompt_conflict" + ], + "primary_expectation_met": true, + "seed": 4102 + }, + { + "audit_status": "FAIL", + "case_sha256": "1f7fcbbab56e0ee8776fbc4b0b1c5254ffcbcb6e9a2244443f7b151fb96fe08e", + "expected_primary_signal": "train_eval_leakage", + "fault_id": "data.train_eval_leakage", + "kind": "fault", + "observed_primary_signal": "train_eval_leakage", + "observed_signal_ids": [ + "train_eval_leakage" + ], + "primary_expectation_met": true, + "seed": 4102 + }, + { + "audit_status": "FAIL", + "case_sha256": "9759bcabf8ca5a20249df2cd3893565df983da09325095301d2d64333a54a4ad", + "expected_primary_signal": "secret_pattern", + "fault_id": "privacy.secret_pattern", + "kind": "fault", + "observed_primary_signal": "secret_pattern", + "observed_signal_ids": [ + "secret_pattern" + ], + "primary_expectation_met": true, + "seed": 4102 + }, + { + "audit_status": "PASS", + "case_sha256": "7b4d254c97c716ce74ef7a36fa0d6f53c2371e0dad7955cfcfc679e9f124fe80", + "expected_primary_signal": null, + "fault_id": null, + "kind": "clean_control", + "observed_primary_signal": null, + "observed_signal_ids": [], + "primary_expectation_met": true, + "seed": 4103 + }, + { + "audit_status": "FAIL", + "case_sha256": "dee111a8f5014d0d3a9dcdb266d2e6313265c45f6fa893929863977f456b7530", + "expected_primary_signal": "invalid_role", + "fault_id": "schema.invalid_role", + "kind": "fault", + "observed_primary_signal": "invalid_role", + "observed_signal_ids": [ + "invalid_role", + "role_order" + ], + "primary_expectation_met": true, + "seed": 4103 + }, + { + "audit_status": "FAIL", + "case_sha256": "932de8dcca5274c7fa28d543a7dd26ac20f1b308adce0077e48400c3139a68ef", + "expected_primary_signal": "role_order", + "fault_id": "schema.role_order", + "kind": "fault", + "observed_primary_signal": "role_order", + "observed_signal_ids": [ + "role_order" + ], + "primary_expectation_met": true, + "seed": 4103 + }, + { + "audit_status": "FAIL", + "case_sha256": "c45202f0ec58f33c7aba100d955571db340da0dfde2f119130dcc8be4c264f05", + "expected_primary_signal": "missing_eos", + "fault_id": "template.missing_eos", + "kind": "fault", + "observed_primary_signal": "missing_eos", + "observed_signal_ids": [ + "missing_eos" + ], + "primary_expectation_met": true, + "seed": 4103 + }, + { + "audit_status": "FAIL", + "case_sha256": "8512832b406abd33bd75d779e3f637d5094548b2f1323c1cdf65447254bea926", + "expected_primary_signal": "prompt_token_supervised", + "fault_id": "mask.prompt_supervised", + "kind": "fault", + "observed_primary_signal": "prompt_token_supervised", + "observed_signal_ids": [ + "prompt_token_supervised" + ], + "primary_expectation_met": true, + "seed": 4103 + }, + { + "audit_status": "FAIL", + "case_sha256": "9ef256075af045c8e1d79bdad7895326766050559a6c6d3c6c3bf53e199915e8", + "expected_primary_signal": "assistant_fully_truncated", + "fault_id": "truncation.answer_removed", + "kind": "fault", + "observed_primary_signal": "assistant_fully_truncated", + "observed_signal_ids": [ + "assistant_fully_truncated" + ], + "primary_expectation_met": true, + "seed": 4103 + }, + { + "audit_status": "PASS", + "case_sha256": "a356c506d51c444b5ff2aa409adc75ecbd3ca5b66ef6b8d57598841cb5af03b4", + "expected_primary_signal": "exact_duplicate", + "fault_id": "data.exact_duplicate", + "kind": "fault", + "observed_primary_signal": "exact_duplicate", + "observed_signal_ids": [ + "exact_duplicate" + ], + "primary_expectation_met": true, + "seed": 4103 + }, + { + "audit_status": "FAIL", + "case_sha256": "e5bed19e47c3622936a09530537ec9da6d0105a2ed37ecb0361e195d36964372", + "expected_primary_signal": "prompt_conflict", + "fault_id": "data.prompt_conflict", + "kind": "fault", + "observed_primary_signal": "prompt_conflict", + "observed_signal_ids": [ + "prompt_conflict" + ], + "primary_expectation_met": true, + "seed": 4103 + }, + { + "audit_status": "FAIL", + "case_sha256": "5ac22a11bd9776e2465d32285e37f49a29ebb751486b0a91acbdbc2d2f1955c9", + "expected_primary_signal": "train_eval_leakage", + "fault_id": "data.train_eval_leakage", + "kind": "fault", + "observed_primary_signal": "train_eval_leakage", + "observed_signal_ids": [ + "train_eval_leakage" + ], + "primary_expectation_met": true, + "seed": 4103 + }, + { + "audit_status": "FAIL", + "case_sha256": "a8f80a191416c92decb18e0d1870d8de8d07bbb7f8f641c8eedd36685a9a1fd3", + "expected_primary_signal": "secret_pattern", + "fault_id": "privacy.secret_pattern", + "kind": "fault", + "observed_primary_signal": "secret_pattern", + "observed_signal_ids": [ + "secret_pattern" + ], + "primary_expectation_met": true, + "seed": 4103 + }, + { + "audit_status": "PASS", + "case_sha256": "6b5bc5465d5fc19760bacd625ebffb6ed07df20734fac9b02f529a55d50784cc", + "expected_primary_signal": null, + "fault_id": null, + "kind": "clean_control", + "observed_primary_signal": null, + "observed_signal_ids": [], + "primary_expectation_met": true, + "seed": 4104 + }, + { + "audit_status": "FAIL", + "case_sha256": "b9a58b5f0f72bfd71d4615c5dfab7d51eddeb328d73583f1a89f7da05f8d6c9d", + "expected_primary_signal": "invalid_role", + "fault_id": "schema.invalid_role", + "kind": "fault", + "observed_primary_signal": "invalid_role", + "observed_signal_ids": [ + "invalid_role", + "role_order" + ], + "primary_expectation_met": true, + "seed": 4104 + }, + { + "audit_status": "FAIL", + "case_sha256": "8cd4438af5c34ec74a32d496b5b17740978284a18caf24cf94823383430d83d0", + "expected_primary_signal": "role_order", + "fault_id": "schema.role_order", + "kind": "fault", + "observed_primary_signal": "role_order", + "observed_signal_ids": [ + "role_order" + ], + "primary_expectation_met": true, + "seed": 4104 + }, + { + "audit_status": "FAIL", + "case_sha256": "de51b9acbd8bf3125dd7e3e4b1fd985d0f64cea1ce09c0504da8627c323b13ab", + "expected_primary_signal": "missing_eos", + "fault_id": "template.missing_eos", + "kind": "fault", + "observed_primary_signal": "missing_eos", + "observed_signal_ids": [ + "missing_eos" + ], + "primary_expectation_met": true, + "seed": 4104 + }, + { + "audit_status": "FAIL", + "case_sha256": "4dd1e127e2b5d71cd1911ca1034f46d498d9e363dd047b6a09d182113e0d4d87", + "expected_primary_signal": "prompt_token_supervised", + "fault_id": "mask.prompt_supervised", + "kind": "fault", + "observed_primary_signal": "prompt_token_supervised", + "observed_signal_ids": [ + "prompt_token_supervised" + ], + "primary_expectation_met": true, + "seed": 4104 + }, + { + "audit_status": "FAIL", + "case_sha256": "56e33d0a805d7211522f1a4105e4c7e77188fdfc85b656641f67cb4ff4146016", + "expected_primary_signal": "assistant_fully_truncated", + "fault_id": "truncation.answer_removed", + "kind": "fault", + "observed_primary_signal": "assistant_fully_truncated", + "observed_signal_ids": [ + "assistant_fully_truncated" + ], + "primary_expectation_met": true, + "seed": 4104 + }, + { + "audit_status": "PASS", + "case_sha256": "7d1d77341d0e6d9ce8cc78567510b0c1e014ddb9c0a1125c8c4a1d3c6ee82cf2", + "expected_primary_signal": "exact_duplicate", + "fault_id": "data.exact_duplicate", + "kind": "fault", + "observed_primary_signal": "exact_duplicate", + "observed_signal_ids": [ + "exact_duplicate" + ], + "primary_expectation_met": true, + "seed": 4104 + }, + { + "audit_status": "FAIL", + "case_sha256": "6786ed7b373b369d4ed06b2a7e217c23fabdb695cd16c4f3ff19dd6f3da57cef", + "expected_primary_signal": "prompt_conflict", + "fault_id": "data.prompt_conflict", + "kind": "fault", + "observed_primary_signal": "prompt_conflict", + "observed_signal_ids": [ + "prompt_conflict" + ], + "primary_expectation_met": true, + "seed": 4104 + }, + { + "audit_status": "FAIL", + "case_sha256": "9e7cf17b027e99c4bb9af650744fa662d0136faf9194b370aabd02fccdf9ce7d", + "expected_primary_signal": "train_eval_leakage", + "fault_id": "data.train_eval_leakage", + "kind": "fault", + "observed_primary_signal": "train_eval_leakage", + "observed_signal_ids": [ + "train_eval_leakage" + ], + "primary_expectation_met": true, + "seed": 4104 + }, + { + "audit_status": "FAIL", + "case_sha256": "a0d4a629a532d604a37ec2b3e728e130afff454208a24293c45416fec6894d32", + "expected_primary_signal": "secret_pattern", + "fault_id": "privacy.secret_pattern", + "kind": "fault", + "observed_primary_signal": "secret_pattern", + "observed_signal_ids": [ + "pii_like", + "secret_pattern" + ], + "primary_expectation_met": true, + "seed": 4104 + }, + { + "audit_status": "PASS", + "case_sha256": "0357c77ddb7b8942eea6dd48c13d13498a34a483ce8d32def788df3903512f7e", + "expected_primary_signal": null, + "fault_id": null, + "kind": "clean_control", + "observed_primary_signal": null, + "observed_signal_ids": [], + "primary_expectation_met": true, + "seed": 4105 + }, + { + "audit_status": "FAIL", + "case_sha256": "d535374ca00d74a1aa5706ba649d0be9509ae7497c84e3ed67fd23966e9b12d8", + "expected_primary_signal": "invalid_role", + "fault_id": "schema.invalid_role", + "kind": "fault", + "observed_primary_signal": "invalid_role", + "observed_signal_ids": [ + "invalid_role", + "role_order" + ], + "primary_expectation_met": true, + "seed": 4105 + }, + { + "audit_status": "FAIL", + "case_sha256": "78b332a3e31c530aed0bac4efaeba2b12d21a857a13c807b0e4d612e7ae00ecd", + "expected_primary_signal": "role_order", + "fault_id": "schema.role_order", + "kind": "fault", + "observed_primary_signal": "role_order", + "observed_signal_ids": [ + "role_order" + ], + "primary_expectation_met": true, + "seed": 4105 + }, + { + "audit_status": "FAIL", + "case_sha256": "301b0b7143a981ec5d5ef2f49f3368ea724fbf84887ff2fe9f81693df423446a", + "expected_primary_signal": "missing_eos", + "fault_id": "template.missing_eos", + "kind": "fault", + "observed_primary_signal": "missing_eos", + "observed_signal_ids": [ + "missing_eos" + ], + "primary_expectation_met": true, + "seed": 4105 + }, + { + "audit_status": "FAIL", + "case_sha256": "c97406f120eb6cd1eca3ac659806bc3d6ee365b1e735352d75aeca0a51437d97", + "expected_primary_signal": "prompt_token_supervised", + "fault_id": "mask.prompt_supervised", + "kind": "fault", + "observed_primary_signal": "prompt_token_supervised", + "observed_signal_ids": [ + "prompt_token_supervised" + ], + "primary_expectation_met": true, + "seed": 4105 + }, + { + "audit_status": "FAIL", + "case_sha256": "c5eefbae3a9dd5582f6a34192366a73fd0db77f960316eb4d763449f5a585d90", + "expected_primary_signal": "assistant_fully_truncated", + "fault_id": "truncation.answer_removed", + "kind": "fault", + "observed_primary_signal": "assistant_fully_truncated", + "observed_signal_ids": [ + "assistant_fully_truncated" + ], + "primary_expectation_met": true, + "seed": 4105 + }, + { + "audit_status": "PASS", + "case_sha256": "573b28256db635507d75ee178391c283a1676402c903b1d323bee0f9e4e7ec21", + "expected_primary_signal": "exact_duplicate", + "fault_id": "data.exact_duplicate", + "kind": "fault", + "observed_primary_signal": "exact_duplicate", + "observed_signal_ids": [ + "exact_duplicate" + ], + "primary_expectation_met": true, + "seed": 4105 + }, + { + "audit_status": "FAIL", + "case_sha256": "4be6046ea204fe4a9d05b91a7e38c2eb1e71cc23d08bfa1a3f94aaa1f8a929d1", + "expected_primary_signal": "prompt_conflict", + "fault_id": "data.prompt_conflict", + "kind": "fault", + "observed_primary_signal": "prompt_conflict", + "observed_signal_ids": [ + "prompt_conflict" + ], + "primary_expectation_met": true, + "seed": 4105 + }, + { + "audit_status": "FAIL", + "case_sha256": "0d89095827d8dd1a028f8ef5fa5714562e099d741c147350fc7e1a1a7e700b45", + "expected_primary_signal": "train_eval_leakage", + "fault_id": "data.train_eval_leakage", + "kind": "fault", + "observed_primary_signal": "train_eval_leakage", + "observed_signal_ids": [ + "train_eval_leakage" + ], + "primary_expectation_met": true, + "seed": 4105 + }, + { + "audit_status": "FAIL", + "case_sha256": "3a05f12c090dffb830820a8a4ba3a0a0a51efaa334f2f00753163b6450f54e86", + "expected_primary_signal": "secret_pattern", + "fault_id": "privacy.secret_pattern", + "kind": "fault", + "observed_primary_signal": "secret_pattern", + "observed_signal_ids": [ + "secret_pattern" + ], + "primary_expectation_met": true, + "seed": 4105 + }, + { + "audit_status": "PASS", + "case_sha256": "35bfa7f3397b36cfb88a64209ada36f1561059efddcda8b15f0cb61f0ba1eada", + "expected_primary_signal": null, + "fault_id": null, + "kind": "clean_control", + "observed_primary_signal": null, + "observed_signal_ids": [], + "primary_expectation_met": true, + "seed": 4106 + }, + { + "audit_status": "FAIL", + "case_sha256": "388b6d77a2733a0b00964b8785f6e997712c2fb2de1b32080b09667dba828c35", + "expected_primary_signal": "invalid_role", + "fault_id": "schema.invalid_role", + "kind": "fault", + "observed_primary_signal": "invalid_role", + "observed_signal_ids": [ + "invalid_role", + "role_order" + ], + "primary_expectation_met": true, + "seed": 4106 + }, + { + "audit_status": "FAIL", + "case_sha256": "0b11d0596e798287903bc1d3adcb723fb4d65aae8282f0dfa77df0fba3155557", + "expected_primary_signal": "role_order", + "fault_id": "schema.role_order", + "kind": "fault", + "observed_primary_signal": "role_order", + "observed_signal_ids": [ + "role_order" + ], + "primary_expectation_met": true, + "seed": 4106 + }, + { + "audit_status": "FAIL", + "case_sha256": "d211f8ebeb51fdbbdeb82ae8d2442b139019305315faa4d895b848b379115f89", + "expected_primary_signal": "missing_eos", + "fault_id": "template.missing_eos", + "kind": "fault", + "observed_primary_signal": "missing_eos", + "observed_signal_ids": [ + "missing_eos" + ], + "primary_expectation_met": true, + "seed": 4106 + }, + { + "audit_status": "FAIL", + "case_sha256": "f2694b79c5a0dfc59d2e974b04bba447ed3592e6497b0eb067ab6e6be0c8ab82", + "expected_primary_signal": "prompt_token_supervised", + "fault_id": "mask.prompt_supervised", + "kind": "fault", + "observed_primary_signal": "prompt_token_supervised", + "observed_signal_ids": [ + "prompt_token_supervised" + ], + "primary_expectation_met": true, + "seed": 4106 + }, + { + "audit_status": "FAIL", + "case_sha256": "a7c0768edfea63f94a95a8e3a02e06989722fe0a3dbfd4718a555112181a8406", + "expected_primary_signal": "assistant_fully_truncated", + "fault_id": "truncation.answer_removed", + "kind": "fault", + "observed_primary_signal": "assistant_fully_truncated", + "observed_signal_ids": [ + "assistant_fully_truncated" + ], + "primary_expectation_met": true, + "seed": 4106 + }, + { + "audit_status": "PASS", + "case_sha256": "f14f86467ad8e543a32cb971f4cc25ef8acf94b58870442df9a8d58dfefbffbf", + "expected_primary_signal": "exact_duplicate", + "fault_id": "data.exact_duplicate", + "kind": "fault", + "observed_primary_signal": "exact_duplicate", + "observed_signal_ids": [ + "exact_duplicate" + ], + "primary_expectation_met": true, + "seed": 4106 + }, + { + "audit_status": "FAIL", + "case_sha256": "bbbd1410e779b083ec3d9244180f4b5836d47d568ffb4bbfec8de2b257d33ae0", + "expected_primary_signal": "prompt_conflict", + "fault_id": "data.prompt_conflict", + "kind": "fault", + "observed_primary_signal": "prompt_conflict", + "observed_signal_ids": [ + "prompt_conflict" + ], + "primary_expectation_met": true, + "seed": 4106 + }, + { + "audit_status": "FAIL", + "case_sha256": "33f97bc06672fef67cabfd447de3e170be89df4867fe0caac23de6570a70b6cd", + "expected_primary_signal": "train_eval_leakage", + "fault_id": "data.train_eval_leakage", + "kind": "fault", + "observed_primary_signal": "train_eval_leakage", + "observed_signal_ids": [ + "train_eval_leakage" + ], + "primary_expectation_met": true, + "seed": 4106 + }, + { + "audit_status": "FAIL", + "case_sha256": "69f0c24c572488595af7a3ebad44a7eb73bf7f1b9e8ac5ab88a555bbb47d07c1", + "expected_primary_signal": "secret_pattern", + "fault_id": "privacy.secret_pattern", + "kind": "fault", + "observed_primary_signal": "secret_pattern", + "observed_signal_ids": [ + "secret_pattern" + ], + "primary_expectation_met": true, + "seed": 4106 + }, + { + "audit_status": "PASS", + "case_sha256": "81f5b10709af8749c64f2c74b4c121f56f063aab38b25a94fad2be049643454f", + "expected_primary_signal": null, + "fault_id": null, + "kind": "clean_control", + "observed_primary_signal": null, + "observed_signal_ids": [], + "primary_expectation_met": true, + "seed": 4107 + }, + { + "audit_status": "FAIL", + "case_sha256": "957d37519b287f6288f437ef940656179f843840e783afdb9ba0176ddfbdcbbd", + "expected_primary_signal": "invalid_role", + "fault_id": "schema.invalid_role", + "kind": "fault", + "observed_primary_signal": "invalid_role", + "observed_signal_ids": [ + "invalid_role", + "role_order" + ], + "primary_expectation_met": true, + "seed": 4107 + }, + { + "audit_status": "FAIL", + "case_sha256": "c824d2e040d07850d875e6a09edd91f102df9358f8b6d7140b41fdce6e146ea6", + "expected_primary_signal": "role_order", + "fault_id": "schema.role_order", + "kind": "fault", + "observed_primary_signal": "role_order", + "observed_signal_ids": [ + "role_order" + ], + "primary_expectation_met": true, + "seed": 4107 + }, + { + "audit_status": "FAIL", + "case_sha256": "e3f42e545ebdcd701932f8d3aada843dbd084537d3cfa7b14846ea3ce8bbadc6", + "expected_primary_signal": "missing_eos", + "fault_id": "template.missing_eos", + "kind": "fault", + "observed_primary_signal": "missing_eos", + "observed_signal_ids": [ + "missing_eos" + ], + "primary_expectation_met": true, + "seed": 4107 + }, + { + "audit_status": "FAIL", + "case_sha256": "539b776c87f1f131537f1ad25bb03ae4155eeacfc8a941b0b66ecf401e124c79", + "expected_primary_signal": "prompt_token_supervised", + "fault_id": "mask.prompt_supervised", + "kind": "fault", + "observed_primary_signal": "prompt_token_supervised", + "observed_signal_ids": [ + "prompt_token_supervised" + ], + "primary_expectation_met": true, + "seed": 4107 + }, + { + "audit_status": "FAIL", + "case_sha256": "1501d0b8811558fa19287c34e128caab1fab28fb5f7308f99f888f0ba7edef50", + "expected_primary_signal": "assistant_fully_truncated", + "fault_id": "truncation.answer_removed", + "kind": "fault", + "observed_primary_signal": "assistant_fully_truncated", + "observed_signal_ids": [ + "assistant_fully_truncated" + ], + "primary_expectation_met": true, + "seed": 4107 + }, + { + "audit_status": "PASS", + "case_sha256": "87ba242d74cfe2c8fedc44a4dfec37a5e14126d21f8137e0c9ea4ce34709cb69", + "expected_primary_signal": "exact_duplicate", + "fault_id": "data.exact_duplicate", + "kind": "fault", + "observed_primary_signal": "exact_duplicate", + "observed_signal_ids": [ + "exact_duplicate" + ], + "primary_expectation_met": true, + "seed": 4107 + }, + { + "audit_status": "FAIL", + "case_sha256": "a7175063e375b753d54089a270b189ecd3a33791c2f17f75c2c963cb053ce8f2", + "expected_primary_signal": "prompt_conflict", + "fault_id": "data.prompt_conflict", + "kind": "fault", + "observed_primary_signal": "prompt_conflict", + "observed_signal_ids": [ + "prompt_conflict" + ], + "primary_expectation_met": true, + "seed": 4107 + }, + { + "audit_status": "FAIL", + "case_sha256": "c177e4613e011f0ea32ed030fe199c25d715594d1ba71d0504556778f5aab5cc", + "expected_primary_signal": "train_eval_leakage", + "fault_id": "data.train_eval_leakage", + "kind": "fault", + "observed_primary_signal": "train_eval_leakage", + "observed_signal_ids": [ + "train_eval_leakage" + ], + "primary_expectation_met": true, + "seed": 4107 + }, + { + "audit_status": "FAIL", + "case_sha256": "5d35601df6a100740e2c4de5e16652c64a90bb4d093672417dec5b95216c4d45", + "expected_primary_signal": "secret_pattern", + "fault_id": "privacy.secret_pattern", + "kind": "fault", + "observed_primary_signal": "secret_pattern", + "observed_signal_ids": [ + "secret_pattern" + ], + "primary_expectation_met": true, + "seed": 4107 + }, + { + "audit_status": "PASS", + "case_sha256": "3f55c9481bed5ccd26a3c4961acd704fba2f1c29d44ff91adb4dbf951a2e551e", + "expected_primary_signal": null, + "fault_id": null, + "kind": "clean_control", + "observed_primary_signal": null, + "observed_signal_ids": [], + "primary_expectation_met": true, + "seed": 4108 + }, + { + "audit_status": "FAIL", + "case_sha256": "4e4bf52788b2088ae42448f7b128e58c7afd35b08d989dbee51e245d5602b0b3", + "expected_primary_signal": "invalid_role", + "fault_id": "schema.invalid_role", + "kind": "fault", + "observed_primary_signal": "invalid_role", + "observed_signal_ids": [ + "invalid_role", + "role_order" + ], + "primary_expectation_met": true, + "seed": 4108 + }, + { + "audit_status": "FAIL", + "case_sha256": "7d1093d24803feaabc083226aa28078c649174c79ed5e16d7812942e40c6db88", + "expected_primary_signal": "role_order", + "fault_id": "schema.role_order", + "kind": "fault", + "observed_primary_signal": "role_order", + "observed_signal_ids": [ + "role_order" + ], + "primary_expectation_met": true, + "seed": 4108 + }, + { + "audit_status": "FAIL", + "case_sha256": "23ac621787cfb11972865e4a03a2b305d96e25ea570145d2f5b074fb6c89fb17", + "expected_primary_signal": "missing_eos", + "fault_id": "template.missing_eos", + "kind": "fault", + "observed_primary_signal": "missing_eos", + "observed_signal_ids": [ + "missing_eos" + ], + "primary_expectation_met": true, + "seed": 4108 + }, + { + "audit_status": "FAIL", + "case_sha256": "7210d38db56ef25b9d6405162656d4f2d29516382ed1540b13dd0c3a7fe3ccf5", + "expected_primary_signal": "prompt_token_supervised", + "fault_id": "mask.prompt_supervised", + "kind": "fault", + "observed_primary_signal": "prompt_token_supervised", + "observed_signal_ids": [ + "prompt_token_supervised" + ], + "primary_expectation_met": true, + "seed": 4108 + }, + { + "audit_status": "FAIL", + "case_sha256": "dc990136da2c86f76cff299f0d7cb418ffa06f1bc2f0e0396cd3829ab72a1d41", + "expected_primary_signal": "assistant_fully_truncated", + "fault_id": "truncation.answer_removed", + "kind": "fault", + "observed_primary_signal": "assistant_fully_truncated", + "observed_signal_ids": [ + "assistant_fully_truncated" + ], + "primary_expectation_met": true, + "seed": 4108 + }, + { + "audit_status": "PASS", + "case_sha256": "4da630ea56f99fe5abdf2a052d52a84d9c1139cff98fafa2150bcb1fce5addeb", + "expected_primary_signal": "exact_duplicate", + "fault_id": "data.exact_duplicate", + "kind": "fault", + "observed_primary_signal": "exact_duplicate", + "observed_signal_ids": [ + "exact_duplicate" + ], + "primary_expectation_met": true, + "seed": 4108 + }, + { + "audit_status": "FAIL", + "case_sha256": "b946eed36e29dd2dcb2fd99b7f5d89b8c3bc406d1fbc902e6445d25bf9d81942", + "expected_primary_signal": "prompt_conflict", + "fault_id": "data.prompt_conflict", + "kind": "fault", + "observed_primary_signal": "prompt_conflict", + "observed_signal_ids": [ + "prompt_conflict" + ], + "primary_expectation_met": true, + "seed": 4108 + }, + { + "audit_status": "FAIL", + "case_sha256": "7e50faf6d57a47afa4e4788bddf5c8a2a56dc0f030dacdb88d1416e092309b17", + "expected_primary_signal": "train_eval_leakage", + "fault_id": "data.train_eval_leakage", + "kind": "fault", + "observed_primary_signal": "train_eval_leakage", + "observed_signal_ids": [ + "train_eval_leakage" + ], + "primary_expectation_met": true, + "seed": 4108 + }, + { + "audit_status": "FAIL", + "case_sha256": "e84634accd0546bc1f7428633989550641c6d535425d70bcbc79cbf70a51eb59", + "expected_primary_signal": "secret_pattern", + "fault_id": "privacy.secret_pattern", + "kind": "fault", + "observed_primary_signal": "secret_pattern", + "observed_signal_ids": [ + "secret_pattern" + ], + "primary_expectation_met": true, + "seed": 4108 + }, + { + "audit_status": "PASS", + "case_sha256": "16ab3ae2999883dfff98fe5c1b9f25bf145d9c111243f61aa59fce5f3393131e", + "expected_primary_signal": null, + "fault_id": null, + "kind": "clean_control", + "observed_primary_signal": null, + "observed_signal_ids": [], + "primary_expectation_met": true, + "seed": 4109 + }, + { + "audit_status": "FAIL", + "case_sha256": "404fad990a87680a5c453030d12b521731161751155602a1679da464a01758a6", + "expected_primary_signal": "invalid_role", + "fault_id": "schema.invalid_role", + "kind": "fault", + "observed_primary_signal": "invalid_role", + "observed_signal_ids": [ + "invalid_role", + "role_order" + ], + "primary_expectation_met": true, + "seed": 4109 + }, + { + "audit_status": "FAIL", + "case_sha256": "54f646751f4d9728aaf5f1b1acf4b387abef8839ca5b3a84418f865fbca2d414", + "expected_primary_signal": "role_order", + "fault_id": "schema.role_order", + "kind": "fault", + "observed_primary_signal": "role_order", + "observed_signal_ids": [ + "role_order" + ], + "primary_expectation_met": true, + "seed": 4109 + }, + { + "audit_status": "FAIL", + "case_sha256": "a8cc437e757c12ac3e8583007c76524da7cdfe3e73c94491dcd72b0497b6315c", + "expected_primary_signal": "missing_eos", + "fault_id": "template.missing_eos", + "kind": "fault", + "observed_primary_signal": "missing_eos", + "observed_signal_ids": [ + "missing_eos" + ], + "primary_expectation_met": true, + "seed": 4109 + }, + { + "audit_status": "FAIL", + "case_sha256": "b4cc2d8e8d880c4c56ca51fffd59f7c84f34c171ea027c93b4eb05ddf3e3cce8", + "expected_primary_signal": "prompt_token_supervised", + "fault_id": "mask.prompt_supervised", + "kind": "fault", + "observed_primary_signal": "prompt_token_supervised", + "observed_signal_ids": [ + "prompt_token_supervised" + ], + "primary_expectation_met": true, + "seed": 4109 + }, + { + "audit_status": "FAIL", + "case_sha256": "3bbcffb09f5f270c189e5809d5536313092ee4b4d0b0b63262a0333d0ad7feef", + "expected_primary_signal": "assistant_fully_truncated", + "fault_id": "truncation.answer_removed", + "kind": "fault", + "observed_primary_signal": "assistant_fully_truncated", + "observed_signal_ids": [ + "assistant_fully_truncated" + ], + "primary_expectation_met": true, + "seed": 4109 + }, + { + "audit_status": "PASS", + "case_sha256": "44a4315abd61560ed375319dbfc32b7935db02f8fdae9ed13628d0859fa46c83", + "expected_primary_signal": "exact_duplicate", + "fault_id": "data.exact_duplicate", + "kind": "fault", + "observed_primary_signal": "exact_duplicate", + "observed_signal_ids": [ + "exact_duplicate" + ], + "primary_expectation_met": true, + "seed": 4109 + }, + { + "audit_status": "FAIL", + "case_sha256": "87fe08cfdc001af12f52cda0e976e33ac176594bc7dad1f3355b8aa6ba6e5725", + "expected_primary_signal": "prompt_conflict", + "fault_id": "data.prompt_conflict", + "kind": "fault", + "observed_primary_signal": "prompt_conflict", + "observed_signal_ids": [ + "prompt_conflict" + ], + "primary_expectation_met": true, + "seed": 4109 + }, + { + "audit_status": "FAIL", + "case_sha256": "ebb6d84aade1e122483ccf316fe832d696d25643507f2ade49312c2c53523220", + "expected_primary_signal": "train_eval_leakage", + "fault_id": "data.train_eval_leakage", + "kind": "fault", + "observed_primary_signal": "train_eval_leakage", + "observed_signal_ids": [ + "train_eval_leakage" + ], + "primary_expectation_met": true, + "seed": 4109 + }, + { + "audit_status": "FAIL", + "case_sha256": "91e6a1877a4110abbeb9c4bfee9e7f5114364a92e81bece8a1847b9e3da4d34c", + "expected_primary_signal": "secret_pattern", + "fault_id": "privacy.secret_pattern", + "kind": "fault", + "observed_primary_signal": "secret_pattern", + "observed_signal_ids": [ + "pii_like", + "secret_pattern" + ], + "primary_expectation_met": true, + "seed": 4109 + }, + { + "audit_status": "PASS", + "case_sha256": "13e22dbd3f550c00d041d6bda9b9af93182d420ec5ce262c7eb1e5be0895596f", + "expected_primary_signal": null, + "fault_id": null, + "kind": "clean_control", + "observed_primary_signal": null, + "observed_signal_ids": [], + "primary_expectation_met": true, + "seed": 4110 + }, + { + "audit_status": "FAIL", + "case_sha256": "a0e045acc17b0cde75dd141deba704525bcf43005e5ac33d003a4aee92ba4dd7", + "expected_primary_signal": "invalid_role", + "fault_id": "schema.invalid_role", + "kind": "fault", + "observed_primary_signal": "invalid_role", + "observed_signal_ids": [ + "invalid_role", + "role_order" + ], + "primary_expectation_met": true, + "seed": 4110 + }, + { + "audit_status": "FAIL", + "case_sha256": "eb7574be69350248970eeefe1c00f22bbe89359d67065d9a44c36b30beeccc4c", + "expected_primary_signal": "role_order", + "fault_id": "schema.role_order", + "kind": "fault", + "observed_primary_signal": "role_order", + "observed_signal_ids": [ + "role_order" + ], + "primary_expectation_met": true, + "seed": 4110 + }, + { + "audit_status": "FAIL", + "case_sha256": "976129c7dfed8f68337072dbbe37ae13f09331212b383a2db767c741842fb8da", + "expected_primary_signal": "missing_eos", + "fault_id": "template.missing_eos", + "kind": "fault", + "observed_primary_signal": "missing_eos", + "observed_signal_ids": [ + "missing_eos" + ], + "primary_expectation_met": true, + "seed": 4110 + }, + { + "audit_status": "FAIL", + "case_sha256": "43241a51afff04a02b750c7cbc5745815455ff9bdd9971e6347e46176211a5b7", + "expected_primary_signal": "prompt_token_supervised", + "fault_id": "mask.prompt_supervised", + "kind": "fault", + "observed_primary_signal": "prompt_token_supervised", + "observed_signal_ids": [ + "prompt_token_supervised" + ], + "primary_expectation_met": true, + "seed": 4110 + }, + { + "audit_status": "FAIL", + "case_sha256": "53e251e6f05f21939c1268553e01a64b60375e4d03c045974ad8c0891c969d5d", + "expected_primary_signal": "assistant_fully_truncated", + "fault_id": "truncation.answer_removed", + "kind": "fault", + "observed_primary_signal": "assistant_fully_truncated", + "observed_signal_ids": [ + "assistant_fully_truncated" + ], + "primary_expectation_met": true, + "seed": 4110 + }, + { + "audit_status": "PASS", + "case_sha256": "44459ba9ab22aa4d9a50b2f43fda8aaa5b1e821ac485aa6872552f11293b5ead", + "expected_primary_signal": "exact_duplicate", + "fault_id": "data.exact_duplicate", + "kind": "fault", + "observed_primary_signal": "exact_duplicate", + "observed_signal_ids": [ + "exact_duplicate" + ], + "primary_expectation_met": true, + "seed": 4110 + }, + { + "audit_status": "FAIL", + "case_sha256": "48974ab2b2bfa90fa343fb85d51d494beb52158af719ebe7986cbafe7de115df", + "expected_primary_signal": "prompt_conflict", + "fault_id": "data.prompt_conflict", + "kind": "fault", + "observed_primary_signal": "prompt_conflict", + "observed_signal_ids": [ + "prompt_conflict" + ], + "primary_expectation_met": true, + "seed": 4110 + }, + { + "audit_status": "FAIL", + "case_sha256": "e5180f667c629783ea010b565639f925584acc429f1df2f1de466e57e1c8c7f1", + "expected_primary_signal": "train_eval_leakage", + "fault_id": "data.train_eval_leakage", + "kind": "fault", + "observed_primary_signal": "train_eval_leakage", + "observed_signal_ids": [ + "train_eval_leakage" + ], + "primary_expectation_met": true, + "seed": 4110 + }, + { + "audit_status": "FAIL", + "case_sha256": "875c951d9750064bc5098aa338d0dc980121af1fdda5683646aecd63f313d486", + "expected_primary_signal": "secret_pattern", + "fault_id": "privacy.secret_pattern", + "kind": "fault", + "observed_primary_signal": "secret_pattern", + "observed_signal_ids": [ + "secret_pattern" + ], + "primary_expectation_met": true, + "seed": 4110 + }, + { + "audit_status": "PASS", + "case_sha256": "7dfddc4a22ceb0e044e816a6156957ac53db4ca228c3f3189ae97dceca363a73", + "expected_primary_signal": null, + "fault_id": null, + "kind": "clean_control", + "observed_primary_signal": null, + "observed_signal_ids": [], + "primary_expectation_met": true, + "seed": 4111 + }, + { + "audit_status": "FAIL", + "case_sha256": "ef72dedad0a502abd977e88f3b7c033a5c84a5580e55a1a975c66a2aaffd8c6b", + "expected_primary_signal": "invalid_role", + "fault_id": "schema.invalid_role", + "kind": "fault", + "observed_primary_signal": "invalid_role", + "observed_signal_ids": [ + "invalid_role", + "role_order" + ], + "primary_expectation_met": true, + "seed": 4111 + }, + { + "audit_status": "FAIL", + "case_sha256": "eb64c69ddc03e8ea030186f2d7c537671a53117dfb93ee8eff4ae6437d85b708", + "expected_primary_signal": "role_order", + "fault_id": "schema.role_order", + "kind": "fault", + "observed_primary_signal": "role_order", + "observed_signal_ids": [ + "role_order" + ], + "primary_expectation_met": true, + "seed": 4111 + }, + { + "audit_status": "FAIL", + "case_sha256": "6f48b88c5d405e7223316c2daae172f7c6474444b0fdf881cad1b4b20fe1f656", + "expected_primary_signal": "missing_eos", + "fault_id": "template.missing_eos", + "kind": "fault", + "observed_primary_signal": "missing_eos", + "observed_signal_ids": [ + "missing_eos" + ], + "primary_expectation_met": true, + "seed": 4111 + }, + { + "audit_status": "FAIL", + "case_sha256": "e4b108d581e197a5d0a8dde4576982638ccc5041a3d148678fb82f63d321f88e", + "expected_primary_signal": "prompt_token_supervised", + "fault_id": "mask.prompt_supervised", + "kind": "fault", + "observed_primary_signal": "prompt_token_supervised", + "observed_signal_ids": [ + "prompt_token_supervised" + ], + "primary_expectation_met": true, + "seed": 4111 + }, + { + "audit_status": "FAIL", + "case_sha256": "af5d14eab27905b91977d52cce4115f05d97d845f00f92f71d53dfcaf1f9cb74", + "expected_primary_signal": "assistant_fully_truncated", + "fault_id": "truncation.answer_removed", + "kind": "fault", + "observed_primary_signal": "assistant_fully_truncated", + "observed_signal_ids": [ + "assistant_fully_truncated" + ], + "primary_expectation_met": true, + "seed": 4111 + }, + { + "audit_status": "PASS", + "case_sha256": "4a8e75bedbba5833d132a74734e1fff6ede0f10a683b81320304ff73bbe52aa6", + "expected_primary_signal": "exact_duplicate", + "fault_id": "data.exact_duplicate", + "kind": "fault", + "observed_primary_signal": "exact_duplicate", + "observed_signal_ids": [ + "exact_duplicate" + ], + "primary_expectation_met": true, + "seed": 4111 + }, + { + "audit_status": "FAIL", + "case_sha256": "6ea9a18dd86d50add2721abcef3d377e79b82cb4148812d1b49ea7735ee44cab", + "expected_primary_signal": "prompt_conflict", + "fault_id": "data.prompt_conflict", + "kind": "fault", + "observed_primary_signal": "prompt_conflict", + "observed_signal_ids": [ + "prompt_conflict" + ], + "primary_expectation_met": true, + "seed": 4111 + }, + { + "audit_status": "FAIL", + "case_sha256": "2cb7fdedf4c31080700661df79230100ff4850f9d074f540682af35a237a3ddf", + "expected_primary_signal": "train_eval_leakage", + "fault_id": "data.train_eval_leakage", + "kind": "fault", + "observed_primary_signal": "train_eval_leakage", + "observed_signal_ids": [ + "train_eval_leakage" + ], + "primary_expectation_met": true, + "seed": 4111 + }, + { + "audit_status": "FAIL", + "case_sha256": "f50dbc3742daa867f38e4f41b50a3fc32ebfa4a349d973283d5d1584f0438504", + "expected_primary_signal": "secret_pattern", + "fault_id": "privacy.secret_pattern", + "kind": "fault", + "observed_primary_signal": "secret_pattern", + "observed_signal_ids": [ + "pii_like", + "secret_pattern" + ], + "primary_expectation_met": true, + "seed": 4111 + }, + { + "audit_status": "PASS", + "case_sha256": "66c3c8e28b6242fd3adaa982629e024e0786474c81d3dd1b4ff9fdd98c60d875", + "expected_primary_signal": null, + "fault_id": null, + "kind": "clean_control", + "observed_primary_signal": null, + "observed_signal_ids": [], + "primary_expectation_met": true, + "seed": 4112 + }, + { + "audit_status": "FAIL", + "case_sha256": "d8f75a1ddb11959ebc5d680d465a345d7016b32d96712be2e50d8a5cd4e57502", + "expected_primary_signal": "invalid_role", + "fault_id": "schema.invalid_role", + "kind": "fault", + "observed_primary_signal": "invalid_role", + "observed_signal_ids": [ + "invalid_role", + "role_order" + ], + "primary_expectation_met": true, + "seed": 4112 + }, + { + "audit_status": "FAIL", + "case_sha256": "e807cd36d8d23773c4266fc1b08ef77db809c612af46ae408fdd55637b6bcf53", + "expected_primary_signal": "role_order", + "fault_id": "schema.role_order", + "kind": "fault", + "observed_primary_signal": "role_order", + "observed_signal_ids": [ + "role_order" + ], + "primary_expectation_met": true, + "seed": 4112 + }, + { + "audit_status": "FAIL", + "case_sha256": "b3ba48bc8895baccde7036e798f554d2993ea52a92e33fdd2869744b68d93b01", + "expected_primary_signal": "missing_eos", + "fault_id": "template.missing_eos", + "kind": "fault", + "observed_primary_signal": "missing_eos", + "observed_signal_ids": [ + "missing_eos" + ], + "primary_expectation_met": true, + "seed": 4112 + }, + { + "audit_status": "FAIL", + "case_sha256": "baa2b53b0563345ba7364eac8c69e8ef939131b40d46c425d3a242125590cfad", + "expected_primary_signal": "prompt_token_supervised", + "fault_id": "mask.prompt_supervised", + "kind": "fault", + "observed_primary_signal": "prompt_token_supervised", + "observed_signal_ids": [ + "prompt_token_supervised" + ], + "primary_expectation_met": true, + "seed": 4112 + }, + { + "audit_status": "FAIL", + "case_sha256": "2433f33f22bc9e2f89b357c47903ff37bb8622249f1197d95338d826068a0b60", + "expected_primary_signal": "assistant_fully_truncated", + "fault_id": "truncation.answer_removed", + "kind": "fault", + "observed_primary_signal": "assistant_fully_truncated", + "observed_signal_ids": [ + "assistant_fully_truncated" + ], + "primary_expectation_met": true, + "seed": 4112 + }, + { + "audit_status": "PASS", + "case_sha256": "8c4bbc28b53f1de0f445b862cf781381aeb5fa9d880959f4050098365f13ef3e", + "expected_primary_signal": "exact_duplicate", + "fault_id": "data.exact_duplicate", + "kind": "fault", + "observed_primary_signal": "exact_duplicate", + "observed_signal_ids": [ + "exact_duplicate" + ], + "primary_expectation_met": true, + "seed": 4112 + }, + { + "audit_status": "FAIL", + "case_sha256": "551a9c8d2f2d57a27dc2bc7d0907824f603ac2f9436b621390f8e882a4138134", + "expected_primary_signal": "prompt_conflict", + "fault_id": "data.prompt_conflict", + "kind": "fault", + "observed_primary_signal": "prompt_conflict", + "observed_signal_ids": [ + "prompt_conflict" + ], + "primary_expectation_met": true, + "seed": 4112 + }, + { + "audit_status": "FAIL", + "case_sha256": "ae7626b6c200b045e878b2baf287a71cd2de15e7e7bbbf16ac3e2d9cda5c7914", + "expected_primary_signal": "train_eval_leakage", + "fault_id": "data.train_eval_leakage", + "kind": "fault", + "observed_primary_signal": "train_eval_leakage", + "observed_signal_ids": [ + "train_eval_leakage" + ], + "primary_expectation_met": true, + "seed": 4112 + }, + { + "audit_status": "FAIL", + "case_sha256": "57887a784b6993c35bdccfd1c009c02bc26599c03e9b56cac3c86e2fc0eca709", + "expected_primary_signal": "secret_pattern", + "fault_id": "privacy.secret_pattern", + "kind": "fault", + "observed_primary_signal": "secret_pattern", + "observed_signal_ids": [ + "secret_pattern" + ], + "primary_expectation_met": true, + "seed": 4112 + }, + { + "audit_status": "PASS", + "case_sha256": "3a54d9b34bcab93e0a87aae007e57a8e0ba8f67bc1d2724b2a5a703174f900da", + "expected_primary_signal": null, + "fault_id": null, + "kind": "clean_control", + "observed_primary_signal": null, + "observed_signal_ids": [], + "primary_expectation_met": true, + "seed": 4113 + }, + { + "audit_status": "FAIL", + "case_sha256": "fab8ce95fd8f7f3e18b04ea9b47735ca3d3c6b04bafca85b04173ea228a421a5", + "expected_primary_signal": "invalid_role", + "fault_id": "schema.invalid_role", + "kind": "fault", + "observed_primary_signal": "invalid_role", + "observed_signal_ids": [ + "invalid_role", + "role_order" + ], + "primary_expectation_met": true, + "seed": 4113 + }, + { + "audit_status": "FAIL", + "case_sha256": "07fffcf0033b9431c2d528cde516b8b1699e4cd80164eab96b0e579b3714bae4", + "expected_primary_signal": "role_order", + "fault_id": "schema.role_order", + "kind": "fault", + "observed_primary_signal": "role_order", + "observed_signal_ids": [ + "role_order" + ], + "primary_expectation_met": true, + "seed": 4113 + }, + { + "audit_status": "FAIL", + "case_sha256": "25d532430f368a47b2bb68886da036df129f54c25420d34eaad9476d17ebbf26", + "expected_primary_signal": "missing_eos", + "fault_id": "template.missing_eos", + "kind": "fault", + "observed_primary_signal": "missing_eos", + "observed_signal_ids": [ + "missing_eos" + ], + "primary_expectation_met": true, + "seed": 4113 + }, + { + "audit_status": "FAIL", + "case_sha256": "1fad6a0bedbebe24c4c0fcd35e30ac7bfe37466101b3153a82dce868ee709f71", + "expected_primary_signal": "prompt_token_supervised", + "fault_id": "mask.prompt_supervised", + "kind": "fault", + "observed_primary_signal": "prompt_token_supervised", + "observed_signal_ids": [ + "prompt_token_supervised" + ], + "primary_expectation_met": true, + "seed": 4113 + }, + { + "audit_status": "FAIL", + "case_sha256": "b94134ebf5075d0ebdf5f1e575e46099675afa57d5f43149fbcfd4e220f65e32", + "expected_primary_signal": "assistant_fully_truncated", + "fault_id": "truncation.answer_removed", + "kind": "fault", + "observed_primary_signal": "assistant_fully_truncated", + "observed_signal_ids": [ + "assistant_fully_truncated" + ], + "primary_expectation_met": true, + "seed": 4113 + }, + { + "audit_status": "PASS", + "case_sha256": "d238e79b8da9726496b5d47d61054d5efe5782357c939a9e003047c1b0a6ae7f", + "expected_primary_signal": "exact_duplicate", + "fault_id": "data.exact_duplicate", + "kind": "fault", + "observed_primary_signal": "exact_duplicate", + "observed_signal_ids": [ + "exact_duplicate" + ], + "primary_expectation_met": true, + "seed": 4113 + }, + { + "audit_status": "FAIL", + "case_sha256": "fa1feeb189e2a127a285a90d3a79e15820b13acbc0158173ff5e39b80f9977dc", + "expected_primary_signal": "prompt_conflict", + "fault_id": "data.prompt_conflict", + "kind": "fault", + "observed_primary_signal": "prompt_conflict", + "observed_signal_ids": [ + "prompt_conflict" + ], + "primary_expectation_met": true, + "seed": 4113 + }, + { + "audit_status": "FAIL", + "case_sha256": "f406f8bf9510703f4464930841dbd918bddf8ec2891312f308d63f99ae827caf", + "expected_primary_signal": "train_eval_leakage", + "fault_id": "data.train_eval_leakage", + "kind": "fault", + "observed_primary_signal": "train_eval_leakage", + "observed_signal_ids": [ + "train_eval_leakage" + ], + "primary_expectation_met": true, + "seed": 4113 + }, + { + "audit_status": "FAIL", + "case_sha256": "bc2133f04751ad6baa0e1538988e5adeed67c4b1c87ea476f414ca3ceaffffe9", + "expected_primary_signal": "secret_pattern", + "fault_id": "privacy.secret_pattern", + "kind": "fault", + "observed_primary_signal": "secret_pattern", + "observed_signal_ids": [ + "secret_pattern" + ], + "primary_expectation_met": true, + "seed": 4113 + }, + { + "audit_status": "PASS", + "case_sha256": "37edd88f5a6a51c0d079669442b7918e13acfb3053b0c2b2b04baeb1a6b8be04", + "expected_primary_signal": null, + "fault_id": null, + "kind": "clean_control", + "observed_primary_signal": null, + "observed_signal_ids": [], + "primary_expectation_met": true, + "seed": 4114 + }, + { + "audit_status": "FAIL", + "case_sha256": "19ab6a4737cb0bbb576754384935431f9ea290c832a370629471dea31bdd597a", + "expected_primary_signal": "invalid_role", + "fault_id": "schema.invalid_role", + "kind": "fault", + "observed_primary_signal": "invalid_role", + "observed_signal_ids": [ + "invalid_role", + "role_order" + ], + "primary_expectation_met": true, + "seed": 4114 + }, + { + "audit_status": "FAIL", + "case_sha256": "ec6198da48a0cf47a598cb82a58325fd60bb21dd312c966a3fb52fac874d7628", + "expected_primary_signal": "role_order", + "fault_id": "schema.role_order", + "kind": "fault", + "observed_primary_signal": "role_order", + "observed_signal_ids": [ + "role_order" + ], + "primary_expectation_met": true, + "seed": 4114 + }, + { + "audit_status": "FAIL", + "case_sha256": "5ad9284041c1cdfecee063dbc61e7b2d23f387e36deb574d312d1d05e05bb3cb", + "expected_primary_signal": "missing_eos", + "fault_id": "template.missing_eos", + "kind": "fault", + "observed_primary_signal": "missing_eos", + "observed_signal_ids": [ + "missing_eos" + ], + "primary_expectation_met": true, + "seed": 4114 + }, + { + "audit_status": "FAIL", + "case_sha256": "72c7b124c34d89907014e8520892b8f8c9fc208e71c295d87be62e95e67f1817", + "expected_primary_signal": "prompt_token_supervised", + "fault_id": "mask.prompt_supervised", + "kind": "fault", + "observed_primary_signal": "prompt_token_supervised", + "observed_signal_ids": [ + "prompt_token_supervised" + ], + "primary_expectation_met": true, + "seed": 4114 + }, + { + "audit_status": "FAIL", + "case_sha256": "10bcb0ee3a4a896dc949c423d04516be468d4b300de74a6bc5d61f12829bf33f", + "expected_primary_signal": "assistant_fully_truncated", + "fault_id": "truncation.answer_removed", + "kind": "fault", + "observed_primary_signal": "assistant_fully_truncated", + "observed_signal_ids": [ + "assistant_fully_truncated" + ], + "primary_expectation_met": true, + "seed": 4114 + }, + { + "audit_status": "PASS", + "case_sha256": "0b153fd092054585f9e42b7e067768a155b58f330b3ff9cc8475135d8c4d7a2f", + "expected_primary_signal": "exact_duplicate", + "fault_id": "data.exact_duplicate", + "kind": "fault", + "observed_primary_signal": "exact_duplicate", + "observed_signal_ids": [ + "exact_duplicate" + ], + "primary_expectation_met": true, + "seed": 4114 + }, + { + "audit_status": "FAIL", + "case_sha256": "a580a0124311660e8e71b23e3d4524e3bb7d9728938c089e081ae85323ac0abb", + "expected_primary_signal": "prompt_conflict", + "fault_id": "data.prompt_conflict", + "kind": "fault", + "observed_primary_signal": "prompt_conflict", + "observed_signal_ids": [ + "prompt_conflict" + ], + "primary_expectation_met": true, + "seed": 4114 + }, + { + "audit_status": "FAIL", + "case_sha256": "97c8be3f733831e8875daa15c3191ad8db916faa2e79811b7fcf6ea38334ae96", + "expected_primary_signal": "train_eval_leakage", + "fault_id": "data.train_eval_leakage", + "kind": "fault", + "observed_primary_signal": "train_eval_leakage", + "observed_signal_ids": [ + "train_eval_leakage" + ], + "primary_expectation_met": true, + "seed": 4114 + }, + { + "audit_status": "FAIL", + "case_sha256": "1baa6ec0933cb9ab5ead20da872704959e47719c5e1f343ccc88cb7eaeb0f0f1", + "expected_primary_signal": "secret_pattern", + "fault_id": "privacy.secret_pattern", + "kind": "fault", + "observed_primary_signal": "secret_pattern", + "observed_signal_ids": [ + "secret_pattern" + ], + "primary_expectation_met": true, + "seed": 4114 + }, + { + "audit_status": "PASS", + "case_sha256": "bbef328b37eefea4cab9e39000fb9a80543e05ad3c58d3b5035c332ccbb6a4bb", + "expected_primary_signal": null, + "fault_id": null, + "kind": "clean_control", + "observed_primary_signal": null, + "observed_signal_ids": [], + "primary_expectation_met": true, + "seed": 4115 + }, + { + "audit_status": "FAIL", + "case_sha256": "28e9f96f3239b2e11744226be8926d97016c8c2062720a642e1d35169ace75de", + "expected_primary_signal": "invalid_role", + "fault_id": "schema.invalid_role", + "kind": "fault", + "observed_primary_signal": "invalid_role", + "observed_signal_ids": [ + "invalid_role", + "role_order" + ], + "primary_expectation_met": true, + "seed": 4115 + }, + { + "audit_status": "FAIL", + "case_sha256": "7b43a727e7f5b064315e905556a05e6e82d560fa5b53320790f430483b44b26a", + "expected_primary_signal": "role_order", + "fault_id": "schema.role_order", + "kind": "fault", + "observed_primary_signal": "role_order", + "observed_signal_ids": [ + "role_order" + ], + "primary_expectation_met": true, + "seed": 4115 + }, + { + "audit_status": "FAIL", + "case_sha256": "5dd9544480802ef6a5d0f5ed5272a6f462f9e334863ede7ab4b96fedd8e97da3", + "expected_primary_signal": "missing_eos", + "fault_id": "template.missing_eos", + "kind": "fault", + "observed_primary_signal": "missing_eos", + "observed_signal_ids": [ + "missing_eos" + ], + "primary_expectation_met": true, + "seed": 4115 + }, + { + "audit_status": "FAIL", + "case_sha256": "a6426429f1dde802b67152b70d7a951a220039633665ded659405fad3b7f694d", + "expected_primary_signal": "prompt_token_supervised", + "fault_id": "mask.prompt_supervised", + "kind": "fault", + "observed_primary_signal": "prompt_token_supervised", + "observed_signal_ids": [ + "prompt_token_supervised" + ], + "primary_expectation_met": true, + "seed": 4115 + }, + { + "audit_status": "FAIL", + "case_sha256": "6ed2116fb16d55c4045b1ed9f751596e72ca6b81d3501a1306b2870e247a5c71", + "expected_primary_signal": "assistant_fully_truncated", + "fault_id": "truncation.answer_removed", + "kind": "fault", + "observed_primary_signal": "assistant_fully_truncated", + "observed_signal_ids": [ + "assistant_fully_truncated" + ], + "primary_expectation_met": true, + "seed": 4115 + }, + { + "audit_status": "PASS", + "case_sha256": "1946e5194c6b0215a036f9ce87881450d7769562561d20c353b1171fe3ec0e5e", + "expected_primary_signal": "exact_duplicate", + "fault_id": "data.exact_duplicate", + "kind": "fault", + "observed_primary_signal": "exact_duplicate", + "observed_signal_ids": [ + "exact_duplicate" + ], + "primary_expectation_met": true, + "seed": 4115 + }, + { + "audit_status": "FAIL", + "case_sha256": "54803dddf060c5df66ae3a294d2200a9ad74b634cec0e6714a27bb5128041e6d", + "expected_primary_signal": "prompt_conflict", + "fault_id": "data.prompt_conflict", + "kind": "fault", + "observed_primary_signal": "prompt_conflict", + "observed_signal_ids": [ + "prompt_conflict" + ], + "primary_expectation_met": true, + "seed": 4115 + }, + { + "audit_status": "FAIL", + "case_sha256": "67707cb5152ebb39e404484a72579c5d85e9b832bfc3b84f0709347ab5ce2f3c", + "expected_primary_signal": "train_eval_leakage", + "fault_id": "data.train_eval_leakage", + "kind": "fault", + "observed_primary_signal": "train_eval_leakage", + "observed_signal_ids": [ + "train_eval_leakage" + ], + "primary_expectation_met": true, + "seed": 4115 + }, + { + "audit_status": "FAIL", + "case_sha256": "4feff72c16b99af980e07f73af5d09f0653e59064d29572af5c119703d145efd", + "expected_primary_signal": "secret_pattern", + "fault_id": "privacy.secret_pattern", + "kind": "fault", + "observed_primary_signal": "secret_pattern", + "observed_signal_ids": [ + "secret_pattern" + ], + "primary_expectation_met": true, + "seed": 4115 + }, + { + "audit_status": "PASS", + "case_sha256": "544fe66e06df9d8eae8b514f82d188c5805f54c6b44e8c9e99fde81743ae6088", + "expected_primary_signal": null, + "fault_id": null, + "kind": "clean_control", + "observed_primary_signal": null, + "observed_signal_ids": [], + "primary_expectation_met": true, + "seed": 4116 + }, + { + "audit_status": "FAIL", + "case_sha256": "dd1f2be80def6c5c35d27b765bdc260a5f87fc891b04da9f808b5b1aac84d375", + "expected_primary_signal": "invalid_role", + "fault_id": "schema.invalid_role", + "kind": "fault", + "observed_primary_signal": "invalid_role", + "observed_signal_ids": [ + "invalid_role", + "role_order" + ], + "primary_expectation_met": true, + "seed": 4116 + }, + { + "audit_status": "FAIL", + "case_sha256": "1c87f9a38cca9996a642bd468eca53ef29035199a2538771bd53332fc208458b", + "expected_primary_signal": "role_order", + "fault_id": "schema.role_order", + "kind": "fault", + "observed_primary_signal": "role_order", + "observed_signal_ids": [ + "role_order" + ], + "primary_expectation_met": true, + "seed": 4116 + }, + { + "audit_status": "FAIL", + "case_sha256": "eb65d44235588c2701caf61b97096fd6be929066a7a816811f3ade539c07d5d5", + "expected_primary_signal": "missing_eos", + "fault_id": "template.missing_eos", + "kind": "fault", + "observed_primary_signal": "missing_eos", + "observed_signal_ids": [ + "missing_eos" + ], + "primary_expectation_met": true, + "seed": 4116 + }, + { + "audit_status": "FAIL", + "case_sha256": "e82ee26e27e79d60f29af20bb9a8ac0c3a9ed7e2e173106e2ae7b9dff2e31990", + "expected_primary_signal": "prompt_token_supervised", + "fault_id": "mask.prompt_supervised", + "kind": "fault", + "observed_primary_signal": "prompt_token_supervised", + "observed_signal_ids": [ + "prompt_token_supervised" + ], + "primary_expectation_met": true, + "seed": 4116 + }, + { + "audit_status": "FAIL", + "case_sha256": "8c80ab26509dd24f248d856d9f9ae4ea35b417bdb03bc8afe02746b80e0745f1", + "expected_primary_signal": "assistant_fully_truncated", + "fault_id": "truncation.answer_removed", + "kind": "fault", + "observed_primary_signal": "assistant_fully_truncated", + "observed_signal_ids": [ + "assistant_fully_truncated" + ], + "primary_expectation_met": true, + "seed": 4116 + }, + { + "audit_status": "PASS", + "case_sha256": "db01b70329733c65d34c0fb90ccb607a01edbf63798ed62d5d86c605150cf728", + "expected_primary_signal": "exact_duplicate", + "fault_id": "data.exact_duplicate", + "kind": "fault", + "observed_primary_signal": "exact_duplicate", + "observed_signal_ids": [ + "exact_duplicate" + ], + "primary_expectation_met": true, + "seed": 4116 + }, + { + "audit_status": "FAIL", + "case_sha256": "8d8ffe170bb76fb2c6e6e2e8454c1621d186da05346a1bca47cb9217b6e20a53", + "expected_primary_signal": "prompt_conflict", + "fault_id": "data.prompt_conflict", + "kind": "fault", + "observed_primary_signal": "prompt_conflict", + "observed_signal_ids": [ + "prompt_conflict" + ], + "primary_expectation_met": true, + "seed": 4116 + }, + { + "audit_status": "FAIL", + "case_sha256": "f61c7b068547b6b5e13ca75f6a003bc0a83a39ea30ec64f7f34772f765b84827", + "expected_primary_signal": "train_eval_leakage", + "fault_id": "data.train_eval_leakage", + "kind": "fault", + "observed_primary_signal": "train_eval_leakage", + "observed_signal_ids": [ + "train_eval_leakage" + ], + "primary_expectation_met": true, + "seed": 4116 + }, + { + "audit_status": "FAIL", + "case_sha256": "96d3e596f7e837b8099b34b07cb8d5fe1f456b82617411bbf29d38a6e594ced0", + "expected_primary_signal": "secret_pattern", + "fault_id": "privacy.secret_pattern", + "kind": "fault", + "observed_primary_signal": "secret_pattern", + "observed_signal_ids": [ + "secret_pattern" + ], + "primary_expectation_met": true, + "seed": 4116 + }, + { + "audit_status": "PASS", + "case_sha256": "e3578eac6138c96ea5f83a44128a892d5b532df56beb797b336f7b416f4b4cd8", + "expected_primary_signal": null, + "fault_id": null, + "kind": "clean_control", + "observed_primary_signal": null, + "observed_signal_ids": [], + "primary_expectation_met": true, + "seed": 4117 + }, + { + "audit_status": "FAIL", + "case_sha256": "5761871ebadca00ffeb6ce694a8a99b02617b188a26ae96b06def327fbdfa022", + "expected_primary_signal": "invalid_role", + "fault_id": "schema.invalid_role", + "kind": "fault", + "observed_primary_signal": "invalid_role", + "observed_signal_ids": [ + "invalid_role", + "role_order" + ], + "primary_expectation_met": true, + "seed": 4117 + }, + { + "audit_status": "FAIL", + "case_sha256": "26332e6164d02075e6f8a63979be5d2dacc14a6d1357d2d34bf9a595a6e2a3e8", + "expected_primary_signal": "role_order", + "fault_id": "schema.role_order", + "kind": "fault", + "observed_primary_signal": "role_order", + "observed_signal_ids": [ + "role_order" + ], + "primary_expectation_met": true, + "seed": 4117 + }, + { + "audit_status": "FAIL", + "case_sha256": "1020bd0cbbb199b2f53e320429c1a0ddc8e44cd99ec9f3fb3834e5e7a82b54d5", + "expected_primary_signal": "missing_eos", + "fault_id": "template.missing_eos", + "kind": "fault", + "observed_primary_signal": "missing_eos", + "observed_signal_ids": [ + "missing_eos" + ], + "primary_expectation_met": true, + "seed": 4117 + }, + { + "audit_status": "FAIL", + "case_sha256": "d00ff25cf3db7f51ed80fec4de8418a1f6562673a25d818c6c96ca195a6099bf", + "expected_primary_signal": "prompt_token_supervised", + "fault_id": "mask.prompt_supervised", + "kind": "fault", + "observed_primary_signal": "prompt_token_supervised", + "observed_signal_ids": [ + "prompt_token_supervised" + ], + "primary_expectation_met": true, + "seed": 4117 + }, + { + "audit_status": "FAIL", + "case_sha256": "dfa3cbbca0df85b4ae04b734512d5f1bead010cda38c196231ab430c1775dcea", + "expected_primary_signal": "assistant_fully_truncated", + "fault_id": "truncation.answer_removed", + "kind": "fault", + "observed_primary_signal": "assistant_fully_truncated", + "observed_signal_ids": [ + "assistant_fully_truncated" + ], + "primary_expectation_met": true, + "seed": 4117 + }, + { + "audit_status": "PASS", + "case_sha256": "9697c78c821fc1da30e790c182c52545ebeee817f7b985e217a37cb06c202a0b", + "expected_primary_signal": "exact_duplicate", + "fault_id": "data.exact_duplicate", + "kind": "fault", + "observed_primary_signal": "exact_duplicate", + "observed_signal_ids": [ + "exact_duplicate" + ], + "primary_expectation_met": true, + "seed": 4117 + }, + { + "audit_status": "FAIL", + "case_sha256": "f64f6bfd907b608a7e888f9683c2eba0b1d19082254f8a05360728e5e9f2c07e", + "expected_primary_signal": "prompt_conflict", + "fault_id": "data.prompt_conflict", + "kind": "fault", + "observed_primary_signal": "prompt_conflict", + "observed_signal_ids": [ + "prompt_conflict" + ], + "primary_expectation_met": true, + "seed": 4117 + }, + { + "audit_status": "FAIL", + "case_sha256": "bddeee3f5fb20185acd8a7ff345dac9e4beee1dba2d98fe4eccd0442eebf5092", + "expected_primary_signal": "train_eval_leakage", + "fault_id": "data.train_eval_leakage", + "kind": "fault", + "observed_primary_signal": "train_eval_leakage", + "observed_signal_ids": [ + "train_eval_leakage" + ], + "primary_expectation_met": true, + "seed": 4117 + }, + { + "audit_status": "FAIL", + "case_sha256": "3ea3daec9df1f75436493190b62b231f2c422f0c27cf277ea022c0fe72d7a5e4", + "expected_primary_signal": "secret_pattern", + "fault_id": "privacy.secret_pattern", + "kind": "fault", + "observed_primary_signal": "secret_pattern", + "observed_signal_ids": [ + "secret_pattern" + ], + "primary_expectation_met": true, + "seed": 4117 + }, + { + "audit_status": "PASS", + "case_sha256": "215b2b8a2b69cdccb8d86e09367e4c638ef672bc56001bf6af85612fa1d8110a", + "expected_primary_signal": null, + "fault_id": null, + "kind": "clean_control", + "observed_primary_signal": null, + "observed_signal_ids": [], + "primary_expectation_met": true, + "seed": 4118 + }, + { + "audit_status": "FAIL", + "case_sha256": "34602eff64f63429a792fe5b7f4dd70c8b531dd92dce1bcf9cdc89e735acac05", + "expected_primary_signal": "invalid_role", + "fault_id": "schema.invalid_role", + "kind": "fault", + "observed_primary_signal": "invalid_role", + "observed_signal_ids": [ + "invalid_role", + "role_order" + ], + "primary_expectation_met": true, + "seed": 4118 + }, + { + "audit_status": "FAIL", + "case_sha256": "c327b0d7b79a982d094bcb155dc619291b81a07c9405d0cb893ff488fd99cce1", + "expected_primary_signal": "role_order", + "fault_id": "schema.role_order", + "kind": "fault", + "observed_primary_signal": "role_order", + "observed_signal_ids": [ + "role_order" + ], + "primary_expectation_met": true, + "seed": 4118 + }, + { + "audit_status": "FAIL", + "case_sha256": "31b4610f45804044abffa2747bdc0ab0e76c2afc6c2dfec79ae37bfe569c8737", + "expected_primary_signal": "missing_eos", + "fault_id": "template.missing_eos", + "kind": "fault", + "observed_primary_signal": "missing_eos", + "observed_signal_ids": [ + "missing_eos" + ], + "primary_expectation_met": true, + "seed": 4118 + }, + { + "audit_status": "FAIL", + "case_sha256": "27b3db8b00cd853d71beefbb7c22bd38aa6f526614586daa3e44a4bf79bb405a", + "expected_primary_signal": "prompt_token_supervised", + "fault_id": "mask.prompt_supervised", + "kind": "fault", + "observed_primary_signal": "prompt_token_supervised", + "observed_signal_ids": [ + "prompt_token_supervised" + ], + "primary_expectation_met": true, + "seed": 4118 + }, + { + "audit_status": "FAIL", + "case_sha256": "ff6ae3e4a32a5bed0338e57bbb281aace312b55554ac6a73f4edfdf5c25b9300", + "expected_primary_signal": "assistant_fully_truncated", + "fault_id": "truncation.answer_removed", + "kind": "fault", + "observed_primary_signal": "assistant_fully_truncated", + "observed_signal_ids": [ + "assistant_fully_truncated" + ], + "primary_expectation_met": true, + "seed": 4118 + }, + { + "audit_status": "PASS", + "case_sha256": "bf2ac15a331d2219cf33e51ac9bf6f12d68ac2ecb000e97ed4cc88610a95e7db", + "expected_primary_signal": "exact_duplicate", + "fault_id": "data.exact_duplicate", + "kind": "fault", + "observed_primary_signal": "exact_duplicate", + "observed_signal_ids": [ + "exact_duplicate" + ], + "primary_expectation_met": true, + "seed": 4118 + }, + { + "audit_status": "FAIL", + "case_sha256": "1ab7211f6c6d93209b26f92c378773996d72f376819e147ab3c7741c8c24848a", + "expected_primary_signal": "prompt_conflict", + "fault_id": "data.prompt_conflict", + "kind": "fault", + "observed_primary_signal": "prompt_conflict", + "observed_signal_ids": [ + "prompt_conflict" + ], + "primary_expectation_met": true, + "seed": 4118 + }, + { + "audit_status": "FAIL", + "case_sha256": "f9995f5b0657d034246c90353857e919463a3ae791613aead70a12425dfe38d4", + "expected_primary_signal": "train_eval_leakage", + "fault_id": "data.train_eval_leakage", + "kind": "fault", + "observed_primary_signal": "train_eval_leakage", + "observed_signal_ids": [ + "train_eval_leakage" + ], + "primary_expectation_met": true, + "seed": 4118 + }, + { + "audit_status": "FAIL", + "case_sha256": "1b605516e660fed7390537d33aa27bb0905d559f2d7ee54d41bee88de3308c9f", + "expected_primary_signal": "secret_pattern", + "fault_id": "privacy.secret_pattern", + "kind": "fault", + "observed_primary_signal": "secret_pattern", + "observed_signal_ids": [ + "secret_pattern" + ], + "primary_expectation_met": true, + "seed": 4118 + }, + { + "audit_status": "PASS", + "case_sha256": "578d5488ca1f03f66a2e51daa018cbfb4eb3695257cf37f19dce266d5b47f957", + "expected_primary_signal": null, + "fault_id": null, + "kind": "clean_control", + "observed_primary_signal": null, + "observed_signal_ids": [], + "primary_expectation_met": true, + "seed": 4119 + }, + { + "audit_status": "FAIL", + "case_sha256": "dbc2f38040fc5c4aaf389baf680e8134d3f5d673ff28e5d05e7ab1d36cb41b01", + "expected_primary_signal": "invalid_role", + "fault_id": "schema.invalid_role", + "kind": "fault", + "observed_primary_signal": "invalid_role", + "observed_signal_ids": [ + "invalid_role", + "role_order" + ], + "primary_expectation_met": true, + "seed": 4119 + }, + { + "audit_status": "FAIL", + "case_sha256": "cc4d2d5deebbedd2f65e5e00fe290c4a9ba88f880c1c912e38a2f5389c500976", + "expected_primary_signal": "role_order", + "fault_id": "schema.role_order", + "kind": "fault", + "observed_primary_signal": "role_order", + "observed_signal_ids": [ + "role_order" + ], + "primary_expectation_met": true, + "seed": 4119 + }, + { + "audit_status": "FAIL", + "case_sha256": "6aba84bee2b373f47df6d634a47753019219b31071ab1eae5231b7d0c92ebce2", + "expected_primary_signal": "missing_eos", + "fault_id": "template.missing_eos", + "kind": "fault", + "observed_primary_signal": "missing_eos", + "observed_signal_ids": [ + "missing_eos" + ], + "primary_expectation_met": true, + "seed": 4119 + }, + { + "audit_status": "FAIL", + "case_sha256": "49d52d6c85a7251a6b8f7e3048d3a12ab3f5758d6b94457d2c7c1f66d0ef74c3", + "expected_primary_signal": "prompt_token_supervised", + "fault_id": "mask.prompt_supervised", + "kind": "fault", + "observed_primary_signal": "prompt_token_supervised", + "observed_signal_ids": [ + "prompt_token_supervised" + ], + "primary_expectation_met": true, + "seed": 4119 + }, + { + "audit_status": "FAIL", + "case_sha256": "4fc10dad18380fb400341fb4f3c31d43200aa2f61f0dd610c4613892f7949311", + "expected_primary_signal": "assistant_fully_truncated", + "fault_id": "truncation.answer_removed", + "kind": "fault", + "observed_primary_signal": "assistant_fully_truncated", + "observed_signal_ids": [ + "assistant_fully_truncated" + ], + "primary_expectation_met": true, + "seed": 4119 + }, + { + "audit_status": "PASS", + "case_sha256": "26509933959d9b6bb31fed0040277069c4d501b594f26f3d10a421c9de87794e", + "expected_primary_signal": "exact_duplicate", + "fault_id": "data.exact_duplicate", + "kind": "fault", + "observed_primary_signal": "exact_duplicate", + "observed_signal_ids": [ + "exact_duplicate" + ], + "primary_expectation_met": true, + "seed": 4119 + }, + { + "audit_status": "FAIL", + "case_sha256": "b6b6ef537498600353c38b4250cc1c0634e1ef73a9fda8f5f8135f5d25b1c606", + "expected_primary_signal": "prompt_conflict", + "fault_id": "data.prompt_conflict", + "kind": "fault", + "observed_primary_signal": "prompt_conflict", + "observed_signal_ids": [ + "prompt_conflict" + ], + "primary_expectation_met": true, + "seed": 4119 + }, + { + "audit_status": "FAIL", + "case_sha256": "9d5ec3586d48dd994eb523b543561e5b28e1ea7eee902e17a2fb7982e406903a", + "expected_primary_signal": "train_eval_leakage", + "fault_id": "data.train_eval_leakage", + "kind": "fault", + "observed_primary_signal": "train_eval_leakage", + "observed_signal_ids": [ + "train_eval_leakage" + ], + "primary_expectation_met": true, + "seed": 4119 + }, + { + "audit_status": "FAIL", + "case_sha256": "9169c633041ba1391853585cde0fb9596a8eb37c07710f90b5ed790dad8da86a", + "expected_primary_signal": "secret_pattern", + "fault_id": "privacy.secret_pattern", + "kind": "fault", + "observed_primary_signal": "secret_pattern", + "observed_signal_ids": [ + "secret_pattern" + ], + "primary_expectation_met": true, + "seed": 4119 + }, + { + "audit_status": "PASS", + "case_sha256": "bbcd8caa00e3132793f45b5c8f33cd53516d15dca283a5764043c05fbb8d3bf3", + "expected_primary_signal": null, + "fault_id": null, + "kind": "clean_control", + "observed_primary_signal": null, + "observed_signal_ids": [], + "primary_expectation_met": true, + "seed": 4120 + }, + { + "audit_status": "FAIL", + "case_sha256": "4fa740c406c8363a90ea8aedc2978350951b8e15db19384dc15709321fa196c9", + "expected_primary_signal": "invalid_role", + "fault_id": "schema.invalid_role", + "kind": "fault", + "observed_primary_signal": "invalid_role", + "observed_signal_ids": [ + "invalid_role", + "role_order" + ], + "primary_expectation_met": true, + "seed": 4120 + }, + { + "audit_status": "FAIL", + "case_sha256": "578ff72f19c615e74982be92aa0ca95910660d2b4771d0e1119103bec0dcf12a", + "expected_primary_signal": "role_order", + "fault_id": "schema.role_order", + "kind": "fault", + "observed_primary_signal": "role_order", + "observed_signal_ids": [ + "role_order" + ], + "primary_expectation_met": true, + "seed": 4120 + }, + { + "audit_status": "FAIL", + "case_sha256": "7544417a8c3fc5269e10a179fb154fb8325128a50110ff6d8aa215df819ecc0f", + "expected_primary_signal": "missing_eos", + "fault_id": "template.missing_eos", + "kind": "fault", + "observed_primary_signal": "missing_eos", + "observed_signal_ids": [ + "missing_eos" + ], + "primary_expectation_met": true, + "seed": 4120 + }, + { + "audit_status": "FAIL", + "case_sha256": "e2b3b672895770e376fd76c5d0aa4e9b669a4096b00f8bce567381283d042170", + "expected_primary_signal": "prompt_token_supervised", + "fault_id": "mask.prompt_supervised", + "kind": "fault", + "observed_primary_signal": "prompt_token_supervised", + "observed_signal_ids": [ + "prompt_token_supervised" + ], + "primary_expectation_met": true, + "seed": 4120 + }, + { + "audit_status": "FAIL", + "case_sha256": "38df8f121fd79663e94b9fc02600c98931f4a09e9acd33ea44cd8e0e30c784bd", + "expected_primary_signal": "assistant_fully_truncated", + "fault_id": "truncation.answer_removed", + "kind": "fault", + "observed_primary_signal": "assistant_fully_truncated", + "observed_signal_ids": [ + "assistant_fully_truncated" + ], + "primary_expectation_met": true, + "seed": 4120 + }, + { + "audit_status": "PASS", + "case_sha256": "6756cb61538b98de82b7b43102d5d706b5b0e5193c53f730aa89738141f4c062", + "expected_primary_signal": "exact_duplicate", + "fault_id": "data.exact_duplicate", + "kind": "fault", + "observed_primary_signal": "exact_duplicate", + "observed_signal_ids": [ + "exact_duplicate" + ], + "primary_expectation_met": true, + "seed": 4120 + }, + { + "audit_status": "FAIL", + "case_sha256": "048e1dd2077ac83a6f0f57d60a175ca05d833b6b158c039f9cf069cd57f18d51", + "expected_primary_signal": "prompt_conflict", + "fault_id": "data.prompt_conflict", + "kind": "fault", + "observed_primary_signal": "prompt_conflict", + "observed_signal_ids": [ + "prompt_conflict" + ], + "primary_expectation_met": true, + "seed": 4120 + }, + { + "audit_status": "FAIL", + "case_sha256": "7563f724286e0fd0b8f7865c9aae6f5e32c1a48005b084dc6832a0047c53311d", + "expected_primary_signal": "train_eval_leakage", + "fault_id": "data.train_eval_leakage", + "kind": "fault", + "observed_primary_signal": "train_eval_leakage", + "observed_signal_ids": [ + "train_eval_leakage" + ], + "primary_expectation_met": true, + "seed": 4120 + }, + { + "audit_status": "FAIL", + "case_sha256": "867695627e032e4647af711b708ec63ab192f8c916da84639591f4e0c82c3b06", + "expected_primary_signal": "secret_pattern", + "fault_id": "privacy.secret_pattern", + "kind": "fault", + "observed_primary_signal": "secret_pattern", + "observed_signal_ids": [ + "secret_pattern" + ], + "primary_expectation_met": true, + "seed": 4120 + }, + { + "audit_status": "PASS", + "case_sha256": "d97b7d1917ba56d7feb21f6003d888c25495df43edf0512dcf9a0611a7b7a289", + "expected_primary_signal": null, + "fault_id": null, + "kind": "clean_control", + "observed_primary_signal": null, + "observed_signal_ids": [], + "primary_expectation_met": true, + "seed": 4121 + }, + { + "audit_status": "FAIL", + "case_sha256": "411af9edda759f8439213951e037409284f2631a959e8f1fc751d4b664565a7e", + "expected_primary_signal": "invalid_role", + "fault_id": "schema.invalid_role", + "kind": "fault", + "observed_primary_signal": "invalid_role", + "observed_signal_ids": [ + "invalid_role", + "role_order" + ], + "primary_expectation_met": true, + "seed": 4121 + }, + { + "audit_status": "FAIL", + "case_sha256": "1f77e9b8573165cdad20e21f050b20654a5ead43cf0535ba1e6b51eee4d701f2", + "expected_primary_signal": "role_order", + "fault_id": "schema.role_order", + "kind": "fault", + "observed_primary_signal": "role_order", + "observed_signal_ids": [ + "role_order" + ], + "primary_expectation_met": true, + "seed": 4121 + }, + { + "audit_status": "FAIL", + "case_sha256": "132739f8e130a177843ebf49911f6bc1a651fd9d6bebcc7626349b46cba0a230", + "expected_primary_signal": "missing_eos", + "fault_id": "template.missing_eos", + "kind": "fault", + "observed_primary_signal": "missing_eos", + "observed_signal_ids": [ + "missing_eos" + ], + "primary_expectation_met": true, + "seed": 4121 + }, + { + "audit_status": "FAIL", + "case_sha256": "ce87a3034e82cf4a6a60d443c45e3b3f743dda499d5d9f9780e7a2c1c5684797", + "expected_primary_signal": "prompt_token_supervised", + "fault_id": "mask.prompt_supervised", + "kind": "fault", + "observed_primary_signal": "prompt_token_supervised", + "observed_signal_ids": [ + "prompt_token_supervised" + ], + "primary_expectation_met": true, + "seed": 4121 + }, + { + "audit_status": "FAIL", + "case_sha256": "f0a8e6bd6ab4b6e333c76d79f714c5e7982d06970115eb388b2e85e62ac193ac", + "expected_primary_signal": "assistant_fully_truncated", + "fault_id": "truncation.answer_removed", + "kind": "fault", + "observed_primary_signal": "assistant_fully_truncated", + "observed_signal_ids": [ + "assistant_fully_truncated" + ], + "primary_expectation_met": true, + "seed": 4121 + }, + { + "audit_status": "PASS", + "case_sha256": "b19b7835729877ee3cd7e875db97cd8c588013b4f9fd561c0f2c23ad1a0a5957", + "expected_primary_signal": "exact_duplicate", + "fault_id": "data.exact_duplicate", + "kind": "fault", + "observed_primary_signal": "exact_duplicate", + "observed_signal_ids": [ + "exact_duplicate" + ], + "primary_expectation_met": true, + "seed": 4121 + }, + { + "audit_status": "FAIL", + "case_sha256": "a92282fc9334146eabf38a1f71dac494f50cd8d7920167ae30d3a0df67e4e0f1", + "expected_primary_signal": "prompt_conflict", + "fault_id": "data.prompt_conflict", + "kind": "fault", + "observed_primary_signal": "prompt_conflict", + "observed_signal_ids": [ + "prompt_conflict" + ], + "primary_expectation_met": true, + "seed": 4121 + }, + { + "audit_status": "FAIL", + "case_sha256": "be34c3262556af1cc65bad81e503c05e353245281822d4fa2f5cb49c90d55a93", + "expected_primary_signal": "train_eval_leakage", + "fault_id": "data.train_eval_leakage", + "kind": "fault", + "observed_primary_signal": "train_eval_leakage", + "observed_signal_ids": [ + "train_eval_leakage" + ], + "primary_expectation_met": true, + "seed": 4121 + }, + { + "audit_status": "FAIL", + "case_sha256": "a84d79305a8d11bc1c6c30ab20914475e8aa868783e2e65cb861495f2dc23e71", + "expected_primary_signal": "secret_pattern", + "fault_id": "privacy.secret_pattern", + "kind": "fault", + "observed_primary_signal": "secret_pattern", + "observed_signal_ids": [ + "secret_pattern" + ], + "primary_expectation_met": true, + "seed": 4121 + }, + { + "audit_status": "PASS", + "case_sha256": "3821ce93d22fad6a9b4aca153694f02360ec8e48dfb556cb152cbd06fd21408e", + "expected_primary_signal": null, + "fault_id": null, + "kind": "clean_control", + "observed_primary_signal": null, + "observed_signal_ids": [], + "primary_expectation_met": true, + "seed": 4122 + }, + { + "audit_status": "FAIL", + "case_sha256": "e9289e9839d261916b40faf59bf063d16f8938d5e2b0d7d2786b840092b0464d", + "expected_primary_signal": "invalid_role", + "fault_id": "schema.invalid_role", + "kind": "fault", + "observed_primary_signal": "invalid_role", + "observed_signal_ids": [ + "invalid_role", + "role_order" + ], + "primary_expectation_met": true, + "seed": 4122 + }, + { + "audit_status": "FAIL", + "case_sha256": "dd5ac2328dd6fad35f0f26e5d720c40aff8b3cc5016d150d4448570ca3c5dbfb", + "expected_primary_signal": "role_order", + "fault_id": "schema.role_order", + "kind": "fault", + "observed_primary_signal": "role_order", + "observed_signal_ids": [ + "role_order" + ], + "primary_expectation_met": true, + "seed": 4122 + }, + { + "audit_status": "FAIL", + "case_sha256": "8961470746342496bfebc6fd31724c058a1d8fab7c5820e2b42a6a9df9349f52", + "expected_primary_signal": "missing_eos", + "fault_id": "template.missing_eos", + "kind": "fault", + "observed_primary_signal": "missing_eos", + "observed_signal_ids": [ + "missing_eos" + ], + "primary_expectation_met": true, + "seed": 4122 + }, + { + "audit_status": "FAIL", + "case_sha256": "a8117a923396d5a8ed32f5ba72a44d0155ca24fbfc4463da9054a8395b0111ce", + "expected_primary_signal": "prompt_token_supervised", + "fault_id": "mask.prompt_supervised", + "kind": "fault", + "observed_primary_signal": "prompt_token_supervised", + "observed_signal_ids": [ + "prompt_token_supervised" + ], + "primary_expectation_met": true, + "seed": 4122 + }, + { + "audit_status": "FAIL", + "case_sha256": "64c642d6283e190e279d3b1318a15c4d7e0c9f6a0cc9530aae2e17119e23fdec", + "expected_primary_signal": "assistant_fully_truncated", + "fault_id": "truncation.answer_removed", + "kind": "fault", + "observed_primary_signal": "assistant_fully_truncated", + "observed_signal_ids": [ + "assistant_fully_truncated" + ], + "primary_expectation_met": true, + "seed": 4122 + }, + { + "audit_status": "PASS", + "case_sha256": "8fd20fa9287512bbad63bba5b89f085d2424c42f57b513350d7d7c86e665dd1b", + "expected_primary_signal": "exact_duplicate", + "fault_id": "data.exact_duplicate", + "kind": "fault", + "observed_primary_signal": "exact_duplicate", + "observed_signal_ids": [ + "exact_duplicate" + ], + "primary_expectation_met": true, + "seed": 4122 + }, + { + "audit_status": "FAIL", + "case_sha256": "c3f5855921f6652f2cb1e83e7c08361d25743f3402beecb047ebb6c5878465d4", + "expected_primary_signal": "prompt_conflict", + "fault_id": "data.prompt_conflict", + "kind": "fault", + "observed_primary_signal": "prompt_conflict", + "observed_signal_ids": [ + "prompt_conflict" + ], + "primary_expectation_met": true, + "seed": 4122 + }, + { + "audit_status": "FAIL", + "case_sha256": "a40d2bf83ad856814248cfc88b99e405c2ea85860e129e5ae5f571eb0d49f48b", + "expected_primary_signal": "train_eval_leakage", + "fault_id": "data.train_eval_leakage", + "kind": "fault", + "observed_primary_signal": "train_eval_leakage", + "observed_signal_ids": [ + "train_eval_leakage" + ], + "primary_expectation_met": true, + "seed": 4122 + }, + { + "audit_status": "FAIL", + "case_sha256": "50366833aa7c4c849fda79972226f8498c530e8e6f5571b1ca019c5fd9cbf42e", + "expected_primary_signal": "secret_pattern", + "fault_id": "privacy.secret_pattern", + "kind": "fault", + "observed_primary_signal": "secret_pattern", + "observed_signal_ids": [ + "secret_pattern" + ], + "primary_expectation_met": true, + "seed": 4122 + }, + { + "audit_status": "PASS", + "case_sha256": "646fcb1faa8b3f173249614e8cca1446b62a791587830432888729e308b014bd", + "expected_primary_signal": null, + "fault_id": null, + "kind": "clean_control", + "observed_primary_signal": null, + "observed_signal_ids": [], + "primary_expectation_met": true, + "seed": 4123 + }, + { + "audit_status": "FAIL", + "case_sha256": "798c037ab41b6b924eca3a20e953c579dfcfe2ef7d0530b5a47f1e9de97e7e85", + "expected_primary_signal": "invalid_role", + "fault_id": "schema.invalid_role", + "kind": "fault", + "observed_primary_signal": "invalid_role", + "observed_signal_ids": [ + "invalid_role", + "role_order" + ], + "primary_expectation_met": true, + "seed": 4123 + }, + { + "audit_status": "FAIL", + "case_sha256": "2585d8d02cd838a171dc13e2b955cb71cc9607e10f06591717e73ccca1e9cf94", + "expected_primary_signal": "role_order", + "fault_id": "schema.role_order", + "kind": "fault", + "observed_primary_signal": "role_order", + "observed_signal_ids": [ + "role_order" + ], + "primary_expectation_met": true, + "seed": 4123 + }, + { + "audit_status": "FAIL", + "case_sha256": "9277cd52353d1b28a444f9b6045186649dad9dfeaf1116c135d55f7bcf488384", + "expected_primary_signal": "missing_eos", + "fault_id": "template.missing_eos", + "kind": "fault", + "observed_primary_signal": "missing_eos", + "observed_signal_ids": [ + "missing_eos" + ], + "primary_expectation_met": true, + "seed": 4123 + }, + { + "audit_status": "FAIL", + "case_sha256": "6873d3a4ca6385ff12812af03fe126911aa1fa14e0ae17d184a8bd2a74ccd9a6", + "expected_primary_signal": "prompt_token_supervised", + "fault_id": "mask.prompt_supervised", + "kind": "fault", + "observed_primary_signal": "prompt_token_supervised", + "observed_signal_ids": [ + "prompt_token_supervised" + ], + "primary_expectation_met": true, + "seed": 4123 + }, + { + "audit_status": "FAIL", + "case_sha256": "27f70b80eb6ae96505a6762ba8b2b65785cccdcae231ffa5f63d5137140e1fb2", + "expected_primary_signal": "assistant_fully_truncated", + "fault_id": "truncation.answer_removed", + "kind": "fault", + "observed_primary_signal": "assistant_fully_truncated", + "observed_signal_ids": [ + "assistant_fully_truncated" + ], + "primary_expectation_met": true, + "seed": 4123 + }, + { + "audit_status": "PASS", + "case_sha256": "0dadafaf18d0a35bb5ba6ac9bd893de8ee9b07bfe9b7664b8553833b73b403e4", + "expected_primary_signal": "exact_duplicate", + "fault_id": "data.exact_duplicate", + "kind": "fault", + "observed_primary_signal": "exact_duplicate", + "observed_signal_ids": [ + "exact_duplicate" + ], + "primary_expectation_met": true, + "seed": 4123 + }, + { + "audit_status": "FAIL", + "case_sha256": "1896027123bc3c71daaf579952bc43f74b1a9adcf8c06c7c6d81f0484e28c408", + "expected_primary_signal": "prompt_conflict", + "fault_id": "data.prompt_conflict", + "kind": "fault", + "observed_primary_signal": "prompt_conflict", + "observed_signal_ids": [ + "prompt_conflict" + ], + "primary_expectation_met": true, + "seed": 4123 + }, + { + "audit_status": "FAIL", + "case_sha256": "26d786e5bff4924acf6a01c4dea6a838e52ebc085b244eeaba9bec64fd01ef3a", + "expected_primary_signal": "train_eval_leakage", + "fault_id": "data.train_eval_leakage", + "kind": "fault", + "observed_primary_signal": "train_eval_leakage", + "observed_signal_ids": [ + "train_eval_leakage" + ], + "primary_expectation_met": true, + "seed": 4123 + }, + { + "audit_status": "FAIL", + "case_sha256": "8bf5be2d60c97115f3122b6b54a0c34db8db3c1f289a5ff8a63ad9a41d6bd7d8", + "expected_primary_signal": "secret_pattern", + "fault_id": "privacy.secret_pattern", + "kind": "fault", + "observed_primary_signal": "secret_pattern", + "observed_signal_ids": [ + "secret_pattern" + ], + "primary_expectation_met": true, + "seed": 4123 + }, + { + "audit_status": "PASS", + "case_sha256": "0e732215ed158974025756a085608e5b0366d01daa2cc2ab777526123e171cdb", + "expected_primary_signal": null, + "fault_id": null, + "kind": "clean_control", + "observed_primary_signal": null, + "observed_signal_ids": [], + "primary_expectation_met": true, + "seed": 4124 + }, + { + "audit_status": "FAIL", + "case_sha256": "9da5a47e584420e62d34db505129dbe1e015c0af68507dbdae475543ba95fabb", + "expected_primary_signal": "invalid_role", + "fault_id": "schema.invalid_role", + "kind": "fault", + "observed_primary_signal": "invalid_role", + "observed_signal_ids": [ + "invalid_role", + "role_order" + ], + "primary_expectation_met": true, + "seed": 4124 + }, + { + "audit_status": "FAIL", + "case_sha256": "1da487165c1ff68558b35a2c160f87c5f419400493b849c20e829a1ade0c8c4c", + "expected_primary_signal": "role_order", + "fault_id": "schema.role_order", + "kind": "fault", + "observed_primary_signal": "role_order", + "observed_signal_ids": [ + "role_order" + ], + "primary_expectation_met": true, + "seed": 4124 + }, + { + "audit_status": "FAIL", + "case_sha256": "807ee62284217dde4564c6f01138254b8c6a05d3aff7de9be88ed9b4c38e5b91", + "expected_primary_signal": "missing_eos", + "fault_id": "template.missing_eos", + "kind": "fault", + "observed_primary_signal": "missing_eos", + "observed_signal_ids": [ + "missing_eos" + ], + "primary_expectation_met": true, + "seed": 4124 + }, + { + "audit_status": "FAIL", + "case_sha256": "ef9197610c8b70abfeb05e1e6b51188c5cbbc3a9ea1cf076a29ee9a3ec1c17f8", + "expected_primary_signal": "prompt_token_supervised", + "fault_id": "mask.prompt_supervised", + "kind": "fault", + "observed_primary_signal": "prompt_token_supervised", + "observed_signal_ids": [ + "prompt_token_supervised" + ], + "primary_expectation_met": true, + "seed": 4124 + }, + { + "audit_status": "FAIL", + "case_sha256": "8366bed91b48aa74b6394a9cd45193b30a415512f4b74c9614a974c1065c7501", + "expected_primary_signal": "assistant_fully_truncated", + "fault_id": "truncation.answer_removed", + "kind": "fault", + "observed_primary_signal": "assistant_fully_truncated", + "observed_signal_ids": [ + "assistant_fully_truncated" + ], + "primary_expectation_met": true, + "seed": 4124 + }, + { + "audit_status": "PASS", + "case_sha256": "dd87856c254b69aa6b3a0fcf348e48acd5b27b761440ad7449719a4c9495e803", + "expected_primary_signal": "exact_duplicate", + "fault_id": "data.exact_duplicate", + "kind": "fault", + "observed_primary_signal": "exact_duplicate", + "observed_signal_ids": [ + "exact_duplicate" + ], + "primary_expectation_met": true, + "seed": 4124 + }, + { + "audit_status": "FAIL", + "case_sha256": "5e846efac574b9980587f38bae8365217cf8d243a116f099771298da03fd7470", + "expected_primary_signal": "prompt_conflict", + "fault_id": "data.prompt_conflict", + "kind": "fault", + "observed_primary_signal": "prompt_conflict", + "observed_signal_ids": [ + "prompt_conflict" + ], + "primary_expectation_met": true, + "seed": 4124 + }, + { + "audit_status": "FAIL", + "case_sha256": "66521150c766c7a89380e135d37c7cf1e8407eea97b744bd4aa5a28b73197f2d", + "expected_primary_signal": "train_eval_leakage", + "fault_id": "data.train_eval_leakage", + "kind": "fault", + "observed_primary_signal": "train_eval_leakage", + "observed_signal_ids": [ + "train_eval_leakage" + ], + "primary_expectation_met": true, + "seed": 4124 + }, + { + "audit_status": "FAIL", + "case_sha256": "33554dcacf18b1dfb30f7a890f81857fa0bb325b99509304dce565b509457bde", + "expected_primary_signal": "secret_pattern", + "fault_id": "privacy.secret_pattern", + "kind": "fault", + "observed_primary_signal": "secret_pattern", + "observed_signal_ids": [ + "secret_pattern" + ], + "primary_expectation_met": true, + "seed": 4124 + }, + { + "audit_status": "PASS", + "case_sha256": "ace9cc5fc923ef4aba52567657243163556880685e5f325a58d5c9b99601cead", + "expected_primary_signal": null, + "fault_id": null, + "kind": "clean_control", + "observed_primary_signal": null, + "observed_signal_ids": [], + "primary_expectation_met": true, + "seed": 4125 + }, + { + "audit_status": "FAIL", + "case_sha256": "553e11b53c6a7557bbe3fa51ec8709fb7cba2ffe8433e6c8e5c2e2c9ebc0db4f", + "expected_primary_signal": "invalid_role", + "fault_id": "schema.invalid_role", + "kind": "fault", + "observed_primary_signal": "invalid_role", + "observed_signal_ids": [ + "invalid_role", + "role_order" + ], + "primary_expectation_met": true, + "seed": 4125 + }, + { + "audit_status": "FAIL", + "case_sha256": "14253fb73cd8cfd707ef19085cdce71069a08384d497dad464b71010e5326bd0", + "expected_primary_signal": "role_order", + "fault_id": "schema.role_order", + "kind": "fault", + "observed_primary_signal": "role_order", + "observed_signal_ids": [ + "role_order" + ], + "primary_expectation_met": true, + "seed": 4125 + }, + { + "audit_status": "FAIL", + "case_sha256": "f2538bd621b5ebac17f84979c03f1e8fc801ff1e73cb91401d95e2c2ef6348b9", + "expected_primary_signal": "missing_eos", + "fault_id": "template.missing_eos", + "kind": "fault", + "observed_primary_signal": "missing_eos", + "observed_signal_ids": [ + "missing_eos" + ], + "primary_expectation_met": true, + "seed": 4125 + }, + { + "audit_status": "FAIL", + "case_sha256": "a5dd4f010bc624d9b5a42f6353c7689b4956dec905c0e84780c389f7575030b9", + "expected_primary_signal": "prompt_token_supervised", + "fault_id": "mask.prompt_supervised", + "kind": "fault", + "observed_primary_signal": "prompt_token_supervised", + "observed_signal_ids": [ + "prompt_token_supervised" + ], + "primary_expectation_met": true, + "seed": 4125 + }, + { + "audit_status": "FAIL", + "case_sha256": "209d24608f720906feb78176794b9e65befb79f207f4a19aefd29d5db6eed22e", + "expected_primary_signal": "assistant_fully_truncated", + "fault_id": "truncation.answer_removed", + "kind": "fault", + "observed_primary_signal": "assistant_fully_truncated", + "observed_signal_ids": [ + "assistant_fully_truncated" + ], + "primary_expectation_met": true, + "seed": 4125 + }, + { + "audit_status": "PASS", + "case_sha256": "915213d0822e9e4ec6d2f7cc5a3a9c4ca468b6c24552ffb2da9f705d8134f778", + "expected_primary_signal": "exact_duplicate", + "fault_id": "data.exact_duplicate", + "kind": "fault", + "observed_primary_signal": "exact_duplicate", + "observed_signal_ids": [ + "exact_duplicate" + ], + "primary_expectation_met": true, + "seed": 4125 + }, + { + "audit_status": "FAIL", + "case_sha256": "fe528d268c95566c4c5c8a95dc50e8d3e3f9b4e3aa2e8d93e32ced13f3c6a8c9", + "expected_primary_signal": "prompt_conflict", + "fault_id": "data.prompt_conflict", + "kind": "fault", + "observed_primary_signal": "prompt_conflict", + "observed_signal_ids": [ + "prompt_conflict" + ], + "primary_expectation_met": true, + "seed": 4125 + }, + { + "audit_status": "FAIL", + "case_sha256": "785c17546e76870cfd6ae94f8f6590d0c568c78cebaf2483bba0608e9a842589", + "expected_primary_signal": "train_eval_leakage", + "fault_id": "data.train_eval_leakage", + "kind": "fault", + "observed_primary_signal": "train_eval_leakage", + "observed_signal_ids": [ + "train_eval_leakage" + ], + "primary_expectation_met": true, + "seed": 4125 + }, + { + "audit_status": "FAIL", + "case_sha256": "773337be8c79756471b1b682f76cf8de910b7e1a8d2e8e2e20199cf299d02279", + "expected_primary_signal": "secret_pattern", + "fault_id": "privacy.secret_pattern", + "kind": "fault", + "observed_primary_signal": "secret_pattern", + "observed_signal_ids": [ + "secret_pattern" + ], + "primary_expectation_met": true, + "seed": 4125 + }, + { + "audit_status": "PASS", + "case_sha256": "9da9642c4ad79e0a58bae384083b6124b0d91116060ad898e7a41981f7dedf01", + "expected_primary_signal": null, + "fault_id": null, + "kind": "clean_control", + "observed_primary_signal": null, + "observed_signal_ids": [], + "primary_expectation_met": true, + "seed": 4126 + }, + { + "audit_status": "FAIL", + "case_sha256": "ecadf36b9421e676893b7ad6a9cea00b75264a242b9b3012c0587f83bef3461b", + "expected_primary_signal": "invalid_role", + "fault_id": "schema.invalid_role", + "kind": "fault", + "observed_primary_signal": "invalid_role", + "observed_signal_ids": [ + "invalid_role", + "role_order" + ], + "primary_expectation_met": true, + "seed": 4126 + }, + { + "audit_status": "FAIL", + "case_sha256": "45b7685fe0cc4f18ebd7f1329d53af30a39c86eb3efbe15a7dbb3908959d2d2f", + "expected_primary_signal": "role_order", + "fault_id": "schema.role_order", + "kind": "fault", + "observed_primary_signal": "role_order", + "observed_signal_ids": [ + "role_order" + ], + "primary_expectation_met": true, + "seed": 4126 + }, + { + "audit_status": "FAIL", + "case_sha256": "c7dc803be7016e987b3acb087bbc0a386f019f24a16194511b7d3b017e3c302f", + "expected_primary_signal": "missing_eos", + "fault_id": "template.missing_eos", + "kind": "fault", + "observed_primary_signal": "missing_eos", + "observed_signal_ids": [ + "missing_eos" + ], + "primary_expectation_met": true, + "seed": 4126 + }, + { + "audit_status": "FAIL", + "case_sha256": "10746fd1b74a5d41f7b033be72a8af5485fe0894356d6338f60c677094a14037", + "expected_primary_signal": "prompt_token_supervised", + "fault_id": "mask.prompt_supervised", + "kind": "fault", + "observed_primary_signal": "prompt_token_supervised", + "observed_signal_ids": [ + "prompt_token_supervised" + ], + "primary_expectation_met": true, + "seed": 4126 + }, + { + "audit_status": "FAIL", + "case_sha256": "f08d878405300c29302418b5f7733b8ec67c59c73665f37338b95c0db9a1af44", + "expected_primary_signal": "assistant_fully_truncated", + "fault_id": "truncation.answer_removed", + "kind": "fault", + "observed_primary_signal": "assistant_fully_truncated", + "observed_signal_ids": [ + "assistant_fully_truncated" + ], + "primary_expectation_met": true, + "seed": 4126 + }, + { + "audit_status": "PASS", + "case_sha256": "23078ab97b51def96a5987736b0481e6b793bb566d5365c8c8a18c59fb10cdd9", + "expected_primary_signal": "exact_duplicate", + "fault_id": "data.exact_duplicate", + "kind": "fault", + "observed_primary_signal": "exact_duplicate", + "observed_signal_ids": [ + "exact_duplicate" + ], + "primary_expectation_met": true, + "seed": 4126 + }, + { + "audit_status": "FAIL", + "case_sha256": "960c6f8ae94e6f923e0c721d773fc5848ee30b3ccdb7bda446d86eb15d581d8c", + "expected_primary_signal": "prompt_conflict", + "fault_id": "data.prompt_conflict", + "kind": "fault", + "observed_primary_signal": "prompt_conflict", + "observed_signal_ids": [ + "prompt_conflict" + ], + "primary_expectation_met": true, + "seed": 4126 + }, + { + "audit_status": "FAIL", + "case_sha256": "4415b1cc4f4978c9b92215595e47d8ac5e2c9f4dbcf6e588a3bf8f2a6d336129", + "expected_primary_signal": "train_eval_leakage", + "fault_id": "data.train_eval_leakage", + "kind": "fault", + "observed_primary_signal": "train_eval_leakage", + "observed_signal_ids": [ + "train_eval_leakage" + ], + "primary_expectation_met": true, + "seed": 4126 + }, + { + "audit_status": "FAIL", + "case_sha256": "4aa4ca025171bbf93dc618b5e1131de9ef7abcf9131c1a09cfca4037b2903b1f", + "expected_primary_signal": "secret_pattern", + "fault_id": "privacy.secret_pattern", + "kind": "fault", + "observed_primary_signal": "secret_pattern", + "observed_signal_ids": [ + "secret_pattern" + ], + "primary_expectation_met": true, + "seed": 4126 + }, + { + "audit_status": "PASS", + "case_sha256": "5e7619217bacbe800be15894fdf3ad12349b56d141e49ef144bcdf6cb50225be", + "expected_primary_signal": null, + "fault_id": null, + "kind": "clean_control", + "observed_primary_signal": null, + "observed_signal_ids": [], + "primary_expectation_met": true, + "seed": 4127 + }, + { + "audit_status": "FAIL", + "case_sha256": "63817644c1806993b5dc199b4d44b7c3ab2b4e461cc48813f2ea75c390fbe46f", + "expected_primary_signal": "invalid_role", + "fault_id": "schema.invalid_role", + "kind": "fault", + "observed_primary_signal": "invalid_role", + "observed_signal_ids": [ + "invalid_role", + "role_order" + ], + "primary_expectation_met": true, + "seed": 4127 + }, + { + "audit_status": "FAIL", + "case_sha256": "daae4347d57097c8d85acd647ce66ef4f78b34c74a9fe42897061fb42131186d", + "expected_primary_signal": "role_order", + "fault_id": "schema.role_order", + "kind": "fault", + "observed_primary_signal": "role_order", + "observed_signal_ids": [ + "role_order" + ], + "primary_expectation_met": true, + "seed": 4127 + }, + { + "audit_status": "FAIL", + "case_sha256": "4baaad4dadfda8644d706b6e00f2a63ae34938453c6407723f4e7f61ce0b4215", + "expected_primary_signal": "missing_eos", + "fault_id": "template.missing_eos", + "kind": "fault", + "observed_primary_signal": "missing_eos", + "observed_signal_ids": [ + "missing_eos" + ], + "primary_expectation_met": true, + "seed": 4127 + }, + { + "audit_status": "FAIL", + "case_sha256": "6cef96304628e0b8d323b973f2ed16f8c10847236d7967304aea9d24f6deafb0", + "expected_primary_signal": "prompt_token_supervised", + "fault_id": "mask.prompt_supervised", + "kind": "fault", + "observed_primary_signal": "prompt_token_supervised", + "observed_signal_ids": [ + "prompt_token_supervised" + ], + "primary_expectation_met": true, + "seed": 4127 + }, + { + "audit_status": "FAIL", + "case_sha256": "824850b3d6d24275733316e6c951de783edff63a4b2491af16e95a2c1c423c40", + "expected_primary_signal": "assistant_fully_truncated", + "fault_id": "truncation.answer_removed", + "kind": "fault", + "observed_primary_signal": "assistant_fully_truncated", + "observed_signal_ids": [ + "assistant_fully_truncated" + ], + "primary_expectation_met": true, + "seed": 4127 + }, + { + "audit_status": "PASS", + "case_sha256": "cbf93ed6573a5a1460bb7244b8cdd161a79fa551b97d607e4ae7ab9d61623f0f", + "expected_primary_signal": "exact_duplicate", + "fault_id": "data.exact_duplicate", + "kind": "fault", + "observed_primary_signal": "exact_duplicate", + "observed_signal_ids": [ + "exact_duplicate" + ], + "primary_expectation_met": true, + "seed": 4127 + }, + { + "audit_status": "FAIL", + "case_sha256": "e1aa015ebdea2f62b0860894478b8e8e361de2be22015e03ef6a5317e7251978", + "expected_primary_signal": "prompt_conflict", + "fault_id": "data.prompt_conflict", + "kind": "fault", + "observed_primary_signal": "prompt_conflict", + "observed_signal_ids": [ + "prompt_conflict" + ], + "primary_expectation_met": true, + "seed": 4127 + }, + { + "audit_status": "FAIL", + "case_sha256": "70027ac367e4429645b880be15be4275d85941606f1470af9d2569bda75aade7", + "expected_primary_signal": "train_eval_leakage", + "fault_id": "data.train_eval_leakage", + "kind": "fault", + "observed_primary_signal": "train_eval_leakage", + "observed_signal_ids": [ + "train_eval_leakage" + ], + "primary_expectation_met": true, + "seed": 4127 + }, + { + "audit_status": "FAIL", + "case_sha256": "c426fd8580ee5c0310ebb04b9baa069dd75d9ffa79861548e5f09d387e05841f", + "expected_primary_signal": "secret_pattern", + "fault_id": "privacy.secret_pattern", + "kind": "fault", + "observed_primary_signal": "secret_pattern", + "observed_signal_ids": [ + "secret_pattern" + ], + "primary_expectation_met": true, + "seed": 4127 + }, + { + "audit_status": "PASS", + "case_sha256": "475557e6e5b5df96038968ea2ab8b172d8d49a1aa512bb9a17abcb367ca7706d", + "expected_primary_signal": null, + "fault_id": null, + "kind": "clean_control", + "observed_primary_signal": null, + "observed_signal_ids": [], + "primary_expectation_met": true, + "seed": 4128 + }, + { + "audit_status": "FAIL", + "case_sha256": "1ce4a2d128a6db9d2ac617a7b1982efa8039cf0c0ec5e380549fbd8c2e241c01", + "expected_primary_signal": "invalid_role", + "fault_id": "schema.invalid_role", + "kind": "fault", + "observed_primary_signal": "invalid_role", + "observed_signal_ids": [ + "invalid_role", + "role_order" + ], + "primary_expectation_met": true, + "seed": 4128 + }, + { + "audit_status": "FAIL", + "case_sha256": "6de06e32e00ad58457e5bc1eb4730fd100220389bdd27ed657bc0e24e569f29b", + "expected_primary_signal": "role_order", + "fault_id": "schema.role_order", + "kind": "fault", + "observed_primary_signal": "role_order", + "observed_signal_ids": [ + "role_order" + ], + "primary_expectation_met": true, + "seed": 4128 + }, + { + "audit_status": "FAIL", + "case_sha256": "f3965d909f2d7c07c8fd8b3a8d12108fadbfe4cd9aa2e851f6fbdb19733d6bf8", + "expected_primary_signal": "missing_eos", + "fault_id": "template.missing_eos", + "kind": "fault", + "observed_primary_signal": "missing_eos", + "observed_signal_ids": [ + "missing_eos" + ], + "primary_expectation_met": true, + "seed": 4128 + }, + { + "audit_status": "FAIL", + "case_sha256": "4bc2f2dd8fcc9ac78ff1b0376448cc431c79d0575b3150c4b4962367db3b3985", + "expected_primary_signal": "prompt_token_supervised", + "fault_id": "mask.prompt_supervised", + "kind": "fault", + "observed_primary_signal": "prompt_token_supervised", + "observed_signal_ids": [ + "prompt_token_supervised" + ], + "primary_expectation_met": true, + "seed": 4128 + }, + { + "audit_status": "FAIL", + "case_sha256": "19bab18fcca10c08100c5062529bb3ba8bcc4a57d326ef73650b7dc04fb1275f", + "expected_primary_signal": "assistant_fully_truncated", + "fault_id": "truncation.answer_removed", + "kind": "fault", + "observed_primary_signal": "assistant_fully_truncated", + "observed_signal_ids": [ + "assistant_fully_truncated" + ], + "primary_expectation_met": true, + "seed": 4128 + }, + { + "audit_status": "PASS", + "case_sha256": "2b04dcadb2ad9c50b0ffb6e520007b34bddba9058ebd5ecc8c663c8682fd3d40", + "expected_primary_signal": "exact_duplicate", + "fault_id": "data.exact_duplicate", + "kind": "fault", + "observed_primary_signal": "exact_duplicate", + "observed_signal_ids": [ + "exact_duplicate" + ], + "primary_expectation_met": true, + "seed": 4128 + }, + { + "audit_status": "FAIL", + "case_sha256": "b2c2213bd18a1ef9041b4e947ac9475157e35ab565fec770d3387376b5c1b7d2", + "expected_primary_signal": "prompt_conflict", + "fault_id": "data.prompt_conflict", + "kind": "fault", + "observed_primary_signal": "prompt_conflict", + "observed_signal_ids": [ + "prompt_conflict" + ], + "primary_expectation_met": true, + "seed": 4128 + }, + { + "audit_status": "FAIL", + "case_sha256": "0dbf42390ad6d9794a99a0eec77c477d59cbbffd9f8fff778e4615dbf0f5503a", + "expected_primary_signal": "train_eval_leakage", + "fault_id": "data.train_eval_leakage", + "kind": "fault", + "observed_primary_signal": "train_eval_leakage", + "observed_signal_ids": [ + "train_eval_leakage" + ], + "primary_expectation_met": true, + "seed": 4128 + }, + { + "audit_status": "FAIL", + "case_sha256": "897eb6d79aae29b5abc0ac4ecb501a80a382197dbb3fddfd738b53b915188108", + "expected_primary_signal": "secret_pattern", + "fault_id": "privacy.secret_pattern", + "kind": "fault", + "observed_primary_signal": "secret_pattern", + "observed_signal_ids": [ + "secret_pattern" + ], + "primary_expectation_met": true, + "seed": 4128 + }, + { + "audit_status": "PASS", + "case_sha256": "f35dc4fe626aeea7bd1c6310cccc2b8ece5eb3334550ae9f4bca91cb9179c039", + "expected_primary_signal": null, + "fault_id": null, + "kind": "clean_control", + "observed_primary_signal": null, + "observed_signal_ids": [], + "primary_expectation_met": true, + "seed": 4129 + }, + { + "audit_status": "FAIL", + "case_sha256": "d8282f6d967d6c102301b3a28ae74115602da73dfdba04219ca185c42a2674da", + "expected_primary_signal": "invalid_role", + "fault_id": "schema.invalid_role", + "kind": "fault", + "observed_primary_signal": "invalid_role", + "observed_signal_ids": [ + "invalid_role", + "role_order" + ], + "primary_expectation_met": true, + "seed": 4129 + }, + { + "audit_status": "FAIL", + "case_sha256": "17e0f95ecef38e00a38defb0d746c11a559c76878503d2f8039f7e1908150286", + "expected_primary_signal": "role_order", + "fault_id": "schema.role_order", + "kind": "fault", + "observed_primary_signal": "role_order", + "observed_signal_ids": [ + "role_order" + ], + "primary_expectation_met": true, + "seed": 4129 + }, + { + "audit_status": "FAIL", + "case_sha256": "05dc1d34b33f4785c3a8897d0802fcb1e399ee9b775c68e4b79680619d50d1dd", + "expected_primary_signal": "missing_eos", + "fault_id": "template.missing_eos", + "kind": "fault", + "observed_primary_signal": "missing_eos", + "observed_signal_ids": [ + "missing_eos" + ], + "primary_expectation_met": true, + "seed": 4129 + }, + { + "audit_status": "FAIL", + "case_sha256": "68da4319e9632b2a92423b2c329b8e0dde098195603b1e11a5ce838401978ad4", + "expected_primary_signal": "prompt_token_supervised", + "fault_id": "mask.prompt_supervised", + "kind": "fault", + "observed_primary_signal": "prompt_token_supervised", + "observed_signal_ids": [ + "prompt_token_supervised" + ], + "primary_expectation_met": true, + "seed": 4129 + }, + { + "audit_status": "FAIL", + "case_sha256": "b25884df6ff0293203fb96d29d45830d52b34a01461bd9c745f16435dfb2cc70", + "expected_primary_signal": "assistant_fully_truncated", + "fault_id": "truncation.answer_removed", + "kind": "fault", + "observed_primary_signal": "assistant_fully_truncated", + "observed_signal_ids": [ + "assistant_fully_truncated" + ], + "primary_expectation_met": true, + "seed": 4129 + }, + { + "audit_status": "PASS", + "case_sha256": "93bfdf6bfe90cc4be235df021647054e5b23cc62e36287d4ca3f8ad03603f86a", + "expected_primary_signal": "exact_duplicate", + "fault_id": "data.exact_duplicate", + "kind": "fault", + "observed_primary_signal": "exact_duplicate", + "observed_signal_ids": [ + "exact_duplicate" + ], + "primary_expectation_met": true, + "seed": 4129 + }, + { + "audit_status": "FAIL", + "case_sha256": "607cfaf4697510a4104b4c7b2647e5e6d31a528ff4bacebcbbf717c915ffd7e5", + "expected_primary_signal": "prompt_conflict", + "fault_id": "data.prompt_conflict", + "kind": "fault", + "observed_primary_signal": "prompt_conflict", + "observed_signal_ids": [ + "prompt_conflict" + ], + "primary_expectation_met": true, + "seed": 4129 + }, + { + "audit_status": "FAIL", + "case_sha256": "a173586ab768cbb3f554912647a6238bb2b2ce715b69ffa183ea8628ea219471", + "expected_primary_signal": "train_eval_leakage", + "fault_id": "data.train_eval_leakage", + "kind": "fault", + "observed_primary_signal": "train_eval_leakage", + "observed_signal_ids": [ + "train_eval_leakage" + ], + "primary_expectation_met": true, + "seed": 4129 + }, + { + "audit_status": "FAIL", + "case_sha256": "1e7d276589dea9ec13d9e741913e6e3e55c305deda7cdcebaf6152ea76bf7ee2", + "expected_primary_signal": "secret_pattern", + "fault_id": "privacy.secret_pattern", + "kind": "fault", + "observed_primary_signal": "secret_pattern", + "observed_signal_ids": [ + "secret_pattern" + ], + "primary_expectation_met": true, + "seed": 4129 + } + ], + "gates": { + "clean_false_positive_rate_at_most_10_percent": true, + "critical_fault_recall_100_percent": true, + "expected_and_observed_primary_recorded": true, + "fresh_process_byte_reproducible": true, + "macro_recall_at_least_90_percent": true, + "raw_synthetic_text_absent": true + }, + "integrity": { + "algorithm": "sha256", + "canonical_payload_sha256": "eb55eaa16a492881ff1bcec8812b2ef37bb2ed6055b6b90a1da408386f632e3a" + }, + "per_fault": [ + { + "case_count": 30, + "detected_count": 30, + "expected_signal": "invalid_role", + "fault_id": "schema.invalid_role", + "recall": 1.0, + "severity": "error" + }, + { + "case_count": 30, + "detected_count": 30, + "expected_signal": "role_order", + "fault_id": "schema.role_order", + "recall": 1.0, + "severity": "error" + }, + { + "case_count": 30, + "detected_count": 30, + "expected_signal": "missing_eos", + "fault_id": "template.missing_eos", + "recall": 1.0, + "severity": "error" + }, + { + "case_count": 30, + "detected_count": 30, + "expected_signal": "prompt_token_supervised", + "fault_id": "mask.prompt_supervised", + "recall": 1.0, + "severity": "critical" + }, + { + "case_count": 30, + "detected_count": 30, + "expected_signal": "assistant_fully_truncated", + "fault_id": "truncation.answer_removed", + "recall": 1.0, + "severity": "critical" + }, + { + "case_count": 30, + "detected_count": 30, + "expected_signal": "exact_duplicate", + "fault_id": "data.exact_duplicate", + "recall": 1.0, + "severity": "warning" + }, + { + "case_count": 30, + "detected_count": 30, + "expected_signal": "prompt_conflict", + "fault_id": "data.prompt_conflict", + "recall": 1.0, + "severity": "error" + }, + { + "case_count": 30, + "detected_count": 30, + "expected_signal": "train_eval_leakage", + "fault_id": "data.train_eval_leakage", + "recall": 1.0, + "severity": "critical" + }, + { + "case_count": 30, + "detected_count": 30, + "expected_signal": "secret_pattern", + "fault_id": "privacy.secret_pattern", + "recall": 1.0, + "severity": "critical" + } + ], + "protocol": { + "audit_config": { + "max_file_bytes": 16777216, + "max_line_bytes": 1048576, + "max_records": 100000, + "require_assistant_mask": true, + "scan_privacy": true + }, + "faults": [ + { + "expected_signal": "invalid_role", + "fault_id": "schema.invalid_role", + "severity": "error" + }, + { + "expected_signal": "role_order", + "fault_id": "schema.role_order", + "severity": "error" + }, + { + "expected_signal": "missing_eos", + "fault_id": "template.missing_eos", + "severity": "error" + }, + { + "expected_signal": "prompt_token_supervised", + "fault_id": "mask.prompt_supervised", + "severity": "critical" + }, + { + "expected_signal": "assistant_fully_truncated", + "fault_id": "truncation.answer_removed", + "severity": "critical" + }, + { + "expected_signal": "exact_duplicate", + "fault_id": "data.exact_duplicate", + "severity": "warning" + }, + { + "expected_signal": "prompt_conflict", + "fault_id": "data.prompt_conflict", + "severity": "error" + }, + { + "expected_signal": "train_eval_leakage", + "fault_id": "data.train_eval_leakage", + "severity": "critical" + }, + { + "expected_signal": "secret_pattern", + "fault_id": "privacy.secret_pattern", + "severity": "critical" + } + ], + "seed_range": { + "count": 30, + "first": 4100, + "last": 4129 + } + }, + "provenance": { + "implementation_git_commit": "b0f168ab4d6a959d4e28611e345a250c14ca2ea9", + "protocol_git_commit": "d4c74cefca6729124142635dc8893b5e3ddec2d3", + "python_implementation": "CPython", + "python_version": "3.11" + }, + "schema_version": "sftguard.confirmatory-fault-matrix.v1", + "status": "PASS", + "summary": { + "case_count": 300, + "clean_control_count": 30, + "clean_false_positive_count": 0, + "clean_false_positive_rate": 0.0, + "fault_case_count": 270, + "macro_recall": 1.0 + }, + "tool": { + "name": "sftguard", + "version": "0.1.0" + } +} diff --git a/evidence/v0.1.0-confirmatory.manifest.json b/evidence/v0.1.0-confirmatory.manifest.json new file mode 100644 index 0000000..8111161 --- /dev/null +++ b/evidence/v0.1.0-confirmatory.manifest.json @@ -0,0 +1,12 @@ +{ + "artifact_file_sha256": "961b2842d7791f6f3af7bec5bce6105e433b68d41bf6f7b82a9f3a98e1f695ab", + "artifact_integrity_sha256": "eb55eaa16a492881ff1bcec8812b2ef37bb2ed6055b6b90a1da408386f632e3a", + "artifact_path": "evidence/v0.1.0-confirmatory.json", + "implementation_git_commit": "b0f168ab4d6a959d4e28611e345a250c14ca2ea9", + "integrity": { + "algorithm": "sha256", + "canonical_payload_sha256": "39badf5b3dc862852b890b347f6015127ee33bfb295bd1d6a022cfd028bd2131" + }, + "protocol_git_commit": "d4c74cefca6729124142635dc8893b5e3ddec2d3", + "schema_version": "sftguard.confirmatory-manifest.v1" +} diff --git a/pyproject.toml b/pyproject.toml index 8462fd3..b237418 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,6 @@ keywords = ["fine-tuning", "llm", "regression-testing", "sft"] classifiers = [ "Development Status :: 3 - Alpha", "Environment :: Console", - "License :: OSI Approved :: Apache Software License", "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.11", diff --git a/web/README.md b/web/README.md index c6aa2a7..f44e85b 100644 --- a/web/README.md +++ b/web/README.md @@ -37,5 +37,6 @@ and explicitly marked unverified because this static viewer does not reproduce the Python canonical-JSON hash calculation. Imports are read into the current browser tab only and are neither uploaded nor -stored. The three sample artifacts are intentionally illustrative placeholders, -not confirmatory project evidence. +stored. The default entry is adapted from the checked-in v0.1 confirmatory +artifact; the regression, failing fault-matrix, and abstaining audit entries are +clearly labelled illustrative UI fixtures. diff --git a/web/src/App.tsx b/web/src/App.tsx index 15d177e..100b83e 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -571,7 +571,13 @@ export default function App() { {source.report.title} - {source.origin === "local import" ? "Local import" : "Illustrative sample"} + + {source.origin === "local import" + ? "Local import" + : source.origin === "checked-in evidence" + ? "Confirmatory evidence" + : "Illustrative sample"} + ))} diff --git a/web/src/sampleReports.ts b/web/src/sampleReports.ts index 10829f2..af3237b 100644 --- a/web/src/sampleReports.ts +++ b/web/src/sampleReports.ts @@ -1,15 +1,21 @@ import auditJson from "./data/sample-audit.json"; import faultJson from "./data/sample-fault.json"; import regressionJson from "./data/sample-regression.json"; +import confirmatoryJson from "../../evidence/v0.1.0-confirmatory.json"; import { parseReport, type SftGuardReport } from "./reportSchema"; export interface ReportSource { report: SftGuardReport; - origin: "checked-in sample" | "local import"; + origin: "checked-in evidence" | "checked-in sample" | "local import"; sourceLabel: string; } export const SAMPLE_REPORTS: ReportSource[] = [ + { + report: parseReport(confirmatoryJson), + origin: "checked-in evidence", + sourceLabel: "v0.1 confirmatory evidence", + }, { report: parseReport(regressionJson), origin: "checked-in sample", From 36c96a45871d3a9e0a20fff17338f64205dcb520 Mon Sep 17 00:00:00 2001 From: Muhammad Labeeb Aryan <93521146+Labeeb2339@users.noreply.github.com> Date: Sun, 19 Jul 2026 20:52:25 +0800 Subject: [PATCH 3/4] Fix Python CI and finalize v0.1 metadata --- .gitignore | 1 + CHANGELOG.md | 5 +++++ CITATION.cff | 1 + pyproject.toml | 2 +- 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index e2949c9..11f206f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ __pycache__/ *.py[cod] *.egg-info/ .pytest_cache/ +.pytest-tmp/ .pytest-tmp-*/ .ruff_cache/ .coverage diff --git a/CHANGELOG.md b/CHANGELOG.md index 62f7619..745ccdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ All notable changes to SFTGuard are documented here. The project follows ## [Unreleased] +## [0.1.0] - 2026-07-19 + ### Added - Preregistered v0.1 product and fault-injection protocols. @@ -19,3 +21,6 @@ All notable changes to SFTGuard are documented here. The project follows - Static browser-local report viewer for native SFTGuard artifacts. - Cross-platform CI, CodeQL, dependency audits, Pages deployment, community templates, citation metadata, and security documentation. + +[Unreleased]: https://github.com/Labeeb2339/sftguard/compare/v0.1.0...HEAD +[0.1.0]: https://github.com/Labeeb2339/sftguard/releases/tag/v0.1.0 diff --git a/CITATION.cff b/CITATION.cff index 4006410..96ce0c1 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -12,6 +12,7 @@ repository-code: https://github.com/Labeeb2339/sftguard url: https://github.com/Labeeb2339/sftguard license: Apache-2.0 version: 0.1.0 +date-released: 2026-07-19 abstract: >- SFTGuard is a local-first correctness and regression gate for supervised fine-tuning pipelines. It audits bounded pipeline evidence and emits diff --git a/pyproject.toml b/pyproject.toml index b237418..d37ca64 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,7 +47,7 @@ where = ["src"] [tool.pytest.ini_options] testpaths = ["tests/python"] -addopts = "-q --basetemp=.pytest_cache/basetemp" +addopts = "-q --basetemp=.pytest-tmp-basetemp" [tool.ruff] target-version = "py311" From b0364d65f8084d25e861e01798846ff4acb05e44 Mon Sep 17 00:00:00 2001 From: Muhammad Labeeb Aryan <93521146+Labeeb2339@users.noreply.github.com> Date: Sun, 19 Jul 2026 20:55:18 +0800 Subject: [PATCH 4/4] Harden public sharing materials --- README.md | 5 +++-- docs/ANNOUNCEMENT.md | 7 +++---- .../sftguard-confirmatory-dashboard.png | Bin 62580 -> 294632 bytes 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 6d344bd..c1fb4de 100644 --- a/README.md +++ b/README.md @@ -222,8 +222,9 @@ claim that 300 varied real-world fine-tuning pipelines were evaluated. - Findings contain signal categories and bounded locations, not dataset text. - Reports contain aggregate values and hashes; hashes are identifiers, **not anonymization**. -- The repository contains no model weights, adapters, real datasets, prompts, - responses, or credentials. +- The repository contains no model weights, adapters, private datasets, + private prompt/response content, or real credentials. Example conversations + and credential-shaped test strings are synthetic. - Do not publish a real report until you have followed the [safe-sharing checklist](docs/SHARING.md). diff --git a/docs/ANNOUNCEMENT.md b/docs/ANNOUNCEMENT.md index fcf303a..7eb1f31 100644 --- a/docs/ANNOUNCEMENT.md +++ b/docs/ANNOUNCEMENT.md @@ -53,11 +53,10 @@ current self-promotion rules before posting and do not mass-post identical copy. ## Optional author line -Use this only where age/context is relevant; the technical work should stand on -its own: +Use this only if you want to identify yourself as a student; otherwise omit it: -> I’m a Form 3 student in Sarawak, Malaysia, and I’m using this project to learn -> how to make AI tooling claims reproducible instead of relying on demos alone. +> I’m a student learning how to make AI tooling claims reproducible instead of +> relying on demos alone. ## Before posting diff --git a/docs/assets/sftguard-confirmatory-dashboard.png b/docs/assets/sftguard-confirmatory-dashboard.png index acbe4eecb829915fe0aa61e63863fd7f9190947e..1f43b0b248da6e2e0072fa0c8f0afaf1bb5a2578 100644 GIT binary patch literal 294632 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

>trNy`l~3+Bv1n9s}P1w0^(`akb-n6P*o6 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

UfB|Lo`i%jqhuf4NhpJ=iK8!dywN)fboDpV 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

|5f+lw$|lvH!1vJ`3qRRy-dUduJ?0SH{6T~ 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 N002ovPDHLkV1lFT3Ml{p literal 62580 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

hHR!?50WRfGJCgmXKKr@ij%`RCp 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 NKg|(A8u$8t69CrV0Vx0g